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

1061 lines
23 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
*/
#ifndef HDR_dbGerberImporter
#define HDR_dbGerberImporter
#include "dbPluginCommon.h"
#include "dbLayout.h"
#include "dbTrans.h"
#include "dbEdgeProcessor.h"
#include "dbRegion.h"
#include "tlStream.h"
#include "tlProgress.h"
#include <iostream>
namespace db
{
class Manager;
}
namespace lay
{
class LayoutView;
}
namespace db
{
/**
* @brief A structure holding the meta data for a Gerber (X2) file
*/
struct GerberMetaData
{
/**
* @brief Identifies the function of the layer
*/
enum Function
{
NoFunction = 0,
Copper,
Hole,
PlatedHole,
NonPlatedHole,
Profile,
SolderMask,
Legend,
Other
};
/**
* @brief Identifies the position of the layer
*/
enum Position
{
NoPosition = 0,
Bottom,
Inner,
Top
};
/**
* @brief Constructor
*/
GerberMetaData ()
: function (NoFunction),
cu_layer_number (0),
from_cu (0),
to_cu (0),
position (NoPosition)
{ }
/**
* @brief The project name or an empty string if none is given
*/
std::string project_id;
/**
* @brief The creation date or an empty string if none is given
*/
std::string creation_date;
/**
* @brief The generation software or an empty string if none is given
*/
std::string generation_software;
/**
* @brief The function of the layer
*/
Function function;
/**
* @brief The copper layer number
*
* This is a number identifying the layer in the copper stack.
* The topmost layer is 1, the bottom layer 2 or larger.
* This value is 0 if no layer is specified.
*/
int cu_layer_number;
/**
* @brief The drill hole start copper layer
*
* This is number of the copper layer that the drill hole connects (upper layer).
* This number is applicable only if the function is PlatedHole or NonPlatedHole.
* It is a value > 0.
*/
int from_cu;
/**
* @brief The drill hole end copper layer
*
* This is number of the copper layer that the drill hole connects (lower layer).
* This number is applicable only if the function is PlatedHole or NonPlatedHole.
* It is a value > 0.
*/
int to_cu;
/**
* @brief This is the position of the layer in the stack
*
* This value is applicable for Copper, SolderMask and Legend.
*/
Position position;
};
/**
* @brief A class holding the graphics state of the reader
*/
struct GraphicsState
{
GraphicsState ()
: inverse (false),
m_rot (0.0), m_s (1.0), m_ox (false), m_oy (false), m_mx (false), m_my (false),
m_orot (0.0), m_os (1.0), m_omx (false), m_omy (false)
{
displacements.push_back (db::DVector ());
}
bool inverse;
db::DCplxTrans global_trans;
double m_rot;
double m_s;
double m_ox, m_oy;
bool m_mx, m_my;
double m_orot;
double m_os;
bool m_omx, m_omy;
std::vector<db::Path> lines;
std::vector<db::Polygon> polygons;
std::vector<db::Polygon> clear_polygons;
std::vector<db::DVector> displacements;
std::string token;
};
/**
* @brief The base class for all readers
*/
class GerberFileReader
: public tl::Object
{
public:
/**
* @brief Constructor
*/
GerberFileReader ();
/**
* @brief Destructor
*/
virtual ~GerberFileReader () { }
/**
* @brief Detect the file format
*
* This method returns true, if the given file is accepted by this reader.
*/
bool accepts (tl::TextInputStream &stream);
/**
* @brief Read the file from the given stream into the set of target containers
*
* The reader reads the file and produces the same polygons in all layers of topcell
* provided by the "targets" parameter.
*/
void read (tl::TextInputStream &stream, db::Layout &layout, db::Cell &cell, const std::vector <unsigned int> &targets);
/**
* @brief Scans the stream and extracts the metadata
*/
GerberMetaData scan (tl::TextInputStream &stream);
/**
* @brief Sets the number of points for a circle interpolation
*
* The value must be larger or equal than 4.
*/
void set_circle_points (int c)
{
m_circle_points = (c >= 4 ? c : 64);
}
/**
* @brief Sets the number of points for a circle interpolation
*/
int get_circle_points () const
{
return m_circle_points;
}
/**
* @brief Sets the merge flag
*
* If the merge flag is set, all shapes will be merged after reading.
*/
void set_merge (bool m)
{
m_merge = m;
}
/**
* @brief Get the merge flag
*/
bool merge () const
{
return m_merge;
}
/**
* @brief Set the database unit
*/
void set_dbu (double dbu)
{
m_dbu = dbu;
}
/**
* @brief Gets the current database unit
*/
double dbu () const
{
return m_dbu;
}
/**
* @brief Set the global transformation
*
* The global transformation is applied after all other transformations have been applied
*/
void set_global_trans (const db::DCplxTrans &trans)
{
m_global_trans = trans;
}
/**
* @brief Get the global transformation
*/
const db::DCplxTrans &global_trans () const
{
return m_global_trans;
}
/**
* @brief Set the unit
*
* The unit is given in micron, i.e. mm=1000, inch=25400
*/
void set_unit (double u)
{
m_unit = u;
}
/**
* @brief Get the unit
*/
double unit () const
{
return m_unit;
}
/**
* @brief Set the format string
*/
void set_format_string (const std::string &format);
/**
* @brief Get the format string
*/
std::string format_string () const;
/**
* @brief Return true, if the format is specified
*/
bool has_format () const
{
return (m_omit_leading_zeroes && m_digits_after >= 0) || (! m_omit_leading_zeroes && m_digits_before >= 0);
}
/**
* @brief Set the format
*
* @param before Number of digits before the decimal point or -1 for unspecified
* @param after Number of digits after the decimal point or -1 for unspecified
* @param omit_leading_zeroes True, if leading zeroes should be omitted
*/
void set_format (int before, int after, bool omit_leading_zeroes)
{
m_digits_before = before;
m_digits_after = after;
m_omit_leading_zeroes = omit_leading_zeroes;
}
/**
* @brief Set the format (only leading zeroes flag)
*
* @param omit_leading_zeroes True, if leading zeroes should be omitted
*/
void set_format (bool omit_leading_zeroes)
{
m_omit_leading_zeroes = omit_leading_zeroes;
}
/**
* @brief Set the format (without leading zeroes flag)
*
* @param before Number of digits before the decimal point or -1 for unspecified
* @param after Number of digits after the decimal point or -1 for unspecified
*/
void set_format (int before, int after)
{
m_digits_before = before;
m_digits_after = after;
}
/**
* @brief Produce the given line on the output
*
* This method will produce a line, represented by a zero-width path.
* Lines are special objects created for zero-diameter apertures.
* Clear lines are ignored currently and lines don't participate in the
* boolean operations.
*
* @param p The zero-width path in micron units.
* @param clear Currently, the path is ignored when clear is true.
*/
void produce_line (const db::DPath &p, bool clear);
/**
* @brief Produce the given polygon on the output
*
* @param p The polygon in micron units.
* @param clear True, if the polygon must be cleared from the output area
*/
void produce_polygon (const db::DPolygon &p, bool clear);
/**
* @brief Returns true, if the inverse layer flag was set during read
*/
bool is_inverse () const
{
return m_inverse;
}
protected:
/**
* @brief Read the file from the given stream into the set of target containers
*
* The reader reads the file and produces the same polygons in all containers
* provided by the "targets" parameter.
* The implementation must use "produce_polygons" to produce the output.
*/
virtual void do_read () = 0;
/**
* @brief Scans the stream and returns the metadata
*/
virtual GerberMetaData do_scan () = 0;
/**
* @brief Returns true, if the reader accepts the stream
*/
virtual bool does_accept () = 0;
/**
* @brief Issue a warning
*/
void warn (const std::string &warning);
/**
* @brief Issue a non-fatal error
*/
void error (const std::string &error);
/**
* @brief Issue a fatal error
*
* This method does not return.
*/
void fatal (const std::string &error);
/**
* @brief Gets the local transformation
*/
db::DCplxTrans local_trans () const;
/**
* @brief Gets the local transformation
*/
db::DCplxTrans object_trans () const;
/**
* @brief Read a coordinate from the extractor using the format and unit
*
* The coordinate is returned in micron.
*/
double read_coord (tl::Extractor &ex);
/**
* @brief Returns the accuracy of the coordinates (the resolution of the format)
*/
double accuracy () const;
/**
* @brief Flush the stored data to the output
* This method is similar to collect(), but writes the data to the layout.
*/
void flush ();
/**
* @brief Collects the data taken so far into the given region
* This method is similar to flush(), but will return a Region object.
*/
void collect (db::Region &region);
/**
* @brief Pushes the graphics state
*/
void push_state (const std::string &token);
/**
* @brief Pops the graphics state
* Returns the token given in "push_state"
*/
std::string pop_state ();
/**
* @brief Returns true if the graphics stack is empty
*/
bool graphics_stack_empty () const;
/**
* @brief Access to the edge processor
*/
db::EdgeProcessor &ep ()
{
return m_ep;
}
/**
* @brief Enter a repeated sequence
*
* The shapes produced after this method has been called are put into
* a subcell which is placed with the given displacements
*/
void step_and_repeat (const std::vector <db::DVector> &displacements);
/**
* @brief Leaves step-and-repeat mode
*/
void reset_step_and_repeat ();
/**
* @brief Sets or resets the inverse layer flag
*/
void set_inverse (bool inverse)
{
m_inverse = inverse;
}
/**
* @brief This method updates the progress counter
*
* This method should be called regularly
*/
void progress_checkpoint ();
/**
* @brief Gets the stream object
*/
tl::TextInputStream &stream ()
{
return *mp_stream;
}
/**
* @brief Updates the local mirror flags
*/
void update_local_mirror (bool mx, bool my)
{
m_mx = mx; m_my = my;
}
/**
* @brief Updates the local orientation
*/
void update_local_angle (double rot)
{
m_rot = rot;
}
/**
* @brief Updates the local scale factor
*/
void update_local_scale (double s)
{
m_s = s;
}
/**
* @brief Updates the local offset
*/
void update_local_offset (double x, double y)
{
m_ox = x; m_oy = y;
}
/**
* @brief Updates the object mirror flags
*/
void update_object_mirror (bool mx, bool my)
{
m_omx = mx; m_omy = my;
}
/**
* @brief Updates the object orientation
*/
void update_object_angle (double rot)
{
m_orot = rot;
}
/**
* @brief Updates the object scale factor
*/
void update_object_scale (double s)
{
m_os = s;
}
private:
int m_circle_points;
int m_digits_before;
int m_digits_after;
bool m_omit_leading_zeroes;
bool m_merge;
bool m_inverse;
double m_dbu;
double m_unit;
db::DCplxTrans m_global_trans;
double m_rot;
double m_s;
double m_ox, m_oy;
bool m_mx, m_my;
double m_orot;
double m_os;
bool m_omx, m_omy;
std::vector<db::Path> m_lines;
std::vector<db::Polygon> m_polygons;
std::vector<db::Polygon> m_clear_polygons;
db::EdgeProcessor m_ep;
std::vector<unsigned int> m_target_layers;
std::vector<db::DVector> m_displacements;
db::Layout *mp_layout;
db::Cell *mp_top_cell;
tl::TextInputStream *mp_stream;
tl::AbsoluteProgress m_progress;
std::list<GraphicsState> m_graphics_stack;
void process_clear_polygons ();
void swap_graphics_state (GraphicsState &state);
};
/**
* @brief Represents one file in a Gerber stack.
*/
class DB_PLUGIN_PUBLIC GerberFile
{
public:
GerberFile ();
/**
* @brief Set the name (or path) of the file to load
*
* This can either be a relative or absolute path. Relative paths will be
* interpreted relative to the importers "dir" member.
*/
void set_filename (const std::string &filename)
{
m_filename = filename;
}
/**
* @brief Get the name of the file to load
*/
const std::string &filename () const
{
return m_filename;
}
/**
* @brief Set the merge mode
*
* Merge mode can be -1 (default), 0 (don't merge) and 1 (merge).
*/
void set_merge_mode (int merge_mode)
{
m_merge_mode = merge_mode;
}
/**
* @brief Get the merge mode
*/
int merge_mode () const
{
return m_merge_mode;
}
/**
* @brief Set the circle interpolation mode (number of points on full circle)
*/
void set_circle_points (int circle_points)
{
m_circle_points = circle_points;
}
/**
* @brief Get the circle interpolation mode (number of points on full circle)
*/
int circle_points () const
{
return m_circle_points;
}
/**
* @brief Set the format string
*
* The format string is "n:m[TL]". n, m and T or L are optional. n are the
* digits before the decimal point, m are the digits after. L specifies leading zeroes (omit trailing zeroes),
* T specifies trailing zeroes (omit leading zeroes). "T" is the default.
* If "T" is specified, m must be given, otherwise n must be specified. If n or m are not specified "*" can be used instead.
* If "L" or "T" is not specified, a guess will be made from n or m.
*/
void set_format_string (const std::string &format);
/**
* @brief Get the format string
*/
std::string format_string () const;
/**
* @brief Set the format
*
* @param before Number of digits before the decimal point or -1 for unspecified
* @param after Number of digits after the decimal point or -1 for unspecified
* @param omit_leading_zeroes True, if leading zeroes should be omitted
*/
void set_format (int before, int after, bool omit_leading_zeroes)
{
m_digits_before = before;
m_digits_after = after;
m_omit_leading_zeroes = omit_leading_zeroes;
}
/**
* @brief Get the format: number of digits before the decimal point
*/
int digits_before () const
{
return m_digits_before;
}
/**
* @brief Get the format: number of digits after the decimal point
*/
int digits_after () const
{
return m_digits_after;
}
/**
* @brief Get the format: omit leading zeroes
*/
bool omit_leading_zeroes () const
{
return m_omit_leading_zeroes;
}
/**
* @brief Return true, if the format is specified
*/
bool has_format () const
{
return (m_omit_leading_zeroes && m_digits_after >= 0) || (! m_omit_leading_zeroes && m_digits_before >= 0);
}
/**
* @brief Specify a list of layout layers to which to write this layer
*
* This must be a comma-separated list of db::LayerProperties string representations.
*/
void set_layers_string (const std::string &layers);
/**
* @brief Get a string specifying the list of layout layers
*/
std::string layers_string () const;
/**
* @brief Add a new layer specification
*/
void add_layer_spec (const db::LayerProperties &lp)
{
m_layer_specs.push_back (lp);
}
/**
* @brief Add a new layer specification
*/
const std::vector <db::LayerProperties> &layer_specs ()
{
return m_layer_specs;
}
private:
int m_circle_points;
int m_merge_mode;
int m_digits_before;
int m_digits_after;
bool m_omit_leading_zeroes;
std::vector <db::LayerProperties> m_layer_specs;
std::string m_filename;
};
/**
* @brief The Gerber format importer object
*
* This class provides a importer for Gerber layer stacks. It can be
* loaded from project files and saved to such.
*/
class DB_PLUGIN_PUBLIC GerberImporter
{
public:
/**
* @brief Default constructor
*/
GerberImporter ();
/**
* @brief Scans the given file and extracts the metadata from it
*/
static GerberMetaData scan (const std::string &fn);
/**
* @brief Scans the given stream and extracts the metadata from it
*/
static GerberMetaData scan (tl::TextInputStream &stream);
/**
* @brief Load the project file from the given stream
*
* This method will use the directory part of the file parameter
* and ignore the "dir" member.
*/
void load_project (const std::string &file);
/**
* @brief Load the project file from the given stream
*
* This method will use the base directory specified in the
* "dir" member for the files.
*/
void load_project (tl::TextInputStream &stream);
/**
* @brief Save the project to the given stream
*/
void save_project (std::ostream &stream);
/**
* @brief Read into an existing layout
*
* This method reads the layer stack into the layout and cell provided.
* The database unit and cell name members are ignored.
*/
void read (db::Layout &layout, db::cell_index_type cell_index);
/**
* @brief Read into a fresh layout
*
* This method reads the layer stack into the layout provided.
* The layout should be empty and is initialized with the given
* database unit and a cell with the given name is created.
*/
db::cell_index_type read (db::Layout &layout);
/**
* @brief Sets the merge flag
*
* If the merge flag is set, all shapes will be merged after reading.
*
* This value will be used unless a merge mode is explicitly specified per file.
*/
void set_merge (bool m)
{
m_merge = m;
}
/**
* @brief Get the merge flag
*/
bool merge () const
{
return m_merge;
}
/**
* @brief Sets the flag indicating whether to invert negative layers
*
* If the merge flag is set, layers with negative contrast will be inverted.
*/
void set_invert_negative_layers (bool i)
{
m_invert_negative_layers = i;
}
/**
* @brief Gets the flag indicating whether to invert negative layers
*/
bool invert_negative_layers () const
{
return m_invert_negative_layers;
}
/**
* @brief Sets the border width by which the bounding box is oversized to render the background for inversion
*
* This value is given in micrometer
*/
void set_border (double w)
{
m_border = w;
}
/**
* @brief Gets the border width
*/
double border () const
{
return m_border;
}
/**
* @brief Set the circle interpolation mode (number of points on full circle)
*
* This value will be used unless a number of points is explicitly specified per file.
*/
void set_circle_points (int circle_points)
{
m_circle_points = circle_points;
}
/**
* @brief Get the circle interpolation mode (number of points on full circle)
*/
int circle_points () const
{
return m_circle_points;
}
/**
* @brief Specifies the layer styles to use
*
* This allows one to specify a layer style file (path) which will be loaded
* after the Gerber files have been imported.
*
* Specify an empty string to disable that feature.
*/
void set_layer_styles (const std::string ls)
{
m_layer_styles = ls;
}
/**
* @brief Gets the layer styles to use
*/
const std::string &layer_styles () const
{
return m_layer_styles;
}
/**
* @brief Specifies the global transformation
*
* This specifies the global transformation to apply (in micron units).
*/
void set_global_trans (const db::DCplxTrans &trans)
{
m_global_trans = trans;
}
/**
* @brief Gets the global transformation
*/
const db::DCplxTrans &global_trans () const
{
return m_global_trans;
}
/**
* @brief Set the reference points
*/
void set_reference_points (const std::vector<std::pair <db::DPoint, db::DPoint> > &pts)
{
m_reference_points = pts;
}
/**
* @brief Set the directory where the files are read from (base directory)
*
* All files specified in the components are looked up relative to this path.
* This member is set with the directory part of the file name when "load_from_file"
* is used with a file name argument.
*/
void set_dir (const std::string &dir)
{
m_dir = dir;
}
/**
* @brief Get the directory where the files are read from (base directory)
*/
const std::string &dir () const
{
return m_dir;
}
/**
* @brief Set the cell name to which the geometries are written
*
* This is the name of the cell created when the read method is called without a
* cell argument.
*/
void set_cell_name (const std::string &cell_name)
{
m_cell_name = cell_name;
}
/**
* @brief Get the cell name to which the geometries are written
*/
const std::string &cell_name () const
{
return m_cell_name;
}
/**
* @brief Set the data base unit of the layout to generate
*
* This is the database unit used when a new cell is created. The layout will be
* given this database unit.
*/
void set_dbu (double dbu)
{
m_dbu = dbu;
}
/**
* @brief Get the data base unit of the layout to generate
*/
double dbu () const
{
return m_dbu;
}
/**
* @brief Clear the file specifications
*/
void clear_files ()
{
m_files.clear ();
}
/**
* @brief Add a file specification
*/
void add_file (const db::GerberFile &file)
{
m_files.push_back (file);
}
/**
* @brief Get the files: begin iterator
*/
std::vector<db::GerberFile>::const_iterator begin_files () const
{
return m_files.begin ();
}
/**
* @brief Get the files: end iterator
*/
std::vector<db::GerberFile>::const_iterator end_files () const
{
return m_files.end ();
}
private:
std::string m_cell_name;
double m_dbu;
bool m_merge;
bool m_invert_negative_layers;
double m_border;
int m_circle_points;
std::string m_format_string;
std::string m_layer_styles;
std::string m_dir;
db::DCplxTrans m_global_trans;
std::vector <std::pair <db::DPoint, db::DPoint> > m_reference_points;
std::vector <db::GerberFile> m_files;
void do_read (db::Layout &layout, db::cell_index_type cell_index);
void do_load_project (tl::TextInputStream &stream);
};
}
#endif