WIP: basic framework

This commit is contained in:
Matthias Koefferlein 2019-11-25 00:07:56 +01:00
parent 20976d7521
commit 2acb66182a
20 changed files with 3387 additions and 5 deletions

View File

@ -190,7 +190,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
</property>
</widget>
@ -204,7 +204,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/add.png</normaloff>:/add.png</iconset>
</property>
</widget>
@ -218,7 +218,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/up.png</normaloff>:/up.png</iconset>
</property>
</widget>
@ -232,7 +232,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/down.png</normaloff>:/down.png</iconset>
</property>
</widget>
@ -298,7 +298,7 @@
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="../../lay/lay/layResources.qrc"/>
<include location="../../../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>

View File

@ -0,0 +1,118 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAG.h"
#include "dbMAGReader.h"
#include "dbMAGWriter.h"
#include "dbStream.h"
#include "tlClassRegistry.h"
namespace db
{
// ---------------------------------------------------------------
// MAGDiagnostics implementation
MAGDiagnostics::~MAGDiagnostics ()
{
// .. nothing yet ..
}
// ---------------------------------------------------------------
// MAG format declaration
class MAGFormatDeclaration
: public db::StreamFormatDeclaration
{
public:
MAGFormatDeclaration ()
{
// .. nothing yet ..
}
virtual std::string format_name () const { return "MAG"; }
virtual std::string format_desc () const { return "Magic"; }
virtual std::string format_title () const { return "MAG (Magic layout format)"; }
virtual std::string file_format () const { return "MAG files (*.MAG *.msg *.mag.gz *.MAG.gz)"; }
virtual bool detect (tl::InputStream &s) const
{
return s.read_all (5) == "magic";
}
virtual ReaderBase *create_reader (tl::InputStream &s) const
{
return new db::MAGReader (s);
}
virtual WriterBase *create_writer () const
{
return new db::MAGWriter ();
}
virtual bool can_read () const
{
return true;
}
virtual bool can_write () const
{
return true;
}
virtual tl::XMLElementBase *xml_reader_options_element () const
{
return new db::ReaderOptionsXMLElement<db::MAGReaderOptions> ("cif",
tl::make_member (&db::MAGReaderOptions::lambda, "lambda") +
tl::make_member (&db::MAGReaderOptions::dbu, "dbu") +
tl::make_member (&db::MAGReaderOptions::layer_map, "layer-map") +
tl::make_member (&db::MAGReaderOptions::create_other_layers, "create-other-layers") +
tl::make_member (&db::MAGReaderOptions::keep_layer_names, "keep-layer-names") +
tl::make_element<std::vector<std::string>, db::MAGReaderOptions> (&db::MAGReaderOptions::lib_paths, "lib-paths",
tl::make_member<std::string, std::vector<std::string>::const_iterator, std::vector<std::string> > (&std::vector<std::string>::begin, &std::vector<std::string>::end, &std::vector<std::string>::push_back, "lib-path")
)
);
}
virtual tl::XMLElementBase *xml_writer_options_element () const
{
// @@@
return new db::WriterOptionsXMLElement<db::MAGWriterOptions> ("cif",
tl::make_member (&db::MAGWriterOptions::dummy_calls, "dummy-calls") +
tl::make_member (&db::MAGWriterOptions::blank_separator, "blank-separator")
);
// @@@
}
};
// NOTE: Because MAG has such a high degree of syntactic freedom, the detection is somewhat
// fuzzy: do MAG at the very end of the detection chain
static tl::RegisteredClass<db::StreamFormatDeclaration> reader_decl (new MAGFormatDeclaration (), 2200, "MAG");
// provide a symbol to force linking against
int force_link_MAG = 0;
}

View File

@ -0,0 +1,62 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbMAG
#define HDR_dbMAG
#include "dbPoint.h"
#include "tlException.h"
#include "tlInternational.h"
#include "tlString.h"
#include "tlAssert.h"
#include <string>
#include <vector>
namespace db
{
/**
* @brief The diagnostics interface for reporting problems in the reader or writer
*/
class MAGDiagnostics
{
public:
virtual ~MAGDiagnostics ();
/**
* @brief Issue an error with positional information
*/
virtual void error (const std::string &txt) = 0;
/**
* @brief Issue a warning with positional information
*/
virtual void warn (const std::string &txt) = 0;
};
}
#endif

View File

@ -0,0 +1,180 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbMAGFormat
#define HDR_dbMAGFormat
#include "dbSaveLayoutOptions.h"
#include "dbLoadLayoutOptions.h"
#include "dbPluginCommon.h"
namespace db
{
/**
* @brief Structure that holds the MAG specific options for the reader
* NOTE: this structure is non-public linkage by intention. This way it's instantiated
* in all compile units and the shared object does not need to be linked.
*/
class DB_PLUGIN_PUBLIC MAGReaderOptions
: public FormatSpecificReaderOptions
{
public:
/**
* @brief The constructor
*/
MAGReaderOptions ()
: lambda (1.0),
dbu (0.001),
create_other_layers (true),
keep_layer_names (false)
{
// .. nothing yet ..
}
/**
* @brief Specifies the lambda value
*
* The lambda value is the basic scaling parameter
*/
double lambda;
/**
* @brief Specify the database unit to produce
*
* Specify the database unit which the resulting layout will receive.
*/
double dbu;
/**
* @brief Specifies a layer mapping
*
* If a layer mapping is specified, only the given layers are read.
* Otherwise, all layers are read.
* Setting "create_other_layers" to true will make the reader
* create other layers for all layers not given in the layer map.
* Setting an empty layer map and create_other_layers to true effectively
* enables all layers for reading.
*/
db::LayerMap layer_map;
/**
* @brief A flag indicating that a new layers shall be created
*
* If this flag is set to true, layers not listed in the layer map a created
* too.
*/
bool create_other_layers;
/**
* @brief A flag indicating whether the names of layers shall be kept as such
*
* If this flag is set to false (the default), layer name translation happens.
* If set to true, translation will not happen.
* Name translation will try to extract GDS layer/datatype numbers from the
* layer names. If this value is set to true, no name translation happens.
*/
bool keep_layer_names;
/**
* @brief The library paths
*
* The library paths are the places where library references are looked up from.
* Expression interpolation happens inside these paths.
* "tech_dir", "tech_file", "tech_name" are variables by which you can refer to
* technology parameters. Relative paths will be resolved relative to the current
* file read.
*/
std::vector<std::string> lib_paths;
/**
* @brief Implementation of FormatSpecificReaderOptions
*/
virtual FormatSpecificReaderOptions *clone () const
{
return new MAGReaderOptions (*this);
}
/**
* @brief Implementation of FormatSpecificReaderOptions
*/
virtual const std::string &format_name () const
{
static const std::string n ("MAG");
return n;
}
};
/**
* @brief Structure that holds the MAG specific options for the Writer
* NOTE: this structure is non-public linkage by intention. This way it's instantiated
* in all compile units and the shared object does not need to be linked.
*/
class DB_PLUGIN_PUBLIC MAGWriterOptions
: public FormatSpecificWriterOptions
{
public:
/**
* @brief The constructor
*/
MAGWriterOptions ()
: dummy_calls (false), blank_separator (false)
{
// .. nothing yet ..
}
/**
* @brief A flag indicating whether dummy calls shall be written
* If this flag is true, the writer will produce dummy cell calls on global
* level for all top cells.
*/
bool dummy_calls;
/**
* @brief A flag indicating whether to use blanks as x/y separators
* If this flag is true, blank characters will be used to separate x and y values.
* Otherwise comma characters will be used.
*/
bool blank_separator;
/**
* @brief Implementation of FormatSpecificWriterOptions
*/
virtual FormatSpecificWriterOptions *clone () const
{
return new MAGWriterOptions (*this);
}
/**
* @brief Implementation of FormatSpecificWriterOptions
*/
virtual const std::string &format_name () const
{
static std::string n ("MAG");
return n;
}
};
}
#endif

View File

