klayout/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc

1185 lines
30 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2018 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 "dbGerberImporter.h"
#include "dbGerberImportData.h"
#include "dbGerberDrillFileReader.h"
#include "dbRS274XReader.h"
#include "dbReader.h"
#include "dbStream.h"
#include "tlStream.h"
#include "tlString.h"
#include "tlString.h"
#include "tlLog.h"
#include "tlFileUtils.h"
#include "dbShapeProcessor.h"
#include <cmath>
#include <cctype>
namespace db
{
// ---------------------------------------------------------------------------------------
// Some utilities
static void parse_format (const std::string &format, int &l, int &t, bool &tz)
{
tl::Extractor ex (format.c_str ());
try {
l = -1; t = -1;
tz = true;
if (! ex.at_end ()) {
if (! ex.test ("*")) {
ex.try_read (l);
}
ex.expect (":");
if (! ex.test ("*")) {
ex.try_read (t);
}
if (ex.test ("T") || ex.test ("t")) {
tz = true;
} else if (ex.test ("L") || ex.test ("l")) {
tz = false;
} else {
if (t < 0) {
tz = false;
}
}
ex.expect_end ();
}
} catch (tl::Exception &ex) {
throw tl::Exception (tl::to_string (tr ("Gerber format specification '%s' is invalid: %s")), format, ex.msg ());
}
}
static std::string format_to_string (int l, int t, bool tz)
{
std::string r;
if (l >= 0) {
r += tl::to_string (l);
} else {
r += "*";
}
r += ":";
if (t >= 0) {
r += tl::to_string (t);
} else {
r += "*";
}
r += (tz ? "T" : "L");
return r;
}
// ---------------------------------------------------------------------------------------
// Implementation of GerberFile
GerberFileReader::GerberFileReader ()
: m_circle_points (64), m_digits_before (-1), m_digits_after (-1), m_omit_leading_zeroes (true),
m_merge (false), m_inverse (false),
m_dbu (0.001), m_unit (1000.0),
m_rot (0.0), m_s (1.0), m_ox (0.0), m_oy (0.0),
m_mx (false), m_my (false),
m_orot (0.0), m_os (1.0), m_omx (false), m_omy (false),
m_ep (true /*report progress*/),
mp_layout (0), mp_top_cell (0), mp_stream (0),
m_progress (tl::to_string (tr ("Reading Gerber file")), 10000)
{
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
m_progress.set_unit (1024 * 1024);
}
bool
GerberFileReader::accepts (tl::TextInputStream &stream)
{
mp_stream = &stream;
bool result = does_accept ();
mp_stream = 0;
return result;
}
GerberMetaData
GerberFileReader::scan (tl::TextInputStream &stream)
{
mp_stream = &stream;
mp_layout = 0;
mp_top_cell = 0;
m_target_layers.clear ();
GerberMetaData meta_data;
try {
meta_data = do_scan();
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ()));
}
mp_stream = 0;
return meta_data;
}
void
GerberFileReader::read (tl::TextInputStream &stream, db::Layout &layout, db::Cell &cell, const std::vector <unsigned int> &targets)
{
GraphicsState state;
state.global_trans = m_global_trans;
swap_graphics_state (state);
mp_stream = &stream;
mp_layout = &layout;
mp_top_cell = &cell;
m_target_layers = targets;
try {
do_read ();
} catch (tl::BreakException &) {
throw;
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ()));
}
flush ();
mp_stream = 0;
m_target_layers.clear ();
}
void
GerberFileReader::set_format_string (const std::string &format)
{
int l = -1, t = -1;
bool tz = true;
parse_format (format, l, t, tz);
set_format (l, t, tz);
}
std::string
GerberFileReader::format_string () const
{
return format_to_string (m_digits_before, m_digits_after, m_omit_leading_zeroes);
}
void
GerberFileReader::warn (const std::string &warning)
{
tl::warn << warning << tl::to_string (tr (" in line ")) << mp_stream->line_number () << tl::to_string (tr (" (file ")) << mp_stream->source () << ")";
}
void
GerberFileReader::error (const std::string &error)
{
tl::error << error << tl::to_string (tr (" in line ")) << mp_stream->line_number () << tl::to_string (tr (" (file ")) << mp_stream->source () << ")";
}
void
GerberFileReader::fatal (const std::string &error)
{
throw tl::Exception (error);
}
double
GerberFileReader::accuracy() const
{
return m_unit / pow (10.0, m_digits_after);
}
double
GerberFileReader::read_coord (tl::Extractor &ex)
{
ex.skip ();
int sign = 1;
if (*ex == '+') {
++ex;
} else if (*ex == '-') {
sign = -1;
++ex;
}
double number = 0.0;
int ndigits = 0;
bool has_dot = false;
while (! ex.at_end () && (isdigit (*ex) || *ex == '.')) {
if (*ex == '.') {
has_dot = true;
ndigits = 0;
} else {
number = number * 10.0 + (*ex - '0');
++ndigits;
}
++ex;
}
if (has_dot) {
number /= pow (10.0, ndigits);
} else if (m_omit_leading_zeroes) {
if (m_digits_after < 0) {
error (tl::to_string (tr ("Undefined number of digits - format missing")));
}
number /= pow (10.0, m_digits_after);
} else {
if (m_digits_before < 0) {
error (tl::to_string (tr ("Undefined number of digits - format missing")));
}
number /= pow (10.0, ndigits - m_digits_before);
}
return number * m_unit * sign;
}
void
GerberFileReader::swap_graphics_state (GraphicsState &state)
{
std::swap (m_inverse, state.inverse);
std::swap (m_global_trans, state.global_trans);
std::swap (m_s, state.m_s);
std::swap (m_mx, state.m_mx);
std::swap (m_my, state.m_my);
std::swap (m_ox, state.m_ox);
std::swap (m_oy, state.m_oy);
std::swap (m_rot, state.m_rot);
std::swap (m_os, state.m_os);
std::swap (m_omx, state.m_omx);
std::swap (m_omy, state.m_omy);
std::swap (m_orot, state.m_orot);
std::swap (m_lines, state.lines);
std::swap (m_polygons, state.polygons);
std::swap (m_clear_polygons, state.clear_polygons);
std::swap (m_displacements, state.displacements);
}
void
GerberFileReader::push_state (const std::string &token)
{
m_graphics_stack.push_back (GraphicsState ());
swap_graphics_state (m_graphics_stack.back ());
m_graphics_stack.back ().token = token;
}
std::string
GerberFileReader::pop_state ()
{
std::string token;
if (! m_graphics_stack.empty ()) {
swap_graphics_state (m_graphics_stack.back ());
token = m_graphics_stack.back ().token;
}
m_graphics_stack.pop_back ();
return token;
}
bool
GerberFileReader::graphics_stack_empty () const
{
return m_graphics_stack.empty ();
}
db::DCplxTrans
GerberFileReader::local_trans () const
{
// TODO: is this order correct?
db::DCplxTrans lt = db::DCplxTrans (m_s, m_rot, false, db::DVector (m_ox, m_oy));
if (m_mx) {
lt *= db::DCplxTrans (db::DTrans (db::FTrans::m0));
}
if (m_my) {
lt *= db::DCplxTrans (db::DTrans (db::FTrans::m90));
}
return lt;
}
db::DCplxTrans
GerberFileReader::object_trans () const
{
db::DCplxTrans ot = db::DCplxTrans (m_os, m_orot, false, db::DVector ());
if (m_omx) {
ot *= db::DCplxTrans (db::DTrans (db::FTrans::m0));
}
if (m_omy) {
ot *= db::DCplxTrans (db::DTrans (db::FTrans::m90));
}
return ot;
}
void
GerberFileReader::produce_line (const db::DPath &p, bool clear)
{
db::DCplxTrans t = global_trans () * db::DCplxTrans (1.0 / dbu ()) * local_trans ();
// Ignore clear paths for now - they cannot be subtracted from anything.
// Clear is just provided for completeness.
if (clear) {
return;
}
process_clear_polygons ();
for (std::vector<db::DVector>::const_iterator d = m_displacements.begin (); d != m_displacements.end (); ++d) {
m_lines.push_back (db::Path ());
m_lines.back() = db::Path (p.transformed (t * db::DCplxTrans (*d)));
}
}
void
GerberFileReader::produce_polygon (const db::DPolygon &p, bool clear)
{
db::DCplxTrans t = global_trans () * db::DCplxTrans (1.0 / dbu ()) * local_trans ();
if (! clear) {
process_clear_polygons ();
}
for (std::vector<db::DVector>::const_iterator d = m_displacements.begin (); d != m_displacements.end (); ++d) {
db::Polygon *poly = 0;
if (clear) {
m_clear_polygons.push_back (db::Polygon ());
poly = &m_clear_polygons.back ();
} else {
m_polygons.push_back (db::Polygon ());
poly = &m_polygons.back ();
}
*poly = db::Polygon (p.transformed (t * db::DCplxTrans (*d)));
}
}
void
GerberFileReader::process_clear_polygons ()
{
if (! m_clear_polygons.empty ()) {
std::vector<db::Polygon> input;
m_polygons.swap (input);
m_ep.boolean (input, m_clear_polygons, m_polygons, db::BooleanOp::ANotB, false, true);
m_clear_polygons.clear ();
}
}
void
GerberFileReader::collect (db::Region &region)
{
process_clear_polygons ();
if (m_merge) {
std::vector<db::Polygon> merged_polygons;
m_ep.merge (m_polygons, merged_polygons, 0, false /*don't resolve holes*/);
m_polygons.swap (merged_polygons);
}
for (std::vector<db::Polygon>::const_iterator p = m_polygons.begin (); p != m_polygons.end (); ++p) {
region.insert (*p);
}
for (std::vector<db::Path>::const_iterator p = m_lines.begin (); p != m_lines.end (); ++p) {
region.insert (*p);
}
m_polygons.clear ();
m_lines.clear ();
}
void
GerberFileReader::flush ()
{
process_clear_polygons ();
if (m_merge) {
std::vector<db::Polygon> merged_polygons;
m_ep.merge (m_polygons, merged_polygons, 0, false /*don't resolve holes*/);
m_polygons.swap (merged_polygons);
}
for (std::vector <unsigned int>::const_iterator t = m_target_layers.begin (); t != m_target_layers.end (); ++t) {
db::Shapes &shapes = mp_top_cell->shapes (*t);
for (std::vector<db::Polygon>::const_iterator p = m_polygons.begin (); p != m_polygons.end (); ++p) {
shapes.insert (*p);
}
for (std::vector<db::Path>::const_iterator p = m_lines.begin (); p != m_lines.end (); ++p) {
shapes.insert (*p);
}
}
m_polygons.clear ();
m_lines.clear ();
}
void
GerberFileReader::progress_checkpoint ()
{
if (mp_stream) {
m_progress.set (mp_stream->raw_stream ().pos ());
}
}
void
GerberFileReader::step_and_repeat (const std::vector <db::DVector> &displacements)
{
reset_step_and_repeat ();
if (! displacements.empty ()) {
m_displacements = displacements;
}
}
void
GerberFileReader::reset_step_and_repeat ()
{
m_displacements.clear ();
m_displacements.push_back (db::DVector ());
}
// ---------------------------------------------------------------------------------------
// Implementation of GerberFile
GerberFile::GerberFile ()
: m_circle_points (-1), m_merge_mode (-1), m_digits_before (-1), m_digits_after (-1), m_omit_leading_zeroes (true)
{
// .. nothing yet ..
}
void
GerberFile::set_format_string (const std::string &format)
{
int l = -1, t = -1;
bool tz = true;
parse_format (format, l, t, tz);
set_format (l, t, tz);
}
std::string
GerberFile::format_string () const
{
return format_to_string (digits_before (), digits_after (), omit_leading_zeroes ());
}
void
GerberFile::set_layers_string (const std::string &layers)
{
tl::Extractor ex (layers.c_str ());
while (! ex.at_end ()) {
db::LayerProperties lp;
lp.read (ex);
add_layer_spec (lp);
ex.test (",");
}
}
std::string
GerberFile::layers_string () const
{
std::string r;
for (std::vector<db::LayerProperties>::const_iterator ls = m_layer_specs.begin (); ls != m_layer_specs.end (); ++ls) {
if (! r.empty ()) {
r += ",";
}
r += ls->to_string ();
}
return r;
}
// ---------------------------------------------------------------------------------------
// Implementation of GerberImporter
// TODO: generalize this:
std::vector <tl::shared_ptr<db::GerberFileReader> > get_readers ()
{
std::vector <tl::shared_ptr<db::GerberFileReader> > readers;
readers.push_back (new db::GerberDrillFileReader ());
readers.push_back (new db::RS274XReader ());
return readers;
}
GerberImporter::GerberImporter ()
: m_cell_name ("PCB"), m_dbu (0.001), m_merge (false),
m_invert_negative_layers (false), m_border (5000),
m_circle_points (64)
{
// .. nothing yet ..
}
void
GerberImporter::load_project (const std::string &fn)
{
// use the file's absolute path as the base directory
m_dir = tl::absolute_file_path (fn);
tl::InputStream stream (fn);
tl::TextInputStream text_stream (stream);
load_project (text_stream);
}
void
GerberImporter::load_project (tl::TextInputStream &stream)
{
try {
do_load_project (stream);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ()));
}
}
GerberMetaData
GerberImporter::scan (const std::string &fn)
{
tl::InputStream stream (fn);
tl::TextInputStream text_stream (stream);
return scan (text_stream);
}
GerberMetaData
GerberImporter::scan (tl::TextInputStream &stream)
{
try {
std::vector <tl::shared_ptr<db::GerberFileReader> > readers = get_readers ();
// determine the reader to use:
for (std::vector <tl::shared_ptr<db::GerberFileReader> >::iterator r = readers.begin (); r != readers.end (); ++r) {
stream.reset ();
if ((*r)->accepts (stream)) {
return (*r)->scan (stream);
}
}
} catch (tl::Exception &ex) {
tl::warn << ex.msg ();
}
return GerberMetaData ();
}
static void read_ref_point_spec (tl::Extractor &l, std::vector<std::pair<db::DBox, db::DBox> > &ref_points, size_t n, bool pcb)
{
while (ref_points.size () < n + 1) {
ref_points.push_back (std::pair<db::DBox, db::DBox> ());
}
l.expect ("=");
double x, y;
l.read (x);
l.test (",");
l.read (y);
if (pcb) {
ref_points[n].first = db::DBox (x, y, x, y);
} else {
ref_points[n].second = db::DBox (x, y, x, y);
}
}
void
GerberImporter::do_load_project (tl::TextInputStream &stream)
{
std::vector<std::pair<db::DBox, db::DBox> > ref_points; // Reference points PCB/Layout: boxes can be "empty" ..
while (! stream.at_end ()) {
std::string lstr = stream.get_line ();
tl::Extractor l (lstr.c_str ());
// skip empty or pure comment lines
if (l.at_end () || l.test ("#")) {
continue;
}
if (l.test ("dbu")) {
l.expect ("=");
l.read (m_dbu);
if (m_dbu < 1e-6) {
throw tl::Exception (tl::to_string (tr ("Invalid database unit %g")), m_dbu);
}
} else if (l.test ("cell-name")) {
l.expect ("=");
l.read_word_or_quoted (m_cell_name);
} else if (l.test ("layer-styles")) {
l.expect ("=");
l.read_word_or_quoted (m_layer_styles, "%!.:/\\+-,=_$");
} else if (l.test ("circle-points")) {
l.expect ("=");
l.read (m_circle_points);
if (m_circle_points < 4) {
throw tl::Exception (tl::to_string (tr ("Invalid number of points for full circle (%d)")), m_circle_points);
}
} else if (l.test ("keep-path")) {
// ignored currently, kept for compatibility with prototype
l.expect ("=");
int d; l.read (d);
} else if (l.test ("border")) {
l.expect ("=");
double d;
l.read (d);
m_border = d;
} else if (l.test ("invert-negative-layers")) {
l.expect ("=");
int d;
l.read (d);
m_invert_negative_layers = d;
} else if (l.test ("merge")) {
l.expect ("=");
int d;
l.read (d);
m_merge = d;
// provided for compatibility with prototype, use ref-point instead
} else if (l.test ("p1-pcb")) {
read_ref_point_spec (l, ref_points, 0, true);
} else if (l.test ("p2-pcb")) {
read_ref_point_spec (l, ref_points, 1, true);
} else if (l.test ("p3-pcb")) {
read_ref_point_spec (l, ref_points, 2, true);
} else if (l.test ("p1-gds")) {
read_ref_point_spec (l, ref_points, 0, false);
} else if (l.test ("p2-gds")) {
read_ref_point_spec (l, ref_points, 1, false);
} else if (l.test ("p3-gds")) {
read_ref_point_spec (l, ref_points, 2, false);
} else if (l.test ("ref-point")) {
l.expect ("=");
l.expect ("(");
double x_pcb, y_pcb;
l.read (x_pcb);
l.expect (",");
l.read (y_pcb);
l.expect (")");
l.expect (",");
l.expect ("(");
double x_ly, y_ly;
l.read (x_ly);
l.expect (",");
l.read (y_ly);
l.expect (")");
ref_points.push_back (std::make_pair (db::DBox (x_pcb, y_pcb, x_pcb, y_pcb), db::DBox (x_ly, y_ly, x_ly, y_ly)));
} else if (l.test ("transformation")) {
l.expect ("=");
db::DCplxTrans gt;
l.read (gt);
m_global_trans = gt * m_global_trans;
} else if (l.test ("mirror")) {
l.expect ("=");
int d;
l.read (d);
if (d != 0) {
m_global_trans = db::DCplxTrans (db::DFTrans::m0) * m_global_trans;
}
} else if (l.test ("dir")) {
l.expect ("=");
l.read_word_or_quoted (m_dir, "%!.:/\\+-,=_$");
} else if (l.test ("format")) {
l.expect ("=");
l.read_word_or_quoted (m_format_string, "*:");
// check the string ...
int l = 0, t = 0;
bool tz = false;
parse_format (m_format_string, l, t, tz);
} else {
l.test ("file");
db::GerberFile file;
std::string fn;
l.read_word_or_quoted (fn, "%!.:/\\+-,=_$");
file.set_filename (fn);
while (! l.at_end () && ! l.test ("#")) {
if (l.test ("circle-points")) {
l.expect ("=");
int cp = -1;
l.read (cp);
if (cp < 4) {
throw tl::Exception (tl::to_string (tr ("Invalid number of points for full circle (%d)")), m_circle_points);
}
file.set_circle_points (cp);
} else if (l.test ("format")) {
l.expect ("=");
std::string f;
l.read_word_or_quoted (f, "*:");
file.set_format_string (f);
} else if (l.test ("merge")) {
l.expect ("=");
int m = 0;
l.read (m);
file.set_merge_mode (m != 0 ? 1 : 0);
} else {
db::LayerProperties lp;
lp.read (l);
file.add_layer_spec (lp);
}
}
add_file (file);
}
if (! l.test ("#")) {
l.expect_end ();
}
}
// transfer the reference points
m_reference_points.clear ();
if (ref_points.size () > 3) {
throw tl::Exception (tl::to_string (tr ("Not more than three reference points can be specified")));
}
for (unsigned int i = 0; i < ref_points.size (); ++i) {
if (ref_points [i].first.empty () || ref_points [i].second.empty ()) {
throw tl::Exception (tl::to_string (tr ("Reference point #%d is not fully specified (either PCB or layout coordinate is missing)")), int (i + 1));
}
m_reference_points.push_back (std::make_pair (ref_points [i].first.center (), ref_points [i].second.center ()));
}
}
void
GerberImporter::save_project (std::ostream &stream)
{
stream << "# Gerber PCB import project" << std::endl;
stream << "# Created by KLayout" << std::endl;
stream << "dir=" << tl::to_quoted_string (m_dir) << std::endl;
stream << "cell=" << tl::to_quoted_string (m_cell_name) << std::endl;
stream << "dbu=" << tl::to_string (m_dbu) << std::endl;
stream << "circle-points=" << tl::to_string (m_circle_points) << std::endl;
stream << "transformation=" << tl::to_quoted_string (m_global_trans.to_string ()) << std::endl;
for (std::vector <std::pair<db::DPoint, db::DPoint> >::const_iterator r = m_reference_points.begin (); r != m_reference_points.end (); ++r) {
stream << "ref-point=(" << tl::to_string (r->first.x ()) << "," << tl::to_string (r->first.y ()) << "),(" << tl::to_string (r->second.x ()) << "," << tl::to_string (r->second.y ()) << ")" << std::endl;
}
stream << "merge=" << (m_merge ? 1 : 0) << std::endl;
stream << "invert-negative-layers=" << (m_invert_negative_layers ? 1 : 0) << std::endl;
stream << "border=" << tl::to_string (m_border) << std::endl;
if (! m_layer_styles.empty ()) {
stream << "layer-styles=" << tl::to_quoted_string (m_layer_styles) << std::endl;
}
for (std::vector<db::GerberFile>::iterator file = m_files.begin (); file != m_files.end (); ++file) {
stream << "file " << tl::to_quoted_string (file->filename ());
for (std::vector <db::LayerProperties>::const_iterator ls = file->layer_specs ().begin (); ls != file->layer_specs ().end (); ++ls) {
stream << " " << tl::to_quoted_string (ls->to_string ());
}
if (file->circle_points () > 0) {
stream << " circle-points=" << tl::to_string (file->circle_points ());
}
if (file->has_format ()) {
stream << " format=" << tl::to_quoted_string (file->format_string ());
}
if (file->merge_mode () >= 0) {
stream << " merge=" << tl::to_string (file->merge_mode ());
}
stream << std::endl;
}
}
void
GerberImporter::read (db::Layout &layout, db::cell_index_type cell_index)
{
m_cell_name = layout.cell_name (cell_index);
m_dbu = layout.dbu ();
do_read (layout, cell_index);
}
db::cell_index_type
GerberImporter::read (db::Layout &layout)
{
db::cell_index_type ci = layout.add_cell (m_cell_name.c_str ());
layout.dbu (m_dbu);
do_read (layout, ci);
return ci;
}
void
GerberImporter::do_read (db::Layout &layout, db::cell_index_type cell_index)
{
tl::log << tl::to_string (tr ("Importing PCB data"));
std::set<unsigned int> inverse_layers;
{
tl::RelativeProgress progress (tl::to_string (tr ("Importing PCB data")), m_files.size (), 1);
// derive the actual global transformation from the reference points
db::DCplxTrans global_trans (m_global_trans);
if (! m_reference_points.empty ()) {
db::DPoint p1_pcb = m_reference_points[0].first;
db::DPoint p1_ly = m_reference_points[0].second;
if (m_reference_points.size () > 1) {
db::DPoint p2_pcb = m_reference_points[1].first;
db::DPoint p2_ly = m_reference_points[1].second;
db::DVector d12_pcb = (p2_pcb - p1_pcb) * (1.0 / p2_pcb.distance (p1_pcb));
db::DVector d12_ly = (p2_ly - p1_ly) * (1.0 / p2_ly.distance (p1_ly));
int ru = -1;
int rm = -1;
for (int f = 0; f < 8; ++f) {
db::DVector d12 = db::DTrans (f) * d12_pcb;
if ((d12 - d12_ly).length () < 0.1) {
if (f < 4) {
ru = f;
} else {
rm = f;
}
}
}
if (ru < 0 || rm < 0) {
throw tl::Exception (tl::to_string (tr ("Unable to deduce rotation from reference points p1 and p2 (PCB and layout)")));
}
if (m_reference_points.size () > 2) {
db::DPoint p3_pcb = m_reference_points[2].first;
db::DPoint p3_ly = m_reference_points[2].second;
db::DVector d13_pcb = (p3_pcb - p1_pcb) * (1.0 / p3_pcb.distance (p1_pcb));
db::DVector d13_ly = (p3_ly - p1_ly) * (1.0 / p3_ly.distance (p1_ly));
double vp_pcb = d13_pcb.x () * d12_pcb.y () - d13_pcb.y () * d12_pcb.x ();
double vp_gds = d13_ly.x () * d12_ly.y () - d13_ly.y () * d12_ly.x ();
if (vp_pcb * vp_gds < 0.0) {
global_trans = db::DCplxTrans (db::DFTrans (rm));
} else {
global_trans = db::DCplxTrans (db::DFTrans (ru));
}
} else {
if (global_trans.is_mirror ()) {
global_trans = db::DCplxTrans (db::DFTrans (rm));
} else {
global_trans = db::DCplxTrans (db::DFTrans (ru));
}
}
}
global_trans = db::DCplxTrans (p1_ly - (db::DPoint () + global_trans.disp ())) * global_trans * db::DCplxTrans (db::DPoint () - p1_pcb);
}
std::string format (m_format_string);
for (std::vector<db::GerberFile>::iterator file = m_files.begin (); file != m_files.end (); ++file) {
++progress;
std::vector <unsigned int> targets;
for (std::vector <db::LayerProperties>::const_iterator ls = file->layer_specs ().begin (); ls != file->layer_specs ().end (); ++ls) {
int layer_index = -1;
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
if (layout.get_properties ((*l).first).log_equal (*ls)) {
layer_index = int ((*l).first);
break;
}
}
if (layer_index < 0) {
layer_index = int (layout.insert_layer (*ls));
}
targets.push_back (layer_index);
}
std::string fp = tl::combine_path (tl::absolute_file_path (m_dir), file->filename ());
tl::InputStream input_file (fp);
tl::TextInputStream stream (input_file);
std::vector <tl::shared_ptr<db::GerberFileReader> > readers = get_readers ();
// determine the reader to use:
db::GerberFileReader *reader = 0;
for (std::vector <tl::shared_ptr<db::GerberFileReader> >::iterator r = readers.begin (); r != readers.end (); ++r) {
stream.reset ();
if ((*r)->accepts (stream)) {
reader = r->operator-> ();
break;
}
}
if (! reader) {
throw tl::Exception (tl::to_string (tr ("Unable to determine format for file '%s'")), fp);
}
stream.reset ();
// set up the reader
reader->set_dbu (m_dbu);
reader->set_global_trans (db::DCplxTrans (1.0 / m_dbu) * global_trans * db::DCplxTrans (m_dbu));
reader->set_format_string (file->format_string ());
if (! reader->has_format ()) {
reader->set_format_string (format);
}
reader->set_merge (file->merge_mode () >= 0 ? (file->merge_mode () != 0) : m_merge);
reader->set_circle_points (file->circle_points () >= 0 ? file->circle_points () : m_circle_points);
// actually read
try {
tl::log << "Reading PCB file '" << file->filename () << "' with format '" << file->format_string () << "'";
reader->read (stream, layout, layout.cell (cell_index), targets);
} catch (tl::BreakException &) {
throw;
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + ", reading file " + file->filename ());
}
// use the current format as further default
format = reader->format_string ();
if (reader->is_inverse ()) {
inverse_layers.insert (targets.begin (), targets.end ());
}
}
}
// Invert the negative layers if requested
if (m_invert_negative_layers && ! inverse_layers.empty ()) {
db::Cell &cell = layout.cell (cell_index);
double e = m_border / layout.dbu ();
db::Box bbox = cell.bbox ().enlarged (db::Vector (db::DVector (e, e)));
int bbox_layer = layout.insert_layer ();
cell.shapes (bbox_layer).insert (bbox);
db::ShapeProcessor sp;
int n = 0;
for (std::set<unsigned int>::const_iterator l = inverse_layers.begin (); l != inverse_layers.end (); ++l) {
tl::log << "Inverting layer " << layout.get_properties (*l).to_string ();
sp.enable_progress (tl::to_string (tr ("Inverting layer")) + " " + tl::to_string (n + 1) + " " + tl::to_string (tr ("of")) + " " + tl::to_string (inverse_layers.size ()));
sp.boolean (layout, cell, *l, layout, cell, bbox_layer, cell.shapes (*l), db::BooleanOp::BNotA, true);
// clear the result layer for all called cells (if there are some)
std::set<db::cell_index_type> called_cells;
cell.collect_called_cells (called_cells);
for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
layout.cell (*c).shapes (*l).clear ();
}
}
layout.delete_layer (bbox_layer);
}
}
// ---------------------------------------------------------------
// Plugin for the stream reader
class GerberReader
: public db::ReaderBase
{
public:
GerberReader (tl::InputStream &s)
: m_stream (s)
{
// .. nothing yet ..
}
~GerberReader ()
{
// .. nothing yet ..
}
virtual const db::LayerMap &read (db::Layout &layout, const db::LoadLayoutOptions & /*options*/)
{
// TODO: too simple, should provide at least a layer filtering.
return read (layout);
}
virtual const db::LayerMap &read (db::Layout &layout)
{
db::GerberImportData data;
std::string fn (m_stream.source ());
if (! fn.empty ()) {
data.base_dir = tl::absolute_path (fn);
}
data.load (m_stream);
db::GerberImporter importer;
data.setup_importer (&importer);
importer.read (layout);
std::string lyr_file = data.get_layer_properties_file ();
if (! lyr_file.empty ()) {
layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file));
}
return m_layers;
}
virtual const char *format () const
{
return "GerberPCB";
}
private:
tl::InputStream &m_stream;
db::LayerMap m_layers;
};
class GerberFormatDeclaration
: public db::StreamFormatDeclaration
{
virtual std::string format_name () const { return "GerberPCB"; }
virtual std::string format_desc () const { return "Gerber PCB"; }
virtual std::string format_title () const { return "Gerber PCB (project files)"; }
virtual std::string file_format () const { return "Gerber PCB project files (*.pcb)"; }
virtual bool detect (tl::InputStream &stream) const
{
// The test is that somewhere within the first 1000 bytes, a <pcb-project> XML tag appears.
// 1000 bytes are within the initial block that the stream reader reads and hence
// this does not trigger any reread which is not available on some sources.
// TODO: this is pretty simple test. A more elaborate test would be in place here.
std::string h = stream.read_all (1000);
// HINT: this assumes UTF8 or ISO encoding ...
if (h.find ("<pcb-project>") != std::string::npos) {
return true;
} else {
return false;
}
}
virtual db::ReaderBase *create_reader (tl::InputStream &s) const
{
return new GerberReader (s);
}
virtual db::WriterBase *create_writer () const
{
return 0;
}
virtual bool can_read () const
{
return true;
}
virtual bool can_write () const
{
return false;
}
};
static tl::RegisteredClass<db::StreamFormatDeclaration> format_decl (new GerberFormatDeclaration (), 1000, "GerberPCB");
}