klayout/src/plugins/streamers/dxf/db_plugin/dbDXFWriter.cc

658 lines
20 KiB
C++

/*
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 <time.h>
#include <string.h>
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 <db::cell_index_type> &cell_set, const std::vector <std::pair <unsigned int, db::LayerProperties> > &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 <std::pair <unsigned int, db::LayerProperties> >::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<DXFWriterOptions> ();
mp_stream = &stream;
// compute the scale factor
double sf = options.scale_factor () * layout.dbu ();
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignName);
std::set <db::cell_index_type> 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 <std::pair <unsigned int, db::LayerProperties> >::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 <db::cell_index_type> 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 <db::cell_index_type> caller_cells;
layout.cell (*cell).collect_caller_cells(caller_cells);
for (std::set <db::cell_index_type>::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<db::cell_index_type>::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<std::string> 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<db::Polygon> 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<db::Polygon>::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
write_polygon (*p, sf);
}
} else if (polygon.vertices () > 4) {
std::vector <db::Polygon> polygons;
db::split_polygon (polygon, polygons);
for (std::vector<db::Polygon>::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<db::Coord, db::Coord> 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;
}
}
}