@ -0,0 +1,862 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAGReader.h"
#include "dbStream.h"
#include "dbObjectWithProperties.h"
#include "dbArray.h"
#include "dbStatic.h"
#include "tlException.h"
#include "tlString.h"
#include "tlClassRegistry.h"
#include <cctype>
namespace db
{
// ---------------------------------------------------------------
// MAGReader
MAGReader::MAGReader (tl::InputStream &s)
: m_stream (s),
m_progress (tl::to_string (tr ("Reading MAG file")), 1000),
m_lambda (1.0), m_dbu (0.001)
{
m_progress.set_format (tl::to_string (tr ("%.0fk lines")));
m_progress.set_format_unit (1000.0);
m_progress.set_unit (100000.0);
}
MAGReader::~MAGReader ()
{
// .. nothing yet ..
}
const LayerMap &
MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
{
prepare_layers ();
const db::MAGReaderOptions &specific_options = options.get_options<db::MAGReaderOptions> ();
m_lambda = specific_options.lambda;
m_dbu = specific_options.dbu;
m_lib_paths = specific_options.lib_paths;
db::LayerMap lm = specific_options.layer_map;
lm.prepare (layout);
set_layer_map (lm);
set_create_layers (specific_options.create_other_layers);
set_keep_layer_names (specific_options.keep_layer_names);
do_read (layout);
finish_layers (layout);
return layer_map ();
}
const LayerMap &
MAGReader::read (db::Layout &layout)
{
return read (layout, db::LoadLayoutOptions ());
}
void
MAGReader::error (const std::string &msg)
{
throw MAGReaderException (msg, m_stream.line_number (), m_stream.source ());
}
void
MAGReader::warn (const std::string &msg)
{
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (" (line=")) << m_stream.line_number ()
<< tl::to_string (tr (", file=")) << m_stream.source ()
<< ")";
}
#if 0 // @@@
/**
* @brief Skip blanks in the sense of MAG
* A blank in MAG is "any ASCII character except digit, upperChar, '-', '(', ')', or ';'"
*/
void
MAGReader::skip_blanks()
{
while (! m_stream.at_end ()) {
char c = m_stream.peek_char ();
if (isupper (c) || isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') {
return;
}
m_stream.get_char ();
}
}
/**
* @brief Skips separators
*/
void
MAGReader::skip_sep ()
{
while (! m_stream.at_end ()) {
char c = m_stream.peek_char ();
if (isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') {
return;
}
m_stream.get_char ();
}
}
/**
* @brief Skip comments
* This assumes that the reader is after the first '(' and it will stop
* after the final ')'.
*/
void
MAGReader::skip_comment ()
{
char c;
int bl = 0;
while (! m_stream.at_end () && ((c = m_stream.get_char ()) != ')' || bl > 0)) {
// check for nested comments (bl is the nesting level)
if (c == '(') {
++bl;
} else if (c == ')') {
--bl;
}
}
}
/**
* @brief Gets a character and issues an error if the stream is at the end
*/
char
MAGReader::get_char ()
{
if (m_stream.at_end ()) {
error ("Unexpected end of file");
return 0;
} else {
return m_stream.get_char ();
}
}
/**
* @brief Tests whether the next character is a semicolon (after blanks)
*/
bool
MAGReader::test_semi ()
{
skip_blanks ();
if (! m_stream.at_end () && m_stream.peek_char () == ';') {
return true;
} else {
return false;
}
}
/**
* @brief Tests whether a semicolon follows and issue an error if not
*/
void
MAGReader::expect_semi ()
{
if (! test_semi ()) {
error ("Expected ';' command terminator");
} else {
get_char ();
}
}
/**
* @brief Skips all until the next semicolon
*/
void
MAGReader::skip_to_end ()
{
while (! m_stream.at_end () && m_stream.get_char () != ';') {
;
}
}
/**
* @brief Fetches an integer
*/
int
MAGReader::read_integer_digits ()
{
if (m_stream.at_end () || ! isdigit (m_stream.peek_char ())) {
error ("Digit expected");
}
int i = 0;
while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) {
if (i > std::numeric_limits<int>::max () / 10) {
error ("Integer overflow");
while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) {
m_stream.get_char ();
}
return 0;
}
char c = m_stream.get_char ();
i = i * 10 + int (c - '0');
}
return i;
}
/**
* @brief Fetches an integer
*/
int
MAGReader::read_integer ()
{
skip_sep ();
return read_integer_digits ();
}
/**
* @brief Fetches a signed integer
*/
int
MAGReader::read_sinteger ()
{
skip_sep ();
bool neg = false;
if (m_stream.peek_char () == '-') {
m_stream.get_char ();
neg = true;
}
int i = read_integer_digits ();
return neg ? -i : i;
}
/**
* @brief Fetches a string (layer name)
*/
const std::string &
MAGReader::read_name ()
{
skip_blanks ();
m_cmd_buffer.clear ();
if (m_stream.at_end ()) {
return m_cmd_buffer;
}
// Note: officially only upper and digits are allowed in names. But we allow lower case and "_" too ...
while (! m_stream.at_end () && (isupper (m_stream.peek_char ()) || islower (m_stream.peek_char ()) || m_stream.peek_char () == '_' || isdigit (m_stream.peek_char ()))) {
m_cmd_buffer += m_stream.get_char ();
}
return m_cmd_buffer;
}
/**
* @brief Fetches a string (in labels, texts)
*/
const std::string &
MAGReader::read_string ()
{
m_stream.skip ();
m_cmd_buffer.clear ();
if (m_stream.at_end ()) {
return m_cmd_buffer;
}
char q = m_stream.peek_char ();
if (q == '"' || q == '\'') {
get_char ();
// read a quoted string (KLayout extension)
while (! m_stream.at_end () && m_stream.peek_char () != q) {
char c = m_stream.get_char ();
if (c == '\\' && ! m_stream.at_end ()) {
c = m_stream.get_char ();
}
m_cmd_buffer += c;
}
if (! m_stream.at_end ()) {
get_char ();
}
} else {
while (! m_stream.at_end () && !isspace (m_stream.peek_char ()) && m_stream.peek_char () != ';') {
m_cmd_buffer += m_stream.get_char ();
}
}
return m_cmd_buffer;
}
/**
* @brief Reads a double value (extension)
*/
double
MAGReader::read_double ()
{
m_stream.skip ();
// read a quoted string (KLayout extension)
m_cmd_buffer.clear ();
while (! m_stream.at_end () && (isdigit (m_stream.peek_char ()) || m_stream.peek_char () == '.' || m_stream.peek_char () == '-' || m_stream.peek_char () == 'e' || m_stream.peek_char () == 'E')) {
m_cmd_buffer += m_stream.get_char ();
}
double v = 0.0;
tl::from_string (m_cmd_buffer, v);
return v;
}
bool
MAGReader::read_cell (db::Layout &layout, db::Cell &cell, double sf, int level)
{
if (fabs (sf - floor (sf + 0.5)) > 1e-6) {
warn ("Scaling factor is not an integer - snapping errors may occur in cell '" + m_cellname + "'");
}
int nx = 0, ny = 0, dx = 0, dy = 0;
int layer = -2; // no layer defined yet.
int path_mode = -1;
size_t insts = 0;
size_t shapes = 0;
size_t layer_specs = 0;
std::vector <db::Point> poly_pts;
while (true) {
skip_blanks ();
char c = get_char ();
if (c == ';') {
// empty command
} else if (c == '(') {
skip_comment ();
} else if (c == 'E') {
if (level > 0) {
error ("'E' command must be outside a cell specification");
}
skip_blanks ();
break;
} else if (c == 'D') {
skip_blanks ();
c = get_char ();
if (c == 'S') {
// DS command:
// "D" blank* "S" integer (sep integer sep integer)?
unsigned int n = 0, denom = 1, divider = 1;
n = read_integer ();
if (! test_semi ()) {
denom = read_integer ();
divider = read_integer ();
if (divider == 0) {
error ("'DS' command: divider cannot be zero");
}
}
expect_semi ();
std::string outer_cell = "C" + tl::to_string (n);
std::swap (m_cellname, outer_cell);
std::map <unsigned int, db::cell_index_type>::const_iterator c = m_cells_by_id.find (n);
db::cell_index_type ci;
if (c == m_cells_by_id.end ()) {
ci = layout.add_cell (m_cellname.c_str ());
m_cells_by_id.insert (std::make_pair (n, ci));
} else {
ci = c->second;
}
db::Cell &cell = layout.cell (ci);
read_cell (layout, cell, sf * double (denom) / double (divider), level + 1);
std::swap (m_cellname, outer_cell);
} else if (c == 'F') {
// DF command:
// "D" blank* "F"
if (level == 0) {
error ("'DF' command must be inside a cell specification");
}
// skip the rest of the command
skip_to_end ();
break;
} else if (c == 'D') {
// DD command:
// "D" blank* "D" integer
read_integer ();
warn ("'DD' command ignored");
skip_to_end ();
} else {
error ("Invalid 'D' sub-command");
skip_to_end ();
}
} else if (c == 'C') {
// C command:
// "C" integer transformation
// transformation := (blank* ("T" point |"M" blank* "X" |"M" blank* "Y" |"R" point)*)*
++insts;
unsigned int n = read_integer ();
std::map <unsigned int, db::cell_index_type>::const_iterator c = m_cells_by_id.find (n);
if (c == m_cells_by_id.end ()) {
std::string cn = "C" + tl::to_string (n);
c = m_cells_by_id.insert (std::make_pair (n, layout.add_cell (cn.c_str ()))).first;
}
db::DCplxTrans trans;
while (! test_semi ()) {
skip_blanks ();
char ct = get_char ();
if (ct == 'M') {
skip_blanks ();
char ct2 = get_char ();
if (ct2 == 'X') {
trans = db::DCplxTrans (db::FTrans::m90) * trans;
} else if (ct2 == 'Y') {
trans = db::DCplxTrans (db::FTrans::m0) * trans;
} else {
error ("Invalid 'M' transformation specification");
// skip everything to avoid more errors here
while (! test_semi ()) {
get_char ();
}
}
} else if (ct == 'T') {
int x = read_sinteger ();
int y = read_sinteger ();
trans = db::DCplxTrans (db::DVector (x * sf, y * sf)) * trans;
} else if (ct == 'R') {
int x = read_sinteger ();
int y = read_sinteger ();
if (y != 0 || x != 0) {
double a = atan2 (double (y), double (x)) * 180.0 / M_PI;
trans = db::DCplxTrans (1.0, a, false, db::DVector ()) * trans;
}
} else {
error ("Invalid transformation specification");
// skip everything to avoid more errors here
while (! test_semi ()) {
get_char ();
}
}
}
if (nx > 0 || ny > 0) {
if (trans.is_ortho () && ! trans.is_mag ()) {
cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans)), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny)));
} else {
cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny)));
}
} else {
if (trans.is_ortho () && ! trans.is_mag ()) {
cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans))));
} else {
cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans)));
}
}
nx = ny = 0;
dx = dy = 0;
expect_semi ();
} else if (c == 'L') {
skip_blanks ();
++layer_specs;
std::string name = read_name ();
if (name.empty ()) {
error ("Missing layer name in 'L' command");
}
std::pair<bool, unsigned int> ll = open_layer (layout, name);
if (! ll.first) {
layer = -1; // ignore geometric objects on this layer
} else {
layer = int (ll.second);
}
expect_semi ();
} else if (c == 'B') {
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'B' command ignored since no layer was selected");
}
skip_to_end ();
} else {
unsigned int w = 0, h = 0;
int x = 0, y = 0;
w = read_integer ();
h = read_integer ();
x = read_sinteger ();
y = read_sinteger ();
int rx = 0, ry = 0;
if (! test_semi ()) {
rx = read_sinteger ();
ry = read_sinteger ();
}
if (rx >= 0 && ry == 0) {
cell.shapes ((unsigned int) layer).insert (db::Box (sf * (x - 0.5 * w), sf * (y - 0.5 * h), sf * (x + 0.5 * w), sf * (y + 0.5 * h)));
} else {
double n = 1.0 / sqrt (double (rx) * double (rx) + double (ry) * double (ry));
double xw = sf * w * 0.5 * rx * n, yw = sf * w * 0.5 * ry * n;
double xh = -sf * h * 0.5 * ry * n, yh = sf * h * 0.5 * rx * n;
db::Point c (sf * x, sf * y);
db::Point points [4];
points [0] = c + db::Vector (-xw - xh, -yw - yh);
points [1] = c + db::Vector (-xw + xh, -yw + yh);
points [2] = c + db::Vector (xw + xh, yw + yh);
points [3] = c + db::Vector (xw - xh, yw - yh);
db::Polygon p;
p.assign_hull (points, points + 4);
cell.shapes ((unsigned int) layer).insert (p);
}
expect_semi ();
}
} else if (c == 'P') {
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'P' command ignored since no layer was selected");
}
skip_to_end ();
} else {
poly_pts.clear ();
while (! test_semi ()) {
int rx = read_sinteger ();
int ry = read_sinteger ();
poly_pts.push_back (db::Point (sf * rx, sf * ry));
}
db::Polygon p;
p.assign_hull (poly_pts.begin (), poly_pts.end ());
cell.shapes ((unsigned int) layer).insert (p);
expect_semi ();
}
} else if (c == 'R') {
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'R' command ignored since no layer was selected");
}
skip_to_end ();
} else {
int w = read_integer ();
poly_pts.clear ();
int rx = read_sinteger ();
int ry = read_sinteger ();
poly_pts.push_back (db::Point (sf * rx, sf * ry));
db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits <db::Coord>::rounded (sf * w), db::coord_traits <db::Coord>::rounded (sf * w / 2), db::coord_traits <db::Coord>::rounded (sf * w / 2), true);
cell.shapes ((unsigned int) layer).insert (p);
expect_semi ();
}
} else if (c == 'W') {
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'W' command ignored since no layer was selected");
}
skip_to_end ();
} else {
int w = read_integer ();
poly_pts.clear ();
while (! test_semi ()) {
int rx = read_sinteger ();
int ry = read_sinteger ();
poly_pts.push_back (db::Point (sf * rx, sf * ry));
}
if (path_mode == 0 || (path_mode < 0 && m_wire_mode == 1)) {
// Flush-ended paths
db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits <db::Coord>::rounded (sf * w), 0, 0, false);
cell.shapes ((unsigned int) layer).insert (p);
} else if (path_mode == 1 || (path_mode < 0 && m_wire_mode == 2)) {
// Round-ended paths
db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits <db::Coord>::rounded (sf * w), db::coord_traits <db::Coord>::rounded (sf * w / 2), db::coord_traits <db::Coord>::rounded (sf * w / 2), true);
cell.shapes ((unsigned int) layer).insert (p);
} else {
// Square-ended paths
db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits <db::Coord>::rounded (sf * w), db::coord_traits <db::Coord>::rounded (sf * w / 2), db::coord_traits <db::Coord>::rounded (sf * w / 2), false);
cell.shapes ((unsigned int) layer).insert (p);
}
expect_semi ();
}
} else if (isdigit (c)) {
char cc = m_stream.peek_char ();
if (c == '9' && cc == '3') {
get_char ();
nx = read_sinteger ();
dx = read_sinteger ();
ny = read_sinteger ();
dy = read_sinteger ();
} else if (c == '9' && cc == '4') {
get_char ();
// label at location
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'94' command ignored since no layer was selected");
}
} else {
std::string text = read_string ();
int rx = read_sinteger ();
int ry = read_sinteger ();
double h = 0.0;
if (! test_semi ()) {
h = read_double ();
}
db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry)), db::coord_traits <db::Coord>::rounded (h / m_dbu));
cell.shapes ((unsigned int) layer).insert (t);
}
} else if (c == '9' && cc == '5') {
get_char ();
// label in box
++shapes;
if (layer < 0) {
if (layer < -1) {
warn ("'95' command ignored since no layer was selected");
}
} else {
std::string text = read_string ();
// TODO: box dimensions are ignored currently.
read_sinteger ();
read_sinteger ();
int rx = read_sinteger ();
int ry = read_sinteger ();
db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry)));
cell.shapes ((unsigned int) layer).insert (t);
}
} else if (c == '9' && cc == '8') {
get_char ();
// Path type (0: flush, 1: round, 2: square)
path_mode = read_integer ();
} else if (c == '9' && ! isdigit (cc)) {
m_cellname = read_string ();
m_cellname = layout.uniquify_cell_name (m_cellname.c_str ());
layout.rename_cell (cell.cell_index (), m_cellname.c_str ());
} else {
// ignore the command
}
skip_to_end ();
} else {
// ignore the command
warn ("Unknown command ignored");
skip_to_end ();
}
}
// The cell is considered non-empty if it contains more than one instance, at least one shape or
// has at least one "L" command.
return insts > 1 || shapes > 0 || layer_specs > 0;
}
#endif
void
MAGReader::do_read (db::Layout &layout)
{
#if 0 // @@@
tl::SelfTimer timer (tl::verbosity () >= 21, "File read");
try {
double sf = 0.01 / m_dbu;
layout.dbu (m_dbu);
m_cellname = "{MAG top level}";
db::Cell &cell = layout.cell (layout.add_cell ());
if (! read_cell (layout, cell, sf, 0)) {
// The top cell is empty or contains a single instance: discard it.
layout.delete_cell (cell.cell_index ());
} else {
layout.rename_cell (cell.cell_index (), layout.uniquify_cell_name ("MAG_TOP").c_str ());
}
m_cellname = std::string ();
skip_blanks ();
if (! m_stream.at_end ()) {
warn ("E command is followed by more text");
}
} catch (db::MAGReaderException &ex) {
throw ex;
} catch (tl::Exception &ex) {
error (ex.msg ());
}
#endif
}
}

