mirror of https://github.com/KLayout/klayout.git
514 lines
13 KiB
C++
514 lines
13 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 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 "rdb.h"
|
|
#include "rdbReader.h"
|
|
#include "tlTimer.h"
|
|
#include "tlString.h"
|
|
#include "tlClassRegistry.h"
|
|
#include "tlProgress.h"
|
|
#include "dbTrans.h"
|
|
#include "dbPolygon.h"
|
|
#include "dbEdge.h"
|
|
#include "dbEdgePair.h"
|
|
|
|
#include <cctype>
|
|
#include <cstring>
|
|
|
|
namespace rdb
|
|
{
|
|
|
|
class RVEReaderException
|
|
: public ReaderException
|
|
{
|
|
public:
|
|
RVEReaderException (const std::string &msg, size_t line)
|
|
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (line=%lu)")), msg, line))
|
|
{ }
|
|
};
|
|
|
|
class RVEReader
|
|
: public ReaderBase
|
|
{
|
|
public:
|
|
RVEReader (tl::InputStream &stream)
|
|
: m_input_stream (stream),
|
|
m_progress (tl::to_string (QObject::tr ("Reading RVE DB")), 10000)
|
|
{
|
|
m_progress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
|
m_progress.set_unit (1024 * 1024);
|
|
}
|
|
|
|
virtual void read (Database &db)
|
|
{
|
|
try {
|
|
do_read (db);
|
|
} catch (tl::Exception &ex) {
|
|
error (ex.msg ());
|
|
}
|
|
}
|
|
|
|
void do_read (Database &db)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, "Reading RVE file");
|
|
|
|
tl::Extractor ex;
|
|
std::string s;
|
|
double res;
|
|
double dbu;
|
|
|
|
std::string cell_name;
|
|
db::DCplxTrans trans;
|
|
db::DCplxTrans shape_trans;
|
|
std::vector <db::DPoint> points;
|
|
std::vector <db::DEdge> edges;
|
|
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
ex.read (s, " ");
|
|
ex.read (res);
|
|
|
|
if (res < 0.001 || res > 1e6) {
|
|
error (tl::sprintf (tl::to_string (QObject::tr ("Invalid resolution value: %g")), res));
|
|
}
|
|
|
|
dbu = 1.0 / res;
|
|
|
|
Cell *top_cell = db.create_cell (s);
|
|
db.set_top_cell_name (s);
|
|
|
|
std::string cat_name;
|
|
id_type waived_tag_id = db.tags ().tag ("waived").id ();
|
|
|
|
while (! m_input_stream.at_end ()) {
|
|
|
|
// TODO: check if this is correct: when a new category is started the
|
|
// cell name is reset. Any shape not having a specific cell will go into the
|
|
// top cell then.
|
|
cell_name.clear ();
|
|
|
|
// Read the category name unless we have some already (that we got when parsing the shapes).
|
|
if (cat_name.empty ()) {
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
const char *start = ex.skip ();
|
|
if (! *start) {
|
|
break;
|
|
} else {
|
|
const char *end = start + strlen (start);
|
|
while (end > start && isspace (end [-1])) {
|
|
--end;
|
|
}
|
|
if (end > start && end[-1] == '.') {
|
|
--end;
|
|
}
|
|
cat_name = std::string (start, end - start);
|
|
}
|
|
}
|
|
|
|
Category *cath = db.create_category (cat_name);
|
|
cat_name.clear ();
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
error (tl::to_string (QObject::tr ("Unexpected end of file")));
|
|
}
|
|
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
size_t n1, n2, n3;
|
|
ex.read (n1);
|
|
ex.read (n2);
|
|
ex.read (n3);
|
|
|
|
std::map<size_t, std::string> waivers;
|
|
|
|
std::string desc;
|
|
for (size_t i = 0; i < n3; ++i) {
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
error (tl::to_string (QObject::tr ("Unexpected end of file")));
|
|
}
|
|
|
|
std::string l = m_input_stream.get_line ();
|
|
if (l.size () > 3 && l[0] == 'W' && l[1] == 'E' && isdigit (l[2])) {
|
|
|
|
size_t n = 0;
|
|
const char *cp;
|
|
for (cp = l.c_str () + 2; *cp && isdigit (*cp); ++cp) {
|
|
n = n * 10 + size_t (*cp - '0');
|
|
}
|
|
while (*cp && isspace (*cp)) {
|
|
++cp;
|
|
}
|
|
waivers.insert (std::make_pair (n, std::string ())).first->second = cp;
|
|
|
|
} else {
|
|
|
|
if (! desc.empty ()) {
|
|
desc += "\n";
|
|
}
|
|
desc += l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cath->set_description (desc);
|
|
|
|
for (size_t shape = 0; shape < n1; ++shape) {
|
|
|
|
std::map<size_t, std::string>::const_iterator w = waivers.find (shape);
|
|
bool waived = (w != waivers.end ());
|
|
// TODO: add waiver string somehow ...
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
warn (tl::to_string (QObject::tr ("Unexpected end of file before the specified number of shapes was read - stopping.")));
|
|
break;
|
|
}
|
|
|
|
s = m_input_stream.get_line ();
|
|
|
|
ex = tl::Extractor (s.c_str ());
|
|
|
|
char shape_type = tolower (*ex.skip ());
|
|
bool valid = (shape_type == 'p' || shape_type == 'e');
|
|
|
|
if (valid) {
|
|
++ex;
|
|
}
|
|
|
|
Values values;
|
|
|
|
size_t nshape, npoints;
|
|
valid = valid && ex.try_read (nshape);
|
|
valid = valid && ex.try_read (npoints);
|
|
|
|
if (! valid) {
|
|
ex = tl::Extractor (s.c_str ());
|
|
const char *start = ex.skip ();
|
|
if (! *start) {
|
|
warn (tl::to_string (QObject::tr ("Unexpected end of file before the specified number of shapes was read - stopping.")));
|
|
} else {
|
|
const char *end = start + strlen (start);
|
|
while (end > start && isspace (end [-1])) {
|
|
--end;
|
|
}
|
|
if (end > start && end[-1] == '.') {
|
|
--end;
|
|
}
|
|
cat_name = std::string (start, end - start);
|
|
warn (tl::to_string (QObject::tr ("Obviously reaching end of shapes list before the specified number of shapes was read - parsing next category.")));
|
|
}
|
|
break;
|
|
}
|
|
|
|
while (true) {
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
error (tl::to_string (QObject::tr ("Unexpected end of file")));
|
|
}
|
|
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
char c = *ex.skip ();
|
|
|
|
if (isalpha (c)) {
|
|
|
|
std::string prop_name;
|
|
ex.read_word (prop_name);
|
|
|
|
if (prop_name == "CN") {
|
|
|
|
ex.read_word (cell_name, "_.$-");
|
|
|
|
int m11 = 1, m12 = 0, m21 = 0, m22 = 1;
|
|
int x = 0, y = 0;
|
|
|
|
bool cspace = (ex.test ("c") || ex.test ("C"));
|
|
|
|
if (! ex.at_end ()) {
|
|
|
|
ex.read (m11);
|
|
ex.read (m21);
|
|
ex.read (m12);
|
|
ex.read (m22);
|
|
|
|
ex.read (x);
|
|
ex.read (y);
|
|
|
|
}
|
|
|
|
int f = 0;
|
|
if (m11 * m22 - m21 * m12 < 0) {
|
|
f = 4;
|
|
m12 = -m12;
|
|
m22 = -m22;
|
|
}
|
|
|
|
if (m11 == 1 && m21 == 0) {
|
|
// r0 or m0
|
|
} else if (m11 == 0 && m21 == 1) {
|
|
// r90 or m45
|
|
f += 1;
|
|
} else if (m11 == -1 && m21 == 0) {
|
|
// r180 or m90
|
|
f += 2;
|
|
} else if (m11 == 0 && m21 == -1) {
|
|
// r270 or m135
|
|
f += 3;
|
|
}
|
|
|
|
if (cspace) {
|
|
shape_trans = db::DCplxTrans ();
|
|
trans = db::DCplxTrans (db::DTrans (f, db::DVector (x * dbu, y * dbu)));
|
|
} else {
|
|
shape_trans = db::DCplxTrans (db::DTrans (f, db::DVector (x * dbu, y * dbu)));
|
|
trans = shape_trans.inverted ();
|
|
}
|
|
|
|
} else {
|
|
|
|
double v = 0.0;
|
|
if (ex.try_read (v)) {
|
|
|
|
// custom properties get a tag name with a hash
|
|
id_type tag_id = db.tags ().tag (prop_name, true).id ();
|
|
|
|
Value<double> *value = new Value<double> (v);
|
|
values.add (value, tag_id);
|
|
|
|
} else {
|
|
|
|
// TODO: other property formats?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
const Cell *cell;
|
|
|
|
// Use the last cell or the top cell if no cell is specified since the start of the category.
|
|
if (cell_name.empty ()) {
|
|
|
|
cell = top_cell;
|
|
|
|
} else {
|
|
|
|
cell = db.cell_by_qname (cell_name);
|
|
if (! cell) {
|
|
|
|
Cell *nc_cell = db.create_cell (cell_name);
|
|
|
|
Reference ref (trans, top_cell->id ());
|
|
nc_cell->references ().insert (ref);
|
|
|
|
cell = nc_cell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_progress.set (m_input_stream.raw_stream ().pos ());
|
|
|
|
if (shape_type == 'p') {
|
|
|
|
points.clear ();
|
|
|
|
for (size_t point = 0; point < npoints; ++point) {
|
|
|
|
if (point > 0) {
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
error (tl::to_string (QObject::tr ("Unexpected end of file")));
|
|
}
|
|
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
|
|
}
|
|
|
|
int x, y;
|
|
ex.read (x);
|
|
ex.read (y);
|
|
ex.expect_end ();
|
|
points.push_back (db::DPoint (x * dbu, y * dbu));
|
|
|
|
}
|
|
|
|
Value<db::DPolygon> *value = new Value<db::DPolygon> (db::DPolygon ());
|
|
value->value ().assign_hull (points.begin (), points.end (), shape_trans);
|
|
values.add (value);
|
|
|
|
} else if (shape_type == 'e') {
|
|
|
|
edges.clear ();
|
|
|
|
for (size_t point = 0; point < npoints; ++point) {
|
|
|
|
if (point > 0) {
|
|
|
|
if (m_input_stream.at_end ()) {
|
|
error (tl::to_string (QObject::tr ("Unexpected end of file")));
|
|
}
|
|
|
|
ex = tl::Extractor (m_input_stream.get_line ().c_str ());
|
|
|
|
}
|
|
|
|
int x1, y1;
|
|
ex.read (x1);
|
|
ex.read (y1);
|
|
|
|
int x2, y2;
|
|
ex.read (x2);
|
|
ex.read (y2);
|
|
|
|
ex.expect_end ();
|
|
|
|
edges.push_back (db::DEdge (db::DPoint (x1 * dbu, y1 * dbu), db::DPoint (x2 * dbu, y2 * dbu)).transformed (shape_trans));
|
|
|
|
}
|
|
|
|
if (edges.size () == 2) {
|
|
// produce an edge pair from two edges
|
|
values.add (new Value<db::DEdgePair> (db::DEdgePair (edges [0], edges [1])));
|
|
} else {
|
|
for (std::vector <db::DEdge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
|
|
values.add (new Value<db::DEdge> (*e));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
error (tl::sprintf (tl::to_string (QObject::tr ("Invalid shape type: %c")), shape_type));
|
|
}
|
|
|
|
Item *item = db.create_item (cell->id (), cath->id ());
|
|
if (waived) {
|
|
db.add_item_tag (item, waived_tag_id);
|
|
}
|
|
|
|
item->values ().swap (values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual const char *format () const
|
|
{
|
|
return "RVE";
|
|
}
|
|
|
|
private:
|
|
tl::TextInputStream m_input_stream;
|
|
tl::AbsoluteProgress m_progress;
|
|
|
|
void warn (const std::string &msg)
|
|
{
|
|
tl::warn << tl::sprintf (tl::to_string (QObject::tr ("%s (line=%lu)")), msg, m_input_stream.line_number ());
|
|
}
|
|
|
|
void error (const std::string &msg)
|
|
{
|
|
throw RVEReaderException (msg, m_input_stream.line_number ());
|
|
}
|
|
};
|
|
|
|
class RVEFormatDeclaration
|
|
: public FormatDeclaration
|
|
{
|
|
virtual std::string format_name () const { return "RVE"; }
|
|
virtual std::string format_desc () const { return "RVE format"; }
|
|
virtual std::string file_format () const { return "RVE files (*.rve *.rve.gz *.db *.db.gz)"; }
|
|
|
|
virtual bool detect (tl::InputStream &stream) const
|
|
{
|
|
tl::TextInputStream text_stream (stream);
|
|
|
|
std::string l;
|
|
tl::Extractor ex;
|
|
std::string s;
|
|
double d;
|
|
int i;
|
|
|
|
// The first line must be "<cellname> <resolution>"
|
|
if (text_stream.at_end ()) {
|
|
return false;
|
|
}
|
|
|
|
l = text_stream.get_line ();
|
|
ex = tl::Extractor (l.c_str ());
|
|
ex.read (s, " ");
|
|
|
|
if (! ex.try_read (d)) {
|
|
return false;
|
|
}
|
|
|
|
// If we are at the end, this is probably an empty file. No checks and no results.
|
|
if (text_stream.at_end ()) {
|
|
return true;
|
|
}
|
|
|
|
// The second line is skipped
|
|
l = text_stream.get_line ();
|
|
|
|
ex = tl::Extractor (l.c_str ());
|
|
|
|
// If we are at the end, this is probably an empty file. No checks and no results.
|
|
if (text_stream.at_end ()) {
|
|
return ex.at_end ();
|
|
}
|
|
|
|
// The third line must be "<n> <n> <n> <date...>"
|
|
l = text_stream.get_line ();
|
|
|
|
ex = tl::Extractor (l.c_str ());
|
|
if (! ex.try_read (i)) {
|
|
return false;
|
|
}
|
|
|
|
if (! ex.try_read (i)) {
|
|
return false;
|
|
}
|
|
|
|
if (! ex.try_read (i)) {
|
|
return false;
|
|
}
|
|
|
|
// That's it.
|
|
return true;
|
|
}
|
|
|
|
virtual ReaderBase *create_reader (tl::InputStream &s) const
|
|
{
|
|
return new RVEReader (s);
|
|
}
|
|
};
|
|
|
|
static tl::RegisteredClass<rdb::FormatDeclaration> format_decl (new RVEFormatDeclaration (), 0, "RVE");
|
|
|
|
}
|
|
|