/* KLayout Layout Viewer Copyright (C) 2006-2021 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dbDXFWriter.h" #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" #include "tlStream.h" #include "tlUtils.h" #include #include namespace db { // --------------------------------------------------------------------------------- // DXFWriter implementation DXFWriter::DXFWriter () : mp_stream (0), m_progress (tl::to_string (tr ("Writing DXF file")), 10000) { m_progress.set_format (tl::to_string (tr ("%.0f MB"))); m_progress.set_unit (1024 * 1024); } DXFWriter & DXFWriter::operator<<(const char *s) { mp_stream->put(s, strlen(s)); return *this; } DXFWriter & DXFWriter::operator<<(const std::string &s) { mp_stream->put(s.c_str(), s.size()); return *this; } DXFWriter & DXFWriter::operator<<(endl_tag) { #ifdef _WIN32 *this << "\r\n"; #else *this << "\n"; #endif return *this; } void DXFWriter::write (const db::Layout &layout, const db::Cell &cref, const std::set &cell_set, const std::vector > &layers, double sf) { // instances for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { // write only instances to selected cells if (cell_set.find (inst->cell_index ()) != cell_set.end ()) { m_progress.set (mp_stream->pos ()); // resolve instance arrays for (db::Cell::cell_inst_array_type::iterator pp = inst->begin (); ! pp.at_end (); ++pp) { // convert the transformation into DXF's notation db::DCplxTrans t (inst->complex_trans (*pp)); db::DVector d (t.disp()); *this << 0 << endl << "INSERT" << endl; *this << 8 << endl << 0 << endl; // required by TrueView *this << 2 << endl << layout.cell_name (inst->cell_index ()) << endl; *this << 10 << endl << d.x () * sf << endl; *this << 20 << endl << d.y () * sf << endl; *this << 41 << endl << t.mag () << endl; *this << 42 << endl << (t.is_mirror () ? -t.mag () : t.mag ()) << endl; *this << 50 << endl << t.angle () << endl; } } } // shapes for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { m_layer = l->second; write_texts (layout, cref, l->first, sf); write_polygons (layout, cref, l->first, sf); write_paths (layout, cref, l->first, sf); write_boxes (layout, cref, l->first, sf); m_progress.set (mp_stream->pos ()); } } void DXFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { m_options = options.get_options (); mp_stream = &stream; // compute the scale factor double sf = options.scale_factor () * layout.dbu (); std::vector > layers; options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignName); std::set cell_set; options.get_cells (layout, cell_set, layers); // header *this << 0 << endl << "SECTION" << endl; *this << 2 << endl << "HEADER" << endl; // fake a dummy AutoCAD version (apparent required by some tools) *this << 9 << endl << "$ACADVER" << endl; *this << 1 << endl << "AC1006" << endl; *this << 0 << endl << "ENDSEC" << endl; // layer table *this << 0 << endl << "SECTION" << endl; *this << 2 << endl << "TABLES" << endl; *this << 0 << endl << "TABLE" << endl; *this << 2 << endl << "LAYER" << endl; *this << 70 << endl << layers.size () << endl; // colors are simply numbered starting from 1 currently, linestyle is "CONTINUOUS" currently. int color = 1; std::string linestyle = "CONTINUOUS"; for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { *this << 0 << endl << "LAYER" << endl; *this << 70 << endl << 0 << endl; // flags: seems to be required by some tools *this << 62 << endl << color << endl; // color *this << 6 << endl << linestyle << endl; // line style *this << 2 << endl; emit_layer (l->second); color += 1; } *this << 0 << endl << "ENDTAB" << endl; *this << 0 << endl << "ENDSEC" << endl; // create a cell index vector sorted bottom-up std::vector cells; cells.reserve (cell_set.size ()); const db::Cell *top_cell = 0; for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) { if (cell_set.find (*cell) != cell_set.end ()) { // determine if the current cell is a top-level cell bool is_top_cell = true; std::set caller_cells; layout.cell (*cell).collect_caller_cells(caller_cells); for (std::set ::const_iterator cc = caller_cells.begin (); cc != caller_cells.end () && is_top_cell; ++cc) { if (cell_set.find (*cc) != cell_set.end ()) { is_top_cell = false; } } if (is_top_cell) { if (top_cell) { throw tl::Exception (tl::to_string (tr ("Top-level cell is not unique - DXF can only store a single top cell"))); } else { top_cell = &layout.cell (*cell); } } else { cells.push_back (*cell); } } } // body *this << 0 << endl << "SECTION" << endl; *this << 2 << endl << "BLOCKS" << endl; for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); // cell body const db::Cell &cref (layout.cell (*cell)); *this << 0 << endl << "BLOCK" << endl; *this << 2 << endl << layout.cell_name (*cell) << endl; // this has been determined empirically with TrueView: int flags = (layout.cell_name (*cell) [0] == '*' ? 1 : 0); *this << 70 << endl << flags << endl; // base point x, y *this << 10 << endl << 0.0 << endl; *this << 20 << endl << 0.0 << endl; // write cell write (layout, cref, cell_set, layers, sf); // end of cell *this << 0 << endl << "ENDBLK" << endl; } *this << 0 << endl << "ENDSEC" << endl; // entities (empty) *this << 0 << endl << "SECTION" << endl; *this << 2 << endl << "ENTITIES" << endl; m_progress.set (mp_stream->pos ()); // write top cell if (top_cell) { write (layout, *top_cell, cell_set, layers, sf); } *this << 0 << endl << "ENDSEC" << endl; // end of file *this << 0 << endl << "EOF" << endl; m_progress.set (mp_stream->pos ()); } void DXFWriter::emit_layer(const db::LayerProperties &lp) { if (lp.layer == 0 && lp.datatype == 0 && lp.name == "L0D0") { // zero layer *this << "0" << endl; } else { *this << lp.name << endl; } } void DXFWriter::write_texts (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) { db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Texts)); while (! shape.at_end ()) { m_progress.set (mp_stream->pos ()); db::Vector p (shape->text_trans ().disp ()); // use MTEXT if the text contains line feeds. // split the text into chunks with less than 250 characters std::string s = shape->text_string (); std::vector chunks; chunks.push_back (std::string ()); bool use_mtext = false; for (const char *c = s.c_str (); *c; c++) { if (*c == '\n') { use_mtext = true; if (chunks.back ().size () > 248) { chunks.push_back (std::string ()); } chunks.back () += "\\P"; } else if ((unsigned char)*c >= (unsigned char)32) { if (chunks.back ().size () > 249) { chunks.push_back (std::string ()); } chunks.back () += *c; } } if (use_mtext) { *this << 0 << endl << "MTEXT" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 10 << endl << p.x () * sf << endl; *this << 20 << endl << p.y () * sf << endl; *this << 40 << endl << shape->text_size () * sf << endl; int v = 1; db::HAlign halign = shape->text_halign (); if (halign == db::HAlignLeft) { v += 0; } else if (halign == db::HAlignCenter) { v += 1; } else if (halign == db::HAlignRight) { v += 2; } db::VAlign valign = shape->text_valign (); if (valign == db::VAlignTop) { v += 0; } else if (valign == db::VAlignCenter) { v += 3; } else if (valign == db::VAlignBottom) { v += 6; } *this << 71 << endl << v << endl; *this << 72 << endl << 0 << endl; for (size_t i = 0; i + 1 < chunks.size (); ++i) { *this << 3 << endl << chunks [i] << endl; } *this << 1 << endl << chunks.back () << endl; *this << 50 << endl << (shape->text_trans ().rot () % 4) * 90.0 << endl; } else { *this << 0 << endl << "TEXT" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 10 << endl << p.x () * sf << endl; *this << 20 << endl << p.y () * sf << endl; *this << 40 << endl << shape->text_size () * sf << endl; *this << 1 << endl << chunks.front () << endl; *this << 50 << endl << (shape->text_trans ().rot () % 4) * 90.0 << endl; db::HAlign halign = shape->text_halign (); if (halign == db::HAlignLeft) { *this << 72 << endl << 0 << endl; } else if (halign == db::HAlignCenter) { *this << 72 << endl << 1 << endl; } else if (halign == db::HAlignRight) { *this << 72 << endl << 2 << endl; } *this << 11 << endl << p.x () * sf << endl; *this << 21 << endl << p.y () * sf << endl; db::VAlign valign = shape->text_valign (); if (valign == db::VAlignTop) { *this << 73 << endl << 3 << endl; } else if (valign == db::VAlignCenter) { *this << 73 << endl << 2 << endl; } else if (valign == db::VAlignBottom) { *this << 73 << endl << 0 << endl; } } ++shape; } } void DXFWriter::write_polygons (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) { db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Polygons)); while (! shape.at_end ()) { m_progress.set (mp_stream->pos ()); db::Polygon poly; shape->polygon (poly); write_polygon (poly, sf); ++shape; } } void DXFWriter::write_polygon (const db::Polygon &polygon, double sf) { if (m_options.polygon_mode == 0) { for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { *this << 0 << endl << "POLYLINE" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 70 << endl << 1 << endl; *this << 40 << endl << 0.0 << endl; *this << 41 << endl << 0.0 << endl; *this << 66 << endl << 1 << endl; // required by TrueView for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) { *this << 0 << endl << "VERTEX" << endl; *this << 8 << endl; emit_layer (m_layer); // required by TrueView *this << 10 << endl << (*p).x () * sf << endl; *this << 20 << endl << (*p).y () * sf << endl; } *this << 0 << endl << "SEQEND" << endl; } } else if (m_options.polygon_mode == 1) { for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { *this << 0 << endl << "LWPOLYLINE" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 90 << endl << polygon.contour (0).size () << endl; *this << 70 << endl << 1 << endl; *this << 43 << endl << 0.0 << endl; for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) { *this << 10 << endl << (*p).x () * sf << endl; *this << 20 << endl << (*p).y () * sf << endl; } } } else if (m_options.polygon_mode == 2) { if (polygon.holes () > 0) { // resolve holes or merge polygon as a preparation step for split_polygon which only works properly // on merged polygons ... std::vector polygons; db::EdgeProcessor ep; ep.insert_sequence (polygon.begin_edge ()); db::PolygonContainer pc (polygons); db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/); db::SimpleMerge op; ep.process (out, op); for (std::vector::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { write_polygon (*p, sf); } } else if (polygon.vertices () > 4) { std::vector polygons; db::split_polygon (polygon, polygons); for (std::vector::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { write_polygon (*p, sf); } } else if (polygon.vertices () >= 3) { *this << 0 << endl << "SOLID" << endl; *this << 8 << endl; emit_layer (m_layer); double x [4], y [4]; unsigned int i = 0; for (db::Polygon::polygon_contour_iterator p = polygon.begin_hull (); p != polygon.end_hull (); ++p) { x [i] = (*p).x () * sf; y [i] = (*p).y () * sf; ++i; } if (i == 4) { *this << 10 << endl << x[0] << endl; *this << 20 << endl << y[0] << endl; *this << 11 << endl << x[1] << endl; *this << 21 << endl << y[1] << endl; *this << 12 << endl << x[3] << endl; *this << 22 << endl << y[3] << endl; *this << 13 << endl << x[2] << endl; *this << 23 << endl << y[2] << endl; } else { *this << 10 << endl << x[0] << endl; *this << 20 << endl << y[0] << endl; *this << 11 << endl << x[1] << endl; *this << 21 << endl << y[1] << endl; *this << 12 << endl << x[2] << endl; *this << 22 << endl << y[2] << endl; *this << 13 << endl << x[2] << endl; *this << 23 << endl << y[2] << endl; } } } else if (m_options.polygon_mode == 3) { *this << 0 << endl << "HATCH" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 70 << endl << 1 << endl; // solid fill *this << 91 << endl << polygon.holes () + 1 << endl; for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { *this << 92 << endl << 3 << endl; // external polyline *this << 72 << endl << 0 << endl; // has bulge flag *this << 73 << endl << 1 << endl; // closed flag *this << 93 << endl << polygon.contour (c).size () << endl; for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) { *this << 10 << endl << (*p).x () * sf << endl; *this << 20 << endl << (*p).y () * sf << endl; } } } else if (m_options.polygon_mode == 4) { //-------------------------------------------------------------------------------------- // Last modified by: Kazzz-S on October 21, 2018 (newly added) // // Description: When importing a DXF file comprising POLYLINEs or LWPOLYLINEs into // Abaqus CAE, they are forcibly converted to points! // *** This is a "specification" of Abaqus CAE. *** // In contrast, LINEs are kept as lines, which will be then assembled into // polygonal objects internally if required. //-------------------------------------------------------------------------------------- for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) { db::Polygon::polygon_contour_iterator q = p + 1; if (q == polygon.contour (c).end ()) { q = polygon.contour (c).begin (); } *this << 0 << endl << "LINE" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 66 << endl << 1 << endl; // required by TrueView *this << 10 << endl << (*p).x () * sf << endl; *this << 20 << endl << (*p).y () * sf << endl; *this << 11 << endl << (*q).x () * sf << endl; *this << 21 << endl << (*q).y () * sf << endl; } } } } void DXFWriter::write_boxes (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) { db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Boxes)); while (! shape.at_end ()) { // TODO: write as SOLID's? m_progress.set (mp_stream->pos ()); db::Polygon p (shape->bbox ()); write_polygon (p, sf); ++shape; } } void DXFWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) { db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Paths)); while (! shape.at_end ()) { m_progress.set (mp_stream->pos ()); size_t npts = 0; for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) { ++npts; } if (shape->round_path () && npts == 1) { db::Point pp (*shape->begin_point ()); *this << 0 << endl << "CIRCLE" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 10 << endl << pp.x () * sf << endl; *this << 20 << endl << pp.y () * sf << endl; *this << 40 << endl << shape->path_width () * sf * 0.5 << endl; } else if (shape->round_path ()) { // emit round paths as polygons db::Polygon poly; shape->polygon (poly); write_polygon (poly, sf); } else { *this << 0 << endl << "POLYLINE" << endl; *this << 8 << endl; emit_layer (m_layer); *this << 70 << endl << 0 << endl; *this << 40 << endl << shape->path_width () * sf << endl; *this << 41 << endl << shape->path_width () * sf << endl; *this << 66 << endl << 1 << endl; // required by TrueView size_t n = 0; std::pair ext = shape->path_extensions (); db::DPoint plast; for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) { db::DPoint pp (db::DPoint (*p) * sf); // correct extensions if required if (n == 0 && ext.first != 0) { db::Shape::point_iterator q = shape->begin_point (); db::DPoint pnext; if (q != shape->end_point ()) { ++q; if (q != shape->end_point ()) { pnext = db::DPoint (*q) * sf; } } db::DVector v (pnext - pp); double lv = v.double_length (); if (lv >= 1e-6) { v = v * (1.0 / lv); pp = pp + v * (-ext.first * sf); } } else if (n == npts - 1 && ext.second != 0) { db::DVector v (pp - plast); double lv = v.double_length (); if (lv >= 1e-6) { v = v * (1.0 / lv); pp = pp + v * (ext.second * sf); } } *this << 0 << endl << "VERTEX" << endl; *this << 8 << endl; emit_layer (m_layer); // required by TrueView *this << 10 << endl << pp.x () << endl; *this << 20 << endl << pp.y () << endl; plast = pp; ++n; } *this << 0 << endl << "SEQEND" << endl; } ++shape; } } }