View File

@ -0,0 +1,146 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbMAGReader
#define HDR_dbMAGReader
#include "dbPluginCommon.h"
#include "dbNamedLayerReader.h"
#include "dbLayout.h"
#include "dbMAG.h"
#include "dbMAGFormat.h"
#include "dbStreamLayers.h"
#include "dbPropertiesRepository.h"
#include "tlException.h"
#include "tlInternational.h"
#include "tlProgress.h"
#include "tlString.h"
#include "tlStream.h"
#include <map>
#include <set>
namespace db
{
/**
* @brief Generic base class of MAG reader exceptions
*/
class DB_PLUGIN_PUBLIC MAGReaderException
: public ReaderException
{
public:
MAGReaderException (const std::string &msg, size_t l, const std::string &file)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, file=%s)")), msg, l, file))
{ }
};
/**
* @brief The MAG format stream reader
*/
class DB_PLUGIN_PUBLIC MAGReader
: public NamedLayerReader,
public MAGDiagnostics
{
public:
typedef std::vector<tl::Variant> property_value_list;
/**
* @brief Construct a stream reader object
*
* @param s The stream delegate from which to read stream data from
*/
MAGReader (tl::InputStream &s);
/**
* @brief Destructor
*/
~MAGReader ();
/**
* @brief The basic read method
*
* This method will read the stream data and translate this to
* insert calls into the layout object. This will not do much
* on the layout object beside inserting the objects.
* A set of options can be specified with the LoadLayoutOptions
* object.
* The returned map will contain all layers, the passed
* ones and the newly created ones.
*
* @param layout The layout object to write to
* @param map The LayerMap object
* @param create true, if new layers should be created
* @return The LayerMap object that tells where which layer was loaded
*/
virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options);
/**
* @brief The basic read method (without mapping)
*
* This method will read the stream data and translate this to
* insert calls into the layout object. This will not do much
* on the layout object beside inserting the objects.
* This version will read all input layers and return a map
* which tells which MAG layer has been read into which logical
* layer.
*
* @param layout The layout object to write to
* @return The LayerMap object
*/
virtual const LayerMap &read (db::Layout &layout);
/**
* @brief Format
*/
virtual const char *format () const { return "MAG"; }
/**
* @brief Issue an error with positional information
*
* Reimplements MAGDiagnostics
*/
virtual void error (const std::string &txt);
/**
* @brief Issue a warning with positional information
*
* Reimplements MAGDiagnostics
*/
virtual void warn (const std::string &txt);
private:
tl::TextInputStream m_stream;
tl::AbsoluteProgress m_progress;
double m_lambda, m_dbu;
std::vector<std::string> m_lib_paths;
void do_read (db::Layout &layout);
};
}
#endif

