2017-02-12 13:21:08 +01:00
/*
KLayout Layout Viewer
2019-01-08 00:58:45 +01:00
Copyright ( C ) 2006 - 2019 Matthias Koefferlein
2017-02-12 13:21:08 +01:00
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"
2017-04-01 23:18:04 +02:00
# include "tlMath.h"
2017-02-12 13:21:08 +01:00
# 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
2018-04-06 23:27:29 +02:00
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 ) | |
2017-02-12 13:21:08 +01:00
( 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 ;
2018-08-28 23:19:58 +02:00
for ( typename std : : unordered_map < Obj , disp_vector > : : iterator n = m_normalized . begin ( ) ; n ! = m_normalized . end ( ) ; + + n ) {
2017-02-12 13:21:08 +01:00
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 ;
2018-08-28 23:19:58 +02:00
std : : unordered_set < db : : Coord > xcoords , ycoords ;
2017-02-12 13:21:08 +01:00
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 ;
}
}
}
2019-04-03 19:15:09 +02:00
// Apply some heuristic criterion that allows the algorithm to determine whether it's worth doing the compression
2017-02-12 13:21:08 +01:00
// 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 ) ,
2018-03-19 18:24:09 +01:00
m_propname_id ( 0 ) ,
m_propstring_id ( 0 ) ,
m_proptables_written ( false ) ,
2018-07-03 00:51:36 +02:00
m_progress ( tl : : to_string ( tr ( " Writing OASIS file " ) ) , 10000 )
2017-02-12 13:21:08 +01:00
{
2018-07-03 00:51:36 +02:00
m_progress . set_format ( tl : : to_string ( tr ( " %.0f MB " ) ) ) ;
2017-02-12 13:21:08 +01:00
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 ) ;
2018-09-02 00:40:35 +02:00
// 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 ( ) ) ;
}
2017-02-12 13:21:08 +01:00
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 ( ) ) ;
2018-09-02 00:40:35 +02:00
} else if ( m_cblock_buffer . size ( ) > 0 ) { // Reasoning for if(...): we don't want to access data from an empty vector through data()
2017-02-12 13:21:08 +01:00
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 ) {
2017-07-18 22:33:54 +02:00
g = ( g = = 0 ) ? x : tl : : gcd ( g , x ) ;
2017-02-12 13:21:08 +01:00
}
db : : Coord y = safe_scale ( m_sf , p - > y ( ) ) ;
if ( y < 0 ) {
y = - y ;
}
if ( y ! = 0 ) {
2017-07-18 22:33:54 +02:00
g = ( g = = 0 ) ? y : tl : : gcd ( g , y ) ;
2017-02-12 13:21:08 +01:00
}
}
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 ) {
2018-07-03 00:51:36 +02:00
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) " ;
2017-11-29 22:30:14 +01:00
if ( m_options . permissive ) {
tl : : warn < < msg ;
return ;
} else {
throw tl : : Exception ( msg ) ;
}
2017-02-12 13:21:08 +01:00
}
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 ) {
2018-07-03 00:51:36 +02:00
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) " ;
2017-11-29 22:30:14 +01:00
if ( m_options . permissive ) {
tl : : warn < < msg ;
return ;
} else {
throw tl : : Exception ( msg ) ;
}
2017-02-12 13:21:08 +01:00
}
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 )
{
2017-11-29 22:30:14 +01:00
typedef db : : coord_traits < db : : Coord > : : distance_type ucoord ;
2017-02-12 13:21:08 +01:00
// 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 ( ) ;
2017-11-29 22:30:14 +01:00
// 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 ) ;
2017-02-12 13:21:08 +01:00
}
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 ( ) ) {
2017-11-29 22:30:14 +01:00
db : : Coord w = safe_scale ( m_sf , path . width ( ) ) ;
db : : Coord hw = w / 2 ;
if ( hw * 2 ! = w ) {
2018-07-03 00:51:36 +02:00
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) " ;
2017-11-29 22:30:14 +01:00
if ( m_options . permissive ) {
2018-07-03 00:51:36 +02:00
tl : : warn < < msg < < " - " < < tl : : to_string ( tr ( " circle diameter is rounded " ) ) ;
2017-11-29 22:30:14 +01:00
} else {
throw tl : : Exception ( msg ) ;
}
}
2017-02-12 13:21:08 +01:00
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 ;
2017-11-29 22:30:14 +01:00
// NOTE: the radius has already been scaled, so we don't use write_ucoord here
write ( ( ucoord ) hw ) ;
2017-02-12 13:21:08 +01:00
}
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 {
2017-11-29 22:30:14 +01:00
// Single-point paths are translated into polygons
2017-02-12 13:21:08 +01:00
write ( path . polygon ( ) , prop_id , rep ) ;
}
} else {
2017-11-29 22:30:14 +01:00
db : : Coord w = safe_scale ( m_sf , path . width ( ) ) ;
db : : Coord hw = w / 2 ;
if ( hw * 2 ! = w ) {
2018-07-03 00:51:36 +02:00
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) " ;
2017-11-29 22:30:14 +01:00
if ( m_options . permissive ) {
2018-07-03 00:51:36 +02:00
tl : : warn < < msg < < " - " < < tl : : to_string ( tr ( " path diameter is rounded " ) ) ;
2017-11-29 22:30:14 +01:00
} else {
throw tl : : Exception ( msg ) ;
}
2017-02-12 13:21:08 +01:00
}
2017-11-29 22:30:14 +01:00
db : : Point end = start + m_pointlist . back ( ) ;
2017-02-12 13:21:08 +01:00
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 ;
2017-11-29 22:30:14 +01:00
// NOTE: the half-width has already been scaled, so we don't use write_ucoord here
write ( ( ucoord ) hw ) ;
2017-02-12 13:21:08 +01:00
}
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 ) {
2017-11-29 22:30:14 +01:00
// NOTE: ext.first is already scaled, so we don't use write_coord
write ( ext . first ) ;
2017-02-12 13:21:08 +01:00
}
if ( ( ext_scheme & 0x03 ) = = 0x03 ) {
2017-11-29 22:30:14 +01:00
// NOTE: ext.second is already scaled, so we don't use write_coord
write ( ext . second ) ;
2017-02-12 13:21:08 +01:00
}
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 ;
2017-11-29 22:30:14 +01:00
// NOTE: the half-width has already been scaled, so we don't use write_ucoord here
write ( ( ucoord ) hw ) ;
2017-02-12 13:21:08 +01:00
}
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 ) ;
}
}