klayout/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc

3370 lines
92 KiB
C++

/*
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 "dbOASISWriter.h"
#include "dbPolygonGenerators.h"
#include "tlDeflate.h"
#include "tlMath.h"
#include <math.h>
namespace db
{
// ---------------------------------------------------------------------------------
// Some definitions
static const char *klayout_context_name = "KLAYOUT_CONTEXT";
static const char *s_gds_property_name = "S_GDS_PROPERTY";
static const char *s_cell_offset_name = "S_CELL_OFFSET";
static const char *s_max_signed_integer_width_name = "S_MAX_SIGNED_INTEGER_WIDTH";
static const char *s_max_unsigned_integer_width_name = "S_MAX_UNSIGNED_INTEGER_WIDTH";
static const char *s_top_cell_name = "S_TOP_CELL";
static const char *s_bounding_boxes_available_name = "S_BOUNDING_BOXES_AVAILABLE";
static const char *s_bounding_box_name = "S_BOUNDING_BOX";
// ---------------------------------------------------------------------------------
/**
* @brief Determines whether a property shall be produced as S_GDS_PROPERTY
*/
static bool
make_gds_property (const tl::Variant &name)
{
// We write S_GDS_PROPERTY properties, because that is the only way to write properties
// with numerical keys
return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) ||
(name.is_ulonglong () && name.to_ulonglong () < 0x8000) ||
(name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) ||
(name.is_ulong () && name.to_ulong () < 0x8000);
}
// ---------------------------------------------------------------------------------
/**
* @brief Within UTF-8 advance the pointer to the next character
*/
static void
next_utf8 (const char * &s)
{
int skip = 0;
if (((unsigned char) *s) < 0x80) {
// single-byte character
} else if (((unsigned char) *s) < 0xe0) {
// two-byte character
skip = 1;
} else if (((unsigned char) *s) < 0xf0) {
// three-byte character
skip = 2;
} else if (((unsigned char) *s) < 0xf8) {
// four-byte character
skip = 3;
}
++s;
while (skip > 0 && ((unsigned char) *s) >= 0x80 && ((unsigned char) *s) < 0xc0) {
++s;
--skip;
}
}
// ---------------------------------------------------------------------------------
/**
* @brief Makes an nstring or astring from the given string
* This function employs the substitution string to replace invalid characters.
* The substitution string must be a valid nstring itself.
*/
static std::string
make_n_or_astring (const char *s, const std::string &subst, bool make_nstring)
{
// Empty strings will render the substitution string when producing nstrings
if (make_nstring && !*s) {
return subst;
}
bool valid = true;
for (const char *c = s; *c && valid; ++c) {
if (*c == 0x20 && make_nstring) {
valid = false;
} else if (((unsigned char) *c) < 0x20 || ((unsigned char) *c) > 0x7e) {
valid = false;
}
}
if (valid) {
// No need to translate
return std::string (s);
} else {
std::string nstr;
for (const char *c = s; *c; ) {
if (*c == 0x20 && make_nstring) {
nstr += subst;
} else if (((unsigned char) *c) < 0x20 || ((unsigned char) *c) > 0x7e) {
nstr += subst;
} else {
nstr += *c;
}
next_utf8 (c);
}
return nstr;
}
}
// ---------------------------------------------------------------------------------
/**
* @brief Determines the type of a string
* The return value is 0 for an a-string, 1 for a b-string and 2 for a n-string.
* The return value is determined in a way that the property value type can be
* determined by adding 10 or 13 for direct value or reference respectively.
*/
static int
string_type (const char *s)
{
if (! *s) {
// an empty string gives an a-string
return 0;
}
bool is_nstring = true;
while (*s) {
unsigned char c = (unsigned char) *s;
if (c == 0x20) {
// space -> produces a-string instead of n-string
is_nstring = false;
} else if (c < 0x20 || c > 0x7e) {
// non-printable character: produces a b-string always
return 1;
}
next_utf8 (s);
}
return is_nstring ? 2 : 0;
}
// ---------------------------------------------------------------------------------
// Utilities that prevent signed coordinate overflow
template <class R>
inline R safe_scale (double sf, R value)
{
double i = floor (sf * value + 0.5);
if (i < double (std::numeric_limits<R>::min ())) {
throw tl::Exception ("Scaling failed: coordinate underflow");
}
if (i > double (std::numeric_limits<R>::max ())) {
throw tl::Exception ("Scaling failed: coordinate overflow");
}
return R (i);
}
template <class R>
inline R safe_diff (R a, R b)
{
R d = a - b;
if ((a > b && d < 0) || (a < b && d > 0)) {
throw tl::Exception ("Signed coordinate difference overflow");
}
return d;
}
// ---------------------------------------------------------------------------------
// Generic delivery of shapes (specialized to compressing / non-compressing variants)
/**
* @brief Convert a shape (basic object) to a repetition
*/
template <class Tag> void create_repetition_by_type (const db::Shape &array_shape, db::Repetition &rep, const Tag &tag)
{
const typename Tag::object_type *array = array_shape.basic_ptr (tag);
std::vector<db::Vector> pts;
db::Vector a, b;
unsigned long amax = 0, bmax = 0;
if (array->is_iterated_array (&pts)) {
// Remove the first point which is implicitly contained in the repetition
// Note: we can do so because below we instantiate the shape at the front of the array which includes
// the first transformation already.
tl_assert (! pts.empty ());
db::Vector po = pts.front ();
std::vector<db::Vector>::iterator pw = pts.begin();
for (std::vector<db::Vector>::iterator p = pw + 1; p != pts.end (); ++p) {
*pw++ = *p - po;
}
pts.erase (pw, pts.end ());
db::IrregularRepetition *rep_base = new db::IrregularRepetition ();
rep_base->points ().swap (pts);
rep.set_base (rep_base);
} else if (array->is_regular_array (a, b, amax, bmax)) {
db::RegularRepetition *rep_base = new db::RegularRepetition (a, b, size_t (std::max ((unsigned long) 1, amax)), size_t (std::max ((unsigned long) 1, bmax)));
rep.set_base (rep_base);
} else {
tl_assert (false);
}
}
/**
* @brief Produce a repetition from an array shape
*/
void create_repetition (const db::Shape &array, db::Repetition &rep)
{
switch (array.type ()) {
case db::Shape::PolygonPtrArray:
create_repetition_by_type (array, rep, db::Shape::polygon_ptr_array_type::tag ());
break;
case db::Shape::SimplePolygonPtrArray:
create_repetition_by_type (array, rep, db::Shape::simple_polygon_ptr_array_type::tag ());
break;
case db::Shape::PathPtrArray:
create_repetition_by_type (array, rep, db::Shape::path_ptr_array_type::tag ());
break;
case db::Shape::BoxArray:
create_repetition_by_type (array, rep, db::Shape::box_array_type::tag ());
break;
case db::Shape::ShortBoxArray:
create_repetition_by_type (array, rep, db::Shape::short_box_array_type::tag ());
break;
case db::Shape::TextPtrArray:
create_repetition_by_type (array, rep, db::Shape::text_ptr_array_type::tag ());
break;
default:
tl_assert (false);
break;
}
}
/**
* @brief Compare operator for points, distinct x clustered (with same y)
*/
struct vector_cmp_x
{
bool operator() (const db::Vector &a, const db::Vector &b) const
{
if (a.y () != b.y ()) {
return a.y () < b.y ();
} else {
return a.x () < b.x ();
}
}
};
/**
* @brief Compare operator for points, distinct y clustered (with same x)
*/
struct vector_cmp_y
{
bool operator() (const db::Vector &a, const db::Vector &b) const
{
if (a.x () != b.x ()) {
return a.x () < b.x ();
} else {
return a.y () < b.y ();
}
}
};
/**
* @brief Compare operator for points/abstract repetition pair with configurable point compare operator
*/
template <class PC>
struct rep_vector_cmp
{
bool operator () (const std::pair <db::Vector, std::pair <db::Coord, int> > &a, const std::pair <db::Vector, std::pair <db::Coord, int> > &b)
{
if (a.second != b.second) {
return a.second < b.second;
}
PC pc;
return pc (a.first, b.first);
}
};
/**
* @brief Return the cost value of a coordinate difference (or coordinate)
* The cost is used to estimate the size cost of a coordinate difference
* in the OASIS output.
* The cost is roughly the number of bytes required to represent the
* number. It does not consider gdelta compression, actual byte count or similar.
*/
inline double cost_of (double d)
{
int exp = 0;
frexp (d, &exp);
return double ((exp + 7) / 8);
}
template <class Obj>
void
Compressor<Obj>::flush (db::OASISWriter *writer)
{
static const db::Repetition rep_single;
// produce the repetitions
disp_vector displacements;
typedef std::vector <std::pair <db::Vector, std::pair <db::Coord, int> > > tmp_rep_vector;
tmp_rep_vector repetitions;
std::vector<std::pair<db::Vector, db::Repetition> > rep_vector;
for (typename std::unordered_map <Obj, disp_vector>::iterator n = m_normalized.begin (); n != m_normalized.end (); ++n) {
rep_vector.clear ();
// don't compress below a threshold of 10 shapes
if (m_level < 1 || n->second.size () < 10) {
// Simple compression: just sort and make irregular repetitions
std::sort (n->second.begin (), n->second.end (), vector_cmp_x ());
} else {
disp_vector::iterator d;
tmp_rep_vector::iterator rw;
std::unordered_set<db::Coord> xcoords, ycoords;
if (m_level > 1) {
for (d = n->second.begin (); d != n->second.end (); ++d) {
xcoords.insert (d->x ());
ycoords.insert (d->y ());
}
}
bool xfirst = xcoords.size () < ycoords.size ();
double simple_rep_cost = 0;
double array_cost = 0;
// Try single-point compression to repetitions in the x and y direction. For the first
// direction, use the one with more distinct values. For this, a better compression is
// expected.
for (int xypass = 0; xypass <= 1; ++xypass) {
bool xrep = (xfirst == (xypass == 0));
displacements.clear ();
repetitions.clear ();
displacements.swap (n->second);
if (xrep) {
std::sort (displacements.begin (), displacements.end (), vector_cmp_x ());
} else {
std::sort (displacements.begin (), displacements.end (), vector_cmp_y ());
}
if (xypass == 0 && m_level > 1) {
// Establish a baseline for the repetition cost
simple_rep_cost += cost_of (displacements.front ().x ()) + cost_of (displacements.front ().y ());
for (d = displacements.begin () + 1; d != displacements.end (); ++d) {
simple_rep_cost += std::max (1.0, cost_of (double (d->x ()) - double (d[-1].x ())) + cost_of (double (d->y ()) - double (d[-1].y ())));
}
}
disp_vector::iterator dwindow = displacements.begin ();
for (d = displacements.begin (); d != displacements.end (); ) {
if (m_level < 2) {
disp_vector::iterator dd = d;
++dd;
db::Vector dxy;
int nxy = 1;
if (dd != displacements.end ()) {
dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ()));
while (dd != displacements.end () && *dd == dd[-1] + dxy) {
++dd;
++nxy;
}
}
// Note in level 1 optimization, no cost estimation is done, hence small arrays won't be removed.
// To compensate that, we use a minimum size of 3 items per array.
if (nxy < 3) {
n->second.push_back (*d++);
} else {
repetitions.push_back (std::make_pair (*d, std::make_pair (xrep ? dxy.x () : dxy.y (), nxy)));
d = dd;
}
} else {
// collect the nearest neighbor distances and counts for 2..level order neighbors
int nxy_max = 1;
unsigned int nn_max = 0;
// move the window of identical x/y coordinates if necessary
if (d == dwindow) {
for (dwindow = d + 1; dwindow != displacements.end () && (xrep ? (dwindow->y () == d->y ()) : (dwindow->x () == d->x ())); ++dwindow)
;
}
for (unsigned int nn = 0; nn < m_level; ++nn) {
disp_vector::iterator dd = d + (nn + 1);
if (dd >= dwindow) {
break;
}
db::Vector dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ()));
int nxy = 2;
while (dd != dwindow) {
disp_vector::iterator df = std::lower_bound (dd + 1, dwindow, *dd + dxy);
if (df == dwindow || *df != *dd + dxy) {
break;
}
++nxy;
dd = df;
}
if (nxy > nxy_max) {
nxy_max = nxy;
nn_max = nn;
}
}
if (nxy_max < 2) {
// no candidate found - just keep that one
n->second.push_back (*d++);
} else {
// take out the ones of this sequence from the list
db::Vector dxy_max = xrep ? db::Vector (safe_diff ((d + nn_max + 1)->x (), d->x ()), 0) : db::Vector (0, safe_diff ((d + nn_max + 1)->y (), d->y ()));
disp_vector::iterator ds = dwindow;
disp_vector::iterator dt = dwindow;
db::Vector df = *d + dxy_max * long (nxy_max - 1);
do {
--ds;
if (*ds != df) {
*--dt = *ds;
} else {
df -= dxy_max;
}
} while (ds != d);
repetitions.push_back (std::make_pair (*d, std::make_pair (xrep ? dxy_max.x () : dxy_max.y (), nxy_max)));
d = dt;
}
}
}
// Apply some heuristic criterion that allows the algorithm to determine whether it's worth doing the compression
// Try to compact these repetitions further, y direction first, then x direction
for (int xypass2 = 1; xypass2 >= 0; --xypass2) {
if (xypass2) {
std::sort (repetitions.begin (), repetitions.end (), rep_vector_cmp<vector_cmp_y> ());
} else {
std::sort (repetitions.begin (), repetitions.end (), rep_vector_cmp<vector_cmp_x> ());
}
rw = repetitions.begin ();
for (tmp_rep_vector::const_iterator r = repetitions.begin (); r != repetitions.end (); ) {
tmp_rep_vector::const_iterator rr = r;
++rr;
db::Vector dxy2;
if (rr != repetitions.end ()) {
dxy2 = xypass2 ? db::Vector (0, safe_diff (rr->first.y (), r->first.y ())) : db::Vector (safe_diff (rr->first.x (), r->first.x ()), 0);
}
int nxy2 = 1;
db::Vector dxy2n (dxy2);
while (rr != repetitions.end () && rr->second == r->second && rr->first == r->first + dxy2n) {
++nxy2;
++rr;
dxy2n += dxy2;
}
if (nxy2 < 2 && xypass2) {
*rw++ = *r;
} else {
if (m_level < 2) {
Obj obj = n->first;
obj.move (r->first);
db::Vector a (xrep ? r->second.first : 0, xrep ? 0 : r->second.first);
writer->write (obj, db::Repetition (new RegularRepetition (a, dxy2, r->second.second, nxy2)));
} else {
db::Vector a (xrep ? r->second.first : 0, xrep ? 0 : r->second.first);
rep_vector.push_back (std::make_pair (r->first, db::Repetition (new RegularRepetition (a, dxy2, r->second.second, nxy2))));
}
}
r = rr;
}
repetitions.erase (rw, repetitions.end ());
}
}
if (m_level > 1) {
// Compute a cost for the repetitions
if (! n->second.empty ()) {
// irregular repetition contribution
array_cost += cost_of (n->second.front ().x ()) + cost_of (n->second.front ().y ());
for (std::vector<db::Vector>::const_iterator d = n->second.begin () + 1; d != n->second.end (); ++d) {
array_cost += std::max(1.0, cost_of (d->x () - d[-1].x ()) + cost_of (d->y () - d[-1].y ()));
}
}
bool array_set = false;
db::Vector a_ref, b_ref;
size_t in_ref = 0, im_ref = 0;
bool ref_set = false;
db::Coord x_ref = 0, y_ref = 0;
for (std::vector<std::pair<db::Vector, db::Repetition> >::const_iterator r = rep_vector.begin (); r != rep_vector.end (); ++r) {
db::Vector a, b;
size_t in = 0, im = 0;
tl_assert (r->second.is_regular (a, b, in, im));
array_cost += 2; // two bytes for the shape
// The cost of the first point (takes into account compression by reuse of one coordinate)
if (!ref_set || x_ref != r->first.x ()) {
array_cost += cost_of (r->first.x ());
}
if (!ref_set || y_ref != r->first.y ()) {
array_cost += cost_of (r->first.y ());
}
ref_set = true;
x_ref = r->first.x ();
y_ref = r->first.y ();
// Cost of the repetition (takes into account reuse)
if (! array_set || a != a_ref || b != b_ref || in != in_ref || im != im_ref) {
array_set = true;
a_ref = a;
b_ref = b;
in_ref = in;
im_ref = im;
array_cost += cost_of (a.x ()) + cost_of (b.x ()) + cost_of (a.y ()) + cost_of (b.y ()) + cost_of (in) + cost_of (im);
} else {
array_cost += 1; // one byte
}
// Note: the pointlist is reused, hence does not contribute
}
// And resolve the repetitions if it does not make sense to keep them
if (array_cost > simple_rep_cost) {
for (std::vector<std::pair<db::Vector, db::Repetition> >::const_iterator r = rep_vector.begin (); r != rep_vector.end (); ++r) {
for (db::RepetitionIterator i = r->second.begin (); ! i.at_end (); ++i) {
n->second.push_back (r->first + *i);
}
}
rep_vector.clear ();
std::sort (n->second.begin (), n->second.end (), vector_cmp_x ());
}
}
}
for (std::vector<std::pair<db::Vector, db::Repetition> >::const_iterator r = rep_vector.begin (); r != rep_vector.end (); ++r) {
Obj obj = n->first;
obj.move (r->first);
writer->write (obj, r->second);
}
if (n->second.size () > 1) {
// need to normalize?
db::Vector p0 = n->second.front ();
std::vector<db::Vector>::iterator pw = n->second.begin();
for (std::vector<db::Vector>::iterator p = pw + 1; p != n->second.end (); ++p) {
*pw++ = *p - p0;
}
n->second.erase (pw, n->second.end ());
IrregularRepetition *iterated_rep = new IrregularRepetition ();
iterated_rep->points ().swap (n->second);
Obj obj = n->first;
obj.move (p0);
writer->write (obj, Repetition (iterated_rep));
} else if (! n->second.empty ()) {
Obj obj = n->first;
obj.move (n->second.front ());
writer->write (obj, rep_single);
}
}
}
// ---------------------------------------------------------------------------------
// OASISWriter implementation
OASISWriter::OASISWriter ()
: mp_stream (0),
m_sf (1.0),
mp_layout (0),
mp_cell (0),
m_layer (0), m_datatype (0),
m_in_cblock (false),
m_propname_id (0),
m_propstring_id (0),
m_proptables_written (false),
m_progress (tl::to_string (tr ("Writing OASIS file")), 10000)
{
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
m_progress.set_unit (1024 * 1024);
}
// 1M CBLOCK buffer size
const size_t cblock_buffer_size = 1024 * 1024;
void
OASISWriter::write_record_id (char b)
{
if (m_in_cblock) {
if (m_cblock_buffer.size () > cblock_buffer_size) {
end_cblock ();
begin_cblock ();
}
m_cblock_buffer.write ((const char *) &b, 1);
} else {
mp_stream->put ((const char *) &b, 1);
}
}
void
OASISWriter::write_byte (char b)
{
if (m_in_cblock) {
m_cblock_buffer.write ((const char *) &b, 1);
} else {
mp_stream->put ((const char *) &b, 1);
}
}
void
OASISWriter::write_bytes (const char *b, size_t n)
{
if (m_in_cblock) {
m_cblock_buffer.write (b, n);
} else {
mp_stream->put (b, n);
}
}
void
OASISWriter::write (long long n)
{
if (n < 0) {
write (((unsigned long long) (-n) << 1) | 1);
} else {
write ((unsigned long long) n << 1);
}
}
void
OASISWriter::write (unsigned long long n)
{
char buffer [50];
char *bptr = buffer;
do {
unsigned char b = n & 0x7f;
n >>= 7;
if (n > 0) {
b |= 0x80;
}
*bptr++ = (char) b;
} while (n > 0);
write_bytes (buffer, bptr - buffer);
}
void
OASISWriter::write (long n)
{
if (n < 0) {
write (((unsigned long) (-n) << 1) | 1);
} else {
write ((unsigned long) n << 1);
}
}
void
OASISWriter::write (unsigned long n)
{
char buffer [50];
char *bptr = buffer;
do {
unsigned char b = n & 0x7f;
n >>= 7;
if (n > 0) {
b |= 0x80;
}
*bptr++ = (char) b;
} while (n > 0);
write_bytes (buffer, bptr - buffer);
}
void
OASISWriter::write (float d)
{
if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-6 && fabs (d) < double (std::numeric_limits<long>::max ())) {
// whole number (negative or positive)
if (d < 0.0) {
write_byte (1);
write ((unsigned long) floor (-d + 0.5));
} else {
write_byte (0);
write ((unsigned long) floor (d + 0.5));
}
} else {
write_byte (6);
// 4-Byte IEEE real
union {
float d;
uint32_t i;
} f2i;
f2i.d = d;
uint32_t i = f2i.i;
char b[sizeof (f2i.i)];
for (unsigned int n = 0; n < sizeof (f2i.i); n++) {
b[n] = char (i & 0xff);
i >>= 8;
}
write_bytes (b, sizeof (f2i.i));
}
}
void
OASISWriter::write (double d)
{
if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-10 && fabs (d) < double (std::numeric_limits<long>::max ())) {
// whole number (negative or positive)
if (d < 0.0) {
write_byte (1);
write ((unsigned long) floor (-d + 0.5));
} else {
write_byte (0);
write ((unsigned long) floor (d + 0.5));
}
} else {
write_byte (7);
// 8-Byte IEEE real
union {
double d;
uint64_t i;
} f2i;
f2i.d = d;
uint64_t i = f2i.i;
char b[sizeof (f2i.i)];
for (unsigned int n = 0; n < sizeof (f2i.i); n++) {
b[n] = char (i & 0xff);
i >>= 8;
}
write_bytes (b, sizeof (f2i.i));
}
}
void
OASISWriter::write_bstring (const char *s)
{
size_t l = strlen (s);
write (l);
write_bytes (s, l);
}
std::string
OASISWriter::make_astring (const char *s)
{
if (m_options.subst_char.empty ()) {
// No substitution: leave text as it is
return std::string (s);
} else {
return make_n_or_astring (s, m_options.subst_char, false);
}
}
void
OASISWriter::write_astring (const char *s)
{
std::string nstr = make_astring (s);
write (nstr.size ());
write_bytes (nstr.c_str (), nstr.size ());
}
std::string
OASISWriter::make_nstring (const char *s)
{
if (m_options.subst_char.empty ()) {
// No substitution: leave text as it is
return std::string (s);
} else {
return make_n_or_astring (s, m_options.subst_char, true);
}
}
void
OASISWriter::write_nstring (const char *s)
{
std::string nstr = make_nstring (s);
write (nstr.size ());
write_bytes (nstr.c_str (), nstr.size ());
}
void
OASISWriter::write_gdelta (const db::Vector &p, double sf)
{
db::Coord x = p.x ();
db::Coord y = p.y ();
if (sf != 1.0) {
x = safe_scale (sf, x);
y = safe_scale (sf, y);
}
if (x == -y || x == y || x == 0 || y == 0) {
unsigned long long dir = 0;
unsigned long long l = 0;
if (x > 0) {
l = x;
if (y == 0) {
dir = 0;
} else if (y < 0) {
dir = 7;
} else {
dir = 4;
}
} else if (x == 0) {
if (y < 0) {
l = -y;
dir = 3;
} else {
l = y;
dir = 1;
}
} else if (x < 0) {
l = -x;
if (y == 0) {
dir = 2;
} else if (y < 0) {
dir = 6;
} else {
dir = 5;
}
}
write ((l << 4) | (dir << 1));
} else {
unsigned long long d;
if (x < 0) {
d = ((unsigned long long) -x << 2) | 3;
} else {
d = ((unsigned long long) x << 2) | 1;
}
write (d);
write (y);
}
}
void
OASISWriter::write_coord (db::Coord c, double sf)
{
if (sf == 1.0) {
return write (c);
} else {
return write (safe_scale (sf, c));
}
}
void
OASISWriter::write_ucoord (db::Coord c, double sf)
{
// HACK: we misuse distance type as unsigned coord type here.
typedef db::coord_traits<db::Coord>::distance_type ucoord;
if (sf == 1.0) {
return write ((ucoord) c);
} else {
return write (safe_scale (sf, (ucoord) c));
}
}
void
OASISWriter::write_coord (db::Coord c)
{
if (m_sf == 1.0) {
return write (c);
} else {
return write (safe_scale (m_sf, c));
}
}
void
OASISWriter::write_ucoord (db::Coord c)
{
// HACK: we misuse distance type as unsigned coord type here.
typedef db::coord_traits<db::Coord>::distance_type ucoord;
if (m_sf == 1.0) {
return write ((ucoord) c);
} else {
return write (safe_scale (m_sf, (ucoord) c));
}
}
void
OASISWriter::emit_propname_def (db::properties_id_type prop_id)
{
const db::PropertiesRepository::properties_set &props = mp_layout->properties_repository ().properties (prop_id);
for (db::PropertiesRepository::properties_set::const_iterator p = props.begin (); p != props.end (); ++p) {
const tl::Variant &name = mp_layout->properties_repository ().prop_name (p->first);
const char *name_str = s_gds_property_name;
if (! make_gds_property (name)) {
name_str = name.to_string ();
}
if (m_propnames.insert (std::make_pair (name_str, m_propname_id)).second) {
write_record_id (7);
write_nstring (name_str);
++m_propname_id;
}
}
}
void
OASISWriter::emit_propstring_def (db::properties_id_type prop_id)
{
std::vector<tl::Variant> pv_list;
const db::PropertiesRepository::properties_set &props = mp_layout->properties_repository ().properties (prop_id);
for (db::PropertiesRepository::properties_set::const_iterator p = props.begin (); p != props.end (); ++p) {
pv_list.clear ();
const std::vector<tl::Variant> *pvl = &pv_list;
const tl::Variant &name = mp_layout->properties_repository ().prop_name (p->first);
if (! make_gds_property (name)) {
if (p->second.is_list ()) {
pvl = &p->second.get_list ();
} else if (!p->second.is_nil ()) {
pv_list.reserve (1);
pv_list.push_back (p->second);
}
} else {
pv_list.reserve (2);
pv_list.push_back (name.to_ulong ());
pv_list.push_back (p->second.to_string ());
}
for (std::vector<tl::Variant>::const_iterator pv = pvl->begin (); pv != pvl->end (); ++pv) {
if (!pv->is_double () && !pv->is_longlong () && !pv->is_ulonglong () && !pv->is_long () && !pv->is_ulong ()) {
if (m_propstrings.insert (std::make_pair (pv->to_string (), m_propstring_id)).second) {
write_record_id (9);
write_bstring (pv->to_string ());
++m_propstring_id;
}
}
}
}
}
void
OASISWriter::begin_cblock ()
{
tl_assert (! m_in_cblock);
m_in_cblock = true;
}
void
OASISWriter::end_cblock ()
{
tl_assert (m_in_cblock);
m_cblock_compressed.clear ();
tl::OutputStream deflated_stream (m_cblock_compressed);
tl::DeflateFilter deflate (deflated_stream);
// Reasoning for if(...): we don't want to access data from an empty vector through data()
if (m_cblock_buffer.size () > 0) {
deflate.put (m_cblock_buffer.data (), m_cblock_buffer.size ());
}
deflate.flush ();
const size_t compression_overhead = 4;
m_in_cblock = false;
if (m_cblock_buffer.size () > m_cblock_compressed.size () + compression_overhead) {
write_byte (34); // CBLOCK
// RFC1951 compression:
write_byte (0);
write (m_cblock_buffer.size ());
write (m_cblock_compressed.size ());
write_bytes (m_cblock_compressed.data (), m_cblock_compressed.size ());
} else if (m_cblock_buffer.size () > 0) { // Reasoning for if(...): we don't want to access data from an empty vector through data()
write_bytes (m_cblock_buffer.data (), m_cblock_buffer.size ());
}
m_cblock_buffer.clear ();
m_cblock_compressed.clear ();
}
void
OASISWriter::begin_table (size_t &pos)
{
if (pos == 0) {
pos = mp_stream->pos ();
if (m_options.write_cblocks) {
begin_cblock ();
}
}
}
void
OASISWriter::end_table (size_t pos)
{
if (pos != 0 && m_options.write_cblocks) {
end_cblock ();
}
}
void
OASISWriter::reset_modal_variables ()
{
// reset modal variables
mm_repetition.reset ();
mm_placement_cell.reset ();
mm_placement_x = 0;
mm_placement_y = 0;
mm_layer.reset ();
mm_datatype.reset ();
mm_textlayer.reset ();
mm_texttype.reset ();
mm_text_x = 0;
mm_text_y = 0;
mm_text_string.reset ();
mm_geometry_x = 0;
mm_geometry_y = 0;
mm_geometry_w.reset ();
mm_geometry_h.reset ();
mm_polygon_point_list.reset ();
mm_path_halfwidth.reset ();
mm_path_start_extension.reset ();
mm_path_end_extension.reset ();
mm_path_point_list.reset ();
mm_ctrapezoid_type.reset ();
mm_circle_radius.reset ();
mm_last_property_name.reset ();
mm_last_property_is_sprop.reset ();
mm_last_value_list.reset ();
}
void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
typedef db::coord_traits<db::Coord>::distance_type coord_distance_type;
mp_layout = &layout;
mp_cell = 0;
m_layer = m_datatype = 0;
m_in_cblock = false;
m_cblock_buffer.clear ();
m_options = options.get_options<OASISWriterOptions> ();
mp_stream = &stream;
double dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu ();
m_sf = options.scale_factor () * (layout.dbu () / dbu);
if (fabs (m_sf - 1.0) < 1e-9) {
// to avoid rounding problems, set to 1.0 exactly if possible.
m_sf = 1.0;
}
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber);
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_by_index;
cells.reserve (cell_set.size ());
cells_by_index.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);
}
}
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
if (cell_set.find (cell->cell_index ()) != cell_set.end ()) {
cells_by_index.push_back (cell->cell_index ());
}
}
// write header
char magic[] = "%SEMI-OASIS\015\012";
write_bytes (magic, sizeof (magic) - 1);
// START record
write_record_id (1);
write_bstring ("1.0");
write (1.0 / dbu);
write_byte (m_options.strict_mode ? 1 : 0); // offset-flag (strict mode: at the end, non-strict mode: at the beginning)
size_t cellnames_table_pos = 0;
size_t textstrings_table_pos = 0;
size_t propnames_table_pos = 0;
size_t propstrings_table_pos = 0;
size_t layernames_table_pos = 0;
std::map<db::cell_index_type, size_t> cell_positions;
if (! m_options.strict_mode) {
// offset table:
for (unsigned int i = 0; i < 12; ++i) {
write_byte (0);
}
}
// Reset the global variables
reset_modal_variables ();
// Prepare name tables
m_textstrings.clear ();
m_propnames.clear ();
m_propstrings.clear ();
// We will collect the standard properties here:
m_propstring_id = m_propname_id = 0;
m_proptables_written = false;
std::vector<std::pair<std::string, unsigned int> > init_props;
// write file properties (must happen before any other PROPNAME record since formally the
// PROPERTY records are associated with the names rather than the file)
// prepare some property ID's in strict mode .. in non-strict mode we write strings to
// avoid forward references
if (m_options.strict_mode) {
m_propnames.insert (std::make_pair (s_cell_offset_name, m_propname_id++));
m_propnames.insert (std::make_pair (s_gds_property_name, m_propname_id++));
if (m_options.write_std_properties > 0) {
m_propnames.insert (std::make_pair (s_max_signed_integer_width_name, m_propname_id++));
m_propnames.insert (std::make_pair (s_max_unsigned_integer_width_name, m_propname_id++));
m_propnames.insert (std::make_pair (s_top_cell_name, m_propname_id++));
if (m_options.write_std_properties > 1) {
m_propnames.insert (std::make_pair (s_bounding_boxes_available_name, m_propname_id++));
}
}
}
if (m_options.write_std_properties > 0) {
write_property_def (s_max_signed_integer_width_name, tl::Variant (sizeof (db::Coord)), true);
write_property_def (s_max_unsigned_integer_width_name, tl::Variant (sizeof (coord_distance_type)), true);
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &c = layout.cell (*cell);
bool is_top = true;
for (db::Cell::parent_cell_iterator p = c.begin_parent_cells (); p != c.end_parent_cells () && is_top; ++p) {
is_top = (cell_set.find (*p) == cell_set.end ());
}
if (is_top) {
write_property_def (s_top_cell_name, tl::Variant (make_nstring (layout.cell_name (*cell))), true);
}
}
if (m_options.write_std_properties > 1) {
write_property_def (s_bounding_boxes_available_name, tl::Variant ((unsigned int) 2), true);
}
}
if (m_options.write_std_properties > 1) {
m_propnames.insert (std::make_pair (s_bounding_box_name, m_propname_id++));
}
if (layout.prop_id () != 0) {
write_props (layout.prop_id ());
}
// build property name and value string tables
{
// write the property names collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_pn;
rev_pn.reserve (m_propnames.size ());
for (std::map <std::string, unsigned long>::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) {
rev_pn.push_back (std::make_pair (p->second, p->first));
}
std::sort (rev_pn.begin (), rev_pn.end ());
for (std::vector<std::pair<unsigned long, std::string> >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_pn.begin ()));
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (p->second.c_str ());
}
// collect and write the future property names
std::set <db::properties_id_type> prop_ids_done;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0) {
begin_table (propnames_table_pos);
emit_propname_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
// emit property name required for the PCell context information
std::vector <std::string> context_prop_strings;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) {
if (m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id)).second) {
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (klayout_context_name);
++m_propname_id;
}
break;
}
}
end_table (propnames_table_pos);
}
{
// write the property strings collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_ps;
rev_ps.reserve (m_propstrings.size ());
for (std::map <std::string, unsigned long>::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
rev_ps.push_back (std::make_pair (p->second, p->first));
}
std::sort (rev_ps.begin (), rev_ps.end ());
for (std::vector<std::pair<unsigned long, std::string> >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_ps.begin ()));
begin_table (propstrings_table_pos);
write_record_id (9);
write_nstring (p->second.c_str ());
}
// collect and write the future property strings
std::set <db::properties_id_type> prop_ids_done;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (cref.prop_id ());
begin_table (propnames_table_pos);
emit_propstring_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
// emit property string id's required for the PCell context information
std::vector <std::string> context_prop_strings;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
m_progress.set (mp_stream->pos ());
const db::Cell &cref (layout.cell (*cell));
if (cref.is_proxy () && ! cref.is_top ()) {
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) {
begin_table (propstrings_table_pos);
write_record_id (9);
write_bstring (c->c_str ());
++m_propstring_id;
}
}
}
}
}
end_table (propstrings_table_pos);
}
// Now we cannot open new property ID's in strict mode
m_proptables_written = true;
// build cell name table now in non-strict mode (in strict mode it is written at the
// end because then we have the cell positions fo S_CELL_OFFSET)
if (! m_options.strict_mode) {
size_t pos = 0;
bool sequential = true;
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
}
// CELLNAME (implicit or explicit)
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
begin_table (pos);
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
if (m_options.write_std_properties > 1) {
reset_modal_variables ();
// write S_BOUNDING_BOX entries
std::vector<tl::Variant> values;
// TODO: how to set the "depends on external cells" flag?
db::Box bbox = layout.cell (*cell).bbox ();
if (bbox.empty ()) {
// empty box
values.push_back (tl::Variant ((unsigned int) 0x2));
bbox = db::Box (0, 0, 0, 0);
} else {
values.push_back (tl::Variant ((unsigned int) 0x0));
}
values.push_back (tl::Variant (bbox.left ()));
values.push_back (tl::Variant (bbox.bottom ()));
values.push_back (tl::Variant (bbox.width ()));
values.push_back (tl::Variant (bbox.height ()));
write_property_def (s_bounding_box_name, values, true);
}
}
end_table (pos);
}
// build text string table
{
unsigned int id = 0;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) {
begin_table (textstrings_table_pos);
write_record_id (5);
write_astring (shape->text_string ());
++id;
m_progress.set (mp_stream->pos ());
}
++shape;
}
}
}
end_table (textstrings_table_pos);
}
// write layernames table
{
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (! l->second.name.empty ()) {
begin_table (layernames_table_pos);
// write mappings to text layer and shape layers
write_record_id (11);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
write_record_id (12);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
m_progress.set (mp_stream->pos ());
}
}
end_table (layernames_table_pos);
}
std::vector <std::string> context_prop_strings;
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));
mp_cell = &cref;
// don't write ghost cells unless they are not empty (any more)
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
// cell header
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
write_record_id (13); // CELL
write ((unsigned long) *cell);
reset_modal_variables ();
if (m_options.write_cblocks) {
begin_cblock ();
}
// context information as property named KLAYOUT_CONTEXT
if (cref.is_proxy ()) {
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
write_record_id (28);
write_byte (char (0xf6));
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
tl_assert (pni != m_propnames.end ());
write (pni->second);
write ((unsigned long) context_prop_strings.size ());
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
tl_assert (psi != m_propstrings.end ());
write (psi->second);
}
mm_last_property_name = klayout_context_name;
mm_last_property_is_sprop = false;
mm_last_value_list.reset ();
}
}
if (cref.prop_id () != 0) {
write_props (cref.prop_id ());
}
// instances
if (cref.cell_instances () > 0) {
write_insts (cell_set);
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
const db::Shapes &shapes = cref.shapes (l->first);
if (! shapes.empty ()) {
write_shapes (l->second, shapes);
m_progress.set (mp_stream->pos ());
}
}
// end CBLOCK if required
if (m_options.write_cblocks) {
end_cblock ();
}
// end of cell
}
}
// write cell table at the end in strict mode (in that mode we need the cell positions
// for the S_CELL_OFFSET properties)
if (m_options.strict_mode) {
bool sequential = true;
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
}
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
begin_table (cellnames_table_pos);
// CELLNAME (explicit)
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
reset_modal_variables ();
if (m_options.write_std_properties > 1) {
// write S_BOUNDING_BOX entries
std::vector<tl::Variant> values;
// TODO: how to set the "depends on external cells" flag?
db::Box bbox = layout.cell (*cell).bbox ();
if (bbox.empty ()) {
// empty box
values.push_back (tl::Variant ((unsigned int) 0x2));
bbox = db::Box (0, 0, 0, 0);
} else {
values.push_back (tl::Variant ((unsigned int) 0x0));
}
values.push_back (tl::Variant (bbox.left ()));
values.push_back (tl::Variant (bbox.bottom ()));
values.push_back (tl::Variant (bbox.width ()));
values.push_back (tl::Variant (bbox.height ()));
write_property_def (s_bounding_box_name, values, true);
}
// PROPERTY record with S_CELL_OFFSET
std::map<db::cell_index_type, size_t>::const_iterator pp = cell_positions.find (*cell);
if (pp != cell_positions.end ()) {
write_property_def (s_cell_offset_name, tl::Variant (pp->second), true);
} else {
write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true);
}
}
end_table (cellnames_table_pos);
}
// END record
size_t end_record_pos = mp_stream->pos ();
write_record_id (2);
if (m_options.strict_mode) {
// offset table for strict mode (write it now since we have the table offsets now)
// cellnames
write_byte (1);
write (cellnames_table_pos);
// textstrings
write_byte (1);
write (textstrings_table_pos);
// propnames
write_byte (1);
write (propnames_table_pos);
// propstrings
write_byte (1);
write (propstrings_table_pos);
// layernames
write_byte (1);
write (layernames_table_pos);
// xnames (not used)
write_byte (1);
write (0);
}
// write a b-string to pad up to 255 bytes
// (this bstring consists of a "long zero" and no characters
while (mp_stream->pos () < end_record_pos + 254) {
write_byte (char (0x80));
}
write_byte (0);
// validation-scheme
write_byte (0);
m_progress.set (mp_stream->pos ());
}
void
OASISWriter::write (const Repetition &rep)
{
if (mm_repetition == rep) {
write_byte (0); // reuse
} else {
mm_repetition = rep;
db::Vector a, b;
size_t amax, bmax;
bool is_reg = rep.is_regular (a, b, amax, bmax);
const std::vector<db::Vector> *iterated = rep.is_iterated ();
if (iterated) {
tl_assert (! iterated->empty ());
// extract common grid
db::Coord g = 0;
for (std::vector<db::Vector>::const_iterator p = iterated->begin (); p != iterated->end (); ++p) {
db::Coord x = safe_scale (m_sf, p->x ());
if (x < 0) {
x = -x;
}
if (x != 0) {
g = (g == 0) ? x : tl::gcd (g, x);
}
db::Coord y = safe_scale (m_sf, p->y ());
if (y < 0) {
y = -y;
}
if (y != 0) {
g = (g == 0) ? y : tl::gcd (g, y);
}
}
if (g <= 1) {
write_byte (10);
write (iterated->size () - 1);
g = 1;
} else {
write_byte (11);
write (iterated->size () - 1);
write_ucoord (g, 1.0);
}
db::Vector last_point;
for (std::vector<db::Vector>::const_iterator p = iterated->begin (); p != iterated->end (); ++p) {
db::Vector s (safe_scale (m_sf, p->x ()), safe_scale (m_sf, p->y ()));
db::Vector delta = s - last_point;
last_point = s;
write_gdelta (db::Vector (delta.x () / g, delta.y () / g), 1.0);
}
} else {
tl_assert (is_reg);
// currently there are only regular repetitions
// TODO: optimize for orthogonal cases
tl_assert (is_reg);
tl_assert (amax >= 2 || bmax >= 2);
if (amax == 1 || bmax == 1) {
if (bmax == 1) {
b = a;
bmax = amax;
}
if (b.x () == 0 && b.y () >= 0) {
write_byte (3);
write (bmax - 2);
write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0) {
write_byte (2);
write (bmax - 2);
write_ucoord (b.x ());
} else {
write_byte (9);
write (bmax - 2);
write_gdelta (b);
}
} else if (b.x () == 0 && b.y () >= 0 && a.y () == 0 && a.x () >= 0) {
write_byte (1);
write (amax - 2);
write (bmax - 2);
write_ucoord (a.x ());
write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0 && a.x () == 0 && a.y () >= 0) {
write_byte (1);
write (bmax - 2);
write (amax - 2);
write_ucoord (b.x ());
write_ucoord (a.y ());
} else {
write_byte (8);
write (amax - 2);
write (bmax - 2);
write_gdelta (a);
write_gdelta (b);
}
}
}
}
void
OASISWriter::write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep)
{
db::Vector tr = inst.front ().disp () + disp;
unsigned char info = 0x40; // by reference number
if (mm_placement_cell != inst.object ().cell_index ()) {
info |= 0x80;
}
if (mm_placement_x != tr.x ()) {
info |= 0x20;
}
if (mm_placement_y != tr.y ()) {
info |= 0x10;
}
if (rep != Repetition ()) {
info |= 0x08;
}
if (inst.front ().is_mirror ()) {
info |= 0x01;
}
if (inst.is_complex ()) {
write_record_id (18);
write_byte (info | 0x06);
} else {
write_record_id (17);
write_byte (info | ((inst.front ().rot () & 0x03) << 1));
}
if (info & 0x80) {
mm_placement_cell = inst.object ().cell_index ();
write ((unsigned long) mm_placement_cell.get ());
}
if (inst.is_complex ()) {
write (inst.complex_trans ().mag ());
write (inst.complex_trans ().angle ());
}
if (info & 0x20) {
mm_placement_x = tr.x ();
write_coord (mm_placement_x.get ());
}
if (info & 0x10) {
mm_placement_y = tr.y ();
write_coord (mm_placement_y.get ());
}
if (info & 0x08) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
void
OASISWriter::write (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Repetition &rep)
{
m_progress.set (mp_stream->pos ());
db::Vector a, b;
unsigned long amax, bmax;
bool is_reg = inst.is_regular_array (a, b, amax, bmax);
if (is_reg && (amax > 1 || bmax > 1)) {
// we cannot use the repetition - instead we write every single instance and use the repetition
// for the array information
db::Repetition array_rep (new db::RegularRepetition (a, b, amax, bmax));
if (rep != db::Repetition ()) {
for (db::RepetitionIterator r = rep.begin (); ! r.at_end (); ++r) {
write_inst_with_rep (inst, prop_id, *r, array_rep);
}
} else {
write_inst_with_rep (inst, prop_id, db::Vector (), array_rep);
}
} else {
write_inst_with_rep (inst, prop_id, db::Vector (), rep);
}
}
void
OASISWriter::write_insts (const std::set <db::cell_index_type> &cell_set)
{
int level = m_options.compression_level;
// use compression 0 for the instances - this preserves the arrays and does not create new ones, the
// remaining ones are compressed into irregular arrays
Compressor<db::CellInstArray> inst_compressor (0);
Compressor<db::CellInstArrayWithProperties> inst_with_properties_compressor (0);
db::Repetition single_rep;
// Collect all instances
for (db::Cell::const_iterator inst_iterator = mp_cell->begin (); ! inst_iterator.at_end (); ++inst_iterator) {
if (cell_set.find (inst_iterator->cell_index ()) != cell_set.end ()) {
db::properties_id_type prop_id = inst_iterator->prop_id ();
if (level <= 0) {
// no compression -> just write
write (inst_iterator->cell_inst (), prop_id, single_rep);
} else {
// reduce by displacement
db::CellInstArray inst_array = inst_iterator->cell_inst ();
db::Vector disp = inst_array.front ().disp ();
inst_array.transform (db::Trans (-disp));
if (prop_id != 0) {
inst_with_properties_compressor.add (db::CellInstArrayWithProperties (inst_array, prop_id), disp);
} else {
inst_compressor.add (inst_array, disp);
}
}
}
}
inst_compressor.flush (this);
inst_with_properties_compressor.flush (this);
}
void
OASISWriter::write_props (db::properties_id_type prop_id)
{
std::vector<tl::Variant> pv_list;
const db::PropertiesRepository::properties_set &props = mp_layout->properties_repository ().properties (prop_id);
for (db::PropertiesRepository::properties_set::const_iterator p = props.begin (); p != props.end (); ++p) {
m_progress.set (mp_stream->pos ());
const tl::Variant &name = mp_layout->properties_repository ().prop_name (p->first);
const char *name_str = s_gds_property_name;
bool sflag = true;
pv_list.clear ();
const std::vector<tl::Variant> *pvl = &pv_list;
if (! make_gds_property (name)) {
name_str = name.to_string ();
sflag = false;
if (p->second.is_list ()) {
pvl = &p->second.get_list ();
} else if (!p->second.is_nil ()) {
pv_list.reserve (1);
pv_list.push_back (p->second);
}
} else {
pv_list.reserve (2);
pv_list.push_back (name.to_ulong ());
pv_list.push_back (p->second.to_string ());
}
write_property_def (name_str, *pvl, sflag);
}
}
void
OASISWriter::write_property_def (const char *name_str, const tl::Variant &pv, bool sflag)
{
std::vector<tl::Variant> pvl;
pvl.reserve (1);
pvl.push_back (pv);
write_property_def (name_str, pvl, sflag);
}
void
OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Variant> &pvl, bool sflag)
{
bool same_name = (mm_last_property_name == name_str);
bool same_value = (mm_last_value_list == pvl);
bool same_sflag = (mm_last_property_is_sprop == sflag);
if (same_name && same_value && same_sflag) {
write_record_id (29); // repeat property
} else {
write_record_id (28);
unsigned char info = sflag ? 1 : 0;
if (same_value) {
info |= 0x08;
} else {
if (pvl.size () >= 15) {
info |= 0xf0;
} else {
info |= ((unsigned char)pvl.size ()) << 4;
}
}
if (! same_name) {
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (name_str);
// In strict mode always write property ID's: before we have issued the table we can
// create new ID's.
if (pni == m_propnames.end () && m_options.strict_mode) {
tl_assert (! m_proptables_written);
pni = m_propnames.insert (std::make_pair (name_str, m_propname_id++)).first;
}
if (pni == m_propnames.end ()) {
// write the name itself, if not found in the property repository
write_byte (info | 0x04);
write_nstring (name_str);
} else {
// write the property ID
write_byte (info | 0x06);
write (pni->second);
}
mm_last_property_name = name_str;
} else {
write_byte (info);
}
if (! same_value) {
if (pvl.size () >= 15) {
write ((unsigned long) pvl.size ());
}
// write property values
for (unsigned long i = 0; i < pvl.size (); ++i) {
const tl::Variant &v = pvl[i];
if (v.is_double ()) {
write (v.to_double ());
} else if (v.is_longlong ()) {
write_byte (9);
write (v.to_longlong ());
} else if (v.is_ulonglong ()) {
write_byte (8);
write (v.to_ulonglong ());
} else if (v.is_long ()) {
write_byte (9);
write (v.to_long ());
} else if (v.is_ulong ()) {
write_byte (8);
write (v.to_ulong ());
} else {
const char *pvs = v.to_string ();
std::map <std::string, unsigned long>::const_iterator pvi = m_propstrings.find (pvs);
// In strict mode always write property string ID's: before we have issued the table we can
// create new ID's.
if (pvi == m_propstrings.end () && m_options.strict_mode) {
tl_assert (! m_proptables_written);
pvi = m_propstrings.insert (std::make_pair (pvs, m_propstring_id++)).first;
}
if (pvi != m_propstrings.end ()) {
write_byte (13 + string_type (pvs));
write (pvi->second);
} else {
write_byte (10 + string_type (pvs));
write_bstring (pvs);
}
}
}
mm_last_value_list = pvl;
}
mm_last_property_is_sprop = sflag;
}
}
void
OASISWriter::write_pointlist (const std::vector<db::Vector> &pointlist, bool for_polygons)
{
tl_assert ((for_polygons && pointlist.size () > 1) || (! for_polygons && pointlist.size () > 0));
// determine type: 0 (manhattan, horizontal first), 1 (manhattan, vert. first), -1 other
db::Vector plast (0, 0);
int type = -1;
int hvlast = -1;
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end (); ++p) {
int hv = -1;
if (p->x () == plast.x ()) {
hv = 1;
} else if (p->y () == plast.y ()) {
hv = 0;
} else {
type = -1;
break;
}
if (type == -1) {
type = hv;
} else if (hv != !hvlast) {
type = -1;
break;
}
hvlast = hv;
plast = *p;
}
// test last displacement for polygons
if (for_polygons && type >= 0) {
if (hvlast != type) {
type = -1;
} else if (plast.x () == 0) {
if (hvlast != 0) {
type = -1;
}
} else if (plast.y () == 0) {
if (hvlast != 1) {
type = -1;
}
} else {
type = -1;
}
}
if (type == 0 || type == 1) {
// manhattan pointlist
write_byte (type);
size_t implicit = for_polygons ? 1 : 0;
write ((unsigned long) (pointlist.size () - implicit));
db::Vector plast (0, 0);
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end () - implicit; ++p) {
db::Coord x = (m_sf == 1.0 ? p->x () : safe_scale (m_sf, p->x ()));
db::Coord y = (m_sf == 1.0 ? p->y () : safe_scale (m_sf, p->y ()));
db::Coord d = x - plast.x ();
if (d == 0) {
d = y - plast.y ();
}
write (d);
plast = db::Vector (x, y);
}
} else {
// generic pointlist
write_byte (4);
write ((unsigned long) pointlist.size ());
db::Vector plast (0, 0);
if (m_sf == 1.0) {
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end (); ++p) {
write_gdelta (*p - plast, 1.0);
plast = *p;
}
} else {
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end (); ++p) {
db::Vector ps (safe_scale (m_sf, p->x ()), safe_scale (m_sf, p->y ()));
write_gdelta (ps - plast, 1.0);
plast = ps;
}
}
}
}
void
OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const db::Repetition &rep)
{
m_progress.set (mp_stream->pos ());
db::Trans trans = text.trans ();
std::map <std::string, unsigned long>::const_iterator ts = m_textstrings.find (text.string ());
tl_assert (ts != m_textstrings.end ());
unsigned long text_id = ts->second;
unsigned char info = 0x20;
if (mm_text_string != text.string ()) {
info |= 0x40;
}
if (mm_textlayer != m_layer) {
info |= 0x01;
}
if (mm_texttype != m_datatype) {
info |= 0x02;
}
if (mm_text_x != trans.disp ().x ()) {
info |= 0x10;
}
if (mm_text_y != trans.disp ().y ()) {
info |= 0x08;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (19);
write_byte (info);
if (info & 0x40) {
mm_text_string = text.string ();
write ((unsigned long) text_id);
}
if (info & 0x01) {
mm_textlayer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_texttype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x10) {
mm_text_x = trans.disp ().x ();
write_coord (mm_text_x.get ());
}
if (info & 0x08) {
mm_text_y = trans.disp ().y ();
write_coord (mm_text_y.get ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
void
OASISWriter::write (const db::SimplePolygon &polygon, db::properties_id_type prop_id, const db::Repetition &rep)
{
m_progress.set (mp_stream->pos ());
// TODO: how to deal with max vertex count?
db::Polygon::polygon_contour_iterator e = polygon.begin_hull ();
// don't write empty polygons
if (e == polygon.end_hull ()) {
return;
}
db::Point start = *e;
m_pointlist.clear ();
while (++e != polygon.end_hull ()) {
m_pointlist.push_back (*e - start);
}
if (m_pointlist.size () < 2) {
std::string msg = tl::to_string (tr ("Polygons with less than three points cannot be written to OASIS files (cell ")) + mp_layout->cell_name (mp_cell->cell_index ()) + tl::to_string (tr (", position ")) + tl::to_string (start.x ()) + ", " + tl::to_string (start.y ()) + " DBU)";
if (m_options.permissive) {
tl::warn << msg;
return;
} else {
throw tl::Exception (msg);
}
}
unsigned char info = 0x00;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (mm_geometry_x != start.x ()) {
info |= 0x10;
}
if (mm_geometry_y != start.y ()) {
info |= 0x08;
}
if (mm_polygon_point_list != m_pointlist) {
info |= 0x20;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (21);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x20) {
mm_polygon_point_list.swap (m_pointlist);
write_pointlist (mm_polygon_point_list.get (), true /*for polygons*/);
}
if (info & 0x10) {
mm_geometry_x = start.x ();
write_coord (start.x ());
}
if (info & 0x08) {
mm_geometry_y = start.y ();
write_coord (start.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
void
OASISWriter::write (const db::Polygon &polygon, db::properties_id_type prop_id, const db::Repetition &rep)
{
if (polygon.holes () > 0) {
// resolve holes ...
std::vector<db::Polygon> polygons;
db::EdgeProcessor ep;
ep.insert_sequence (polygon.begin_edge ());
db::PolygonContainer pc (polygons);
db::PolygonGenerator out (pc, true /*resolve holes*/, false /*max coherence*/);
db::SimpleMerge op;
ep.process (out, op);
for (std::vector<db::Polygon>::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
write (*p, prop_id, rep);
}
} else {
m_progress.set (mp_stream->pos ());
// TODO: how to deal with max vertex count?
db::Polygon::polygon_contour_iterator e = polygon.begin_hull ();
// don't write empty polygons
if (e == polygon.end_hull ()) {
return;
}
db::Point start = *e;
m_pointlist.clear ();
while (++e != polygon.end_hull ()) {
m_pointlist.push_back (*e - start);
}
if (m_pointlist.size () < 2) {
std::string msg = tl::to_string (tr ("Polygons with less than three points cannot be written to OASIS files (cell ")) + mp_layout->cell_name (mp_cell->cell_index ()) + tl::to_string (tr (", position ")) + tl::to_string (start.x ()) + ", " + tl::to_string (start.y ()) + " DBU)";
if (m_options.permissive) {
tl::warn << msg;
return;
} else {
throw tl::Exception (msg);
}
}
unsigned char info = 0x00;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (mm_geometry_x != start.x ()) {
info |= 0x10;
}
if (mm_geometry_y != start.y ()) {
info |= 0x08;
}
if (mm_polygon_point_list != m_pointlist) {
info |= 0x20;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (21);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x20) {
mm_polygon_point_list.swap (m_pointlist);
write_pointlist (mm_polygon_point_list.get (), true /*for polygons*/);
}
if (info & 0x10) {
mm_geometry_x = start.x ();
write_coord (start.x ());
}
if (info & 0x08) {
mm_geometry_y = start.y ();
write_coord (start.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
}
void
OASISWriter::write (const db::Box &box, db::properties_id_type prop_id, const db::Repetition &rep)
{
m_progress.set (mp_stream->pos ());
unsigned char info = 0x00;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (box.width () == box.height ()) {
info |= 0x80; // square
} else {
if (mm_geometry_h != box.height ()) {
info |= 0x20;
}
}
if (mm_geometry_w != box.width ()) {
info |= 0x40;
}
if (mm_geometry_x != box.left ()) {
info |= 0x10;
}
if (mm_geometry_y != box.bottom ()) {
info |= 0x08;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (20);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
mm_geometry_w = box.width ();
mm_geometry_h = box.height ();
if (info & 0x40) {
write_ucoord (mm_geometry_w.get ());
}
if (info & 0x20) {
write_ucoord (mm_geometry_h.get ());
}
if (info & 0x10) {
mm_geometry_x = box.left ();
write_coord (mm_geometry_x.get ());
}
if (info & 0x08) {
mm_geometry_y = box.bottom ();
write_coord (mm_geometry_y.get ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
void
OASISWriter::write (const db::Path &path, db::properties_id_type prop_id, const db::Repetition &rep)
{
typedef db::coord_traits<db::Coord>::distance_type ucoord;
// don't write empty paths
if (path.begin () == path.end ()) {
return;
}
m_progress.set (mp_stream->pos ());
std::pair<db::Path::coord_type, db::Path::coord_type> ext (0, 0);
// for round paths, circles are placed to mimic the extensions
if (! path.round ()) {
ext = path.extensions ();
// Because we scale the width already, we also need to scale the extensions for comparing them
ext.first = safe_scale (m_sf, ext.first);
ext.second = safe_scale (m_sf, ext.second);
}
db::Path::iterator e = path.begin ();
db::Point start = *e;
m_pointlist.clear ();
while (++e != path.end ()) {
m_pointlist.push_back (*e - start);
}
if (m_pointlist.empty ()) {
if (path.round ()) {
db::Coord w = safe_scale (m_sf, path.width ());
db::Coord hw = w / 2;
if (hw * 2 != w) {
std::string msg = tl::to_string (tr ("Circles with odd diameter cannot be written to OASIS files (cell ")) + mp_layout->cell_name (mp_cell->cell_index ()) + tl::to_string (tr (", position ")) + tl::to_string (start.x ()) + ", " + tl::to_string (start.y ()) + " DBU)";
if (m_options.permissive) {
tl::warn << msg << " - " << tl::to_string (tr ("circle diameter is rounded"));
} else {
throw tl::Exception (msg);
}
}
unsigned char info = 0;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (mm_circle_radius != hw) {
info |= 0x20;
}
if (mm_geometry_x != start.x ()) {
info |= 0x10;
}
if (mm_geometry_y != start.y ()) {
info |= 0x08;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (27);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x20) {
mm_circle_radius = hw;
// NOTE: the radius has already been scaled, so we don't use write_ucoord here
write ((ucoord) hw);
}
if (info & 0x10) {
mm_geometry_x = start.x ();
write_coord (start.x ());
}
if (info & 0x08) {
mm_geometry_y = start.y ();
write_coord (start.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
} else {
// Single-point paths are translated into polygons
write (path.polygon (), prop_id, rep);
}
} else {
db::Coord w = safe_scale (m_sf, path.width ());
db::Coord hw = w / 2;
if (hw * 2 != w) {
std::string msg = tl::to_string (tr ("Paths with odd width cannot be written to OASIS files (cell ")) + mp_layout->cell_name (mp_cell->cell_index ()) + tl::to_string (tr (", position ")) + tl::to_string (start.x ()) + ", " + tl::to_string (start.y ()) + " DBU)";
if (m_options.permissive) {
tl::warn << msg << " - " << tl::to_string (tr ("path diameter is rounded"));
} else {
throw tl::Exception (msg);
}
}
db::Point end = start + m_pointlist.back ();
unsigned char info = 0x00;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (mm_geometry_x != start.x ()) {
info |= 0x10;
}
if (mm_geometry_y != start.y ()) {
info |= 0x08;
}
if (mm_path_point_list != m_pointlist) {
info |= 0x20;
}
if (mm_path_start_extension != ext.first || mm_path_end_extension != ext.second) {
info |= 0x80;
}
if (mm_path_halfwidth != hw) {
info |= 0x40;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_record_id (22);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x40) {
mm_path_halfwidth = hw;
// NOTE: the half-width has already been scaled, so we don't use write_ucoord here
write ((ucoord) hw);
}
if (info & 0x80) {
unsigned char ext_scheme = 0;
if (mm_path_start_extension == ext.first) {
// 00
} else if (ext.first == 0) {
ext_scheme |= 0x04;
} else if (ext.first == hw) {
ext_scheme |= 0x08;
} else {
ext_scheme |= 0x0c;
}
if (mm_path_end_extension == ext.second) {
// 00
} else if (ext.second == 0) {
ext_scheme |= 0x01;
} else if (ext.second == hw) {
ext_scheme |= 0x02;
} else {
ext_scheme |= 0x03;
}
write_byte (ext_scheme);
if ((ext_scheme & 0x0c) == 0x0c) {
// NOTE: ext.first is already scaled, so we don't use write_coord
write (ext.first);
}
if ((ext_scheme & 0x03) == 0x03) {
// NOTE: ext.second is already scaled, so we don't use write_coord
write (ext.second);
}
mm_path_start_extension = ext.first;
mm_path_end_extension = ext.second;
}
if (info & 0x20) {
mm_path_point_list.swap (m_pointlist);
write_pointlist (mm_path_point_list.get (), false /*=for paths*/);
}
if (info & 0x10) {
mm_geometry_x = start.x ();
write_coord (start.x ());
}
if (info & 0x08) {
mm_geometry_y = start.y ();
write_coord (start.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
if (path.round ()) {
// write two circles at the path ends to mimic the round path ends.
unsigned char info = 0;
if (mm_circle_radius != hw) {
info |= 0x20;
}
if (mm_geometry_x != start.x ()) {
info |= 0x10;
}
if (mm_geometry_y != start.y ()) {
info |= 0x08;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_byte (27);
write_byte (info);
if (info & 0x20) {
mm_circle_radius = hw;
// NOTE: the half-width has already been scaled, so we don't use write_ucoord here
write ((ucoord) hw);
}
if (info & 0x10) {
mm_geometry_x = start.x ();
write_coord (start.x ());
}
if (info & 0x08) {
mm_geometry_y = start.y ();
write_coord (start.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
info = 0;
if (mm_geometry_x != end.x ()) {
info |= 0x10;
}
if (mm_geometry_y != end.y ()) {
info |= 0x08;
}
if (! rep.is_singular ()) {
info |= 0x04;
}
write_byte (27);
write_byte (info);
if (info & 0x10) {
mm_geometry_x = end.x ();
write_coord (end.x ());
}
if (info & 0x08) {
mm_geometry_y = end.y ();
write_coord (end.y ());
}
if (info & 0x04) {
write (rep);
}
if (prop_id != 0) {
write_props (prop_id);
}
}
}
}
void
OASISWriter::write (const db::Edge &edge, db::properties_id_type prop_id, const db::Repetition & /*rep*/)
{
m_progress.set (mp_stream->pos ());
m_pointlist.reserve (1);
m_pointlist.erase (m_pointlist.begin (), m_pointlist.end ());
m_pointlist.push_back (edge.p2 () - edge.p1 ());
unsigned char info = 0x00;
if (mm_layer != m_layer) {
info |= 0x01;
}
if (mm_datatype != m_datatype) {
info |= 0x02;
}
if (mm_geometry_x != edge.p1 ().x ()) {
info |= 0x10;
}
if (mm_geometry_y != edge.p1 ().y ()) {
info |= 0x08;
}
if (mm_path_point_list != m_pointlist) {
info |= 0x20;
}
if (mm_path_start_extension != 0 || mm_path_end_extension != 0) {
info |= 0x80;
}
if (mm_path_halfwidth != 0) {
info |= 0x40;
}
write_record_id (22);
write_byte (info);
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
}
if (info & 0x40) {
mm_path_halfwidth = 0;
write (0);
}
if (info & 0x80) {
write_byte (0x05); // flush
mm_path_start_extension = 0;
mm_path_end_extension = 0;
}
if (info & 0x20) {
mm_path_point_list = m_pointlist;
write_pointlist (m_pointlist, false /*=for paths*/);
}
if (info & 0x10) {
mm_geometry_x = edge.p1 ().x ();
write_coord (edge.p1 ().x ());
}
if (info & 0x08) {
mm_geometry_y = edge.p1 ().y ();
write_coord (edge.p1 ().y ());
}
if (prop_id != 0) {
write_props (prop_id);
}
}
void
OASISWriter::write_shapes (const db::LayerProperties &lprops, const db::Shapes &shapes)
{
int level = m_options.compression_level;
bool recompress = m_options.recompress;
m_layer = lprops.layer;
m_datatype = lprops.datatype;
Compressor<db::Path> path_compressor (level);
Compressor<db::SimplePolygon> simple_polygon_compressor (level);
Compressor<db::Polygon> polygon_compressor (level);
Compressor<db::Edge> edge_compressor (level);
Compressor<db::Box> box_compressor (level);
Compressor<db::Text> text_compressor (level);
Compressor<db::PathWithProperties> path_with_properties_compressor (level);
Compressor<db::SimplePolygonWithProperties> simple_polygon_with_properties_compressor (level);
Compressor<db::PolygonWithProperties> polygon_with_properties_compressor (level);
Compressor<db::EdgeWithProperties> edge_with_properties_compressor (level);
Compressor<db::BoxWithProperties> box_with_properties_compressor (level);
Compressor<db::TextWithProperties> text_with_properties_compressor (level);
db::Repetition single_rep;
for (db::ShapeIterator shape = shapes.begin (db::ShapeIterator::All); ! shape.at_end (); ) {
if (level <= 0) {
if (shape->is_simple_polygon ()) {
db::SimplePolygon sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_polygon ()) {
db::Polygon sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_path ()) {
db::Path sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_text ()) {
db::Text sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_edge ()) {
db::Edge sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_box ()) {
db::Box sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), single_rep);
} else if (shape->is_user_object ()) {
// ignore
} else {
tl_assert (false); // unknown shape type
}
++shape;
} else if (! recompress && shape.in_array ()) {
db::Repetition rep;
create_repetition (shape.array (), rep);
if (shape->is_simple_polygon ()) {
db::SimplePolygon sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_polygon ()) {
db::Polygon sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_path ()) {
db::Path sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_text ()) {
db::Text sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_edge ()) {
db::Edge sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_box ()) {
db::Box sh;
shape->instantiate (sh);
write (sh, shape->prop_id (), rep);
} else if (shape->is_user_object ()) {
// ignore
} else {
tl_assert (false); // unknown shape type
}
shape.finish_array ();
} else {
switch (shape->type ()) {
case db::Shape::Polygon:
if (shape->has_prop_id ()) {
db::PolygonWithProperties polygon = *shape->basic_ptr (db::PolygonWithProperties::tag ());
db::Disp tr;
polygon.reduce (tr);
polygon_with_properties_compressor.add (polygon, tr.disp ());
} else {
db::Polygon polygon = *shape->basic_ptr (db::Polygon::tag ());
db::Disp tr;
polygon.reduce (tr);
polygon_compressor.add (polygon, tr.disp ());
}
break;
case db::Shape::PolygonRef:
if (shape->has_prop_id ()) {
db::object_with_properties<db::PolygonRef> polygon_ref = *shape->basic_ptr (db::object_with_properties<db::PolygonRef>::tag ());
db::PolygonWithProperties polygon (polygon_ref.obj (), polygon_ref.properties_id ());
polygon_with_properties_compressor.add (polygon, polygon_ref.trans ().disp ());
} else {
const db::PolygonRef &polygon_ref = *shape->basic_ptr (db::PolygonRef::tag ());
polygon_compressor.add (polygon_ref.obj (), polygon_ref.trans ().disp ());
}
break;
case db::Shape::PolygonPtrArrayMember:
if (shape->has_prop_id ()) {
db::object_with_properties<db::Shape::polygon_ptr_array_type> polygon_ref = *shape->basic_ptr (db::object_with_properties<db::Shape::polygon_ptr_array_type>::tag ());
db::PolygonWithProperties polygon (polygon_ref.object ().obj (), polygon_ref.properties_id ());
polygon_with_properties_compressor.add (polygon, shape->array_trans ().disp ());
} else {
const db::Shape::polygon_ptr_array_type &polygon_ref = *shape->basic_ptr (db::Shape::polygon_ptr_array_type::tag ());
polygon_compressor.add (polygon_ref.object ().obj (), shape->array_trans ().disp ());
}
break;
case db::Shape::SimplePolygon:
if (shape->has_prop_id ()) {
db::SimplePolygonWithProperties simple_polygon = *shape->basic_ptr (db::SimplePolygonWithProperties::tag ());
db::Disp tr;
simple_polygon.reduce (tr);
simple_polygon_with_properties_compressor.add (simple_polygon, tr.disp ());
} else {
db::SimplePolygon simple_polygon = *shape->basic_ptr (db::SimplePolygon::tag ());
db::Disp tr;
simple_polygon.reduce (tr);
simple_polygon_compressor.add (simple_polygon, tr.disp ());
}
break;
case db::Shape::SimplePolygonRef:
if (shape->has_prop_id ()) {
db::object_with_properties<db::SimplePolygonRef> polygon_ref = *shape->basic_ptr (db::object_with_properties<db::SimplePolygonRef>::tag ());
db::SimplePolygonWithProperties polygon (polygon_ref.obj (), polygon_ref.properties_id ());
simple_polygon_with_properties_compressor.add (polygon, polygon_ref.trans ().disp ());
} else {
const db::SimplePolygonRef &polygon_ref = *shape->basic_ptr (db::SimplePolygonRef::tag ());
simple_polygon_compressor.add (polygon_ref.obj (), polygon_ref.trans ().disp ());
}
break;
case db::Shape::SimplePolygonPtrArrayMember:
if (shape->has_prop_id ()) {
db::object_with_properties<db::Shape::simple_polygon_ptr_array_type> simple_polygon_ref = *shape->basic_ptr (db::object_with_properties<db::Shape::simple_polygon_ptr_array_type>::tag ());
db::SimplePolygonWithProperties simple_polygon (simple_polygon_ref.object ().obj (), simple_polygon_ref.properties_id ());
simple_polygon_with_properties_compressor.add (simple_polygon, shape->array_trans ().disp ());
} else {
const db::Shape::simple_polygon_ptr_array_type &simple_polygon_ref = *shape->basic_ptr (db::Shape::simple_polygon_ptr_array_type::tag ());
simple_polygon_compressor.add (simple_polygon_ref.object ().obj (), shape->array_trans ().disp ());
}
break;
case db::Shape::Edge:
if (shape->has_prop_id ()) {
db::EdgeWithProperties edge = *shape->basic_ptr (db::EdgeWithProperties::tag ());
db::Disp tr;
edge.reduce (tr);
edge_with_properties_compressor.add (edge, tr.disp ());
} else {
db::Edge edge = *shape->basic_ptr (db::Edge::tag ());
db::Disp tr;
edge.reduce (tr);
edge_compressor.add (edge, tr.disp ());
}
break;
case db::Shape::Path:
if (shape->has_prop_id ()) {
db::PathWithProperties path = *shape->basic_ptr (db::PathWithProperties::tag ());
db::Disp tr;
path.reduce (tr);
path_with_properties_compressor.add (path, tr.disp ());
} else {
db::Path path = *shape->basic_ptr (db::Path::tag ());
db::Disp tr;
path.reduce (tr);
path_compressor.add (path, tr.disp ());
}
break;
case db::Shape::PathRef:
if (shape->has_prop_id ()) {
db::object_with_properties<db::PathRef> path_ref = *shape->basic_ptr (db::object_with_properties<db::PathRef>::tag ());
db::PathWithProperties path (path_ref.obj (), path_ref.properties_id ());
path_with_properties_compressor.add (path, path_ref.trans ().disp ());
} else {
const db::PathRef &path_ref = *shape->basic_ptr (db::PathRef::tag ());
path_compressor.add (path_ref.obj (), path_ref.trans ().disp ());
}
break;
case db::Shape::PathPtrArrayMember:
if (shape->has_prop_id ()) {
db::object_with_properties<db::Shape::path_ptr_array_type> path_ref = *shape->basic_ptr (db::object_with_properties<db::Shape::path_ptr_array_type>::tag ());
db::PathWithProperties path (path_ref.object ().obj (), path_ref.properties_id ());
path_with_properties_compressor.add (path, shape->array_trans ().disp ());
} else {
const db::Shape::path_ptr_array_type &path_ref = *shape->basic_ptr (db::Shape::path_ptr_array_type::tag ());
path_compressor.add (path_ref.object ().obj (), shape->array_trans ().disp ());
}
break;
case db::Shape::Box:
if (shape->has_prop_id ()) {
db::BoxWithProperties box = *shape->basic_ptr (db::BoxWithProperties::tag ());
db::Disp tr;
box.reduce (tr);
box_with_properties_compressor.add (box, tr.disp ());
} else {
db::Box box = *shape->basic_ptr (db::Box::tag ());
db::Disp tr;
box.reduce (tr);
box_compressor.add (box, tr.disp ());
}
break;
case db::Shape::BoxArray:
case db::Shape::BoxArrayMember:
case db::Shape::ShortBox:
case db::Shape::ShortBoxArrayMember:
if (shape->has_prop_id ()) {
db::BoxWithProperties box;
shape->instantiate (box);
box.properties_id (shape->prop_id ());
db::Disp tr;
box.reduce (tr);
box_with_properties_compressor.add (box, tr.disp ());
} else {
db::Box box;
shape->instantiate (box);
db::Disp tr;
box.reduce (tr);
box_compressor.add (box, tr.disp ());
}
break;
case db::Shape::Text:
if (shape->has_prop_id ()) {
db::TextWithProperties text = *shape->basic_ptr (db::TextWithProperties::tag ());
db::Disp tr;
text.reduce (tr);
text_with_properties_compressor.add (text, tr.disp ());
} else {
db::Text text = *shape->basic_ptr (db::Text::tag ());
db::Disp tr;
text.reduce (tr);
text_compressor.add (text, tr.disp ());
}
break;
case db::Shape::TextRef:
if (shape->has_prop_id ()) {
db::object_with_properties<db::TextRef> text_ref = *shape->basic_ptr (db::object_with_properties<db::TextRef>::tag ());
db::TextWithProperties text (text_ref.obj (), text_ref.properties_id ());
text_with_properties_compressor.add (text, text_ref.trans ().disp ());
} else {
const db::TextRef &text_ref = *shape->basic_ptr (db::TextRef::tag ());
text_compressor.add (text_ref.obj (), text_ref.trans ().disp ());
}
break;
case db::Shape::TextPtrArrayMember:
if (shape->has_prop_id ()) {
db::object_with_properties<db::Shape::text_ptr_array_type> text_ref = *shape->basic_ptr (db::object_with_properties<db::Shape::text_ptr_array_type>::tag ());
db::TextWithProperties text (text_ref.object ().obj (), text_ref.properties_id ());
text_with_properties_compressor.add (text, shape->array_trans ().disp ());
} else {
const db::Shape::text_ptr_array_type &text_ref = *shape->basic_ptr (db::Shape::text_ptr_array_type::tag ());
text_compressor.add (text_ref.object ().obj (), shape->array_trans ().disp ());
}
break;
case db::Shape::UserObject:
// ignore.
break;
default:
tl_assert (false);
}
++shape;
}
}
path_compressor.flush (this);
simple_polygon_compressor.flush (this);
polygon_compressor.flush (this);
edge_compressor.flush (this);
box_compressor.flush (this);
text_compressor.flush (this);
path_with_properties_compressor.flush (this);
simple_polygon_with_properties_compressor.flush (this);
polygon_with_properties_compressor.flush (this);
edge_with_properties_compressor.flush (this);
box_with_properties_compressor.flush (this);
text_with_properties_compressor.flush (this);
}
}