View File

@ -0,0 +1,445 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAGWriter.h"
#include "dbPolygonGenerators.h"
#include "tlStream.h"
#include "tlUtils.h"
#include <time.h>
#include <string.h>
namespace db
{
// ---------------------------------------------------------------------------------
// MAGWriter implementation
MAGWriter::MAGWriter ()
: mp_stream (0),
m_progress (tl::to_string (tr ("Writing MAG file")), 10000),
m_needs_emit (false)
{
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
m_progress.set_unit (1024 * 1024);
}
MAGWriter &
MAGWriter::operator<<(const char *s)
{
mp_stream->put(s, strlen(s));
return *this;
}
MAGWriter &
MAGWriter::operator<<(const std::string &s)
{
mp_stream->put(s.c_str(), s.size());
return *this;
}
MAGWriter &
MAGWriter::operator<<(endl_tag)
{
#ifdef _WIN32
*this << "\r\n";
#else
*this << "\n";
#endif
return *this;
}
const char *
MAGWriter::xy_sep () const
{
return m_options.blank_separator ? " " : ",";
}
void
MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
m_options = options.get_options<MAGWriterOptions> ();
mp_stream = &stream;
// compute the scale factor to get to the 10 nm basic database unit of MAG
double tl_scale = options.scale_factor () * layout.dbu () / 0.01;
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);
// create a cell index vector sorted bottom-up
std::vector <db::cell_index_type> cells;
cells.reserve (cell_set.size ());
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 ()) {
cells.push_back (*cell);
}
}
time_t t = time(NULL);
struct tm tt = *localtime(&t);
char timestr[100];
strftime(timestr, sizeof (timestr), "%F %T", &tt);
// Write header
*this << "(MAG file written " << (const char *)timestr << " by KLayout);" << endl;
// TODO: this can be done more intelligently ..
int tl_scale_divider;
int tl_scale_denom;
for (tl_scale_divider = 1; tl_scale_divider < 1000; ++tl_scale_divider) {
tl_scale_denom = int (floor (0.5 + tl_scale * tl_scale_divider));
if (fabs (tl_scale_denom - tl_scale * tl_scale_divider) < 1e-6) {
break;
}
}
int cell_index = 0;
std::map <db::cell_index_type, int> db_to_cif_index_map;
std::set <db::cell_index_type> called_cells;
// body
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));
++cell_index;
db_to_cif_index_map.insert (std::make_pair (*cell, cell_index));
double sf = 1.0;
*this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << endl;
*this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << endl;
// 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 ()) {
called_cells.insert (inst->cell_index ());
m_progress.set (mp_stream->pos ());
std::map<db::cell_index_type, int>::const_iterator cif_index = db_to_cif_index_map.find (inst->cell_index ());
tl_assert(cif_index != db_to_cif_index_map.end ());
// resolve instance arrays
for (db::Cell::cell_inst_array_type::iterator pp = inst->begin (); ! pp.at_end (); ++pp) {
*this << "C" << cif_index->second;
// convert the transformation into MAG's notation
db::CplxTrans t (inst->complex_trans (*pp));
db::Vector d (t.disp() * sf);
if (t.is_mirror()) {
*this << " MY";
}
double a = t.angle();
while (a < 0) {
a += 360.0;
}
double ya = 0.0, xa = 0.0;
if (a < 45 || a > 315) {
xa = 1.0;
ya = tan(a / 180.0 * M_PI);
} else if (a < 135) {
xa = 1.0 / tan(a / 180.0 * M_PI);
ya = 1.0;
} else if (a < 225) {
xa = -1.0;
ya = tan(a / 180.0 * M_PI);
} else {
xa = 1.0 / tan(a / 180.0 * M_PI);
ya = -1.0;
}
// TODO: that can be done smarter ...
while (fabs (xa - floor (0.5 + xa)) > 1e-3 || fabs (ya - floor (0.5 + ya)) > 1e-3) {
xa *= 2.0;
ya *= 2.0;
}
*this << " R" << floor (0.5 + xa) << xy_sep () << floor (0.5 + ya);
*this << " T" << d.x() << xy_sep () << d.y();
*this << ";" << endl;
}
}
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
m_needs_emit = true;
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 ());
}
// end of cell
*this << "DF;" << endl;
}
if (m_options.dummy_calls) {
// If requested, write dummy calls for all top cells
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
if (called_cells.find (*cell) == called_cells.end ()) {
std::map<db::cell_index_type, int>::const_iterator cif_index = db_to_cif_index_map.find (*cell);
tl_assert(cif_index != db_to_cif_index_map.end ());
*this << "C" << cif_index->second << ";" << endl;
}
}
}
// end of file
*this << "E" << endl;
m_progress.set (mp_stream->pos ());
}
void
MAGWriter::emit_layer()
{
if (m_needs_emit) {
m_needs_emit = false;
*this << "L " << tl::to_word_or_quoted_string(m_layer.name, "0123456789_.$") << ";" << endl;
}
}
void
MAGWriter::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 ());
emit_layer ();
*this << "94 " << tl::to_word_or_quoted_string(shape->text_string(), "0123456789:<>/&%$!.-_#+*?\\[]{}");
double h = shape->text_size () * layout.dbu ();
db::Vector p (shape->text_trans ().disp () * sf);
*this << " " << p.x() << xy_sep () << p.y () << " " << h << ";" << endl;
++shape;
}
}
void
MAGWriter::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);
if (poly.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 (poly.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 {
write_polygon (poly, sf);
}
++shape;
}
}
void
MAGWriter::write_polygon (const db::Polygon &polygon, double sf)
{
emit_layer ();
*this << "P";
for (db::Polygon::polygon_contour_iterator p = polygon.begin_hull (); p != polygon.end_hull (); ++p) {
db::Point pp (*p * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
}
*this << ";" << endl;
}
void
MAGWriter::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 ()) {
m_progress.set (mp_stream->pos ());
emit_layer ();
db::Box b (shape->bbox () * sf);
*this << "B " << b.width () << " " << b.height () << " " << b.center ().x () << xy_sep () << b.center ().y () << ";" << endl;
++shape;
}
}
void
MAGWriter::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 ());
#if 0
// "official" code: write only round paths as such - other paths are converted to polygons
if (shape->round_path ()) {
emit_layer ();
*this << "W " << long (floor (0.5 + sf * shape->path_width ()));
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
db::Point pp (*p * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
}
*this << ";" << endl;
} else {
db::Polygon poly;
shape->polygon (poly);
write_polygon (poly, sf);
}
#else
// Use 98 extension for path type. Only use polygons for custom extensions.
int path_type = -1;
if (shape->round_path ()) {
if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) {
path_type = 1;
}
} else {
if (shape->path_extensions ().first == 0 && shape->path_extensions ().second == 0) {
path_type = 0;
} else if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) {
path_type = 2;
}
}
size_t npts = 0;
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point () && npts < 2; ++p) {
++npts;
}
if (npts == 0) {
// ignore paths with zero points
} else if (path_type == 1 && npts == 1) {
// produce a round flash for single-point round paths
emit_layer ();
*this << "R " << long (floor (0.5 + sf * shape->path_width ()));
db::Point pp (*shape->begin_point () * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
*this << ";" << endl;
} else if (path_type >= 0 && npts > 1) {
emit_layer ();
*this << "98 " << path_type << ";" << endl;
*this << "W " << long (floor (0.5 + sf * shape->path_width ()));
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
db::Point pp (*p * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
}
*this << ";" << endl;
} else {
db::Polygon poly;
shape->polygon (poly);
write_polygon (poly, sf);
}
#endif
++shape;
}
}
}

