From c7fe1cb1891963ec0b1225aafc688652f1dbc9f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2019 16:48:30 +0200 Subject: [PATCH 1/7] Fixed #275 (don't write PCell context with OASIS) The ability to disable PCell context on OASIS output with the "write_context_info" option was added. --- .../oasis/db_plugin/dbOASISWriter.cc | 60 +++--- .../oasis/unit_tests/dbOASISWriter.cc | 187 ++++++++++++------ testdata/oasis/dbOASISWriter119_au.gds | Bin 0 -> 3838 bytes testdata/oasis/pcell_test.gds | Bin 0 -> 4898 bytes 4 files changed, 166 insertions(+), 81 deletions(-) create mode 100644 testdata/oasis/dbOASISWriter119_au.gds create mode 100644 testdata/oasis/pcell_test.gds diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 76197bc28..15628938a 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1343,20 +1343,24 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - // emit property name required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + if (options.write_context_info ()) { - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) { + // emit property name required for the PCell context information + std::vector context_prop_strings; + for (std::vector::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; - 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; } @@ -1422,25 +1426,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - // emit property string id's required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + if (options.write_context_info ()) { - m_progress.set (mp_stream->pos ()); + // emit property string id's required for the PCell context information + std::vector context_prop_strings; + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top ()) { + m_progress.set (mp_stream->pos ()); - context_prop_strings.clear (); - if (layout.get_context_info (*cell, context_prop_strings)) { + const db::Cell &cref (layout.cell (*cell)); + if (cref.is_proxy () && ! cref.is_top ()) { - for (std::vector ::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; + context_prop_strings.clear (); + if (layout.get_context_info (*cell, context_prop_strings)) { + + for (std::vector ::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; + } } + } } @@ -1604,7 +1612,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } // context information as property named KLAYOUT_CONTEXT - if (cref.is_proxy ()) { + if (cref.is_proxy () && options.write_context_info ()) { context_prop_strings.clear (); diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc index 68bec61d0..13aef7df0 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc @@ -26,6 +26,8 @@ #include "dbLayoutDiff.h" #include "dbWriter.h" #include "dbTextWriter.h" +#include "dbLibraryProxy.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" @@ -1258,61 +1260,6 @@ TEST(115) EXPECT_EQ (std::string (os.string ()), std::string (expected)) } -TEST(118) -{ - // 1x1 arrays (#902) - - db::Manager m; - db::Layout g (&m); - - db::LayerProperties lp1; - lp1.layer = 1; - lp1.datatype = 0; - - g.insert_layer (0, lp1); - - db::Cell &c1 (g.cell (g.add_cell ())); - c1.shapes (0).insert (db::Box (100, 0, 100, 200)); - - db::Cell &c2 (g.cell (g.add_cell ())); - c2.insert (db::array (db::CellInst (c1.cell_index ()), db::Trans (), db::Vector (0, 1), db::Vector (1, 0), 1, 1)); - c2.insert (db::array (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (17, -42)), db::Vector (0, 1), db::Vector (1, 0), 1, 1)); - - std::string tmp_file = tl::TestBase::tmp_file ("tmp.oas"); - - { - tl::OutputStream out (tmp_file); - db::SaveLayoutOptions options; - options.set_format ("OASIS"); - db::Writer writer (options); - writer.write (g, out); - } - - tl::InputStream in (tmp_file); - db::Reader reader (in); - db::Layout gg; - reader.set_warnings_as_errors (true); - reader.read (gg); - - const char *expected = - "begin_lib 0.001\n" - "begin_cell {$1}\n" - "box 1 0 {100 0} {100 200}\n" - "end_cell\n" - "begin_cell {$2}\n" - "sref {$1} 0 0 1 {0 0}\n" - "sref {$1} 0 0 1 {17 -42}\n" - "end_cell\n" - "end_lib\n" - ; - - tl::OutputStringStream os; - tl::OutputStream stream (os); - db::TextWriter textwriter (stream); - textwriter.write (gg); - EXPECT_EQ (std::string (os.string ()), std::string (expected)) -} - TEST(116) { db::Manager m; @@ -1728,3 +1675,133 @@ TEST(117) EXPECT_EQ (std::string (os.string ()), std::string (expected)) } +TEST(118) +{ + // 1x1 arrays (#902) + + db::Manager m; + db::Layout g (&m); + + db::LayerProperties lp1; + lp1.layer = 1; + lp1.datatype = 0; + + g.insert_layer (0, lp1); + + db::Cell &c1 (g.cell (g.add_cell ())); + c1.shapes (0).insert (db::Box (100, 0, 100, 200)); + + db::Cell &c2 (g.cell (g.add_cell ())); + c2.insert (db::array (db::CellInst (c1.cell_index ()), db::Trans (), db::Vector (0, 1), db::Vector (1, 0), 1, 1)); + c2.insert (db::array (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (17, -42)), db::Vector (0, 1), db::Vector (1, 0), 1, 1)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (g, out); + } + + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.set_warnings_as_errors (true); + reader.read (gg); + + const char *expected = + "begin_lib 0.001\n" + "begin_cell {$1}\n" + "box 1 0 {100 0} {100 200}\n" + "end_cell\n" + "begin_cell {$2}\n" + "sref {$1} 0 0 1 {0 0}\n" + "sref {$1} 0 0 1 {17 -42}\n" + "end_cell\n" + "end_lib\n" + ; + + tl::OutputStringStream os; + tl::OutputStream stream (os); + db::TextWriter textwriter (stream); + textwriter.write (gg); + EXPECT_EQ (std::string (os.string ()), std::string (expected)) +} + +TEST(119_WithAndWithoutContext) +{ + // PCells with context and without + + db::Manager m; + db::Layout g (&m); + + // Note: this sample requires the BASIC lib + + { + std::string fn (tl::testsrc ()); + fn += "/testdata/oasis/pcell_test.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (g); + } + + std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter119a.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (g, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.set_warnings_as_errors (true); + reader.read (gg); + + std::pair tc = gg.cell_by_name ("TEXT"); + tl_assert (tc.first); + + const db::Cell &text_cell = gg.cell (tc.second); + EXPECT_EQ (text_cell.is_proxy (), true); + EXPECT_EQ (text_cell.get_display_name (), "Basic.TEXT('KLAYOUT RULES')"); + + CHECKPOINT (); + db::compare_layouts (_this, gg, tl::testsrc () + "/testdata/oasis/dbOASISWriter119_au.gds", db::NoNormalization); + } + + tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter119b.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_write_context_info (false); + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (g, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.set_warnings_as_errors (true); + reader.read (gg); + + std::pair tc = gg.cell_by_name ("TEXT"); + tl_assert (tc.first); + + const db::Cell &text_cell = gg.cell (tc.second); + EXPECT_EQ (text_cell.is_proxy (), false); + EXPECT_EQ (text_cell.get_display_name (), "TEXT"); + + CHECKPOINT (); + db::compare_layouts (_this, gg, tl::testsrc () + "/testdata/oasis/dbOASISWriter119_au.gds", db::NoNormalization); + } + +} diff --git a/testdata/oasis/dbOASISWriter119_au.gds b/testdata/oasis/dbOASISWriter119_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..120a3f609c2bf763dc608de6b4ac849be58bd0e1 GIT binary patch literal 3838 zcma)9LJygvg7r7x9-;5?(VL8(IGbX(q5gpbknUD6DMI}Bg#LrLWKMyR(}W? z3^A>gj3Ff<6b4GBLK5_X^@sR}Noj-7CT-}89F?#wgp{p|dn-~01& z&NZHiMwgmgBs%U_aqbZKVaXU*H*8(eI1 z1$VIXyv6-X7n^v(7_TsDBE~C<8e@jjG2i#Q+{c*sX=5T!hm3uQ`%T8geiS(#+oy3d zdKRCP_!LSWX(Q>6`i_XyC%s#(~#o^VRK?HS;K|litJ(^2a=o z(JzcCpB<9@EMKL5k{ow>B=ae&Qy!W1V#SE$B>iUAD^3tEUtjDzRBn}C7VC<3B^jo(!B;`{EH9lp~*;(fE zzmopzuS>5!n6IW4^4C11{*Y16nC2Af*$U#G`=ohDc8!Z1)sIHZH}^?nmGf(vFTU+( z%WCP}k9lKXNY>Rl5|aIFJ*a+?9B;oEl6GyU(1OGt%ohrv8wTtH$)44~Tot z59M6vy=N4^Z@$>SL~=4Z)tG+t%98h`xChz4zWIt1#IyB|!!o|Zxt!FwT-TES1e~xI z5pc3vVzyE_WHJn*6J+5d7>C@_=@ptWJ6-lQG9AHA9s++6V>3a>>972PnCSu`C#grd zwUd25|8`|Gup>*2A?dI2Td^r4e`NFn$(?JQt)NOUpSKhN9B>fh=5|a7}toU0*EUyT60jWRXd+8@H z$q&hk^XeFRS@Y;fc_CK1Bjcz3lAn_OA=w8$IoQ|q-|9EXxak){(!Tf|=%F5xS4n+H z%I{sG{7Bk!osbhx$uDWw0pz^w`XCSEYeZx|l+$j~?vPwZla@;_hg_&%iGET}Qoj;& z=y+=!D)hsv>LGl=j-+1AcOe)Axs@CfBVa=e}W7Yk=#P zaV_z0(=MwfB7e#`Zgpsl9SY@oPOqg9Pm1b{>=kfKjn-&_qBk~r`+0M&(jb0K56whj?3DEeQ)6l z_N0vizcw!Xx%k{K^v8X{ILw=IEul|2{c+FJkE0upXL&ZUzJTa`e;w{E-MMAQ?&e8Q z>Uob3Jh8CvnMDH&o_u&kM|4+P8mS6V$EAJYUsrUWMYe2lQ zURni0{>MsVD)44-5q-^8`~HQ^;0fO!Mc*rTf_yY>^!?wT#r_)h{qx7MuhxM$Gpe_M zCD>nkeg8MeYmb6>5>LJ%PTwGT!yf519+6$sC|Ow-#?g$T882{d5gH17f+1f022Er?6kK?~_;?jYmMp8}@*2;Jm=E z26FNZ$!m{-(5u-iyXr0SQ?*WhCk-o3ZlmH>?o=Mx6Ur-FtvoAMEAPx@)sewmvaSrz zXVl4gX5E~3&A*7kYgEEc_QMMnblCV>x;f6?NUJA|5yY00EfwVgLXD literal 0 HcmV?d00001 diff --git a/testdata/oasis/pcell_test.gds b/testdata/oasis/pcell_test.gds new file mode 100644 index 0000000000000000000000000000000000000000..031c8f8e2ddcd8fcb68ed11a642a448619c1ebc4 GIT binary patch literal 4898 zcmc(jX>3$g6vxl3lrpVtEoEqjF0|1?+i9nrDO-z_wl1M|8U=zVVuco}wQXpv8i~P# z7>#Sx5TnMBXf)Bdk!Va@qJAKJK%!_|2*EY3fPk7nqCWrsd*1ccIt9O|FPZtxJNMlG zIrrSe!-= zSL$~@vUa7!LR0rG3Wuwzs@l`tD?8V$Tz^sb`DxrrFk7UsAbd%BRd>hwo^;n`7o@wZ z5+aejfY>Ws0?TwL84Ur^f|Pb{fRP5hBaE@49=Id7dR`&D^M@5X`aDO}m4cAv?oda(DV zzAex-T%4$HSZCsjObcrSWG-p=}$yLE923(<|F+*|3={iFl*-nLeS#?XyT*vb1GzbfkB~ zz|bb6r!CVohYb^3;%*LM$zu*z5A>SKG-!{IY7GpH_iY*No7XzF<@!Et39A2fy(T~c zGMu}>X)EbBb*b(j9vVYd4W@@rVvIZ1cOz`)uIOsJEWK)F`6a8mIxjs}BTZHREUo{) zQT^0^UwyOY|E#{9<;nH$?;RZNGj;o)>px9Sdp;C~|9%PtvoN1Ie(^~-*DvD7u5p~7 zKNp$xf#dx2DtO#+PCP6Uo$oluw}IOn=hz4c`;UVnv$s0V(HlgH%N*y(1K3bo=B2g@FkI^r$wgSCenx;v)V)wzkpwX zkefctyur^z;)iiqHH*~k2Vt*$5xh&J=1%3@Q);hxAB11|F6~$Lkm{7)uDWx!Yn<4< z8n@&b&7=5$=2cv#dCuOdc}I`yIHH(Kjw?D}$H{r-xH<1S|9YmLVNGR+?Rh(CB}ogP zFEdaHe~!zec<98C1u~9cG-S?&Xoua<`sHY36n4VOlrHKOy@hOdfa4%$iwrDMJN0HA zQQhc5wUhLt-SpG`=4mlW-{f1n8u_fjY=G3Odq)|MsvhZoT?>a7$ejJM>{d~1*qbA!(w)3(yH?Uj!uX_{n+a zReC6r2pLe`K_eA@(pb#*}vvZkM!fZx8xkv;*=t}?itq*KjY6C zHznqicGlOB{%GgilZ;C{<0Te)q@G#dkm5ezl{^Puu#@!XDQwC1MUU~@CHxk+^&sij zQtgp`=N0kWCHz}3Zt9a9SL^p4nJ?{@%$xl!slU8g_1ku;ouohQ)N6m%*&cXV z+e!b|_#DJ1NA12ONj>^ovPh~cpbM|9P<6(&`vy%72XU^7g7< z-d^O1x3#l&YQ0E~BY(F?)|cmh;B^q|z}$oMD=hWMdKa8fy@C@~f66?zn&)z*`pt|Y z|8|^zVBjO1dzGpi#aj*I%-*4P(*GKs%S%17-m^xu-XzyNBB_2ASewkdJj)|>t8Pk83?ie+X}v*dxA2LELkn)Q@Ywx*ki^1N)>7 z^UZxyS88<)GsQRcOx&z~4VV|!h2*#zcX(ty8~12ENwz1CdSt(*FIC6Y%M`DL<4t|5 zekpfe7P|97GS1?DOY3KtNzcsBlB?8j$yJ)qd51i5K9=s%`5>v!cBSn%rM_AJZSJ_1 zeXR8%eLssV`_3iqIX|>>U$lLx@jF&1J6Efn^q+%s81I3Gyq}4CkoD_Wq4~J+%y`f6 zhR$<2-GeaMc#U%ZIu957pcg3#wUvXsBC*gi^Mk*6Vjn)}jTk+>|Ev6izDJfh&Nqmd PuTjtR-zPhYU{L-5b|A6l literal 0 HcmV?d00001 From 56c622053f500bedf68eb8e7ac2503f6dd4c4ff6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2019 21:42:07 +0200 Subject: [PATCH 2/7] Fixed #276 (Layer properties name cannot be updated) In addition, this fix includes Python-related fixes: because of the short lifetime of Python references, the functionality was not as expected sometimes. Keeping copies of LayerPropertiesIterators helped. Some tweaks were required to maintain the delete() semantics. --- src/laybasic/laybasic/gsiDeclLayLayers.cc | 4 +- src/laybasic/laybasic/gsiDeclLayLayoutView.cc | 2 +- src/laybasic/laybasic/layLayerProperties.cc | 77 +- src/laybasic/laybasic/layLayerProperties.h | 23 +- src/pya/unit_tests/pya.cc | 1 + testdata/python/layLayers.py | 956 ++++++++++++++++++ testdata/ruby/layLayers.rb | 38 + 7 files changed, 1033 insertions(+), 68 deletions(-) create mode 100644 testdata/python/layLayers.py diff --git a/src/laybasic/laybasic/gsiDeclLayLayers.cc b/src/laybasic/laybasic/gsiDeclLayLayers.cc index 88f2dc5c5..897f879ee 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayers.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayers.cc @@ -1388,9 +1388,9 @@ Class decl_LayerPropertiesNodeRef ( "This class has been introduced in version 0.25.\n" ); -static lay::LayerPropertiesNodeRef current (lay::LayerPropertiesConstIterator *iter) +static lay::LayerPropertiesNodeRef current (const lay::LayerPropertiesConstIterator *iter) { - return lay::LayerPropertiesNodeRef (iter); + return lay::LayerPropertiesNodeRef (*iter); } Class decl_LayerPropertiesIterator ( diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index 77ae960f4..85f5c477a 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -339,7 +339,7 @@ namespace { reference operator* () const { - return lay::LayerPropertiesNodeRef (&const_cast (this)->m_iter); + return lay::LayerPropertiesNodeRef (m_iter); } bool at_end () const diff --git a/src/laybasic/laybasic/layLayerProperties.cc b/src/laybasic/laybasic/layLayerProperties.cc index eb5c6c6b5..7f17026fa 100644 --- a/src/laybasic/laybasic/layLayerProperties.cc +++ b/src/laybasic/laybasic/layLayerProperties.cc @@ -197,12 +197,15 @@ LayerProperties::operator= (const LayerProperties &d) flags += nr_source; } + if (m_name != d.m_name) { + m_name = d.m_name; + flags += nr_meta; + } + if (flags) { need_realize (flags, true /*force on children*/); } - m_name = d.m_name; - } return *this; } @@ -806,13 +809,13 @@ LayerPropertiesNode::add_child (const LayerPropertiesNode &child) // LayerPropertiesConstIterator implementation LayerPropertiesConstIterator::LayerPropertiesConstIterator () - : m_uint (0), m_list (), mp_obj (0) + : m_uint (0), m_list () { // .. nothing yet .. } LayerPropertiesConstIterator::LayerPropertiesConstIterator (const lay::LayerPropertiesNode *node) - : m_uint (0), m_list (), mp_obj (0) + : m_uint (0), m_list () { if (!node) { return; @@ -874,7 +877,7 @@ LayerPropertiesConstIterator::LayerPropertiesConstIterator (const lay::LayerProp LayerPropertiesConstIterator::LayerPropertiesConstIterator (const LayerPropertiesList &list, bool last) // NOTE: there should be some "const_weak_ptr" - : m_uint (0), m_list (const_cast (&list)), mp_obj (0) + : m_uint (0), m_list (const_cast (&list)) { if (last) { m_uint = (list.end_const () - list.begin_const ()) + 1; @@ -885,7 +888,7 @@ LayerPropertiesConstIterator::LayerPropertiesConstIterator (const LayerPropertie LayerPropertiesConstIterator::LayerPropertiesConstIterator (const LayerPropertiesList &list, size_t uint) // NOTE: there should be some "const_weak_ptr" - : m_uint (uint), m_list (const_cast (&list)), mp_obj (0) + : m_uint (uint), m_list (const_cast (&list)) { // .. nothing yet .. } @@ -991,7 +994,7 @@ LayerPropertiesConstIterator & LayerPropertiesConstIterator::up () { m_uint %= factor ().first; - mp_obj = 0; + mp_obj.reset (0); return *this; } @@ -999,7 +1002,7 @@ LayerPropertiesConstIterator & LayerPropertiesConstIterator::next_sibling (ptrdiff_t n) { m_uint += factor ().first * n; - mp_obj = 0; + mp_obj.reset (0); return *this; } @@ -1008,7 +1011,7 @@ LayerPropertiesConstIterator::to_sibling (size_t n) { std::pair f = factor (); m_uint = (m_uint % f.first) + (1 + n) * f.first; - mp_obj = 0; + mp_obj.reset (0); return *this; } @@ -1024,7 +1027,7 @@ LayerPropertiesConstIterator::down_first_child () { std::pair f = factor (); m_uint += f.first * f.second; - mp_obj = 0; + mp_obj.reset (0); return *this; } @@ -1034,7 +1037,7 @@ LayerPropertiesConstIterator::down_last_child () std::pair f = factor (); const LayerPropertiesNode *o = obj (); m_uint += f.first * f.second * ((o->end_children () - o->begin_children ()) + 1); - mp_obj = 0; + mp_obj.reset (0); return *this; } @@ -1067,8 +1070,8 @@ LayerPropertiesConstIterator::parent_obj () const void LayerPropertiesConstIterator::invalidate () { - mp_obj = 0; - + mp_obj.reset (0); + // the iterator may be parked at a position behind the last element. // Move one step further in this case. std::pair f = factor (); @@ -1083,7 +1086,7 @@ LayerPropertiesConstIterator::set_obj () const { if (is_null () || !m_list) { - mp_obj = 0; + mp_obj.reset (0); } else { @@ -1104,7 +1107,7 @@ LayerPropertiesConstIterator::set_obj () const iter = iter[rem - 1].begin_children (); } - mp_obj = &iter[uint - 1]; + mp_obj.reset (const_cast (&iter[uint - 1])); } } @@ -1122,7 +1125,7 @@ LayerPropertiesConstIterator::inc (unsigned int d) while (true) { std::pair f = factor (); m_uint += f.first; - mp_obj = 0; + mp_obj.reset (0); if (m_uint / f.first < f.second - 1) { break; } else if (at_top ()) { @@ -1908,32 +1911,11 @@ LayerPropertiesNodeRef::LayerPropertiesNodeRef (LayerPropertiesNode *node) attach_view (node->view (), node->list_index ()); set_parent (node->parent ()); - mp_iter.reset (&m_iter); mp_node.reset (node); } } -LayerPropertiesNodeRef::LayerPropertiesNodeRef (LayerPropertiesConstIterator *iter) -{ - if (iter && !iter->at_end () && !iter->is_null ()) { - - const lay::LayerPropertiesNode *node = (*iter).operator-> (); - - // NOTE: we do assignment before we set the iterator reference - hence there won't be - // updates triggered. - LayerPropertiesNode::operator= (*node); - - // Makes ourself a perfect copy of the original (including reference into the view) - attach_view (node->view (), node->list_index ()); - set_parent (node->parent ()); - - mp_iter.reset (iter); - mp_node.reset (const_cast (node)); - - } -} - LayerPropertiesNodeRef::LayerPropertiesNodeRef (const LayerPropertiesConstIterator &iter) : m_iter (iter) { @@ -1949,7 +1931,6 @@ LayerPropertiesNodeRef::LayerPropertiesNodeRef (const LayerPropertiesConstIterat attach_view (node->view (), node->list_index ()); set_parent (node->parent ()); - mp_iter.reset (&m_iter); mp_node.reset (const_cast (node)); } @@ -1961,7 +1942,7 @@ LayerPropertiesNodeRef::LayerPropertiesNodeRef () } LayerPropertiesNodeRef::LayerPropertiesNodeRef (const LayerPropertiesNodeRef &other) - : LayerPropertiesNode (other), m_iter (other.m_iter), mp_iter (other.mp_iter), mp_node (other.mp_node) + : LayerPropertiesNode (other), m_iter (other.m_iter), mp_node (other.mp_node) { attach_view (other.view (), other.list_index ()); set_parent (other.parent ()); @@ -1971,7 +1952,6 @@ LayerPropertiesNodeRef &LayerPropertiesNodeRef::operator= (const LayerProperties { if (this != &other) { - mp_iter = other.mp_iter; mp_node = other.mp_node; m_iter = other.m_iter; attach_view (other.view (), other.list_index ()); @@ -1988,25 +1968,20 @@ void LayerPropertiesNodeRef::erase () { if (is_valid ()) { - view ()->delete_layer ((unsigned int) list_index (), *mp_iter); + view ()->delete_layer ((unsigned int) list_index (), m_iter); } } const lay::LayerPropertiesConstIterator & LayerPropertiesNodeRef::iter () const { - if (mp_iter) { - return *mp_iter; - } else { - static lay::LayerPropertiesConstIterator null_iter; - return null_iter; - } + return m_iter; } bool LayerPropertiesNodeRef::is_valid () const { - return mp_iter && !mp_iter->at_end () && !mp_iter->is_null () && view (); + return !m_iter.is_null () && !m_iter.at_end () && view (); } void @@ -2015,11 +1990,11 @@ LayerPropertiesNodeRef::need_realize (unsigned int flags, bool force) LayerPropertiesNode::need_realize (flags, force); if (is_valid ()) { - if ((flags & (nr_visual + nr_source)) != 0) { - view ()->set_properties ((unsigned int) list_index (), *mp_iter, *this); + if ((flags & (nr_visual + nr_source + nr_meta)) != 0) { + view ()->set_properties ((unsigned int) list_index (), m_iter, *this); } if ((flags & nr_hierarchy) != 0) { - view ()->replace_layer_node ((unsigned int) list_index (), *mp_iter, *this); + view ()->replace_layer_node ((unsigned int) list_index (), m_iter, *this); } } else if (mp_node) { diff --git a/src/laybasic/laybasic/layLayerProperties.h b/src/laybasic/laybasic/layLayerProperties.h index d41622426..a1cf792c8 100644 --- a/src/laybasic/laybasic/layLayerProperties.h +++ b/src/laybasic/laybasic/layLayerProperties.h @@ -637,7 +637,10 @@ public: */ void set_name (const std::string &n) { - m_name = n; + if (m_name != n) { + m_name = n; + need_realize (nr_meta); + } } /** @@ -916,7 +919,8 @@ protected: enum { nr_visual = 1, nr_source = 2, - nr_hierarchy = 4 + nr_meta = 4, + nr_hierarchy = 8 }; mutable bool m_realize_needed_source : 1; @@ -1407,7 +1411,7 @@ public: if (! mp_obj) { set_obj (); } - return mp_obj; + return mp_obj.get (); } /** @@ -1423,7 +1427,7 @@ private: size_t m_uint; tl::weak_ptr m_list; - mutable const LayerPropertiesNode *mp_obj; + mutable tl::weak_ptr mp_obj; void inc (unsigned int d); std::pair factor () const; @@ -1888,14 +1892,6 @@ public: */ LayerPropertiesNodeRef (LayerPropertiesNode *node); - /** - * @brief Constructor from an iterator - * The iterator is a pointer since the erase implementation requires us to - * modify the iterator. Hence with this version, the original iterator will - * follow up after the erase. - */ - LayerPropertiesNodeRef (LayerPropertiesConstIterator *iter); - /** * @brief Constructor from an iterator with an iterator copy */ @@ -1919,7 +1915,7 @@ public: /** * @brief Deletes the current node - * After this operation, the reference will point to the + * After this operation, the reference will point to the next element. */ void erase (); @@ -1947,7 +1943,6 @@ public: private: LayerPropertiesConstIterator m_iter; - tl::weak_ptr mp_iter; tl::weak_ptr mp_node; void need_realize (unsigned int flags, bool force); diff --git a/src/pya/unit_tests/pya.cc b/src/pya/unit_tests/pya.cc index 5b8dfff27..3f941b473 100644 --- a/src/pya/unit_tests/pya.cc +++ b/src/pya/unit_tests/pya.cc @@ -102,6 +102,7 @@ PYTHONTEST (dbReaders, "dbReaders.py") PYTHONTEST (dbPCellsTest, "dbPCells.py") PYTHONTEST (dbPolygonTest, "dbPolygonTest.py") PYTHONTEST (dbTransTest, "dbTransTest.py") +PYTHONTEST (layLayers, "layLayers.py") PYTHONTEST (tlTest, "tlTest.py") #if defined(HAVE_QT) && defined(HAVE_QTBINDINGS) PYTHONTEST (qtbinding, "qtbinding.py") diff --git a/testdata/python/layLayers.py b/testdata/python/layLayers.py new file mode 100644 index 000000000..f6bb1d820 --- /dev/null +++ b/testdata/python/layLayers.py @@ -0,0 +1,956 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2019 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +import pya +import unittest +import os +import sys + + +class LAYLayersTest(unittest.TestCase): + + def lnode_str(self, space, l): + return space + l.current().source_(True) + "\n"; + + def lnodes_str(self, space, l): + res = "" + while not l.at_end(): + res += self.lnode_str(space, l) + if l.current().has_children(): + l.down_first_child() + res += self.lnodes_str(" " + space, l) + l.up() + l.next_sibling(1) + return res + + def lnodes_str2(self, v): + res = [] + for c in v.each_layer(): + res.append(c.source_(True)) + return "\n".join(res) + + def lnodes_str3(self, v, index): + res = [] + for c in v.each_layer(index): + res.append(c.source_(True)) + return "\n".join(res) + + def test_1(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", 1) + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t10.gds", 2) + + cv = mw.current_view() + + cv.clear_layers() + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "") + + pos = cv.end_layers() + self.assertEqual(pos.parent().is_null(), True) + p = pos.dup() + p.up() + self.assertEqual(p.is_null(), True) + self.assertEqual(pos.is_null(), False) + + self.assertEqual(pos == cv.begin_layers(), True) + self.assertEqual(pos != cv.begin_layers(), False) + + l1 = cv.insert_layer(pos, pya.LayerProperties()) + + self.assertEqual(pos == cv.begin_layers(), True) + self.assertEqual(pos != cv.begin_layers(), False) + self.assertEqual(pos == cv.end_layers(), False) + self.assertEqual(pos != cv.end_layers(), True) + self.assertEqual(pos < cv.end_layers(), True) + self.assertEqual(cv.end_layers() < pos, False) + self.assertEqual(pos < cv.begin_layers(), False) + self.assertEqual(cv.begin_layers() < pos, False) + self.assertEqual(pos.at_top(), True) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n") + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + l11 = cv.insert_layer(pos.last_child(), new_p) + + p12 = pos.last_child() + self.assertEqual(p12.parent().is_null(), False) + self.assertEqual(p12.parent() == pos, True) + + pp = pos.dup() + pp.down_last_child() + self.assertEqual(pp == p12, True) + self.assertEqual(pp == pos, False) + self.assertEqual(pp.parent() == pos, True) + pp.up() + self.assertEqual(pp == pos, True) + + self.assertEqual(p12.at_top(), False) + p12.to_sibling(0) + self.assertEqual(p12 == pos.first_child(), True) + self.assertEqual(p12.child_index(), 0) + p12.to_sibling(1) + self.assertEqual(p12.child_index(), 1) + self.assertEqual(p12 == pos.last_child(), True) + self.assertEqual(p12.num_siblings(), 1) + + l12 = cv.insert_layer(p12, pya.LayerProperties()) + l12_new = pya.LayerProperties() + l12_new.source = "1/0@2" + cv.set_layer_properties(p12, l12_new) + + self.assertEqual(p12.current().cellview(), 1) + self.assertEqual(p12.current().has_upper_hier_level(True), False) + self.assertEqual(p12.current().has_lower_hier_level(True), False) + + l12_new.source = "@* #1..2" + cv.set_layer_properties(p12, l12_new) + + self.assertEqual(p12.current().cellview(), 0) + self.assertEqual(p12.current().has_upper_hier_level(True), True) + self.assertEqual(p12.current().has_upper_hier_level(), True) + self.assertEqual(p12.current().upper_hier_level_(True), 2) + self.assertEqual(p12.current().upper_hier_level, 2) + self.assertEqual(p12.current().has_lower_hier_level(True), True) + self.assertEqual(p12.current().has_lower_hier_level(), True) + self.assertEqual(p12.current().lower_hier_level_(True), 1) + self.assertEqual(p12.current().lower_hier_level, 1) + + l12_new.source = "@* (0,0 *0.5) (0,5 r45 *2.5)" + cv.set_layer_properties(p12, l12_new) + trans = p12.current().trans_(True) + self.assertEqual(str(trans), str(p12.current().trans)) + self.assertEqual(len(trans), 2) + self.assertEqual(str(trans [0]), "r0 *0.5 0,0") + self.assertEqual(str(trans [1]), "r45 *2.5 0,5") + + l12_new.source = "1/0@2" + cv.set_layer_properties(p12, l12_new) + + self.assertEqual(p12.num_siblings(), 2) + + pos = cv.end_layers() + + new_p = pya.LayerProperties() + new_p.source = "@1" + l2 = cv.insert_layer(pos, new_p) + + new_p = pya.LayerProperties() + new_p.source = "7/0@*" + l21 = cv.insert_layer(pos.first_child(), new_p) + + p22 = pos.last_child() + new_p = pya.LayerProperties() + l22 = cv.insert_layer(pos.last_child(), new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@1\n 7/0@1\n */*@1\n") + + new_p = l2.dup() + new_p.source = "@2" + cv.set_layer_properties(pos, new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n */*@2\n") + + pos.first_child().current().source = "7/0@1" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@1\n */*@2\n") + pos.current().source = "@*" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@*\n 7/0@1\n */*@*\n") + pos.current().source = "@2" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@1\n */*@2\n") + pos.first_child().current().source = "7/1@*" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/1@2\n */*@2\n") + pos.first_child().current().source = "7/0@*" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n */*@2\n") + + l22_new = pya.LayerProperties() + l22_new.source = "7/1@*" + cv.replace_layer_node(p22, l22_new) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n 7/1@2\n") + + cv.delete_layer(p22) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n") + + new_p = l2.dup() + new_p.source = "%5@2" + cv.set_layer_properties(pos, new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n%5@2\n %5@2\n") + + mw.close_all() + + def test_1a(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", 1) + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t10.gds", 2) + + cv = mw.current_view() + + cv.clear_layers() + + cv.insert_layer_list(1) + cv.rename_layer_list(1, "x") + self.assertEqual(cv.current_layer_list, 1) + cv.set_current_layer_list(0) + self.assertEqual(cv.current_layer_list, 0) + cv.set_current_layer_list(1) + self.assertEqual(cv.current_layer_list, 1) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "") + self.assertEqual(self.lnodes_str("", cv.begin_layers(1)), "") + + pos = cv.end_layers(0) + self.assertEqual(pos.parent().is_null(), True) + p = pos.dup() + p.up() + self.assertEqual(p.is_null(), True) + self.assertEqual(pos.is_null(), False) + + self.assertEqual(pos == cv.begin_layers(0), True) + self.assertEqual(pos != cv.begin_layers(0), False) + + l1 = cv.insert_layer(0, pos, pya.LayerProperties()) + + self.assertEqual(pos == cv.begin_layers(0), True) + self.assertEqual(pos != cv.begin_layers(0), False) + self.assertEqual(pos == cv.end_layers(0), False) + self.assertEqual(pos != cv.end_layers(0), True) + self.assertEqual(pos < cv.end_layers(0), True) + self.assertEqual(cv.end_layers(0) < pos, False) + self.assertEqual(pos < cv.begin_layers(0), False) + self.assertEqual(cv.begin_layers(0) < pos, False) + self.assertEqual(pos.at_top(), True) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n") + self.assertEqual(self.lnodes_str("", cv.begin_layers(1)), "") + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "") + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + l11 = cv.insert_layer(0, pos.last_child(), new_p) + + p12 = pos.last_child() + self.assertEqual(p12.parent().is_null(), False) + self.assertEqual(p12.parent() == pos, True) + + pp = pos.dup() + pp.down_last_child() + self.assertEqual(pp == p12, True) + self.assertEqual(pp == pos, False) + self.assertEqual(pp.parent() == pos, True) + pp.up() + self.assertEqual(pp == pos, True) + + self.assertEqual(p12.at_top(), False) + p12.to_sibling(0) + self.assertEqual(p12 == pos.first_child(), True) + self.assertEqual(p12.child_index(), 0) + p12.to_sibling(1) + self.assertEqual(p12.child_index(), 1) + self.assertEqual(p12 == pos.last_child(), True) + self.assertEqual(p12.num_siblings(), 1) + + l12 = cv.insert_layer(0, p12, pya.LayerProperties()) + l12_new = pya.LayerProperties() + l12_new.source = "1/0@2" + cv.set_layer_properties(0, p12, l12_new) + + self.assertEqual(p12.current().cellview(), 1) + self.assertEqual(p12.current().has_upper_hier_level(True), False) + self.assertEqual(p12.current().has_upper_hier_level(), False) + self.assertEqual(p12.current().has_lower_hier_level(True), False) + self.assertEqual(p12.current().has_lower_hier_level(), False) + + l12_new.source = "@* #1..2" + cv.set_layer_properties(0, p12, l12_new) + + self.assertEqual(p12.current().cellview(), 0) + self.assertEqual(p12.current().has_upper_hier_level(True), True) + self.assertEqual(p12.current().has_upper_hier_level(), True) + self.assertEqual(p12.current().upper_hier_level_(True), 2) + self.assertEqual(p12.current().upper_hier_level, 2) + self.assertEqual(p12.current().has_lower_hier_level(True), True) + self.assertEqual(p12.current().has_lower_hier_level(), True) + self.assertEqual(p12.current().lower_hier_level_(True), 1) + self.assertEqual(p12.current().lower_hier_level, 1) + + l12_new.source = "@* (0,0 *0.5) (0,5 r45 *2.5)" + cv.set_layer_properties(0, p12, l12_new) + trans = p12.current().trans_(True) + self.assertEqual(len(trans), 2) + self.assertEqual(str(trans [0]), "r0 *0.5 0,0") + self.assertEqual(str(trans [1]), "r45 *2.5 0,5") + + l12_new.source = "1/0@2" + cv.set_layer_properties(0, p12, l12_new) + + self.assertEqual(p12.num_siblings(), 2) + + pos = cv.end_layers(0) + + new_p = pya.LayerProperties() + new_p.source = "@1" + l2 = cv.insert_layer(0, pos, new_p) + + new_p = pya.LayerProperties() + new_p.source = "7/0@*" + l21 = cv.insert_layer(0, pos.first_child(), new_p) + + p22 = pos.last_child() + new_p = pya.LayerProperties() + l22 = cv.insert_layer(0, pos.last_child(), new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n 1/0@1\n 1/0@2\n*/*@1\n 7/0@1\n */*@1\n") + self.assertEqual(self.lnodes_str("", cv.begin_layers(1)), "") + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "") + + new_p = l2.dup() + new_p.source = "@2" + cv.set_layer_properties(0, pos, new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n */*@2\n") + + l22_new = pya.LayerProperties() + l22_new.source = "7/1@*" + cv.replace_layer_node(0, p22, l22_new) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n 7/1@2\n") + + cv.delete_layer(0, p22) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n 1/0@1\n 1/0@2\n*/*@2\n 7/0@2\n") + + new_p = l2.dup() + new_p.source = "%5@2" + cv.set_layer_properties(0, pos, new_p) + + self.assertEqual(self.lnodes_str("", cv.begin_layers(0)), "*/*@*\n 1/0@1\n 1/0@2\n%5@2\n %5@2\n") + + # build a tree by building a separate tree + new_p = pya.LayerPropertiesNode() + self.assertEqual(new_p.has_children(), False) + n1 = new_p.add_child(pya.LayerProperties()) + self.assertEqual(n1.has_children(), False) + n1.source = "101/0" + n2 = pya.LayerPropertiesNode() + self.assertEqual(n2.has_children(), False) + n21 = n2.add_child(pya.LayerProperties()) + n21.source = "102/0" + self.assertEqual(n2.has_children(), True) + n22 = n2.add_child(pya.LayerProperties()) + self.assertEqual(n2.has_children(), True) + n22.source = "103/0" + new_p.add_child(n2) + self.assertEqual(new_p.has_children(), True) + + p = pos.last_child() + ll = cv.insert_layer(0, p, new_p) + self.assertEqual(p.current().has_children(), True) + self.assertEqual(p.first_child().current().has_children(), False) + self.assertEqual(p.first_child().current().source_(False), "101/0@1") + self.assertEqual(p.first_child().current().source, "%5@1") + + # (test clear_children): + new_p.clear_children() + self.assertEqual(new_p.has_children(), False) + + self.assertEqual(ll.has_children(), False) + + cv.transaction("Delete") + li = cv.begin_layers(0) + a = [] + while not li.at_end(): + a.append(li.dup()) + li.next() + self.assertEqual(len(a), 10) + cv.delete_layers(0, a) + self.assertEqual(cv.begin_layers(0).at_end(), True) + cv.commit() + mw.cm_undo() + self.assertEqual(cv.begin_layers(0).at_end(), False) + + cv.transaction("Delete") + i = 0 + while not cv.begin_layers(0).at_end(): + cv.delete_layer(0, cv.begin_layers(0)) + i += 1 + self.assertEqual(i, 2) + self.assertEqual(cv.begin_layers(0).at_end(), True) + cv.commit() + mw.cm_undo() + self.assertEqual(cv.begin_layers(0).at_end(), False) + + mw.close_all() + + def test_2(self): + + p = pya.LayerPropertiesNode() + + self.assertEqual(p.source_(False), "*/*@*") + self.assertEqual(p.source, "*/*@*") + self.assertEqual(p.has_source_name(False), False) + self.assertEqual(p.has_source_name(), False) + self.assertEqual(p.has_frame_color(), False) + self.assertEqual(p.has_frame_color(True), False) + self.assertEqual(p.has_fill_color(), False) + self.assertEqual(p.has_fill_color(True), False) + self.assertEqual(p.has_dither_pattern(), False) + self.assertEqual(p.has_dither_pattern(True), False) + self.assertEqual(p.has_line_style(), False) + self.assertEqual(p.has_line_style(True), False) + + p.name = "u" + self.assertEqual(p.name, "u") + + p.source_name = "x" + self.assertEqual(p.source_name_(False), "x") + self.assertEqual(p.source_name, "x") + self.assertEqual(p.source_(False), "x@*") + self.assertEqual(p.source, "x@*") + self.assertEqual(p.flat().source, "x@*") + self.assertEqual(p.dup().source, "x@*") + self.assertEqual(p.has_source_name(False), True) + self.assertEqual(p.has_source_name(), True) + + p.clear_source_name() + self.assertEqual(p.source_(False), "*/*@*") + self.assertEqual(p.has_source_name(False), False) + + p.source_layer_index = 6 + self.assertEqual(p.source_(False), "%6@*") + self.assertEqual(p.source_layer_index_(False), 6) + self.assertEqual(p.source_layer_index, 6) + + p.source_layer = 6 + p.source_datatype = 5 + self.assertEqual(p.source_(False), "%6@*") + + p.source_layer_index = -1 + self.assertEqual(p.source_(False), "6/5@*") + self.assertEqual(p.source_layer_index_(False), -1) + self.assertEqual(p.source_layer_index, -1) + self.assertEqual(p.source_layer_(False), 6) + self.assertEqual(p.source_layer, 6) + self.assertEqual(p.source_datatype_(False), 5) + self.assertEqual(p.source_datatype, 5) + + arr = [ pya.CplxTrans.new(pya.CplxTrans.M45), pya.CplxTrans.new(pya.CplxTrans.R270) ] + p.trans = arr + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(arr == p.trans_(False), True) + + p.source_cellview = 1 + self.assertEqual(p.source_(False), "6/5@2 (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(p.flat().source, "6/5@2 (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(p.source_cellview_(False), 1) + self.assertEqual(p.source_cellview, 1) + p.source_cellview = -1 + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0)") + + p.upper_hier_level = 17 + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0) #..17") + self.assertEqual(p.upper_hier_level_(False), 17) + self.assertEqual(p.upper_hier_level, 17) + self.assertEqual(p.has_upper_hier_level(False), True) + self.assertEqual(p.upper_hier_level_relative(), False) + self.assertEqual(p.upper_hier_level_relative(True), False) + p.set_upper_hier_level(11, True) + self.assertEqual(p.upper_hier_level_mode(False), 0) + self.assertEqual(p.upper_hier_level_mode(), 0) + self.assertEqual(p.upper_hier_level, 11) + self.assertEqual(p.upper_hier_level_relative(), True) + self.assertEqual(p.upper_hier_level_relative(True), True) + p.set_upper_hier_level(11, True, 1) + self.assertEqual(p.upper_hier_level, 11) + self.assertEqual(p.upper_hier_level_mode(False), 1) + self.assertEqual(p.upper_hier_level_mode(), 1) + p.set_upper_hier_level(11, True, 2) + self.assertEqual(p.upper_hier_level_mode(False), 2) + self.assertEqual(p.upper_hier_level_mode(), 2) + p.clear_upper_hier_level() + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(p.has_upper_hier_level(False), False) + self.assertEqual(p.has_upper_hier_level(), False) + + p.lower_hier_level = 17 + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0) #17..") + self.assertEqual(p.source, "6/5@* (m45 *1 0,0) (r270 *1 0,0) #17..") + self.assertEqual(p.lower_hier_level_(False), 17) + self.assertEqual(p.lower_hier_level, 17) + self.assertEqual(p.has_lower_hier_level(False), True) + self.assertEqual(p.has_lower_hier_level(), True) + self.assertEqual(p.lower_hier_level_relative(), False) + self.assertEqual(p.lower_hier_level_relative(True), False) + p.set_lower_hier_level(10, True) + self.assertEqual(p.lower_hier_level, 10) + self.assertEqual(p.lower_hier_level_relative(), True) + self.assertEqual(p.lower_hier_level_relative(True), True) + p.set_lower_hier_level(11, True, 1) + self.assertEqual(p.lower_hier_level, 11) + self.assertEqual(p.lower_hier_level_mode(False), 1) + self.assertEqual(p.lower_hier_level_mode(), 1) + p.set_lower_hier_level(11, True, 2) + self.assertEqual(p.lower_hier_level_mode(False), 2) + self.assertEqual(p.lower_hier_level_mode(), 2) + p.clear_lower_hier_level() + self.assertEqual(p.source_(False), "6/5@* (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(p.source, "6/5@* (m45 *1 0,0) (r270 *1 0,0)") + self.assertEqual(p.has_lower_hier_level(False), False) + self.assertEqual(p.has_lower_hier_level(), False) + + p.dither_pattern = 18 + self.assertEqual(p.dither_pattern_(True), 18) + self.assertEqual(p.flat().dither_pattern_(True), 18) + self.assertEqual(p.dither_pattern, 18) + self.assertEqual(p.eff_dither_pattern(), 18) + self.assertEqual(p.eff_dither_pattern(True), 18) + self.assertEqual(p.has_dither_pattern(), True) + self.assertEqual(p.has_dither_pattern(True), True) + + p.line_style = 12 + self.assertEqual(p.line_style_(True), 12) + self.assertEqual(p.flat().line_style_(True), 12) + self.assertEqual(p.line_style, 12) + self.assertEqual(p.eff_line_style(), 12) + self.assertEqual(p.eff_line_style(True), 12) + self.assertEqual(p.has_line_style(), True) + self.assertEqual(p.has_line_style(True), True) + + p.animation = 2 + self.assertEqual(p.animation_(True), 2) + self.assertEqual(p.flat().animation_(True), 2) + self.assertEqual(p.animation, 2) + + p.marked = True + self.assertEqual(p.marked_(True), True) + self.assertEqual(p.flat().marked_(True), True) + self.assertEqual(p.marked, True) + + p.marked = False + self.assertEqual(p.marked_(False), False) + self.assertEqual(p.flat().marked_(False), False) + self.assertEqual(p.marked, False) + + p.transparent = True + self.assertEqual(p.transparent_(True), True) + self.assertEqual(p.flat().transparent_(True), True) + self.assertEqual(p.transparent, True) + + p.transparent = False + self.assertEqual(p.transparent_(False), False) + self.assertEqual(p.flat().transparent_(False), False) + self.assertEqual(p.transparent, False) + + p.visible = True + self.assertEqual(p.visible_(True), True) + self.assertEqual(p.flat().visible_(True), True) + self.assertEqual(p.visible, True) + + p.visible = False + self.assertEqual(p.visible_(False), False) + self.assertEqual(p.flat().visible_(False), False) + self.assertEqual(p.visible, False) + + p.valid = True + self.assertEqual(p.valid_(True), True) + self.assertEqual(p.flat().valid_(True), True) + self.assertEqual(p.valid, True) + + p.valid = False + self.assertEqual(p.valid_(False), False) + self.assertEqual(p.flat().valid_(False), False) + self.assertEqual(p.valid, False) + + p.xfill = True + self.assertEqual(p.xfill_(True), True) + self.assertEqual(p.flat().xfill_(True), True) + self.assertEqual(p.xfill, True) + + p.xfill = False + self.assertEqual(p.xfill_(False), False) + self.assertEqual(p.flat().xfill_(False), False) + self.assertEqual(p.xfill, False) + + p.width = 3 + self.assertEqual(p.width_(True), 3) + self.assertEqual(p.flat().width_(True), 3) + self.assertEqual(p.width, 3) + + p.frame_color = 0xff000031 + self.assertEqual(p.frame_color_(True), 0xff000031) + self.assertEqual(p.flat().frame_color_(True), 0xff000031) + self.assertEqual(p.frame_color, 0xff000031) + self.assertEqual(p.has_frame_color(), True) + self.assertEqual(p.has_frame_color(True), True) + self.assertEqual(p.has_fill_color(), False) + self.assertEqual(p.has_fill_color(True), False) + + p.fill_color = 0xff000032 + self.assertEqual(p.fill_color_(True), 0xff000032) + self.assertEqual(p.flat().fill_color_(True), 0xff000032) + self.assertEqual(p.fill_color, 0xff000032) + self.assertEqual(p.has_frame_color(), True) + self.assertEqual(p.has_fill_color(), True) + + p.frame_brightness = 41 + self.assertEqual(p.frame_brightness_(True), 41) + self.assertEqual(p.flat().frame_brightness_(True), 41) + self.assertEqual(p.frame_brightness, 41) + + p.fill_brightness = 42 + self.assertEqual(p.fill_brightness_(True), 42) + self.assertEqual(p.flat().fill_brightness_(True), 42) + self.assertEqual(p.fill_brightness, 42) + self.assertEqual("#%06x" % p.eff_frame_color(), "#33335b") + self.assertEqual("#%06x" % p.eff_fill_color(), "#34345c") + self.assertEqual("#%06x" % p.eff_frame_color(True), "#33335b") + self.assertEqual("#%06x" % p.eff_fill_color(True), "#34345c") + + p.clear_fill_color() + self.assertEqual(p.has_fill_color(), False) + p.clear_frame_color() + self.assertEqual(p.has_frame_color(), False) + p.clear_dither_pattern() + self.assertEqual(p.has_dither_pattern(), False) + p.clear_line_style() + self.assertEqual(p.has_line_style(), False) + + pp = pya.LayerPropertiesNode() + self.assertEqual(pp == p, False) + self.assertEqual(pp != p, True) + pp = p.dup() + self.assertEqual(pp == p, True) + self.assertEqual(pp != p, False) + + # direct replacement of objects and attributes + def test_3(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", pya.LoadLayoutOptions(), "", 1) + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t10.gds", pya.LoadLayoutOptions(), "", 2) + + cv = mw.current_view() + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "1/0@1\n2/0@1\n1/0@2\n2/0@2\n3/0@2\n3/1@2\n4/0@2\n5/0@2\n6/0@2\n6/1@2\n7/0@2\n8/0@2\n8/1@2\n") + + cv.clear_layers() + + pos = cv.end_layers() + self.assertEqual(pos.current().is_valid(), False) + + cv.insert_layer(pos, pya.LayerProperties()) + self.assertEqual(pos.current().is_valid(), True) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*") + + self.assertEqual(cv.begin_layers().current().name, "") + self.assertEqual(cv.begin_layers().current().visible, True) + self.assertEqual(cv.begin_layers().current().dither_pattern, -1) + self.assertEqual(cv.begin_layers().current().line_style, -1) + self.assertEqual(cv.begin_layers().current().valid, True) + self.assertEqual(cv.begin_layers().current().transparent, False) + + # test LayerPropertiesNodeRef + pos.current().name = "NAME" + pos.current().visible = False + pos.current().fill_color = 0xff012345 + pos.current().frame_color = 0xff123456 + pos.current().fill_brightness = 42 + pos.current().frame_brightness = 17 + pos.current().dither_pattern = 4 + pos.current().line_style = 3 + pos.current().valid = False + pos.current().transparent = True + pos.current().marked = False + pos.current().xfill = False + pos.current().width = 2 + pos.current().animation = 2 + + self.assertEqual(cv.begin_layers().current().name, "NAME") + self.assertEqual(cv.begin_layers().current().visible, False) + self.assertEqual(cv.begin_layers().current().fill_color, 0xff012345) + self.assertEqual(cv.begin_layers().current().frame_color, 0xff123456) + self.assertEqual(cv.begin_layers().current().fill_brightness, 42) + self.assertEqual(cv.begin_layers().current().frame_brightness, 17) + self.assertEqual(cv.begin_layers().current().dither_pattern, 4) + self.assertEqual(cv.begin_layers().current().line_style, 3) + self.assertEqual(cv.begin_layers().current().valid, False) + self.assertEqual(cv.begin_layers().current().transparent, True) + self.assertEqual(cv.begin_layers().current().marked, False) + self.assertEqual(cv.begin_layers().current().xfill, False) + self.assertEqual(cv.begin_layers().current().width, 2) + self.assertEqual(cv.begin_layers().current().animation, 2) + + pos.current().valid = True + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + self.assertEqual(new_p.flat().source, "1/0@1") + self.assertEqual(new_p == new_p.flat(), True) + self.assertEqual(new_p != new_p.flat(), False) + new_p_ref = pos.current().add_child(new_p) + self.assertEqual(new_p_ref.layer_index(), cv.cellview(0).layout().layer(1, 0)) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*\n1/0@1") + + p = pos.current().add_child() + p.source = "1/0@2" + self.assertEqual(p.is_valid(), True) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n 1/0@2\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*\n1/0@1\n1/0@2") + self.assertEqual(p.layer_index(), cv.cellview(1).layout().layer(1, 0)) + self.assertEqual(str(p.bbox()), "(-1.4,1.8;25.16,3.8)") + self.assertEqual(p.view() == cv, True) + self.assertEqual(p.list_index(), 0) + + l12_new = pya.LayerProperties() + l12_new.source = "@* #1..2" + self.assertEqual(l12_new.flat().source, "*/*@* #1..2") + self.assertEqual(pos.first_child().current().source, "1/0@1") + self.assertEqual(pos.first_child().current().is_valid(), True) + self.assertEqual(pos.last_child().current().is_valid(), False) + pos.first_child().next().current().assign(l12_new) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n */*@* #1..2\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*\n1/0@1\n*/*@* #1..2") + + pos.first_child().next_sibling(1).current().source = "@* #3..4" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 1/0@1\n */*@* #3..4\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*\n1/0@1\n*/*@* #3..4") + + pos.first_child().to_sibling(1).next_sibling(-1).current().source = "7/0" + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "*/*@*\n 7/0@1\n */*@* #3..4\n") + self.assertEqual(self.lnodes_str2(cv), "*/*@*\n7/0@1\n*/*@* #3..4") + self.assertEqual(self.lnodes_str3(cv, 0), "*/*@*\n7/0@1\n*/*@* #3..4") + self.assertEqual(self.lnodes_str3(cv, 1), "") + + nn = pya.LayerPropertiesNode() + nn.source = "TOP" + + nn1 = pya.LayerPropertiesNode() + nn1.source = "nn1" + + nn2 = pya.LayerProperties() + nn2.source = "nn1" + nn1.add_child(nn2) + + nn.add_child(nn1) + + pos.current().assign(nn) + self.assertEqual(pos.current().id(), nn.id()) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "TOP@1\n nn1@1\n nn1@1\n") + self.assertEqual(self.lnodes_str2(cv), "TOP@1\nnn1@1\nnn1@1") + + mw.close_all() + + # propagation of "real" attributes through the hierarchy + def test_4(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", 1) + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t10.gds", 2) + + cv = mw.current_view() + + cv.clear_layers() + + pos = cv.end_layers() + self.assertEqual(pos.current().is_valid(), False) + + cv.insert_layer(pos, pya.LayerProperties()) + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + pos.current().add_child(new_p) + + self.assertEqual(pos.current().visible_(True), True) + self.assertEqual(pos.current().visible_(False), True) + self.assertEqual(pos.first_child().current().visible_(True), True) + self.assertEqual(pos.first_child().current().visible_(False), True) + pos.current().visible = False + self.assertEqual(pos.current().visible_(True), False) + self.assertEqual(pos.current().visible_(False), False) + self.assertEqual(pos.first_child().current().visible_(True), False) + self.assertEqual(pos.first_child().current().visible_(False), True) + + mw.close_all() + + # delete method of iterator + def test_5(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", 1) + + cv = mw.current_view() + cv.clear_layers() + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + cv.insert_layer(0, cv.end_layers(), new_p) + + new_p = pya.LayerProperties() + new_p.source = "2/0@1" + cv.insert_layer(0, cv.end_layers(), new_p) + + pos = cv.begin_layers() + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "1/0@1\n2/0@1\n") + self.assertEqual(pos.at_end(), False) + self.assertEqual(pos.current().source, "1/0@1") + self.assertEqual(pos.current().is_valid(), True) + + pos.current().delete() + + self.assertEqual(pos.current().source, "2/0@1") + self.assertEqual(pos.current().is_valid(), True) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "2/0@1\n") + self.assertEqual(pos.at_end(), False) + + pos.current().delete() + + self.assertEqual(pos.current().is_valid(), False) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "") + self.assertEqual(pos.at_end(), True) + + pos.current().delete() + + self.assertEqual(pos.current().is_valid(), False) + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "") + self.assertEqual(pos.at_end(), True) + + # With hierarchy + cv.clear_layers() + + new_p = pya.LayerProperties() + new_p.source = "1/0@1" + cv.insert_layer(0, cv.end_layers(), new_p) + + new_p = pya.LayerProperties() + new_p.source = "2/0@1" + cv.insert_layer(0, cv.end_layers(), new_p) + + new_p = pya.LayerProperties() + new_p.source = "3/0@1" + cv.insert_layer(0, cv.end_layers(), new_p) + + pos = cv.begin_layers() + pos.next_sibling(1) + c1 = pos.current().add_child() + c1.source = "21/0@1" + c2 = pos.current().add_child() + c2.source = "22/0@1" + + posn = cv.begin_layers() + posn.next_sibling(2) + c3 = posn.current().add_child() + c3.source = "31/0@1" + + self.assertEqual(self.lnodes_str("", cv.begin_layers()), "1/0@1\n2/0@1\n 21/0@1\n 22/0@1\n3/0@1\n 31/0@1\n") + + pc = pos.first_child() + + self.assertEqual(pc.current().source, "21/0@1") + self.assertEqual(pc.current().is_valid(), True) + self.assertEqual(pc.at_end(), False) + pc.current().delete() + + self.assertEqual(pc.current().source, "22/0@1") + self.assertEqual(pc.current().is_valid(), True) + self.assertEqual(pc.at_end(), False) + pc.current().delete() + + self.assertEqual(pc.at_end(), True) + self.assertEqual(pc.current().is_valid(), False) + + mw.close_all() + + # custom stipples and line styles + def test_6(self): + + app = pya.Application.instance() + mw = app.main_window() + mw.close_all() + + mw.load_layout(os.getenv("TESTSRC") + "/testdata/gds/t11.gds", 1) + + cv = mw.current_view() + + cv.clear_stipples() + + self.assertEqual(cv.get_stipple(0), "*\n") + + index = cv.add_stipple("something", [ 0x1, 0x2, 0x4, 0x8 ], 4) + self.assertEqual(cv.get_stipple(index), "...*\n..*.\n.*..\n*...\n") + + cv.remove_stipple(index) + self.assertEqual(cv.get_stipple(index), "*\n") + + index = cv.add_stipple("something", ".**.\n*..*\n.*.*\n*.*.") + self.assertEqual(cv.get_stipple(index), ".**.\n*..*\n.*.*\n*.*.\n") + + cv.clear_stipples() + self.assertEqual(cv.get_stipple(index), "*\n") + + cv.clear_line_styles() + + self.assertEqual(cv.get_line_style(0), "") + + index = cv.add_line_style("something", 0x5, 4) + self.assertEqual(cv.get_line_style(index), "*.*.") + + cv.remove_line_style(index) + self.assertEqual(cv.get_line_style(index), "") + + index = cv.add_line_style("something", ".**.*..*") + self.assertEqual(cv.get_line_style(index), ".**.*..*") + + cv.clear_line_styles() + self.assertEqual(cv.get_line_style(index), "") + + mw.close_all() + + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + # NOTE: Use this instead of loadTestsfromTestCase to select a specific test: + # suite.addTest(BasicTest("test_26")) + suite = unittest.TestLoader().loadTestsFromTestCase(LAYLayersTest) + + # Only runs with Application available + if "Application" in pya.__all__ and not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + + diff --git a/testdata/ruby/layLayers.rb b/testdata/ruby/layLayers.rb index 5e8cea0d6..cc4972e33 100644 --- a/testdata/ruby/layLayers.rb +++ b/testdata/ruby/layLayers.rb @@ -703,6 +703,44 @@ class LAYLayers_TestClass < TestBase assert_equal( lnodes_str( "", cv.begin_layers ), "*/*@*\n" ) assert_equal( lnodes_str2(cv), "*/*@*" ) + assert_equal( cv.begin_layers.current.name, "" ) + assert_equal( cv.begin_layers.current.visible?, true ) + assert_equal( cv.begin_layers.current.dither_pattern, -1 ) + assert_equal( cv.begin_layers.current.line_style, -1 ) + assert_equal( cv.begin_layers.current.valid?, true ) + assert_equal( cv.begin_layers.current.transparent?, false ) + + # test LayerPropertiesNodeRef + pos.current.name = "NAME" + pos.current.visible = false + pos.current.fill_color = 0xff012345 + pos.current.frame_color = 0xff123456 + pos.current.fill_brightness = 42 + pos.current.frame_brightness = 17 + pos.current.dither_pattern = 4 + pos.current.line_style = 3 + pos.current.valid = false + pos.current.transparent = true + pos.current.marked = false + pos.current.xfill = false + pos.current.width = 2 + pos.current.animation = 2 + + assert_equal( cv.begin_layers.current.name, "NAME" ) + assert_equal( cv.begin_layers.current.visible?, false ) + assert_equal( cv.begin_layers.current.fill_color, 0xff012345 ) + assert_equal( cv.begin_layers.current.frame_color, 0xff123456 ) + assert_equal( cv.begin_layers.current.fill_brightness, 42 ) + assert_equal( cv.begin_layers.current.frame_brightness, 17 ) + assert_equal( cv.begin_layers.current.dither_pattern, 4 ) + assert_equal( cv.begin_layers.current.line_style, 3 ) + assert_equal( cv.begin_layers.current.valid?, false ) + assert_equal( cv.begin_layers.current.transparent?, true ) + assert_equal( cv.begin_layers.current.marked?, false ) + assert_equal( cv.begin_layers.current.xfill?, false ) + assert_equal( cv.begin_layers.current.width, 2 ) + assert_equal( cv.begin_layers.current.animation, 2 ) + new_p = RBA::LayerProperties::new new_p.source = "1/0@1" assert_equal( new_p.flat.source, "1/0@1" ) From 2389d2b391e37368aa905b4cfd167badcbff404f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2019 20:48:07 +0200 Subject: [PATCH 3/7] Fixed #281 (proper reporting of width/space violations in the kissing-corner case) --- src/db/db/dbAsIfFlatEdges.cc | 2 +- src/db/db/dbAsIfFlatRegion.cc | 3 +- src/db/db/dbDeepEdges.cc | 1 + src/db/db/dbDeepRegion.cc | 2 + src/db/db/dbEdgePairRelations.cc | 12 ++++ src/db/db/dbRegionUtils.cc | 18 ++++- src/db/unit_tests/dbEdgePairRelations.cc | 49 ++++++++++++- src/db/unit_tests/dbEdges.cc | 2 +- src/drc/unit_tests/drcSimpleTests.cc | 86 +++++++++++++++++++++++ testdata/drc/drcSimpleTests_13a.drc | 17 +++++ testdata/drc/drcSimpleTests_13b.drc | 19 +++++ testdata/drc/drcSimpleTests_au13a.gds | Bin 0 -> 2330 bytes testdata/drc/drcSimpleTests_au13b.gds | Bin 0 -> 2330 bytes testdata/drc/drcSuiteTests_au6.oas | Bin 18358 -> 17842 bytes testdata/drc/kissing_corners.gds | Bin 0 -> 986 bytes 15 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_13a.drc create mode 100644 testdata/drc/drcSimpleTests_13b.drc create mode 100644 testdata/drc/drcSimpleTests_au13a.gds create mode 100644 testdata/drc/drcSimpleTests_au13b.gds create mode 100644 testdata/drc/kissing_corners.gds diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 4e5e03581..e3d1edba8 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -450,7 +450,7 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co } EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index a13e49823..2ba88254a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -642,7 +642,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, } EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); @@ -664,6 +664,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord std::auto_ptr result (new FlatEdgePairs ()); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index e40c54898..081ac4c99 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -1404,6 +1404,7 @@ DeepEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord ensure_merged_edges_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 0ee8b7195..f71f40f32 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1461,6 +1461,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons ensure_merged_polygons_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); @@ -1489,6 +1490,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, b ensure_merged_polygons_valid (); EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (false); check.set_whole_edges (whole_edges); check.set_ignore_angle (ignore_angle); check.set_min_projection (min_projection); diff --git a/src/db/db/dbEdgePairRelations.cc b/src/db/db/dbEdgePairRelations.cc index 2c60966bb..2ba777d99 100644 --- a/src/db/db/dbEdgePairRelations.cc +++ b/src/db/db/dbEdgePairRelations.cc @@ -66,6 +66,12 @@ bool euclidian_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge db::Edge g (other); int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); + + // "kissing corner" issue: force include zero if the edges are collinear and overlap. + if (! include_zero && s1 == 0 && s2 == 0 && e.intersect (g)) { + include_zero = true; + } + int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e @@ -203,6 +209,12 @@ static bool var_near_part_of_edge (bool include_zero, db::Coord d, db::Coord dd, db::Edge g (other); int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); + + // "kissing corner" issue: force include zero if the edges are collinear and overlap + if (! include_zero && s1 == 0 && s2 == 0 && e.intersect (g)) { + include_zero = true; + } + int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index eb1114e1a..82a70313f 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -66,6 +66,21 @@ Edge2EdgeCheckBase::prepare_next_pass () return false; } +static inline bool shields (const db::EdgePair &ep, const db::Edge &q) +{ + db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); + db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ()); + + std::pair ip1 = pe1.intersect_point (q); + std::pair ip2 = pe2.intersect_point (q); + + if (ip1.first && ip2.first) { + return ip1.second != ip2.second || (ip1.second != q.p1 () && ip2.second != q.p2 ()); + } else { + return false; + } +} + void Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) { @@ -128,8 +143,7 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { if (! m_ep_discarded [*i]) { db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + if (shields (ep, *o2)) { m_ep_discarded [*i] = true; } } diff --git a/src/db/unit_tests/dbEdgePairRelations.cc b/src/db/unit_tests/dbEdgePairRelations.cc index 2097e08ae..da7061089 100644 --- a/src/db/unit_tests/dbEdgePairRelations.cc +++ b/src/db/unit_tests/dbEdgePairRelations.cc @@ -346,7 +346,7 @@ TEST(7) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (-1, 30), db::Point (-1, -20)), &output); EXPECT_EQ (res, false); f.set_include_zero (false); - res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, -20)), &output); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (0, 30), db::Point (0, 11)), &output); EXPECT_EQ (res, false); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 10)), db::Edge (db::Point (1, 30), db::Point (1, -20)), &output); EXPECT_EQ (res, true); @@ -355,3 +355,50 @@ TEST(7) EXPECT_EQ (res, false); } +TEST(8_KissingCornerProblem) +{ + // The kissing corner problem is solved by allowing distance-0 width and space relations and checking them + // if the projection is >0. + + db::EdgeRelationFilter f (db::WidthRelation, 10); + f.set_include_zero (false); + db::EdgePair output; + bool res; + + f.set_metrics (db::Euclidian); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,90;0,100):(0,110;0,100)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,40;0,100):(0,110;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,0;0,10):(0,0;0,-10)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + + f = db::EdgeRelationFilter (db::SpaceRelation, 10); + f.set_include_zero (false); + + f.set_metrics (db::Euclidian); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,90):(0,100;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,40):(0,50;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,10;0,0):(0,-10;0,0)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); +} diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 23e7fd371..1e4b27501 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -740,7 +740,7 @@ TEST(20) EXPECT_EQ (r1.has_valid_edges (), false); db::Edges r2 (db::RecursiveShapeIterator (ly, ly.cell (top), l2), false); EXPECT_EQ (r2.has_valid_edges (), false); - EXPECT_EQ (r1.separation_check (r2, 20).to_string (), "(50,0;50,30)/(40,40;40,10);(63,30;80,30)/(97,40;80,40);(80,30;80,20)/(80,40;80,50);(50,40;50,57)/(40,40;40,23);(80,70;80,40)/(80,40;80,70);(60,40;50,40)/(30,40;40,40)"); + EXPECT_EQ (r1.separation_check (r2, 20).to_string (), "(50,0;50,30)/(40,40;40,10);(63,30;80,30)/(97,40;80,40);(50,40;50,57)/(40,40;40,23);(80,70;80,40)/(80,40;80,70)"); EXPECT_EQ (r1.separation_check (r2, 20, false, db::Projection).to_string (), "(50,10;50,30)/(40,30;40,10);(80,70;80,40)/(80,40;80,70)"); EXPECT_EQ (r1.separation_check (r2, 20, false, db::Euclidian, 90, 1).to_string (), "(50,0;50,30)/(40,40;40,10);(80,70;80,40)/(80,40;80,70)"); } diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index c3a0a68c5..1a78bf780 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -533,3 +533,89 @@ TEST(12_NetlistJoinLabels) CHECKPOINT (); compare_netlists (_this, output, au); } + +TEST(13a_KissingCorners) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_13a.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/kissing_corners.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au13a.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + CHECKPOINT (); + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(13b_KissingCornersDeep) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_13b.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/kissing_corners.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au13b.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + CHECKPOINT (); + db::compare_layouts (_this, layout, au, db::NoNormalization); +} diff --git a/testdata/drc/drcSimpleTests_13a.drc b/testdata/drc/drcSimpleTests_13a.drc new file mode 100644 index 000000000..4f1d62ed0 --- /dev/null +++ b/testdata/drc/drcSimpleTests_13a.drc @@ -0,0 +1,17 @@ +source($drc_test_source) +target($drc_test_target, "TOP") + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +l1.width(100.nm).output(100, 0) +l1.space(100.nm).output(101, 0) +l1.notch(100.nm).output(102, 0) +l2.separation(l3, 100.nm).output(103, 0) +l2.overlap(l3, 100.nm).output(104, 0) + diff --git a/testdata/drc/drcSimpleTests_13b.drc b/testdata/drc/drcSimpleTests_13b.drc new file mode 100644 index 000000000..5dcc56571 --- /dev/null +++ b/testdata/drc/drcSimpleTests_13b.drc @@ -0,0 +1,19 @@ +source($drc_test_source) +target($drc_test_target, "TOP") + +deep + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +l1.width(100.nm).output(100, 0) +l1.space(100.nm).output(101, 0) +l1.notch(100.nm).output(102, 0) +l2.separation(l3, 100.nm).output(103, 0) +l2.overlap(l3, 100.nm).output(104, 0) + diff --git a/testdata/drc/drcSimpleTests_au13a.gds b/testdata/drc/drcSimpleTests_au13a.gds new file mode 100644 index 0000000000000000000000000000000000000000..0e7e5b8c471fdc5b67657ffb22281c1f94c22dc3 GIT binary patch literal 2330 zcmbtUJx>)u6g~UBl4u~$lETu$#6(Pt4GDzC@QfA|#LmR06c%(q;vcZHe~pC+`~(Xt z3KFdR0b1EmQc#M^xjTE`+E7&*aWM_iOH5qMTd!UNX$jeo=5Pji&gUTbOOV{mx68p)6s=7Q91ehO+UmnF)a^`Fb%F6Z?T`WbZ zsB(|O7kduXPmMRl^Jp;Lv+l-H>h%=^&dCy-8cU!zWD1D|22MVe@*%?h(Gc1n+o%2YcFTBZ2lp? zd;LN_ooL_d94M-MY98MC_$$B0G!&UV@>f+A_icR6=bqzc1Lre4Xz))XclWmbLHyYP^_cs_&K~)Ue`3eoTmHz`PxR*F zV}<{m3m0p;+2A+EKzaJoVR8}VANuc}zu}9&X3hTohA*`yzuWu2_w`ffVnb}1-QItO zKd*oD?z8t#;J?TFfAIPDir?^i)z9$hzkB}~zWC>y)^FwK_Rd@Pj+40_1^v4-Xa0uY OEB`6}?C}>I$rM-JtSejq literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au13b.gds b/testdata/drc/drcSimpleTests_au13b.gds new file mode 100644 index 0000000000000000000000000000000000000000..0e7e5b8c471fdc5b67657ffb22281c1f94c22dc3 GIT binary patch literal 2330 zcmbtUJx>)u6g~UBl4u~$lETu$#6(Pt4GDzC@QfA|#LmR06c%(q;vcZHe~pC+`~(Xt z3KFdR0b1EmQc#M^xjTE`+E7&*aWM_iOH5qMTd!UNX$jeo=5Pji&gUTbOOV{mx68p)6s=7Q91ehO+UmnF)a^`Fb%F6Z?T`WbZ zsB(|O7kduXPmMRl^Jp;Lv+l-H>h%=^&dCy-8cU!zWD1D|22MVe@*%?h(Gc1n+o%2YcFTBZ2lp? zd;LN_ooL_d94M-MY98MC_$$B0G!&UV@>f+A_icR6=bqzc1Lre4Xz))XclWmbLHyYP^_cs_&K~)Ue`3eoTmHz`PxR*F zV}<{m3m0p;+2A+EKzaJoVR8}VANuc}zu}9&X3hTohA*`yzuWu2_w`ffVnb}1-QItO zKd*oD?z8t#;J?TFfAIPDir?^i)z9$hzkB}~zWC>y)^FwK_Rd@Pj+40_1^v4-Xa0uY OEB`6}?C}>I$rM-JtSejq literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index bf5fabfd9f9ed364a28d4e6159fc5d104a4e8420..022252773b5d9d0215f14f803ade93b936f0f3b4 100644 GIT binary patch delta 998 zcmdni&$y|ZaRU!4M-$^2jyA|v4Qgp#|g%lmLhj1Fkg)N z8OqI)vVm~}V>BO#6e)PYeukq_Ql#Pl&mHbYql0!LH}|_2BBE#xsaiK%8;VZgQilu(+u4DntvjKn%{hKC7-a#G{(s?H(5jtSeQ3+cJLi4 z*vL6kaHWW-;X_7*;)%?nijRPNWD5l?A0vr?jE6Ym8RO&)qOzN}2^^8${8jIu1OUpb>e8r5D_wor&{wKgMWWmCGLiIrSVV;{T6PXX%i7MWmypc~9B*l*=HCaGFWb!*c ziODts=96vt`BV*9m^X8F@Et1H$T?FG7~Vksp(~8%RF8EsPGlBke8?zj`G|4yI{|rN zG?PRvAA`lwZP?5s_*B|eB({n1495(?1#Ao=H80o~u-#z!APQtIVB5p`!v-W;a)9R! z_X2sK*c7gmjhq`ffvgRjXE*P$+Q SXAkQMpPyU|n?LFtlmGzkB*Rev diff --git a/testdata/drc/kissing_corners.gds b/testdata/drc/kissing_corners.gds new file mode 100644 index 0000000000000000000000000000000000000000..af4b474ffcb997dd5921fe12caf49c8c2de35b37 GIT binary patch literal 986 zcmbW0p-u!b5QhJ4yKO^&90~FS5PRsq4>MCP4c`cur(5`zD@D9_5)*Y6(%P@kG|MpHTBOoL0};z_OP< z;*;JQZ}Ro%(`ue^-u%hF_UIdLxJp>Qh(f7xS556F)lcg;sDJlDT8H9+8sAdm#5})g z|M!3QKcVM*Gu8h~-+R7(&R?f~o9fRxOw+vgJkLhH|E_-a{&n?~M9;n61Et3GJ_Kr& F!#7%9q8R`H literal 0 HcmV?d00001 From 303cda69818fe52913c8e4b062a67aa2b68f55fe Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2019 21:40:37 +0200 Subject: [PATCH 4/7] Fixed #277 (min_coherence not recognized by region size) --- src/db/db/dbAsIfFlatRegion.cc | 4 ++-- src/db/db/dbDeepRegion.cc | 5 +++++ src/db/db/dbDeepRegion.h | 1 + src/db/db/dbFlatRegion.cc | 7 +++++++ src/db/db/dbFlatRegion.h | 1 + src/db/db/dbOriginalLayerRegion.cc | 8 ++++++++ src/db/db/dbOriginalLayerRegion.h | 1 + src/db/db/dbRegionDelegate.cc | 5 ++++- src/db/db/dbRegionDelegate.h | 1 + src/db/unit_tests/dbDeepRegionTests.cc | 27 ++++++++++++++++++++++++++ src/db/unit_tests/dbRegion.cc | 18 +++++++++++++++++ 11 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index a13e49823..e6cd117c1 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -764,7 +764,7 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const db::Box b = bbox ().enlarged (db::Vector (dx, dy)); return region_from_box (b); - } else if (! merged_semantics ()) { + } else if (! merged_semantics () || is_merged ()) { // Generic case std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); @@ -801,7 +801,7 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); db::SizingPolygonFilter siz (pg2, dx, dy, mode); - db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); + db::PolygonGenerator pg (siz, false /*don't resolve holes*/, min_coherence () /*min. coherence*/); db::BooleanOp op (db::BooleanOp::Or); ep.process (pg, op); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 0ee8b7195..572e7a6ba 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -160,6 +160,11 @@ void DeepRegion::merged_semantics_changed () // .. nothing yet .. } +void DeepRegion::min_coherence_changed () +{ + set_is_merged (false); +} + RegionIteratorDelegate * DeepRegion::begin () const { diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 6ede2cee0..ce554ffb9 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -220,6 +220,7 @@ public: protected: virtual void merged_semantics_changed (); + virtual void min_coherence_changed (); void set_is_merged (bool f); private: diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 1ab7ebaa9..3df87bf77 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -94,6 +94,13 @@ void FlatRegion::merged_semantics_changed () m_merged_polygons_valid = false; } +void FlatRegion::min_coherence_changed () +{ + m_is_merged = false; + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + void FlatRegion::reserve (size_t n) { m_polygons.reserve (db::Polygon::tag (), n); diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 5fe5e50ec..5f7d2d2cd 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -180,6 +180,7 @@ public: protected: virtual void merged_semantics_changed (); + virtual void min_coherence_changed (); virtual Box compute_bbox () const; void invalidate_cache (); void set_is_merged (bool m); diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index 73f9aa83f..28a79033f 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -151,6 +151,14 @@ OriginalLayerRegion::merged_semantics_changed () m_merged_polygons_valid = false; } +void +OriginalLayerRegion::min_coherence_changed () +{ + m_is_merged = false; + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + RegionIteratorDelegate * OriginalLayerRegion::begin () const { diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index b99a651ec..23ce7004b 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -72,6 +72,7 @@ public: protected: virtual void merged_semantics_changed (); + virtual void min_coherence_changed (); private: OriginalLayerRegion &operator= (const OriginalLayerRegion &other); diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc index 0e6328ce7..2ce675465 100644 --- a/src/db/db/dbRegionDelegate.cc +++ b/src/db/db/dbRegionDelegate.cc @@ -78,7 +78,10 @@ void RegionDelegate::set_base_verbosity (int vb) void RegionDelegate::set_min_coherence (bool f) { - m_merge_min_coherence = f; + if (f != m_merge_min_coherence) { + m_merge_min_coherence = f; + min_coherence_changed (); + } } void RegionDelegate::set_merged_semantics (bool f) diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index f5eb945d7..8fe79edb9 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -319,6 +319,7 @@ protected: } virtual void merged_semantics_changed () { } + virtual void min_coherence_changed () { } private: bool m_merged_semantics; diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index a23807ecb..5bdb28766 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -1406,3 +1406,30 @@ TEST(101_DeepFlatCollaboration) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au101.gds"); } +TEST(issue_277) +{ + db::Layout ly; + db::cell_index_type top_cell_index = ly.add_cell ("TOP"); + db::Cell &top_cell = ly.cell (top_cell_index); + unsigned int l1 = ly.insert_layer (); + + db::Shapes &s = top_cell.shapes (l1); + s.insert (db::Box (0, 0, 400, 400)); + s.insert (db::Box (400, 400, 800, 800)); + + + db::DeepShapeStore dss; + + db::Region r (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), ""); + + r.set_min_coherence (true); + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), "(399,399;399,401;401,401;401,399)"); + + r.merge (); + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), "(399,399;399,401;401,401;401,399)"); + + r.set_min_coherence (false); // needs to merge again + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), ""); +} diff --git a/src/db/unit_tests/dbRegion.cc b/src/db/unit_tests/dbRegion.cc index 6fb883ae1..31d939d4a 100644 --- a/src/db/unit_tests/dbRegion.cc +++ b/src/db/unit_tests/dbRegion.cc @@ -1397,3 +1397,21 @@ TEST(issue_228) EXPECT_EQ (r.selected_interacting (rr).to_string (), r.to_string ()); EXPECT_EQ (rr.selected_interacting (r).to_string (), rr.to_string ()); } + +TEST(issue_277) +{ + db::Region r; + r.insert (db::Box (0, 0, 400, 400)); + r.insert (db::Box (400, 400, 800, 800)); + + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), ""); + + r.set_min_coherence (true); + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), "(399,399;399,401;401,401;401,399)"); + + r.merge (); + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), "(399,399;399,401;401,401;401,399)"); + + r.set_min_coherence (false); // needs to merge again + EXPECT_EQ (r.sized (1).merged (false, 1).to_string (), ""); +} From 2fd43ec6560f3263a4156d83d78ee3dfdfcd2ad5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2019 23:37:35 +0200 Subject: [PATCH 5/7] Fixed #278 (lost reference to Shape object from ObjectInstPath) --- src/edt/edt/gsiDeclEdt.cc | 2 +- src/pya/pya/pyaHelpers.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/gsiDeclEdt.cc b/src/edt/edt/gsiDeclEdt.cc index 0b69c807f..9af7a6e8c 100644 --- a/src/edt/edt/gsiDeclEdt.cc +++ b/src/edt/edt/gsiDeclEdt.cc @@ -229,7 +229,7 @@ gsi::Class decl_ObjectInstPath ("lay", "ObjectInstPath", "\n" "This method has been introduced in version 0.24." ) + - gsi::method ("shape", (db::Shape &(lay::ObjectInstPath::*) ()) &lay::ObjectInstPath::shape, + gsi::method ("shape", (const db::Shape &(lay::ObjectInstPath::*) () const) &lay::ObjectInstPath::shape, "@brief Gets the shape object that describes the selected shape geometrically\n" "\n" "This method delivers valid results only for object selections that represent shapes, i.e for " diff --git a/src/pya/pya/pyaHelpers.cc b/src/pya/pya/pyaHelpers.cc index 399c26776..284d8754a 100644 --- a/src/pya/pya/pyaHelpers.cc +++ b/src/pya/pya/pyaHelpers.cc @@ -362,7 +362,7 @@ pya_plain_iterator_next (PyObject *self) return NULL; } - // increment on first visit + // increment except on first visit if (! iter->first) { iter->iter->inc (); } From eecb62906ca8c81efe092db6979e107b33565422 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Jun 2019 00:50:21 +0200 Subject: [PATCH 6/7] Fixed #271 (proposal, more choices for the cell origin on 'make cell') --- src/edt/edt/MakeCellOptionsDialog.ui | 372 ++++++++++++++++-- src/edt/edt/edtDialogs.cc | 54 ++- src/edt/edt/edtDialogs.h | 11 +- src/edt/edt/edtMainService.cc | 17 +- src/edt/edt/edtMainService.h | 2 + src/klayout.pro | 3 + .../laybasic/AlignCellOptionsDialog.ui | 288 ++++++++------ 7 files changed, 580 insertions(+), 167 deletions(-) diff --git a/src/edt/edt/MakeCellOptionsDialog.ui b/src/edt/edt/MakeCellOptionsDialog.ui index 231fc8443..9f9d2ba33 100644 --- a/src/edt/edt/MakeCellOptionsDialog.ui +++ b/src/edt/edt/MakeCellOptionsDialog.ui @@ -1,55 +1,351 @@ - + + MakeCellOptionsDialog - - + + 0 0 - 462 - 159 + 483 + 367 - - Dialog + + Make Cell - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - - Make Cell + + + Name of cell to make from selected shapes and instances: - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - - Name of cell to make from selected shapes and instances: - - + + + + + + + + + 0 + 0 + + + + Put origin relative to cell's bounding bo&x at ... + + + true + + - + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Qt::Horizontal + + + + 64 + 20 + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 15 + + + 15 + + + 15 + + + 15 + + + 6 + + + + + ... + + + + :/ct.png:/ct.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/lt.png:/lt.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/rt.png:/rt.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/lc.png:/lc.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/cc.png:/cc.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/rc.png:/rc.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/lb.png:/lb.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/cb.png:/cb.png + + + + 31 + 31 + + + + true + + + + + + + ... + + + + :/rb.png:/rb.png + + + + 31 + 31 + + + + true + + + + + + + + + + Qt::Horizontal + + + + 88 + 20 + + + + + + - + Qt::Vertical - + 20 16 @@ -58,18 +354,20 @@ - - + + Qt::Horizontal - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok - + + + buttonBox @@ -77,11 +375,11 @@ MakeCellOptionsDialog accept() - + 248 254 - + 157 274 @@ -93,11 +391,11 @@ MakeCellOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 9755f19d9..b1a932cba 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -336,28 +336,80 @@ MakeCellOptionsDialog::MakeCellOptionsDialog (QWidget *parent) : QDialog (parent) { setupUi (this); + + setObjectName (QString::fromUtf8 ("make_cell_options_dialog")); + + QToolButton *buttons[3][3] = { { lb, cb, rb }, { lc, cc, rc }, { lt, ct, rt } }; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + connect (buttons[i][j], SIGNAL (clicked ()), this, SLOT (button_clicked ())); + } + } } bool -MakeCellOptionsDialog::exec_dialog (const db::Layout &layout, std::string &name) +MakeCellOptionsDialog::exec_dialog (const db::Layout &layout, std::string &name, int &mode_x, int &mode_y) { do { BEGIN_PROTECTED + + QToolButton *buttons[3][3] = { { lb, cb, rb }, { lc, cc, rc }, { lt, ct, rt } }; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + buttons[i][j]->setChecked (j - 1 == mode_x && i - 1 == mode_y); + } + } + + origin_groupbox->setChecked (mode_x >= -1); + if (QDialog::exec ()) { + + if (origin_groupbox->isChecked ()) { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (buttons[i][j]->isChecked ()) { + mode_x = j - 1; + mode_y = i - 1; + } + } + } + } else { + mode_x = mode_y = -2; + } + name = tl::to_string (cell_name_le->text ()); if (name.empty ()) { throw tl::Exception (tl::to_string (QObject::tr ("Cell name must not be empty"))); } else if (layout.cell_by_name (name.c_str ()).first) { throw tl::Exception (tl::to_string (QObject::tr ("A cell with that name already exists: ")) + name); } + return true; + } else { return false; } + END_PROTECTED } while (true); } +void +MakeCellOptionsDialog::button_clicked () +{ + QToolButton *buttons[3][3] = { { lb, cb, rb }, { lc, cc, rc }, { lt, ct, rt } }; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (buttons [i][j] != sender ()) { + buttons [i][j]->setChecked (false); + } + } + } +} + // -------------------------------------------------------------------------------- // RoundCornerOptionsDialog implementation diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index 9fc17919b..165cc650a 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -134,9 +134,14 @@ class MakeCellOptionsDialog : public QDialog, private Ui::MakeCellOptionsDialog { +Q_OBJECT + public: MakeCellOptionsDialog (QWidget *parent); - bool exec_dialog (const db::Layout &layout, std::string &name); + bool exec_dialog (const db::Layout &layout, std::string &name, int &mode_x, int &mode_y); + +private slots: + void button_clicked (); }; /** @@ -146,6 +151,8 @@ class MakeArrayOptionsDialog : public QDialog, private Ui::MakeArrayOptionsDialog { +Q_OBJECT + public: MakeArrayOptionsDialog (QWidget *parent); bool exec_dialog (db::DVector &a, unsigned int &na, db::DVector &b, unsigned int &nb); @@ -160,6 +167,8 @@ class RoundCornerOptionsDialog : public QDialog, private Ui::RoundCornerOptionsDialog { +Q_OBJECT + public: RoundCornerOptionsDialog (QWidget *parent); ~RoundCornerOptionsDialog (); diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 975293cfc..5e572fc5b 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -58,11 +58,11 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Plug m_flatten_insts_levels (std::numeric_limits::max ()), m_flatten_prune (false), m_align_hmode (0), m_align_vmode (0), m_align_visible_layers (false), + m_origin_mode_x (-1), m_origin_mode_y (-1), m_origin_visible_layers_for_bbox (false), m_array_a (0.0, 1.0), m_array_b (1.0, 0.0), m_array_na (1), m_array_nb (1) -{ - // collect the options pages and build the options dialog - std::vector edt_services = mp_view->get_plugins (); +{ + // .. nothing yet .. } MainService::~MainService () @@ -912,7 +912,7 @@ MainService::cm_make_cell () const lay::CellView &cv = view ()->cellview (cv_index); - if (dialog.exec_dialog (cv->layout (), m_make_cell_name)) { + if (dialog.exec_dialog (cv->layout (), m_make_cell_name, m_origin_mode_x, m_origin_mode_y)) { // Compute the selection's bbox to establish a good origin for the new cell db::Box selection_bbox; @@ -940,8 +940,13 @@ MainService::cm_make_cell () db::Cell &target_cell = cv->layout ().cell (target_ci); // create target cell instance - db::Instance target_cell_inst = cv.cell ()->insert (db::CellInstArray (db::CellInst (target_ci), db::Trans (selection_bbox.lower_left () - db::Point ()))); - db::ICplxTrans to = db::ICplxTrans (db::Trans (db::Point () - selection_bbox.lower_left ())); + db::Vector ref; + if (m_origin_mode_x >= -1) { + ref = db::Vector (selection_bbox.left () + ((m_origin_mode_x + 1) * selection_bbox.width ()) / 2, selection_bbox.bottom () + ((m_origin_mode_y + 1) * selection_bbox.height ()) / 2); + } + + db::Instance target_cell_inst = cv.cell ()->insert (db::CellInstArray (db::CellInst (target_ci), db::Trans (ref))); + db::ICplxTrans to = db::ICplxTrans (db::Trans (-ref)); for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 5f32f493f..67aa8755d 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -201,6 +201,8 @@ private: int m_align_vmode; bool m_align_visible_layers; std::string m_make_cell_name; + int m_origin_mode_x, m_origin_mode_y; + bool m_origin_visible_layers_for_bbox; db::DVector m_array_a, m_array_b; unsigned int m_array_na, m_array_nb; diff --git a/src/klayout.pro b/src/klayout.pro index 2ecee1271..e208dd664 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -99,3 +99,6 @@ plugins.depends += lib rdb db } unit_tests.depends += plugins $$MAIN_DEPENDS + +RESOURCES += \ + laybasic/laybasic/layResources.qrc diff --git a/src/laybasic/laybasic/AlignCellOptionsDialog.ui b/src/laybasic/laybasic/AlignCellOptionsDialog.ui index de09130ee..8e556df7b 100644 --- a/src/laybasic/laybasic/AlignCellOptionsDialog.ui +++ b/src/laybasic/laybasic/AlignCellOptionsDialog.ui @@ -1,7 +1,8 @@ - + + AlignCellOptionsDialog - - + + 0 0 @@ -9,52 +10,68 @@ 342 - - Align Cell + + Adjust Cell Origin - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + Put origin relative to cell's bounding box at ... - - - - 0 - 0 + + + 0 0 - + QFrame::NoFrame - + QFrame::Raised - - + + + 6 + + 9 - - 6 + + 9 + + + 9 + + + 9 - + Qt::Horizontal - + 64 20 @@ -63,187 +80,205 @@ - - + + QFrame::Box - + QFrame::Plain - - + + 15 - + + 15 + + + 15 + + + 15 + + 6 - - - + + + ... - - :/ct.png + + + :/ct.png:/ct.png - + 31 31 - + true - - - + + + ... - - :/lt.png + + + :/lt.png:/lt.png - + 31 31 - + true - - - + + + ... - - :/rt.png + + + :/rt.png:/rt.png - + 31 31 - + true - - - + + + ... - - :/lc.png + + + :/lc.png:/lc.png - + 31 31 - + true - - - + + + ... - - :/cc.png + + + :/cc.png:/cc.png - + 31 31 - + true - - - + + + ... - - :/rc.png + + + :/rc.png:/rc.png - + 31 31 - + true - - - + + + ... - - :/lb.png + + + :/lb.png:/lb.png - + 31 31 - + true - - - + + + ... - - :/cb.png + + + :/cb.png:/cb.png - + 31 31 - + true - - - + + + ... - - :/rb.png + + + :/rb.png:/rb.png - + 31 31 - + true @@ -253,10 +288,10 @@ - + Qt::Horizontal - + 88 20 @@ -268,28 +303,28 @@ - - + + Use visible layers only for bounding box - - + + Adjust instances in parents - + Qt::Vertical - + QSizePolicy::Fixed - + 20 8 @@ -298,26 +333,35 @@ - - + + QFrame::NoFrame - + QFrame::Raised - - + + + 6 + + 0 - - 6 + + 0 + + + 0 + + + 0 - + Qt::Horizontal - + 209 20 @@ -326,18 +370,18 @@ - - + + Ok - + true - - + + Cancel @@ -362,7 +406,7 @@ vis_only_cbx - + @@ -371,11 +415,11 @@ AlignCellOptionsDialog accept() - + 237 203 - + 147 81 @@ -387,11 +431,11 @@ AlignCellOptionsDialog reject() - + 325 202 - + 325 57 From 9d7c8d31fc63f98a40c0d540ab9e8cf61a6867b5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Jun 2019 02:03:54 +0200 Subject: [PATCH 7/7] Fixed #272 (error message if macro interpreter isn't available) --- src/lym/lym/lymMacro.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 503465d25..90755ea28 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -1014,7 +1014,7 @@ bool Macro::can_run () const int Macro::run () const { if (tl::verbosity () >= 20) { - tl::log << "Running macro " << path (); + tl::log << tl::to_string (tr ("Running macro ")) << path (); } try { @@ -1029,6 +1029,8 @@ int Macro::run () const } } else if (interpreter () == lym::Macro::DSLInterpreter) { lym::MacroInterpreter::execute_macro (this); + } else { + throw tl::Exception (tl::to_string (tr ("Can't run macro (no interpreter): ")) + path ()); } } catch (tl::ExitException &ex) { return ex.status (); @@ -1843,10 +1845,10 @@ static void autorun_for (lym::MacroCollection &collection, bool early) } for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) { - if (((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ())) && c->second->can_run ()) { + if ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ())) { BEGIN_PROTECTED_SILENT - c->second->install_doc (); c->second->run (); + c->second->install_doc (); END_PROTECTED_SILENT } }