View File

@ -0,0 +1,96 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbMAGWriter
#define HDR_dbMAGWriter
#include "dbPluginCommon.h"
#include "dbWriter.h"
#include "dbMAG.h"
#include "dbMAGFormat.h"
#include "dbSaveLayoutOptions.h"
#include "tlProgress.h"
namespace tl
{
class OutputStream;
}
namespace db
{
class Layout;
class SaveLayoutOptions;
/**
* @brief A MAG writer abstraction
*/
class DB_PLUGIN_PUBLIC MAGWriter
: public db::WriterBase
{
public:
/**
* @brief Instantiate the writer
*/
MAGWriter ();
/**
* @brief Write the layout object
*/
void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options);
private:
struct endl_tag { };
tl::OutputStream *mp_stream;
MAGWriterOptions m_options;
tl::AbsoluteProgress m_progress;
endl_tag endl;
db::LayerProperties m_layer;
bool m_needs_emit;
MAGWriter &operator<<(const char *s);
MAGWriter &operator<<(const std::string &s);
MAGWriter &operator<<(endl_tag);
template<class X> MAGWriter &operator<<(const X &x)
{
return (*this << tl::to_string(x));
}
void write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
void write_polygons (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
void write_polygon (const db::Polygon &polygon, double tl_scale);
void write_boxes (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
void write_paths (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
void write_edges (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
const char *xy_sep () const;
void emit_layer();
};
} // namespace db
#endif

View File

@ -0,0 +1,17 @@
TARGET = cif
DESTDIR = $$OUT_PWD/../../../../db_plugins
include($$PWD/../../../db_plugin.pri)
HEADERS = \
dbMAG.h \
dbMAGReader.h \
dbMAGWriter.h \
dbMAGFormat.h \
SOURCES = \
dbMAG.cc \
dbMAGReader.cc \
dbMAGWriter.cc \
gsiDeclDbMAG.cc \

View File

@ -0,0 +1,265 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAG.h"
#include "dbMAGReader.h"
#include "dbMAGWriter.h"
#include "dbLoadLayoutOptions.h"
#include "dbSaveLayoutOptions.h"
#include "gsiDecl.h"
namespace gsi
{
// ---------------------------------------------------------------
// gsi Implementation of specific methods
static void set_mag_dbu (db::LoadLayoutOptions *options, double dbu)
{
options->get_options<db::MAGReaderOptions> ().dbu = dbu;
}
static double get_mag_dbu (const db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().dbu;
}
static void set_mag_lambda (db::LoadLayoutOptions *options, double lambda)
{
options->get_options<db::MAGReaderOptions> ().lambda = lambda;
}
static double get_mag_lambda (const db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().lambda;
}
static void set_mag_library_paths (db::LoadLayoutOptions *options, const std::vector<std::string> &lib_paths)
{
options->get_options<db::MAGReaderOptions> ().lib_paths = lib_paths;
}
static std::vector<std::string> get_mag_library_paths (const db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().lib_paths;
}
static void set_layer_map (db::LoadLayoutOptions *options, const db::LayerMap &lm, bool f)
{
options->get_options<db::MAGReaderOptions> ().layer_map = lm;
options->get_options<db::MAGReaderOptions> ().create_other_layers = f;
}
static void set_layer_map1 (db::LoadLayoutOptions *options, const db::LayerMap &lm)
{
options->get_options<db::MAGReaderOptions> ().layer_map = lm;
}
static db::LayerMap &get_layer_map (db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().layer_map;
}
static void select_all_layers (db::LoadLayoutOptions *options)
{
options->get_options<db::MAGReaderOptions> ().layer_map = db::LayerMap ();
options->get_options<db::MAGReaderOptions> ().create_other_layers = true;
}
static bool create_other_layers (const db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().create_other_layers;
}
static void set_create_other_layers (db::LoadLayoutOptions *options, bool l)
{
options->get_options<db::MAGReaderOptions> ().create_other_layers = l;
}
static bool keep_layer_names (const db::LoadLayoutOptions *options)
{
return options->get_options<db::MAGReaderOptions> ().keep_layer_names;
}
static void set_keep_layer_names (db::LoadLayoutOptions *options, bool l)
{
options->get_options<db::MAGReaderOptions> ().keep_layer_names = l;
}
// extend lay::LoadLayoutOptions with the MAG options
static
gsi::ClassExt<db::LoadLayoutOptions> mag_reader_options (
gsi::method_ext ("mag_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"),
"@brief Sets the layer map\n"
"This sets a layer mapping for the reader. The \"create_other_layers\" specifies whether to create layers that are not "
"in the mapping and automatically assign layers to them.\n"
"@param map The layer map to set."
"@param create_other_layers The flag telling whether other layer should be created also. Set to false if just the layers in the mapping table should be read.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_layer_map=", &set_layer_map1, gsi::arg ("map"),
"@brief Sets the layer map\n"
"This sets a layer mapping for the reader. Unlike \\mag_set_layer_map, the 'create_other_layers' flag is not changed.\n"
"@param map The layer map to set."
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_select_all_layers", &select_all_layers,
"@brief Selects all layers and disables the layer map\n"
"\n"
"This disables any layer map and enables reading of all layers.\n"
"New layers will be created when required.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_layer_map", &get_layer_map,
"@brief Gets the layer map\n"
"@return A reference to the layer map\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_create_other_layers?", &create_other_layers,
"@brief Gets a value indicating whether other layers shall be created\n"
"@return True, if other layers should be created.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_create_other_layers=", &set_create_other_layers, gsi::arg ("create"),
"@brief Specifies whether other layers shall be created\n"
"@param create True, if other layers should be created.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_keep_layer_names?", &keep_layer_names,
"@brief Gets a value indicating whether layer names are kept\n"
"@return True, if layer names are kept.\n"
"\n"
"When set to true, no attempt is made to translate "
"layer names to GDS layer/datatype numbers. If set to false (the default), a layer named \"L2D15\" will be translated "
"to GDS layer 2, datatype 15.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_keep_layer_names=", &set_keep_layer_names, gsi::arg ("keep"),
"@brief Gets a value indicating whether layer names are kept\n"
"@param keep True, if layer names are to be kept.\n"
"\n"
"See \\mag_keep_layer_names? for a description of this property.\n"
"\n"
"This method has been added in version 0.26.2."
) +
gsi::method_ext ("mag_library_paths=", &set_mag_library_paths, gsi::arg ("lib_paths"),
"@brief Specifies the locations where to look up libraries (in this order)\n"
"\n"
"The reader will look up library reference in these paths when it can't find them locally.\n"
"Relative paths in this collection are resolved relative to the initial file's path.\n"
"Expression interpolation is supported in the path strings.\n"
"\nThis property has been added in version 0.26.2.\n"
) +
gsi::method_ext ("mag_library_paths", &get_mag_library_paths,
"@brief Get the locations where to look up libraries (in this order)\n"
"See \\mag_library_paths= method for a description of this attribute."
"\nThis property has been added in version 0.26.2.\n"
) +
gsi::method_ext ("mag_lambda=", &set_mag_lambda, gsi::arg ("lambda"),
"@brief Specifies the lambda value to used for reading\n"
"\n"
"The lamdba value is the basic unit of the layout.\n"
"\nThis property has been added in version 0.26.2.\n"
) +
gsi::method_ext ("mag_lambda", &get_mag_lambda,
"@brief Get the lambda value\n"
"See \\mag_lambda= method for a description of this attribute."
"\nThis property has been added in version 0.26.2.\n"
) +
gsi::method_ext ("mag_dbu=", &set_mag_dbu, gsi::arg ("dbu"),
"@brief Specifies the database unit which the reader uses and produces\n"
"\nThis property has been added in version 0.26.2.\n"
) +
gsi::method_ext ("mag_dbu", &get_mag_dbu,
"@brief Specifies the database unit which the reader uses and produces\n"
"See \\mag_dbu= method for a description of this property."
"\nThis property has been added in version 0.26.2.\n"
),
""
);
// ---------------------------------------------------------------
// gsi Implementation of specific methods
static void set_mag_dummy_calls (db::SaveLayoutOptions *options, bool f)
{
options->get_options<db::MAGWriterOptions> ().dummy_calls = f;
}
static bool get_mag_dummy_calls (const db::SaveLayoutOptions *options)
{
return options->get_options<db::MAGWriterOptions> ().dummy_calls;
}
static void set_mag_blank_separator (db::SaveLayoutOptions *options, bool f)
{
options->get_options<db::MAGWriterOptions> ().blank_separator = f;
}
static bool get_mag_blank_separator (const db::SaveLayoutOptions *options)
{
return options->get_options<db::MAGWriterOptions> ().blank_separator;
}
// extend lay::SaveLayoutOptions with the GDS2 options
static
gsi::ClassExt<db::SaveLayoutOptions> mag_writer_options (
gsi::method_ext ("mag_dummy_calls=", &set_mag_dummy_calls,
"@brief Sets a flag indicating whether dummy calls shall be written\n"
"If this property is set to true, dummy calls will be written in the top level entity "
"of the MAG file calling every top cell.\n"
"This option is useful for enhanced compatibility with other tools.\n"
"\nThis property has been added in version 0.23.10.\n"
) +
gsi::method_ext ("mag_dummy_calls?|#mag_dummy_calls", &get_mag_dummy_calls,
"@brief Gets a flag indicating whether dummy calls shall be written\n"
"See \\mag_dummy_calls= method for a description of that property."
"\nThis property has been added in version 0.23.10.\n"
"\nThe predicate version (mag_blank_separator?) has been added in version 0.25.1.\n"
) +
gsi::method_ext ("mag_blank_separator=", &set_mag_blank_separator,
"@brief Sets a flag indicating whether blanks shall be used as x/y separator characters\n"
"If this property is set to true, the x and y coordinates are separated with blank characters "
"rather than comma characters."
"\nThis property has been added in version 0.23.10.\n"
) +
gsi::method_ext ("mag_blank_separator?|#mag_blank_separator", &get_mag_blank_separator,
"@brief Gets a flag indicating whether blanks shall be used as x/y separator characters\n"
"See \\mag_blank_separator= method for a description of that property."
"\nThis property has been added in version 0.23.10.\n"
"\nThe predicate version (mag_blank_separator?) has been added in version 0.25.1.\n"
),
""
);
}

View File

@ -0,0 +1,319 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MAGReaderOptionPage</class>
<widget class="QWidget" name="MAGReaderOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>584</width>
<height>530</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Input Options</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1">
<widget class="QLineEdit" name="dbu_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Micron</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QCheckBox" name="keep_names_cbx">
<property name="text">
<string>Don't attempt to translate into layer/datatype numbers</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Keep layer names</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lambda_le"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Micron</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Lambda value</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Database unit </string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Library paths</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="5">
<widget class="QListWidget" name="lib_path">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="del_lib_path">
<property name="toolTip">
<string>Delete selected paths</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="move_lib_path_down">
<property name="toolTip">
<string>Move selected paths down</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/down.png</normaloff>:/down.png</iconset>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="add_lib_path">
<property name="toolTip">
<string>Add new a directory</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/add.png</normaloff>:/add.png</iconset>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Relative paths are resolved relative to the file read.
You can use expressions inside the path components for variable paths</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QToolButton" name="move_lib_path_up">
<property name="toolTip">
<string>Move selected paths up</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/up.png</normaloff>:/up.png</iconset>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="add_lib_path_with_choose">
<property name="toolTip">
<string>Add new a directory (file browser)</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="layer_subset_grp">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="title">
<string>Layer Subset And Layer Mapping</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="read_all_cbx">
<property name="text">
<string>Read all layers (additionally to the ones in the mapping table)</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0" rowspan="10" colspan="2">
<widget class="lay::LayerMappingWidget" name="layer_map">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>lay::LayerMappingWidget</class>
<extends>QFrame</extends>
<header>layLayerMappingWidget.h</header>
<container>1</container>
<slots>
<signal>enable_all_layers(bool)</signal>
</slots>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>lambda_le</tabstop>
<tabstop>dbu_le</tabstop>
<tabstop>keep_names_cbx</tabstop>
<tabstop>lib_path</tabstop>
<tabstop>add_lib_path</tabstop>
<tabstop>add_lib_path_with_choose</tabstop>
<tabstop>del_lib_path</tabstop>
<tabstop>move_lib_path_up</tabstop>
<tabstop>move_lib_path_down</tabstop>
<tabstop>read_all_cbx</tabstop>
</tabstops>
<resources>
<include location="../../../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>
<sender>layer_map</sender>
<signal>enable_all_layers(bool)</signal>
<receiver>read_all_cbx</receiver>
<slot>setChecked(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>122</x>
<y>186</y>
</hint>
<hint type="destinationlabel">
<x>109</x>
<y>147</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MAGWriterOptionPage</class>
<widget class="QWidget" name="MAGWriterOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>619</width>
<height>209</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>CIF Writer Options</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item row="0" column="1">
<widget class="QCheckBox" name="blank_separator_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>If checked, a blank character is used as x/y coordinate
separator. Otherweise a comma is used.</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Blank as x/y separator</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Dummy cell calls</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="dummy_calls_cbx">
<property name="text">
<string>If checked, dummy cell calls are added on global level</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>601</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,293 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAG.h"
#include "dbMAGReader.h"
#include "dbLoadLayoutOptions.h"
#include "layMAGReaderPlugin.h"
#include "ui_MAGReaderOptionPage.h"
#include "gsiDecl.h"
#include <QFrame>
#include <QFileDialog>
namespace lay
{
// ---------------------------------------------------------------
// List manipulation utilities
// @@@ TODO: move this to a central place
static void
refresh_item_flags (QListWidget *list)
{
for (int i = 0; i < list->count (); ++i) {
list->item (i)->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
}
}
static void
add_items_to_list (QListWidget *list, const QStringList &items)
{
for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) {
list->addItem (*f);
}
refresh_item_flags (list);
}
static void
delete_selected_items_from_list (QListWidget *list)
{
QStringList items;
for (int i = 0; i < list->count (); ++i) {
if (! list->item (i)->isSelected ()) {
items.push_back (list->item (i)->text ());
}
}
list->clear ();
for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) {
list->addItem (*f);
}
refresh_item_flags (list);
}
static void
move_selected_items_up (QListWidget *list)
{
std::set<QString> selected;
for (int i = 0; i < list->count (); ++i) {
if (list->item (i)->isSelected ()) {
selected.insert (list->item (i)->text ());
}
}
QStringList items;
int j = -1;
for (int i = 0; i < list->count (); ++i) {
if (list->item (i)->isSelected ()) {
items.push_back (list->item (i)->text ());
} else {
if (j >= 0) {
items.push_back (list->item (j)->text ());
}
j = i;
}
}
if (j >= 0) {
items.push_back (list->item (j)->text ());
}
list->clear ();
for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) {
list->addItem (*f);
if (selected.find (*f) != selected.end ()) {
list->item (list->count () - 1)->setSelected (true);
}
}
refresh_item_flags (list);
}
static void
move_selected_items_down (QListWidget *list)
{
std::set<QString> selected;
for (int i = 0; i < list->count (); ++i) {
if (list->item (i)->isSelected ()) {
selected.insert (list->item (i)->text ());
}
}
QStringList items;
int j = -1;
for (int i = list->count (); i > 0; ) {
--i;
if (list->item (i)->isSelected ()) {
items.push_back (list->item (i)->text ());
} else {
if (j >= 0) {
items.push_back (list->item (j)->text ());
}
j = i;
}
}
if (j >= 0) {
items.push_back (list->item (j)->text ());
}
list->clear ();
for (QStringList::const_iterator f = items.end (); f != items.begin (); ) {
--f;
list->addItem (*f);
if (selected.find (*f) != selected.end ()) {
list->item (list->count () - 1)->setSelected (true);
}
}
refresh_item_flags (list);
}
// ---------------------------------------------------------------
// MAGReaderOptionPage definition and implementation
MAGReaderOptionPage::MAGReaderOptionPage (QWidget *parent)
: StreamReaderOptionsPage (parent)
{
mp_ui = new Ui::MAGReaderOptionPage ();
mp_ui->setupUi (this);
connect (mp_ui->add_lib_path, SIGNAL (clicked ()), this, SLOT (add_lib_path_clicked ()));
connect (mp_ui->add_lib_path_with_choose, SIGNAL (clicked ()), this, SLOT (add_lib_path_clicked_with_choose ()));
connect (mp_ui->del_lib_path, SIGNAL (clicked ()), this, SLOT (del_lib_paths_clicked ()));
connect (mp_ui->move_lib_path_up, SIGNAL (clicked ()), this, SLOT (move_lib_paths_up_clicked ()));
connect (mp_ui->move_lib_path_down, SIGNAL (clicked ()), this, SLOT (move_lib_paths_down_clicked ()));
}
MAGReaderOptionPage::~MAGReaderOptionPage ()
{
delete mp_ui;
mp_ui = 0;
}
void
MAGReaderOptionPage::setup (const db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/)
{
static const db::MAGReaderOptions default_options;
const db::MAGReaderOptions *options = dynamic_cast<const db::MAGReaderOptions *> (o);
if (!options) {
options = &default_options;
}
mp_ui->dbu_le->setText (tl::to_qstring (tl::to_string (options->dbu)));
mp_ui->lambda_le->setText (tl::to_qstring (tl::to_string (options->lambda)));
mp_ui->layer_map->set_layer_map (options->layer_map);
mp_ui->read_all_cbx->setChecked (options->create_other_layers);
mp_ui->keep_names_cbx->setChecked (options->keep_layer_names);
mp_ui->lib_path->clear ();
QStringList items;
for (std::vector <std::string>::const_iterator f = options->lib_paths.begin (); f != options->lib_paths.end (); ++f) {
items << tl::to_qstring (*f);
}
add_items_to_list (mp_ui->lib_path, items);
}
void
MAGReaderOptionPage::commit (db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/)
{
db::MAGReaderOptions *options = dynamic_cast<db::MAGReaderOptions *> (o);
if (options) {
tl::from_string (tl::to_string (mp_ui->dbu_le->text ()), options->dbu);
if (options->dbu > 1000.0 || options->dbu < 1e-9) {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for database unit")));
}
tl::from_string (tl::to_string (mp_ui->lambda_le->text ()), options->lambda);
if (options->lambda > 10000000.0 || options->lambda < 1e-9) {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for lambda")));
}
options->layer_map = mp_ui->layer_map->get_layer_map ();
options->create_other_layers = mp_ui->read_all_cbx->isChecked ();
options->keep_layer_names = mp_ui->keep_names_cbx->isChecked ();
options->lib_paths.clear ();
options->lib_paths.reserve (mp_ui->lib_path->count ());
for (int i = 0; i < mp_ui->lib_path->count (); ++i) {
options->lib_paths.push_back (tl::to_string (mp_ui->lib_path->item (i)->text ()));
}
}
}
void
MAGReaderOptionPage::add_lib_path_clicked ()
{
QStringList dirs;
dirs << tr ("Enter your path here ..");
add_items_to_list (mp_ui->lib_path, dirs);
mp_ui->lib_path->clearSelection ();
mp_ui->lib_path->setCurrentItem (mp_ui->lib_path->item (mp_ui->lib_path->count () - 1));
}
void
MAGReaderOptionPage::add_lib_path_clicked_with_choose ()
{
QString dir = QFileDialog::getExistingDirectory (this, QObject::tr ("Add library path"));
if (! dir.isNull ()) {
QStringList dirs;
dirs << dir;
add_items_to_list (mp_ui->lib_path, dirs);
mp_ui->lib_path->clearSelection ();
mp_ui->lib_path->setCurrentItem (mp_ui->lib_path->item (mp_ui->lib_path->count () - 1));
}
}
void
MAGReaderOptionPage::del_lib_paths_clicked ()
{
delete_selected_items_from_list (mp_ui->lib_path);
}
void
MAGReaderOptionPage::move_lib_paths_up_clicked ()
{
move_selected_items_up (mp_ui->lib_path);
}
void
MAGReaderOptionPage::move_lib_paths_down_clicked ()
{
move_selected_items_down (mp_ui->lib_path);
}
// ---------------------------------------------------------------
// MAGReaderPluginDeclaration definition and implementation
class MAGReaderPluginDeclaration
: public StreamReaderPluginDeclaration
{
public:
MAGReaderPluginDeclaration ()
: StreamReaderPluginDeclaration (db::MAGReaderOptions ().format_name ())
{
// .. nothing yet ..
}
StreamReaderOptionsPage *format_specific_options_page (QWidget *parent) const
{
return new MAGReaderOptionPage (parent);
}
db::FormatSpecificReaderOptions *create_specific_options () const
{
return new db::MAGReaderOptions ();
}
};
static tl::RegisteredClass<lay::PluginDeclaration> plugin_decl (new lay::MAGReaderPluginDeclaration (), 10000, "MAGReader");
}

View File

@ -0,0 +1,66 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_layMAGReaderPlugin_h
#define HDR_layMAGReaderPlugin_h
#include "layStream.h"
#include <QObject>
namespace Ui
{
class MAGReaderOptionPage;
}
namespace lay
{
class MAGReaderOptionPage
: public StreamReaderOptionsPage
{
Q_OBJECT
public:
MAGReaderOptionPage (QWidget *parent);
~MAGReaderOptionPage ();
void setup (const db::FormatSpecificReaderOptions *options, const db::Technology *tech);
void commit (db::FormatSpecificReaderOptions *options, const db::Technology *tech);
private slots:
void add_lib_path_clicked ();
void add_lib_path_clicked_with_choose ();
void del_lib_paths_clicked ();
void move_lib_paths_up_clicked ();
void move_lib_paths_down_clicked ();
private:
Ui::MAGReaderOptionPage *mp_ui;
};
}
#endif

View File

@ -0,0 +1,100 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAG.h"
#include "dbMAGWriter.h"
#include "dbSaveLayoutOptions.h"
#include "layMAGWriterPlugin.h"
#include "ui_MAGWriterOptionPage.h"
#include <QFrame>
namespace lay
{
// ---------------------------------------------------------------
// MAGWriterOptionPage definition and implementation
MAGWriterOptionPage::MAGWriterOptionPage (QWidget *parent)
: StreamWriterOptionsPage (parent)
{
mp_ui = new Ui::MAGWriterOptionPage ();
mp_ui->setupUi (this);
// .. nothing yet ..
}
MAGWriterOptionPage::~MAGWriterOptionPage ()
{
delete mp_ui;
mp_ui = 0;
}
void
MAGWriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db::Technology * /*tech*/)
{
const db::MAGWriterOptions *options = dynamic_cast<const db::MAGWriterOptions *> (o);
if (options) {
mp_ui->dummy_calls_cbx->setChecked (options->dummy_calls);
mp_ui->blank_separator_cbx->setChecked (options->blank_separator);
}
}
void
MAGWriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Technology * /*tech*/, bool /*gzip*/)
{
db::MAGWriterOptions *options = dynamic_cast<db::MAGWriterOptions *> (o);
if (options) {
options->dummy_calls = mp_ui->dummy_calls_cbx->isChecked ();
options->blank_separator = mp_ui->blank_separator_cbx->isChecked ();
}
}
// ---------------------------------------------------------------
// MAGWriterPluginDeclaration definition and implementation
class MAGWriterPluginDeclaration
: public StreamWriterPluginDeclaration
{
public:
MAGWriterPluginDeclaration ()
: StreamWriterPluginDeclaration (db::MAGWriterOptions ().format_name ())
{
// .. nothing yet ..
}
StreamWriterOptionsPage *format_specific_options_page (QWidget *parent) const
{
return new MAGWriterOptionPage (parent);
}
db::FormatSpecificWriterOptions *create_specific_options () const
{
return new db::MAGWriterOptions ();
}
};
static tl::RegisteredClass<lay::PluginDeclaration> plugin_decl (new lay::MAGWriterPluginDeclaration (), 10000, "MAGWriter");
}

View File

@ -0,0 +1,58 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_layMAGWriterPlugin_h
#define HDR_layMAGWriterPlugin_h
#include "layStream.h"
#include <QObject>
namespace Ui
{
class MAGWriterOptionPage;
}
namespace lay
{
class MAGWriterOptionPage
: public StreamWriterOptionsPage
{
Q_OBJECT
public:
MAGWriterOptionPage (QWidget *parent);
~MAGWriterOptionPage ();
void setup (const db::FormatSpecificWriterOptions *options, const db::Technology *tech);
void commit (db::FormatSpecificWriterOptions *options, const db::Technology *tech, bool gzip);
private:
Ui::MAGWriterOptionPage *mp_ui;
};
}
#endif

View File

@ -0,0 +1,25 @@
TARGET = cif_ui
DESTDIR = $$OUT_PWD/../../../../lay_plugins
include($$PWD/../../../lay_plugin.pri)
INCLUDEPATH += $$PWD/../db_plugin
DEPENDPATH += $$PWD/../db_plugin
LIBS += -L$$DESTDIR/../db_plugins -lcif
!isEmpty(RPATH) {
QMAKE_RPATHDIR += $$RPATH/db_plugins
}
HEADERS = \
layMAGReaderPlugin.h \
layMAGWriterPlugin.h \
SOURCES = \
layMAGReaderPlugin.cc \
layMAGWriterPlugin.cc \
FORMS = \
MAGWriterOptionPage.ui \
MAGReaderOptionPage.ui \

View File

@ -0,0 +1,10 @@
TEMPLATE = subdirs
SUBDIRS = db_plugin unit_tests
unit_tests.depends += db_plugin
!equals(HAVE_QT, "0") {
SUBDIRS += lay_plugin
lay_plugin.depends += db_plugin
}

View File

@ -0,0 +1,196 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbMAGReader.h"
#include "dbLayoutDiff.h"
#include "dbWriter.h"
#include "dbMAGWriter.h"
#include "tlUnitTest.h"
#include <stdlib.h>
static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double dbu = 0.001, bool dummy_calls = false, bool blank_sep = false)
{
db::MAGReaderOptions *opt = new db::MAGReaderOptions();
opt->dbu = dbu;
db::LayerMap lm;
if (map) {
unsigned int ln = 0;
tl::Extractor ex (map);
while (! ex.at_end ()) {
std::string n;
int l;
ex.read_word_or_quoted (n);
ex.test (":");
ex.read (l);
ex.test (",");
lm.map (n, ln++, db::LayerProperties (l, 0));
}
opt->layer_map = lm;
opt->create_other_layers = true;
}
db::LoadLayoutOptions options;
options.set_options (opt);
db::Manager m;
db::Layout layout (&m), layout2 (&m), layout2_cif (&m), layout_au (&m);
{
std::string fn (base);
fn += "/testdata/cif/";
fn += file;
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (layout, options);
}
// generate a "unique" name ...
unsigned int hash = 0;
for (const char *cp = file_au; *cp; ++cp) {
hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp);
}
// normalize the layout by writing to GDS and reading from ..
std::string tmp_gds_file = _this->tmp_file (tl::sprintf ("tmp_%x.gds", hash));
std::string tmp_cif_file = _this->tmp_file (tl::sprintf ("tmp_%x.cif", hash));
{
tl::OutputStream stream (tmp_gds_file);
db::SaveLayoutOptions options;
options.set_format ("GDS2");
db::Writer writer (options);
writer.write (layout, stream);
}
{
tl::InputStream stream (tmp_gds_file);
db::Reader reader (stream);
reader.read (layout2);
}
// normalize the layout by writing to MAG and reading from ..
{
tl::OutputStream stream (tmp_cif_file);
db::MAGWriterOptions *opt = new db::MAGWriterOptions();
opt->dummy_calls = dummy_calls;
opt->blank_separator = blank_sep;
db::MAGWriter writer;
db::SaveLayoutOptions options;
options.set_options (opt);
writer.write (layout, stream, options);
}
{
tl::InputStream stream (tmp_cif_file);
db::MAGReaderOptions *opt = new db::MAGReaderOptions();
opt->dbu = dbu;
db::LoadLayoutOptions reread_options;
reread_options.set_options (opt);
db::Reader reader (stream);
reader.read (layout2_cif, reread_options);
}
{
std::string fn (base);
fn += "/testdata/cif/";
fn += file_au;
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (layout_au);
}
bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1);
if (! equal) {
_this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_gds_file, file_au));
}
equal = db::compare_layouts (layout, layout2_cif, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1);
if (! equal) {
_this->raise (tl::sprintf ("Compare failed after writing - see %s vs %s\n", file, tmp_cif_file));
}
}
TEST(1a)
{
run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1a_au.gds.gz");
}
TEST(1b)
{
run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01);
}
TEST(1c)
{
run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, true);
}
TEST(1d)
{
run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, false, true);
}
TEST(2)
{
run_test (_this, tl::testsrc_private (), "t2.cif.gz", "t2_au.gds.gz");
}
TEST(3a)
{
run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3a_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26");
}
TEST(3b)
{
run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3b_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26", 0.00012);
}
TEST(4)
{
run_test (_this, tl::testsrc_private (), "t4.cif.gz", "t4_au.gds.gz");
}
TEST(5)
{
run_test (_this, tl::testsrc_private (), "t5.cif.gz", "t5_au.gds.gz");
}
// Issue #28
TEST(lasi)
{
run_test (_this, tl::testsrc (), "lasi.cif.gz", "lasi_au.gds.gz");
}
// Issue #305
TEST(rot_boxes)
{
run_test (_this, tl::testsrc (), "issue_305.cif", "issue_305_au.gds");
}

View File

@ -0,0 +1,19 @@
DESTDIR_UT = $$OUT_PWD/../../../..
TARGET = cif_tests
include($$PWD/../../../../lib_ut.pri)
SOURCES = \
dbMAGReader.cc \
INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common
DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common
LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi
PLUGINPATH = $$OUT_PWD/../../../../db_plugins
QMAKE_RPATHDIR += $$PLUGINPATH
LIBS += -L$$PLUGINPATH -lcif