From 18eadb119e275d8f7dc12032638b156fab95b7b2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 22:31:46 +0100 Subject: [PATCH 01/41] Fixed a small problem: browser windows did not get closed when closing the window. --- src/layui/layui/layBrowser.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/layui/layui/layBrowser.cc b/src/layui/layui/layBrowser.cc index edfbf4a26..b5e3bebc3 100644 --- a/src/layui/layui/layBrowser.cc +++ b/src/layui/layui/layBrowser.cc @@ -24,6 +24,7 @@ #include #include +#include #include "layBrowser.h" #include "layLayoutViewBase.h" @@ -81,7 +82,7 @@ Browser::closeEvent (QCloseEvent *event) if (active ()) { m_active = false; deactivated (); - QDialog::closeEvent (event); + event->accept (); } } From b634d93cb246ef855b7c59b8b8e4fc3fcf43fc9e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 17 Apr 2024 22:54:01 +0200 Subject: [PATCH 02/41] Bugfix: timeout for HTTP(S) connection was including the time taken to input the password if one is requested. --- src/tl/tl/tlHttpStreamQt.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlHttpStreamQt.cc b/src/tl/tl/tlHttpStreamQt.cc index 019caeb91..c8c03a572 100644 --- a/src/tl/tl/tlHttpStreamQt.cc +++ b/src/tl/tl/tlHttpStreamQt.cc @@ -27,6 +27,7 @@ #include "tlDeferredExecution.h" #include "tlObject.h" #include "tlTimer.h" +#include "tlSleep.h" #include #include @@ -447,9 +448,18 @@ InputHttpStreamPrivateData::read (char *b, size_t n) issue_request (QUrl (tl::to_qstring (m_url))); } - tl::Clock start_time = tl::Clock::current (); - while (mp_reply == 0 && (m_timeout <= 0.0 || (tl::Clock::current() - start_time).seconds () < m_timeout)) { + const unsigned long tick_ms = 10; + double time_waited = 0.0; + + while (mp_reply == 0 && (m_timeout <= 0.0 || time_waited < m_timeout)) { + mp_stream->tick (); + + // NOTE: as tick() includes waiting for the password dialog, we must not include + // the time spent there. + tl::msleep (tick_ms); + time_waited += tick_ms * 1e-3; + } if (! mp_reply) { From cba126e9eeb00ebaf684efb906185119c6e02dea Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 18 Apr 2024 21:27:09 +0200 Subject: [PATCH 03/41] Small enhancements for Spice reader - Detects recursive subcircuit calls now - Dismisses empty top level circuit which happened to be created when there were not top level elements and control statements were present (such as .param) --- src/db/db/dbCircuit.cc | 5 ++++ src/db/db/dbCircuit.h | 5 ++++ src/db/db/dbNetlistSpiceReader.cc | 17 +++++++++++- src/db/unit_tests/dbNetlistReaderTests.cc | 34 +++++++++++++++++++++++ testdata/algo/nreader24.cir | 14 ++++++++++ testdata/algo/nreader25.cir | 11 ++++++++ 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 testdata/algo/nreader24.cir create mode 100644 testdata/algo/nreader25.cir diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index aaca632c8..c9c4d2153 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -644,6 +644,11 @@ void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) m_pin_refs [pin_id] = iter; } +bool Circuit::is_empty () const +{ + return m_nets.empty () && m_pins.empty () && m_devices.empty () && m_subcircuits.empty (); +} + void Circuit::blank () { tl_assert (netlist () != 0); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 1e0f0a956..abcb74274 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -759,6 +759,11 @@ public: */ void blank (); + /** + * @brief Gets a value indicating whether the circuit is empty + */ + bool is_empty () const; + /** * @brief Generate memory statistics */ diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 88632b71a..05976791d 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -943,6 +943,12 @@ SpiceNetlistBuilder::circuit_for (const SpiceCachedCircuit *cc, const parameters if (cp == c->second.end ()) { return 0; } + + // a null pointer indicates that we are currently defining this circuit + if (cp->second == 0) { + error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' called recursively")), cc->name ())); + } + return cp->second; } @@ -1055,7 +1061,8 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete c->set_name (make_circuit_name (cc->name (), pv)); } - register_circuit_for (cc, pv, c, anonymous_top_level); + // pre-register the circuit - allows detecting recursive calls + register_circuit_for (cc, pv, 0, false); std::unique_ptr > n2n (mp_nets_by_name.release ()); mp_nets_by_name.reset (0); @@ -1095,6 +1102,14 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete std::swap (c, mp_netlist_circuit); std::swap (vars, m_variables); + // final registration if required + if (! anonymous_top_level || ! c->is_empty ()) { + register_circuit_for (cc, pv, c, anonymous_top_level); + } else { + mp_netlist->remove_circuit (c); + c = 0; + } + return c; } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index 19858f26d..c3b67b343 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -880,6 +880,40 @@ TEST(23_endl) ); } +TEST(24_recursive_calls) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader24.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + + try { + reader.read (is, nl); + EXPECT_EQ (false, true); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Subcircuit 'C1' called recursively in /home/matthias/klayout/master/testdata/algo/nreader24.cir, line 8"); + } +} + +TEST(25_dismiss_top_level) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader25.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (A=A,B=B);\n" + " device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} + TEST(100_ExpressionParser) { std::map vars; diff --git a/testdata/algo/nreader24.cir b/testdata/algo/nreader24.cir new file mode 100644 index 000000000..c42a4ff51 --- /dev/null +++ b/testdata/algo/nreader24.cir @@ -0,0 +1,14 @@ +* Testing recursive call detection + +.subckt c1 a b +x1 a b c2 +.end + +.subckt c2 a b +x1 a b c1 +.ends + +xtop vdd vss c2 + +.end + diff --git a/testdata/algo/nreader25.cir b/testdata/algo/nreader25.cir new file mode 100644 index 000000000..9026cfc70 --- /dev/null +++ b/testdata/algo/nreader25.cir @@ -0,0 +1,11 @@ +* Test: dismiss empty top level circuit + +.subckt top a b +m1 a b a b nmos +.ends + +* this triggered generation of a top level circuit +.param p1 17 + +.end + From 424ead4d9a635e9a4e9426d5eb51714e41e65d0c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 18 Apr 2024 22:02:46 +0200 Subject: [PATCH 04/41] Some enhancements to expression parser: - upcase() and downcase() functions - type error messages indicate argument number --- src/doc/doc/about/expressions.xml | 2 + src/tl/tl/tlExpression.cc | 180 ++++++++++++++----------- src/tl/unit_tests/tlExpressionTests.cc | 12 ++ 3 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/doc/doc/about/expressions.xml b/src/doc/doc/about/expressions.xml index f7e405c48..d7f83622f 100644 --- a/src/doc/doc/about/expressions.xml +++ b/src/doc/doc/about/expressions.xml @@ -282,6 +282,7 @@ var x = 3; x = x + 1; x combine(x,y)StringStringCombines the path components x and y using the system specific separator cosh(x)NumericNumericHyperbolic cosine function cos(x)NumericNumericCosine function + downcase(x)StringStringConverts the given string to lower case env(x)StringStringAccess an environment variable error(x)StringRaise an error exp(x)NumericNumericExponential function @@ -318,6 +319,7 @@ var x = 3; x = x + 1; x to_f(x)AnyNumericConvert argument to numeric if possible to_i(x)AnyNumeric (integer)Convert argument to numeric (32 bit integer) to_s(x)AnyStringConvert argument to string + upcase(x)StringStringConverts the given string to upper case diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index dea3d086a..0db330f5c 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -145,14 +145,14 @@ ExpressionParserContext::where () const // ---------------------------------------------------------------------------- // Utilities for evaluation -static double to_double (const ExpressionParserContext &context, const tl::Variant &v) +static double to_double (const ExpressionParserContext &context, const tl::Variant &v, unsigned int narg) { if (v.can_convert_to_double ()) { return v.to_double (); } else if (v.is_list ()) { return v.get_list ().size (); } else { - throw EvalError (tl::to_string (tr ("Double precision floating point value expected")), context); + throw EvalError (tl::to_string (tr ("Double precision floating point value expected for argument #")) + tl::to_string (narg + 1), context); } } @@ -162,50 +162,50 @@ static double to_double (const ExpressionParserContext &context, const std::vect throw EvalError (tl::to_string (tr ("Function expects a single numeric argument")), context); } - return to_double (context, v [0]); + return to_double (context, v [0], 0); } -static long to_long (const ExpressionParserContext &context, const tl::Variant &v) +static long to_long (const ExpressionParserContext &context, const tl::Variant &v, int narg) { if (v.can_convert_to_long ()) { return v.to_long (); } else if (v.is_list ()) { return long (v.get_list ().size ()); } else { - throw EvalError (tl::to_string (tr ("Integer value expected")), context); + throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context); } } -static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v) +static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v, int narg) { if (v.can_convert_to_ulong ()) { return v.to_ulong (); } else if (v.is_list ()) { return (unsigned long) (v.get_list ().size ()); } else { - throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context); + throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context); } } -static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v) +static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v, int narg) { if (v.can_convert_to_longlong ()) { return v.to_longlong (); } else if (v.is_list ()) { return long (v.get_list ().size ()); } else { - throw EvalError (tl::to_string (tr ("Integer value expected")), context); + throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context); } } -static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v) +static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v, int narg) { if (v.can_convert_to_ulonglong ()) { return v.to_ulong (); } else if (v.is_list ()) { return (unsigned long long) (v.get_list ().size ()); } else { - throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context); + throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context); } } @@ -1089,13 +1089,13 @@ public: v.swap (o); } else if (v->is_longlong ()) { - v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b))); + v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b, 1))); } else if (v->is_ulonglong ()) { - v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b))); + v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b, 1))); } else if (v->is_ulong ()) { - v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b))); + v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b, 1))); } else { - v.set (tl::Variant (to_long (m_context, *v) << to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) << to_long (m_context, *b, 1))); } } }; @@ -1145,13 +1145,13 @@ public: v.swap (o); } else if (v->is_longlong ()) { - v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b))); + v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b, 1))); } else if (v->is_ulonglong ()) { - v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b))); + v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b, 1))); } else if (v->is_ulong ()) { - v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b))); + v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b, 1))); } else { - v.set (tl::Variant (to_long (m_context, *v) >> to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) >> to_long (m_context, *b, 1))); } } }; @@ -1203,17 +1203,17 @@ public: } else if (v->is_a_string () || b->is_a_string ()) { v.set (tl::Variant (std::string (v->to_string ()) + b->to_string ())); } else if (v->is_double () || b->is_double ()) { - v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1))); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) + to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) + to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) + to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) + to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) + to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) + to_ulong (m_context, *b, 1))); } else if (v->is_long () || b->is_long ()) { - v.set (tl::Variant (to_long (m_context, *v) + to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) + to_long (m_context, *b, 1))); } else { - v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1))); } } }; @@ -1263,17 +1263,17 @@ public: v.swap (o); } else if (v->is_double () || b->is_double ()) { - v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1))); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) - to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) - to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) - to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) - to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) - to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) - to_ulong (m_context, *b, 1))); } else if (v->is_long () || b->is_long ()) { - v.set (tl::Variant (to_long (m_context, *v) - to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) - to_long (m_context, *b, 1))); } else { - v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1))); } } }; @@ -1324,7 +1324,7 @@ public: } else if (v->is_a_string ()) { - long x = to_long (m_context, *b); + long x = to_long (m_context, *b, 1); if (x < 0) { throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context); } @@ -1339,7 +1339,7 @@ public: } else if (b->is_a_string ()) { - long x = to_long (m_context, *v); + long x = to_long (m_context, *v, 0); if (x < 0) { throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context); } @@ -1353,17 +1353,17 @@ public: v.set (tl::Variant (s)); } else if (v->is_double () || b->is_double ()) { - v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1))); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) * to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) * to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) * to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) * to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) * to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) * to_ulong (m_context, *b, 1))); } else if (v->is_long () || b->is_long ()) { - v.set (tl::Variant (to_long (m_context, *v) * to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) * to_long (m_context, *b, 1))); } else { - v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b))); + v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1))); } } }; @@ -1413,41 +1413,41 @@ public: v.swap (o); } else if (v->is_double () || b->is_double ()) { - double d = to_double (m_context, *b); + double d = to_double (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_double (m_context, *v) / d)); + v.set (tl::Variant (to_double (m_context, *v, 0) / d)); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - unsigned long long d = to_ulonglong (m_context, *b); + unsigned long long d = to_ulonglong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_ulonglong (m_context, *v) / d)); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) / d)); } else if (v->is_longlong () || b->is_longlong ()) { - long long d = to_longlong (m_context, *b); + long long d = to_longlong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_longlong (m_context, *v) / d)); + v.set (tl::Variant (to_longlong (m_context, *v, 0) / d)); } else if (v->is_ulong () || b->is_ulong ()) { - unsigned long d = to_ulong (m_context, *b); + unsigned long d = to_ulong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_ulong (m_context, *v) / d)); + v.set (tl::Variant (to_ulong (m_context, *v, 0) / d)); } else if (v->is_long () || b->is_long ()) { - long d = to_long (m_context, *b); + long d = to_long (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_long (m_context, *v) / d)); + v.set (tl::Variant (to_long (m_context, *v, 0) / d)); } else { - double d = to_double (m_context, *b); + double d = to_double (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Division by zero")), m_context); } - v.set (tl::Variant (to_double (m_context, *v) / d)); + v.set (tl::Variant (to_double (m_context, *v, 0) / d)); } } }; @@ -1497,29 +1497,29 @@ public: v.swap (o); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - unsigned long long d = to_ulonglong (m_context, *b); + unsigned long long d = to_ulonglong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context); } - v.set (tl::Variant (to_ulonglong (m_context, *v) % d)); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) % d)); } else if (v->is_longlong () || b->is_longlong ()) { - long long d = to_longlong (m_context, *b); + long long d = to_longlong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context); } - v.set (tl::Variant (to_longlong (m_context, *v) % d)); + v.set (tl::Variant (to_longlong (m_context, *v, 0) % d)); } else if (v->is_ulong () || b->is_ulong ()) { - unsigned long d = to_ulong (m_context, *b); + unsigned long d = to_ulong (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context); } - v.set (tl::Variant (to_ulong (m_context, *v) % d)); + v.set (tl::Variant (to_ulong (m_context, *v, 0) % d)); } else { - long d = to_long (m_context, *b); + long d = to_long (m_context, *b, 1); if (d == 0) { throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context); } - v.set (tl::Variant (to_long (m_context, *v) % d)); + v.set (tl::Variant (to_long (m_context, *v, 0) % d)); } } }; @@ -1569,13 +1569,13 @@ public: v.swap (o); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) & to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) & to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) & to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) & to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) & to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) & to_ulong (m_context, *b, 1))); } else { - v.set (tl::Variant (to_long (m_context, *v) & to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) & to_long (m_context, *b, 1))); } } }; @@ -1625,13 +1625,13 @@ public: v.swap (o); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) | to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) | to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) | to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) | to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) | to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) | to_ulong (m_context, *b, 1))); } else { - v.set (tl::Variant (to_long (m_context, *v) | to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) | to_long (m_context, *b, 1))); } } }; @@ -1681,13 +1681,13 @@ public: v.swap (o); } else if (v->is_ulonglong () || b->is_ulonglong ()) { - v.set (tl::Variant (to_ulonglong (m_context, *v) ^ to_ulonglong (m_context, *b))); + v.set (tl::Variant (to_ulonglong (m_context, *v, 0) ^ to_ulonglong (m_context, *b, 1))); } else if (v->is_longlong () || b->is_longlong ()) { - v.set (tl::Variant (to_longlong (m_context, *v) ^ to_longlong (m_context, *b))); + v.set (tl::Variant (to_longlong (m_context, *v, 0) ^ to_longlong (m_context, *b, 1))); } else if (v->is_ulong () || b->is_ulong ()) { - v.set (tl::Variant (to_ulong (m_context, *v) ^ to_ulong (m_context, *b))); + v.set (tl::Variant (to_ulong (m_context, *v, 0) ^ to_ulong (m_context, *b, 1))); } else { - v.set (tl::Variant (to_long (m_context, *v) ^ to_long (m_context, *b))); + v.set (tl::Variant (to_long (m_context, *v, 0) ^ to_long (m_context, *b, 1))); } } }; @@ -1826,7 +1826,7 @@ public: } else if (v->is_ulonglong ()) { v.set (-(long long)(v->to_ulonglong ())); } else { - v.set (-to_double (m_context, *v)); + v.set (-to_double (m_context, *v, 0)); } } }; @@ -1881,7 +1881,7 @@ public: } else if (v->is_ulonglong ()) { v.set (~v->to_ulonglong ()); } else { - v.set (~to_long (m_context, *v)); + v.set (~to_long (m_context, *v, 0)); } } }; @@ -2388,7 +2388,7 @@ abs_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect } else if (v[0].is_double ()) { out = fabs (v[0].to_double ()); } else { - out = labs (to_long (context, v[0])); + out = labs (to_long (context, v[0], 0)); } } @@ -2463,7 +2463,7 @@ pow_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect throw EvalError (tl::to_string (tr ("'pow' function expects exactly two arguments")), context); } - out = pow (to_double (context, vv [0]), to_double (context, vv [1])); + out = pow (to_double (context, vv [0], 0), to_double (context, vv [1], 1)); } static void @@ -2473,7 +2473,7 @@ atan2_f (const ExpressionParserContext &context, tl::Variant &out, const std::ve throw EvalError (tl::to_string (tr ("'atan2' function expects exactly two arguments")), context); } - out = atan2 (to_double (context, vv [0]), to_double (context, vv [1])); + out = atan2 (to_double (context, vv [0], 0), to_double (context, vv [1], 1)); } static void @@ -2690,10 +2690,10 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v long len = -1; if (vv.size () > 2) { - len = std::max (long (0), to_long (context, vv [2])); + len = std::max (long (0), to_long (context, vv [2], 2)); } - long l = to_long (context, vv [1]); + long l = to_long (context, vv [1], 1); if (l < 0) { l = long (s.size ()) + l; if (l < 0) { @@ -2713,6 +2713,26 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v } } +static void +upcase_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector &vv) +{ + if (vv.size () != 1) { + throw EvalError (tl::to_string (tr ("'upcase' function expects one argument")), context); + } + + out = tl::to_upper_case (vv [0].to_string ()); +} + +static void +downcase_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector &vv) +{ + if (vv.size () != 1) { + throw EvalError (tl::to_string (tr ("'upcase' function expects one argument")), context); + } + + out = tl::to_lower_case (vv [0].to_string ()); +} + static void join_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector &vv) { @@ -2752,7 +2772,7 @@ item_f (const ExpressionParserContext &context, tl::Variant &out, const std::vec throw EvalError (tl::to_string (tr ("First argument of 'item' function must be a list")), context); } - long index = to_long (context, vv [1]); + long index = to_long (context, vv [1], 1); if (index < 0 || index >= long (vv [0].end () - vv [0].begin ())) { out = tl::Variant (); } else { @@ -3042,6 +3062,8 @@ static EvalStaticFunction f55 ("file_exists", &file_exists_f); static EvalStaticFunction f56 ("is_dir", &is_dir_f); static EvalStaticFunction f57 ("combine", &combine_f); static EvalStaticFunction f58 ("abs", &abs_f); +static EvalStaticFunction f59 ("upcase", &upcase_f); +static EvalStaticFunction f60 ("downcase", &downcase_f); // ---------------------------------------------------------------------------- // Implementation of a constant wrapper diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index a547d7c7d..3d8719900 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -784,6 +784,10 @@ TEST(6) EXPECT_EQ (v.to_string (), std::string ("0")); v = e.parse ("rfind('abcabc','x')").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("upcase('abcABC')").execute (); + EXPECT_EQ (v.to_string (), std::string ("ABCABC")); + v = e.parse ("downcase('abcABC')").execute (); + EXPECT_EQ (v.to_string (), std::string ("abcabc")); v = e.parse ("len('abcabc')").execute (); EXPECT_EQ (v.to_string (), std::string ("6")); v = e.parse ("len([])").execute (); @@ -859,6 +863,14 @@ TEST(6) msg = ex.msg(); } EXPECT_EQ (msg, std::string ("My error")); + // argument index in error messages + msg.clear (); + try { + v = e.parse ("substr('abcabc',2,'xyz')").execute (); + } catch (tl::Exception &ex) { + msg = ex.msg(); + } + EXPECT_EQ (msg, std::string ("Integer value expected for argument #3 at position 0 (substr('abcabc',2,'x..)")); } // compare ops From a18a6be181606a0765146f5b64fd00c315cf7906 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 20 Apr 2024 21:50:22 +0200 Subject: [PATCH 05/41] DRC: New options for "corners", "angle", "with(out)_angle" - layer.corners: absolute and negative option - drc(corners): absolute option, != operator supported - layer.with_angle/without_angle: absolute option - drc(angle): absolute option + corresponding API updates: - EdgePairs#with_angle, with_angle_both: 'absolute' flag added - Edges#with_angle: 'absolute' flag added - Region#corners: 'inverse' and 'absolute' added --- src/db/db/dbEdgePairFilters.cc | 6 +- src/db/db/dbEdgesUtils.cc | 38 ++-- src/db/db/dbEdgesUtils.h | 13 +- src/db/db/dbRegionProcessors.cc | 4 +- src/db/db/dbRegionProcessors.h | 14 +- src/db/db/gsiDeclDbCompoundOperation.cc | 32 +-- src/db/db/gsiDeclDbEdgePairs.cc | 42 ++-- src/db/db/gsiDeclDbEdges.cc | 25 ++- src/db/db/gsiDeclDbRegion.cc | 32 +-- src/db/unit_tests/dbAsIfFlatRegionTests.cc | 10 +- src/db/unit_tests/dbCompoundOperationTests.cc | 4 +- src/db/unit_tests/dbDeepEdgesTests.cc | 4 +- src/db/unit_tests/dbDeepRegionTests.cc | 10 +- src/db/unit_tests/dbEdgePairsTests.cc | 10 + src/db/unit_tests/dbEdgesTests.cc | 34 ++-- src/db/unit_tests/dbRegionTests.cc | 18 +- src/doc/doc/about/drc_ref_drc.xml | 35 +++- src/doc/doc/about/drc_ref_layer.xml | 36 ++-- .../drc/built-in-macros/_drc_complex_ops.rb | 89 +++++++-- .../built-in-macros/_drc_cop_integration.rb | 26 ++- src/drc/drc/built-in-macros/_drc_engine.rb | 4 + src/drc/drc/built-in-macros/_drc_layer.rb | 186 +++++++++++++----- src/drc/drc/built-in-macros/_drc_tags.rb | 10 + src/drc/unit_tests/drcSimpleTests.cc | 10 + testdata/drc/drcGenericTests_12.drc | 2 + testdata/drc/drcGenericTests_6.drc | 2 + testdata/drc/drcGenericTests_au12.gds | Bin 9312 -> 9474 bytes testdata/drc/drcGenericTests_au12d.gds | Bin 9370 -> 9532 bytes testdata/drc/drcGenericTests_au6.gds | Bin 10422 -> 14518 bytes testdata/drc/drcGenericTests_au6d.gds | Bin 10480 -> 14576 bytes testdata/drc/drcSimpleTests_2.drc | 7 + testdata/drc/drcSimpleTests_49.drc | 4 + testdata/drc/drcSimpleTests_93.drc | 35 ++++ testdata/drc/drcSimpleTests_93.gds | Bin 0 -> 360 bytes testdata/drc/drcSimpleTests_au2.gds | Bin 13926 -> 15470 bytes testdata/drc/drcSimpleTests_au49.gds | Bin 10090 -> 12010 bytes testdata/drc/drcSimpleTests_au49d.gds | Bin 6314 -> 7594 bytes testdata/drc/drcSimpleTests_au93.gds | Bin 0 -> 9958 bytes testdata/drc/drcSimpleTests_au93d.gds | Bin 0 -> 5978 bytes testdata/drc/drctest.gds | Bin 3192 -> 3328 bytes 40 files changed, 520 insertions(+), 222 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_93.drc create mode 100644 testdata/drc/drcSimpleTests_93.gds create mode 100644 testdata/drc/drcSimpleTests_au93.gds create mode 100644 testdata/drc/drcSimpleTests_au93d.gds diff --git a/src/db/db/dbEdgePairFilters.cc b/src/db/db/dbEdgePairFilters.cc index 8e5aa3008..602c56b90 100644 --- a/src/db/db/dbEdgePairFilters.cc +++ b/src/db/db/dbEdgePairFilters.cc @@ -98,13 +98,13 @@ bool EdgePairFilterByArea::selected (const db::EdgePair &edge_pair) const // EdgePairFilterByArea implementation InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double a, bool inverted) - : m_inverted (inverted), m_checker (a, true, a, true) + : m_checker (a, true, a, true, inverted, false) { // .. nothing yet .. } InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverted) - : m_inverted (inverted), m_checker (amin, include_amin, amax, include_amax) + : m_checker (amin, include_amin, amax, include_amax, inverted, false) { // .. nothing yet .. } @@ -122,7 +122,7 @@ InternalAngleEdgePairFilter::selected (const db::EdgePair &edge_pair) const std::swap (d1, d2); } - return m_checker (d1, d2) != m_inverted; + return m_checker (d1, d2); } } diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 56d345b2a..fb9407609 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -215,8 +215,13 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector &res) // ------------------------------------------------------------------------------------------------------------- // EdgeAngleChecker implementation -EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) { + if (absolute && angle_start < -db::epsilon) { + angle_start = 0.0; + include_angle_start = true; + } + m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); @@ -225,6 +230,9 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0; m_all = (angle_end - angle_start - db::epsilon) > 360.0; + + m_absolute = absolute; + m_inverse = inverse; } bool @@ -255,14 +263,14 @@ EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const // ------------------------------------------------------------------------------------------------------------- // EdgeOrientationFilter implementation -EdgeOrientationFilter::EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse) - : m_inverse (inverse), m_checker (amin, include_amin, amax, include_amax) +EdgeOrientationFilter::EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse, bool absolute) + : m_checker (amin, include_amin, amax, include_amax, inverse, absolute) { // .. nothing yet .. } -EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse) - : m_inverse (inverse), m_checker (a, true, a, true) +EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absolute) + : m_checker (a, true, a, true, inverse, absolute) { // .. nothing yet .. } @@ -273,9 +281,9 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) { - return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()) != m_inverse; + return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()); } else { - return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()) != m_inverse; + return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()); } } @@ -289,20 +297,20 @@ SpecialEdgeOrientationFilter::SpecialEdgeOrientationFilter (FilterType type, boo } static EdgeAngleChecker s_ortho_checkers [] = { - EdgeAngleChecker (0.0, true, 0.0, true), - EdgeAngleChecker (90.0, true, 90.0, true) + EdgeAngleChecker (0.0, true, 0.0, true, false, false), + EdgeAngleChecker (90.0, true, 90.0, true, false, false) }; static EdgeAngleChecker s_diagonal_checkers [] = { - EdgeAngleChecker (-45.0, true, -45.0, true), - EdgeAngleChecker (45.0, true, 45.0, true) + EdgeAngleChecker (-45.0, true, -45.0, true, false, false), + EdgeAngleChecker (45.0, true, 45.0, true, false, false) }; static EdgeAngleChecker s_orthodiagonal_checkers [] = { - EdgeAngleChecker (-45.0, true, -45.0, true), - EdgeAngleChecker (0.0, true, 0.0, true), - EdgeAngleChecker (45.0, true, 45.0, true), - EdgeAngleChecker (90.0, true, 90.0, true) + EdgeAngleChecker (-45.0, true, -45.0, true, false, false), + EdgeAngleChecker (0.0, true, 0.0, true, false, false), + EdgeAngleChecker (45.0, true, 45.0, true, false, false), + EdgeAngleChecker (90.0, true, 90.0, true, false, false) }; bool diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 531a18fca..ed856e57a 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -142,22 +142,23 @@ private: class DB_PUBLIC EdgeAngleChecker { public: - EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end); + EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute); bool operator() (const db::Edge &a, const db::Edge &b) const { - return m_all || check (a.d (), b.d ()); + return (m_all || check (a.d (), b.d ()) || (m_absolute && check (b.d (), a.d ()))) != m_inverse; } bool operator() (const db::Vector &a, const db::Vector &b) const { - return m_all || check (a, b); + return (m_all || check (a, b) || (m_absolute && check (b, a))) != m_inverse; } private: db::CplxTrans m_t_start, m_t_end; bool m_include_start, m_include_end; bool m_big_angle, m_all; + bool m_inverse, m_absolute; bool check (const db::Vector &a, const db::Vector &b) const; }; @@ -181,22 +182,24 @@ struct DB_PUBLIC EdgeOrientationFilter * @param amin The minimum angle (measured against the x axis) * @param amax The maximum angle (measured against the x axis) * @param inverse If set to true, only edges not matching this criterion will be filtered + * @param absolute Angles are always positive * * This filter will filter out all edges whose angle against x axis * is larger or equal to amin and less than amax. */ - EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse); + EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse, bool absolute); /** * @brief Constructor * * @param a The angle (measured against the x axis) * @param inverse If set to true, only edges not matching this criterion will be filtered + * @param absolute Angles are always positive * * This filter will filter out all edges whose angle against x axis * is equal to a. */ - EdgeOrientationFilter (double a, bool inverse); + EdgeOrientationFilter (double a, bool inverse, bool absolute); /** * @brief Returns true if the edge orientation matches the criterion diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index b61dd2eaf..f432aec21 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -30,8 +30,8 @@ namespace db // ----------------------------------------------------------------------------------- // CornerDetectorCore implementation -CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) - : m_checker (angle_start, include_angle_start, angle_end, include_angle_end) +CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) + : m_checker (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute) { // .. nothing yet .. } diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index de017aa7c..9593ec782 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -114,7 +114,7 @@ private: class DB_PUBLIC CornerDetectorCore { public: - CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end); + CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute); virtual ~CornerDetectorCore () { } void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const; @@ -130,8 +130,8 @@ class DB_PUBLIC CornersAsRectangles : public db::PolygonProcessorBase, private CornerDetectorCore { public: - CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1) - : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end), m_dim (dim) + CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute, db::Coord dim = 1) + : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), m_dim (dim) { // .. nothing yet .. } @@ -159,8 +159,8 @@ class DB_PUBLIC CornersAsDots : public db::PolygonToEdgeProcessorBase, private CornerDetectorCore { public: - CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) - : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end) + CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) + : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute) { // .. nothing yet .. } @@ -184,8 +184,8 @@ class DB_PUBLIC CornersAsEdgePairs : public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore { public: - CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) - : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end) + CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) + : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute) { // .. nothing yet .. } diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 0ee676e20..cc8f93eff 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -214,22 +214,22 @@ static db::CompoundRegionOperationNode *new_count_filter (db::CompoundRegionOper return new db::CompoundRegionCountFilterNode (input, invert, min_count, max_count); } -static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1) +static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim, bool inverse, bool absolute) { check_non_null (input, "input"); - return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim), input, true /*processor is owned*/, dim /*dist adder*/); + return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute, dim), input, true /*processor is owned*/, dim /*dist adder*/); } -static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) { check_non_null (input, "input"); - return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); + return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute) { check_non_null (input, "input"); - return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); + return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) @@ -341,10 +341,10 @@ static db::CompoundRegionOperationNode *new_edge_length_sum_filter (db::Compound return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/, true /*sum*/); } -static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax) +static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax, bool absolute_angle) { check_non_null (input, "input"); - return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, include_amin, amax, include_amax, inverse), input, true /*processor is owned*/); + return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, include_amin, amax, include_amax, inverse, absolute_angle), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_polygons (db::CompoundRegionOperationNode *input, db::Coord e) @@ -617,17 +617,21 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_count_filter", &new_count_filter, gsi::arg ("inputs"), gsi::arg ("invert", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max ()), "@brief Creates a node selecting results but their shape count.\n" ) + - gsi::constructor ("new_corners_as_rectangles", &new_corners_as_rectangles, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("dim"), + gsi::constructor ("new_corners_as_rectangles", &new_corners_as_rectangles, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("dim"), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief Creates a node turning corners into rectangles.\n" + "\n" + "'absolute' and 'inverse' arguments have been added in version 0.29.1.\n" ) + - gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), + gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief Creates a node turning corners into dots (single-point edges).\n" + "\n" + "'absolute' and 'inverse' arguments have been added in version 0.29.1.\n" ) + - gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), + gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n" "The first edge will be the incoming edge and the second one the outgoing edge.\n" "\n" - "This feature has been introduced in version 0.27.1.\n" + "This feature has been introduced in version 0.27.1. 'absolute' and 'inverse' arguments have been added in version 0.29.1.\n" ) + gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0), "@brief Creates a node returning the extents of the objects.\n" @@ -759,8 +763,10 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_edge_length_sum_filter", &new_edge_length_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"), "@brief Creates a node filtering edges by their length sum (over the local set).\n" ) + - gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse"), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"), + gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse"), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"), gsi::arg ("absolute_angle", false), "@brief Creates a node filtering edges by their orientation.\n" + "\n" + "'absolute_angle' has been introduced in version 0.29.1." ) + gsi::constructor ("new_polygons", &new_polygons, gsi::arg ("input"), gsi::arg ("e", 0), "@brief Creates a node converting the input to polygons.\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index b4f6b5447..819ea1b0a 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -430,16 +430,16 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian return r->filtered (ef); } -static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse) +static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse, bool absolute) { - db::EdgeOrientationFilter f (a, inverse); + db::EdgeOrientationFilter f (a, inverse, absolute); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return r->filtered (ef); } -static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return r->filtered (ef); } @@ -451,16 +451,16 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta return r->filtered (ef); } -static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) +static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse, bool absolute) { - db::EdgeOrientationFilter f (a, inverse); + db::EdgeOrientationFilter f (a, inverse, absolute); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return r->filtered (ef); } -static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return r->filtered (ef); } @@ -926,12 +926,16 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with at least one edge having the given angle to the x-axis are returned. If \"inverse\" is true, " "edge pairs not fulfilling this criterion are returned.\n" "\n" + "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " + "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " + "'absolute_angle' set to true, the angles are always positive.\n" + "\n" "This will filter edge pairs with at least one horizontal edge:\n" "\n" "@code\n" @@ -946,9 +950,9 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle_both(0, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1.\n" + "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" ) + - method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with at least one edge having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " @@ -957,6 +961,10 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" + "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " + "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " + "'absolute_angle' set to true, the angles are always positive.\n" + "\n" "Note that the inverse @b result @/b of \\with_angle is delivered by \\with_angle_both with the inverse flag set as edge pairs are unselected when both edges fail to meet the criterion.\n" "I.e\n" "\n" @@ -965,7 +973,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle_both(0, 45, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1.\n" + "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" ) + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" @@ -986,7 +994,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + - method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with both edges having the given angle to the x-axis are returned. If \"inverse\" is true, " @@ -1008,7 +1016,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with both edges having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " @@ -1017,6 +1025,10 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" + "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " + "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " + "'absolute_angle' set to true, the angles are always positive.\n" + "\n" "Note that the inverse @b result @/b of \\with_angle_both is delivered by \\with_angle with the inverse flag set as edge pairs are unselected when one edge fails to meet the criterion.\n" "I.e\n" "\n" @@ -1025,7 +1037,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle(0, 45, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1.\n" + "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" ) + method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 5fc83ec49..7b2fe4ae2 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -438,15 +438,15 @@ static db::Edges with_length2 (const db::Edges *r, const tl::Variant &min, const return r->filtered (f); } -static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse) +static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse, bool absolute) { - db::EdgeOrientationFilter f (a, inverse); + db::EdgeOrientationFilter f (a, inverse, absolute); return r->filtered (f); } -static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); return r->filtered (f); } @@ -901,19 +901,25 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + - method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" "\n" + "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " + "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " + "'absolute_angle' set to true, the angles are always positive.\n" + "\n" "This will select horizontal edges:\n" "\n" "@code\n" "horizontal = edges.with_angle(0, false)\n" "@/code\n" + "\n" + "'absolute_angle' has been introduced in version 0.29.1." ) + - method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have an angle to the x-axis larger or equal to \"min_angle\" (depending on \"include_min_angle\") and equal or less than \"max_angle\" (depending on \"include_max_angle\") are " @@ -923,7 +929,12 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" - "The two \"include..\" arguments have been added in version 0.27." + "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " + "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " + "'absolute_angle' set to true, the angles are always positive.\n" + "\n" + "The two \"include..\" arguments have been added in version 0.27. " + "'absolute_angle' has been introduced in version 0.29.1." ) + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filters the edges by orientation type\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 013c43154..cfd5205e2 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -300,19 +300,19 @@ static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss)); } -static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end) +static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute) { - return r->processed (db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end)); + return r->processed (db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)); } -static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim, bool include_angle_start, bool include_angle_end) +static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute) { - return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim)); + return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute, dim)); } -static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end) +static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute) { - return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end)); + return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)); } static db::Region *new_si (const db::RecursiveShapeIterator &si) @@ -1697,7 +1697,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@hide\n" "This method is provided for DRC implementation.\n" ) + - method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_min", -180.0), gsi::arg ("angle_max", 180.0), gsi::arg ("dim", 1), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), + method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_min", -180.0), gsi::arg ("angle_max", 180.0), gsi::arg ("dim", 1), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "The angle values specify a range of angles: all corners whose attached edges form an angle " @@ -1706,29 +1706,35 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "If 'include_angle_min' is true, the angle condition is >= min. angle, otherwise it is > min. angle. " "Same for 'include_angle_,ax' and the max. angle.\n" "\n" - "The angle is measured " + "With 'absolute' set to false (the default), the angle is measured " "between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left " "while a negative value is a turn right. Since polygon contours are oriented clockwise, positive " "angles will report concave corners while negative ones report convex ones.\n" + "With the 'absolute' option set to true, there is no such distinction and angle values are always positive.\n" + "\n" + "With 'inverse' set to true, the method will select corners not meeting the angle criterion.\n" "\n" "A similar function that reports corners as point-like edges is \\corners_dots.\n" "\n" - "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27. " + "'inverse' and 'absolute' have been added in version 0.29.1.\n" ) + - method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), + method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" "\n" - "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27. " + "'inverse' and 'absolute' have been added in version 0.29.1.\n" ) + - method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), + method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n" "The first edge is the incoming edge of the corner, the second one the outgoing edge.\n" "\n" - "This method has been introduced in version 0.27.1.\n" + "This method has been introduced in version 0.27.1. " + "'inverse' and 'absolute' have been added in version 0.29.1.\n" ) + method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge, "@brief Merge the region\n" diff --git a/src/db/unit_tests/dbAsIfFlatRegionTests.cc b/src/db/unit_tests/dbAsIfFlatRegionTests.cc index e966e6597..55d459000 100644 --- a/src/db/unit_tests/dbAsIfFlatRegionTests.cc +++ b/src/db/unit_tests/dbAsIfFlatRegionTests.cc @@ -1043,13 +1043,13 @@ TEST(21_Processors) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true))); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false))); db::Region ext; - r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 1000, 1000, 2000, 2000); + r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 1000, 1000, 2000, 2000); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2000))); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2000))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::extents_processor (0, 0))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor (1000, 2000))); diff --git a/src/db/unit_tests/dbCompoundOperationTests.cc b/src/db/unit_tests/dbCompoundOperationTests.cc index 95b790268..f64cce6ee 100644 --- a/src/db/unit_tests/dbCompoundOperationTests.cc +++ b/src/db/unit_tests/dbCompoundOperationTests.cc @@ -836,10 +836,10 @@ void run_test15 (tl::TestBase *_this, bool deep) db::CompoundRegionOperationPrimaryNode *primary = new db::CompoundRegionOperationPrimaryNode (); - db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, true, 180.0, true, 1), primary, true /*processor is owned*/); + db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 1), primary, true /*processor is owned*/); db::CompoundRegionCountFilterNode count1 (corners1, false, 5, 10000); - db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, true, 180.0, true), primary, true /*processor is owned*/); + db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, true, 180.0, true, false, false), primary, true /*processor is owned*/); db::CompoundRegionCountFilterNode count2 (corners2, true, 5, 10000); EXPECT_EQ (count1.result_type () == db::CompoundRegionJoinOperationNode::Region, true); diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 381ae4fc7..b078fc808 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -327,8 +327,8 @@ TEST(5_Filters) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); - db::EdgeOrientationFilter eof1 (0, true, 1, true, false); - db::EdgeOrientationFilter eof2 (0, true, 1, true, true); + db::EdgeOrientationFilter eof1 (0, true, 1, true, false, false); + db::EdgeOrientationFilter eof2 (0, true, 1, true, true, false); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.filtered (eof1)); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.filtered (eof2)); diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 9212846a5..8826f1f8d 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -1264,13 +1264,13 @@ TEST(21_Processors) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true))); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false))); db::Region ext; - r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 1000, 1000, 2000, 2000); + r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 1000, 1000, 2000, 2000); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2000))); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2000))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::extents_processor (0, 0))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor (1000, 2000))); diff --git a/src/db/unit_tests/dbEdgePairsTests.cc b/src/db/unit_tests/dbEdgePairsTests.cc index 1e42dc574..b91972ec8 100644 --- a/src/db/unit_tests/dbEdgePairsTests.cc +++ b/src/db/unit_tests/dbEdgePairsTests.cc @@ -166,6 +166,7 @@ TEST(5_InternalAngleFilter) { db::EdgePair ep0 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (0, 0))); db::EdgePair ep45 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 100))); + db::EdgePair ep45inv (db::Edge (db::Point (0, 0), db::Point (100, 100)), db::Edge (db::Point (0, 0), db::Point (100, 0))); db::EdgePair ep180 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 0))); db::EdgePair ep90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (0, 100))); db::EdgePair epm90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 100), db::Point (0, 0))); @@ -187,6 +188,7 @@ TEST(5_InternalAngleFilter) EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep90), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (epm90), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45inv), true); EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep0), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep180), false); @@ -199,4 +201,12 @@ TEST(5_InternalAngleFilter) EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep90), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (epm90), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45inv), true); + + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep180), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep90), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (epm90), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45inv), false); } diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index d9cb66589..56a21d00f 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -202,55 +202,63 @@ TEST(4) EXPECT_EQ (db::compare (rr, "(0,0;0,200);(0,200;100,200);(100,200;100,0);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (0.0, false); + db::EdgeOrientationFilter f1 (0.0, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false); + db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(200,0;250,200);(250,-200;300,0)"), true); } { - db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, true); + db::EdgeOrientationFilter f1 (-80.0, true, -50.0, false, false, false); + EXPECT_EQ (db::compare (r.filtered (f1), "(250,200;300,0);(200,0;250,-200)"), true); + } + { + db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false, true); + EXPECT_EQ (db::compare (r.filtered (f1), "(200,0;250,200);(250,200;300,0);(200,0;250,-200);(250,-200;300,0)"), true); + } + { + db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, true, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(0,200;100,200);(100,200;100,0);(100,0;0,0);(250,200;300,0);(300,0;200,0);(200,0;250,-200)"), true); } { - db::EdgeOrientationFilter f1 (0.0, true, 1.0, false, false); + db::EdgeOrientationFilter f1 (0.0, true, 1.0, false, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (-1.0, true, 1.0, false, false); + db::EdgeOrientationFilter f1 (-1.0, true, 1.0, false, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (-1.0, true, 0.0, false, false); + db::EdgeOrientationFilter f1 (-1.0, true, 0.0, false, false, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } { - db::EdgeOrientationFilter f1 (-1.0, true, 0.0, true, false); + db::EdgeOrientationFilter f1 (-1.0, true, 0.0, true, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (0.0, true, 1.0, true, false); + db::EdgeOrientationFilter f1 (0.0, true, 1.0, true, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true); } { - db::EdgeOrientationFilter f1 (0.0, false, 1.0, true, false); + db::EdgeOrientationFilter f1 (0.0, false, 1.0, true, false, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } { - db::EdgeOrientationFilter f1 (90.0, false); + db::EdgeOrientationFilter f1 (90.0, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true); } { - db::EdgeOrientationFilter f1 (90.0, true, 91.0, false, false); + db::EdgeOrientationFilter f1 (90.0, true, 91.0, false, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true); } { - db::EdgeOrientationFilter f1 (89.0, true, 91.0, false, false); + db::EdgeOrientationFilter f1 (89.0, true, 91.0, false, false, false); EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true); } { - db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false); + db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } } diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 70ac453b2..eaaee9e8c 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2574,17 +2574,17 @@ TEST(100_Processors) r.insert (db::Box (db::Point (0, 300), db::Point (200, 400))); r.insert (db::Box (db::Point (100, 300), db::Point (200, 500))); - EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-180.0, true, 180.0, true)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true); - EXPECT_EQ (r.processed (db::CornersAsDots (0.0, true, 180.0, true)).to_string (), "(100,400;100,400)"); - EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, true)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true); - EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, true)).to_string (), "(100,400;100,400)"); - EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,500;100,500);(200,500;200,500)"), true); - EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, false)).to_string (), ""); + EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true); + EXPECT_EQ (r.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).to_string (), "(100,400;100,400)"); + EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, true, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true); + EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, true, false, false)).to_string (), "(100,400;100,400)"); + EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, false, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,500;100,500);(200,500;200,500)"), true); + EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, false, false, false)).to_string (), ""); db::Region ext; - r.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 10, 10, 20, 20); + r.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 10, 10, 20, 20); EXPECT_EQ (ext.to_string (), "(90,380;90,420;110,420;110,380)"); - EXPECT_EQ (db::compare (r.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2)), "(98,-2;98,2;102,2;102,-2);(-2,-2;-2,2;2,2;2,-2);(-2,198;-2,202;2,202;2,198);(98,198;98,202;102,202;102,198);(198,298;198,302;202,302;202,298);(-2,298;-2,302;2,302;2,298);(-2,398;-2,402;2,402;2,398);(98,398;98,402;102,402;102,398);(98,498;98,502;102,502;102,498);(198,498;198,502;202,502;202,498)"), true); - EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2)).to_string (), "(98,398;98,402;102,402;102,398)"); + EXPECT_EQ (db::compare (r.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2)), "(98,-2;98,2;102,2;102,-2);(-2,-2;-2,2;2,2;2,-2);(-2,198;-2,202;2,202;2,198);(98,198;98,202;102,202;102,198);(198,298;198,302;202,302;202,298);(-2,298;-2,302;2,302;2,298);(-2,398;-2,402;2,402;2,398);(98,398;98,402;102,402;102,398);(98,498;98,502;102,502;102,498);(198,498;198,502;202,502;202,498)"), true); + EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2)).to_string (), "(98,398;98,402;102,402;102,398)"); EXPECT_EQ (db::compare (r.processed (db::extents_processor (0, 0)), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)"), true); EXPECT_EQ (db::compare (r.processed (db::extents_processor (10, 20)), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)"), true); diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 19c63f8b9..a4ad2a239 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -164,7 +164,7 @@ out = in.drc((width < 0.3).edges - secondary(waive)) This operation selects edges by their angle, measured against the horizontal axis in the mathematical sense.

-For this measurement edges are considered without their direction and straight lines. +For this measurement edges are considered without their direction. A horizontal edge has an angle of zero degree. A vertical one has an angle of 90 degrees. The angle range is from -90 (exclusive) to 90 degree (inclusive).

@@ -172,20 +172,28 @@ If the input shapes are not polygons or edge pairs, they are converted to edges before the angle test is made.

For example, the following code selects all edges from the primary shape which are 45 degree -(up) or 135 degree (down). The "+" will join the results: +(up) or -45 degree (down). The "+" operator will join the results:

-out = in.drc((angle == 45) + (angle == 135)) 
-out = in.drc((primary.angle == 45) + (primary.angle == 135))    # equivalent
+out = in.drc((angle == 45) + (angle == -45)) 
+out = in.drc((primary.angle == 45) + (primary.angle == -45))    # equivalent
 

-Note that angle checks usually imply the need to rotation variant formation as cells which +You can avoid using both 45 and -45 degree checks with the 'absolute' option. +With this option, angles are not signed and the value is the absolute angle +the edge encloses with the x axis: +

+

+out = in.drc(angle(absolute) == 45)
+
+

+Note that angle checks usually imply the need for rotation variant formation as cells which are placed non-rotated and rotated by 90 degree cannot be considered identical. This imposes a performance penalty in hierarchical mode. If possible, consider using DRC#rectilinear for example to detect shapes with non-manhattan geometry instead of using angle checks.

The "angle" method is available as a plain function or as a method on DRC expressions. -The plain function is equivalent to "primary.angle". +The plain function is equivalent to the method call "primary.angle".

"area" - Selects the primary shape if the area is meeting the condition

@@ -363,8 +371,7 @@ See
layer#centers for details abo

Usage:

  • expression.corners
  • -
  • expression.corners(as_dots)
  • -
  • expression.corners(as_boxes)
  • +
  • expression.corners([ options ])

This operation acts on polygons and selects the corners of the polygons. @@ -374,10 +381,20 @@ and negative for the turn to the right. Hence positive angles indicate concave (inner) corners, negative ones indicate convex (outer) corners. Angles take values between -180 and 180 degree.

-When using "as_dots" for the argument, the operation will return single-point edges at +When using "as_dots" for an option, the operation will return single-point edges at the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be produced at each selected corner.

+Another option is "absolute" which selects the corners by absolute angle - i.e left +and right turns will both be considered positive angles. +

+Examples for use of the options are: +

+

+corners(as_dots)
+corners(as_boxes, absolute)
+
+

The following example selects all corners:

diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml
index 858e64abc..3d68b9eda 100644
--- a/src/doc/doc/about/drc_ref_layer.xml
+++ b/src/doc/doc/about/drc_ref_layer.xml
@@ -264,8 +264,11 @@ deliver objects that can be converted into polygons. Such objects are of class <
 

This method produces markers on the corners of the polygons. An angle criterion can be given which selects corners based on the angle of the connecting edges. Positive angles indicate a left turn -while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles +while negative angles indicate a right turn. +Since polygons are oriented clockwise, positive angles indicate concave (inner) corners while negative ones indicate convex (outer) corners +The 'absolute' option allows turning this off and considering both left and right turns +positive angles.

The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.

@@ -276,6 +279,8 @@ The options available are:

  • as_dots : with this option, point-like edges will be produced instead of small boxes
  • as_edge_pairs : with this option, an edge pair is produced for each corner selected. The first edge is the incoming edge to the corner, the second edge the outgoing edge.
  • +
  • absolute : with this option, left and right turns will both be considered positive angles
  • +
  • negative : with this option, all corners not matching the angle criterion are selected
  • The following images show the effect of this method: @@ -3615,13 +3620,13 @@ The following images illustrate the effect of the "without_touching_corners" opt

    Usage:

      -
    • layer.with_angle(min .. max)
    • -
    • layer.with_angle(value)
    • -
    • layer.with_angle(min, max)
    • +
    • layer.with_angle(min .. max [, absolute])
    • +
    • layer.with_angle(value [, absolute])
    • +
    • layer.with_angle(min, max [, absolute])
    • layer.with_angle(ortho)
    • layer.with_angle(diagonal)
    • layer.with_angle(diagonal_only)
    • -
    • edge_pair_layer.with_angle(... [, both])
    • +
    • edge_pair_layer.with_angle(... [, both] [, absolute])

    When called on an edge layer, the method selects edges by their angle, @@ -3641,6 +3646,11 @@ meeting the angle criterion. In this case an additional argument is accepted whi either "both" (plain word) to indicate that both edges have to be within the given interval. Without this argument, it is sufficient for one edge to meet the criterion.

    +The "absolute" option is available for edge or edge pair layers. +Without the "absolute" option, edges sloping down are assigned a negative angle while edges sloping up are assigned +a positive angle (vertical edges are always 90 degree). With the "absolute" option, +edges sloping down also have a positive angle which is the one enclosed with the horizontal axis. +

    Here are examples for "with_angle" on edge pair layers:

    @@ -4006,21 +4016,25 @@ This method is available for polygon layers only.
     
     

    Usage:

      -
    • layer.without_angle(min .. max)
    • -
    • layer.without_angle(value)
    • -
    • layer.without_angle(min, max)
    • +
    • layer.without_angle(min .. max [, absolute])
    • +
    • layer.without_angle(value [, absolute])
    • +
    • layer.without_angle(min, max [, absolute])
    • layer.without_angle(ortho)
    • layer.without_angle(diagonal)
    • layer.without_angle(diagonal_only)
    • -
    • edge_pair_layer.without_angle(... [, both])
    • +
    • edge_pair_layer.without_angle(... [, both] [, absolute])

    The method basically is the inverse of with_angle. It selects all edges of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle is not inside the given interval (first and third form) or of the given type (other forms).

    -When called on edge pairs, it selects -edge pairs by the angles of their edges. +When called on edge pairs, it selects edge pairs by the angles of their edges. +

    +The "absolute" option is available for edge or edge pair layers. Without the "absolute" option, +edges sloping down are assigned a negative angle while edges sloping up are assigned +a positive angle (vertical edges are always 90 degree). With the "absolute" option, +edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.

    A note on the "both" modifier (without_angle called on edge pairs): "both" means that both edges need to be "without_angle". For example diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index 3f093df45..e47290685 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -691,7 +691,7 @@ CODE # This operation selects edges by their angle, measured against the horizontal # axis in the mathematical sense. # - # For this measurement edges are considered without their direction and straight lines. + # For this measurement edges are considered without their direction. # A horizontal edge has an angle of zero degree. A vertical one has # an angle of 90 degrees. The angle range is from -90 (exclusive) to 90 degree (inclusive). # @@ -699,23 +699,43 @@ CODE # before the angle test is made. # # For example, the following code selects all edges from the primary shape which are 45 degree - # (up) or 135 degree (down). The "+" will join the results: + # (up) or -45 degree (down). The "+" operator will join the results: # # @code - # out = in.drc((angle == 45) + (angle == 135)) - # out = in.drc((primary.angle == 45) + (primary.angle == 135)) # equivalent + # out = in.drc((angle == 45) + (angle == -45)) + # out = in.drc((primary.angle == 45) + (primary.angle == -45)) # equivalent # @/code # - # Note that angle checks usually imply the need to rotation variant formation as cells which + # You can avoid using both 45 and -45 degree checks with the 'absolute' option. + # With this option, angles are not signed and the value is the absolute angle + # the edge encloses with the x axis: + # + # @code + # out = in.drc(angle(absolute) == 45) + # @/code + # + # Note that angle checks usually imply the need for rotation variant formation as cells which # are placed non-rotated and rotated by 90 degree cannot be considered identical. This imposes # a performance penalty in hierarchical mode. If possible, consider using \DRC#rectilinear for # example to detect shapes with non-manhattan geometry instead of using angle checks. # # The "angle" method is available as a plain function or as a method on \DRC# expressions. - # The plain function is equivalent to "primary.angle". + # The plain function is equivalent to the method call "primary.angle". - def angle - DRCOpNodeEdgeOrientationFilter::new(@engine, self) + def angle(*args) + + filter = DRCOpNodeEdgeOrientationFilter::new(@engine, self) + + args.each do |a| + if a.is_a?(DRCAbsoluteMode) + filter.absolute = a.value + else + raise("Invalid argument (#{a.inspect}) for 'angle' method") + end + end + + filter + end # %DRC% @@ -758,8 +778,7 @@ CODE # @name corners # @brief Selects corners of polygons # @synopsis expression.corners - # @synopsis expression.corners(as_dots) - # @synopsis expression.corners(as_boxes) + # @synopsis expression.corners([ options ]) # # This operation acts on polygons and selects the corners of the polygons. # It can be put into a condition to select corners by their angles. The angle of @@ -768,9 +787,19 @@ CODE # (inner) corners, negative ones indicate convex (outer) corners. # Angles take values between -180 and 180 degree. # - # When using "as_dots" for the argument, the operation will return single-point edges at + # When using "as_dots" for an option, the operation will return single-point edges at # the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be # produced at each selected corner. + # + # Another option is "absolute" which selects the corners by absolute angle - i.e left + # and right turns will both be considered positive angles. + # + # Examples for use of the options are: + # + # @code + # corners(as_dots) + # corners(as_boxes, absolute) + # @/code # # The following example selects all corners: # @@ -789,14 +818,20 @@ CODE # The "corners" method is available as a plain function or as a method on \DRC# expressions. # The plain function is equivalent to "primary.corners". - def corners(output_mode = DRCOutputMode::new(:dots)) + def corners(*args) @engine._context("corners") do - if output_mode.is_a?(DRCOutputMode) - output_mode = output_mode.value - else - raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") + output_mode = :as_boxes + absolute = false + args.each do |a| + if a.is_a?(DRCOutputMode) + output_mode = a.value + elsif a.is_a?(DRCAbsoluteMode) + absolute = a.value + else + raise("Invalid argument (#{a.inspect}) for 'corners' method") + end end - DRCOpNodeCornersFilter::new(@engine, output_mode, self) + DRCOpNodeCornersFilter::new(@engine, output_mode, absolute, self) end end @@ -1721,6 +1756,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare attr_accessor :input attr_accessor :inverse + attr_accessor :absolute def initialize(engine, input) super(engine) @@ -1748,6 +1784,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare args << (self.gt ? false : true) args << (self.lt ? self.lt : (self.le ? self.le + angle_delta : 180.0)) args << (self.lt ? false : true) + args << self.absolute RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args) @@ -2037,12 +2074,16 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare attr_accessor :input attr_accessor :output_mode + attr_accessor :inverse + attr_accessor :absolute - def initialize(engine, output_mode, input) + def initialize(engine, output_mode, absolute, input) super(engine) self.output_mode = output_mode self.input = input self.description = "corners" + self.inverse = false + self.absolute = absolute end def do_create_node(cache) @@ -2052,14 +2093,26 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare args << (self.lt ? self.lt : (self.le ? self.le : 180.0)) args << (self.lt ? false : true) if self.output_mode == :dots || self.output_mode == :edges + args << self.inverse + args << self.absolute RBA::CompoundRegionOperationNode::new_corners_as_dots(*args) elsif self.output_mode == :edge_pairs + args << self.inverse + args << self.absolute RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args) else args << 2 # dimension is 2x2 DBU + args << self.inverse + args << self.absolute RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args) end end + + def inverted + res = self.dup + res.inverse = !res.inverse + return res + end end diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 8bf358bf0..72d73f44f 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -770,17 +770,7 @@ CODE # \DRC# expressions (see \Layer#drc and \DRC#length for more details). In this context, # the operation acts similar to \Layer#with_length. - # %DRC% - # @name angle - # @brief In universal DRC context: selects edges based on their orientation - # @synopsis angle (in condition) - # - # "angle" represents the edge orientation filter on the primary shape edges in - # \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context, - # the operation acts similar to \Layer#with_angle. - %w( - angle area holes hulls @@ -798,6 +788,15 @@ CODE CODE end + # %DRC% + # @name angle + # @brief In universal DRC context: selects edges based on their orientation + # @synopsis angle (in condition) + # + # "angle" represents the edge orientation filter on the primary shape edges in + # \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context, + # the operation acts similar to \Layer#with_angle. + # %DRC% # @name corners # @brief Selects corners of polygons @@ -817,11 +816,6 @@ CODE # The "corners" operator can be put into a condition which means it's # applied to corners meeting a particular angle constraint. - def _cop_corners(output_mode = DRCOutputMode::new(:boxes)) - # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. - return primary.corners(output_mode) - end - # %DRC% # @name extent_refs # @brief Returns partial references to the boundings boxes of the polygons @@ -897,6 +891,8 @@ CODE rounded_corners sized smoothed + corners + angle ).each do |f| # NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here. eval <<"CODE" diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 81e325ff3..15e9dc824 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -551,6 +551,10 @@ module DRC end end + def absolute + DRCAbsoluteMode::new(true) + end + def as_dots DRCOutputMode::new(:dots) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 870591330..c5aa2a64b 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -861,13 +861,13 @@ CODE # %DRC% # @name with_angle # @brief Selects edges by their angle - # @synopsis layer.with_angle(min .. max) - # @synopsis layer.with_angle(value) - # @synopsis layer.with_angle(min, max) + # @synopsis layer.with_angle(min .. max [, absolute]) + # @synopsis layer.with_angle(value [, absolute]) + # @synopsis layer.with_angle(min, max [, absolute]) # @synopsis layer.with_angle(ortho) # @synopsis layer.with_angle(diagonal) # @synopsis layer.with_angle(diagonal_only) - # @synopsis edge_pair_layer.with_angle(... [, both]) + # @synopsis edge_pair_layer.with_angle(... [, both] [, absolute]) # # When called on an edge layer, the method selects edges by their angle, # measured against the horizontal axis in the mathematical sense. @@ -886,6 +886,11 @@ CODE # either "both" (plain word) to indicate that both edges have to be within the given interval. # Without this argument, it is sufficient for one edge to meet the criterion. # + # The "absolute" option is available for edge or edge pair layers. + # Without the "absolute" option, edges sloping down are assigned a negative angle while edges sloping up are assigned + # a positive angle (vertical edges are always 90 degree). With the "absolute" option, + # edges sloping down also have a positive angle which is the one enclosed with the horizontal axis. + # # Here are examples for "with_angle" on edge pair layers: # # @code @@ -931,21 +936,25 @@ CODE # %DRC% # @name without_angle # @brief Selects edges by the their angle - # @synopsis layer.without_angle(min .. max) - # @synopsis layer.without_angle(value) - # @synopsis layer.without_angle(min, max) + # @synopsis layer.without_angle(min .. max [, absolute]) + # @synopsis layer.without_angle(value [, absolute]) + # @synopsis layer.without_angle(min, max [, absolute]) # @synopsis layer.without_angle(ortho) # @synopsis layer.without_angle(diagonal) # @synopsis layer.without_angle(diagonal_only) - # @synopsis edge_pair_layer.without_angle(... [, both]) + # @synopsis edge_pair_layer.without_angle(... [, both] [, absolute]) # # The method basically is the inverse of \with_angle. It selects all edges # of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle # is not inside the given interval (first and third form) or of the given type (other forms). # - # When called on edge pairs, it selects - # edge pairs by the angles of their edges. + # When called on edge pairs, it selects edge pairs by the angles of their edges. # + # The "absolute" option is available for edge or edge pair layers. Without the "absolute" option, + # edges sloping down are assigned a negative angle while edges sloping up are assigned + # a positive angle (vertical edges are always 90 degree). With the "absolute" option, + # edges sloping down also have a positive angle which is the one enclosed with the horizontal axis. + # # A note on the "both" modifier (without_angle called on edge pairs): "both" means that # both edges need to be "without_angle". For example # @@ -989,57 +998,107 @@ CODE # The method basically is the inverse of \with_internal_angle. It selects all # edge pairs by the angle enclosed by their edges, applying the opposite criterion than \with_internal_angle. - %w(angle internal_angle).each do |f| - [true, false].each do |inv| - mn = (inv ? "without" : "with") + "_" + f - eval <<"CODE" - def #{mn}(*args) + [true, false].each do |inv| + mn = (inv ? "without" : "with") + "_angle" + eval <<"CODE" + def #{mn}(*args) - @engine._context("#{mn}") do + @engine._context("#{mn}") do - f = :with_#{f} + self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer") - if "#{f}" == "angle" - self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer") - args = args.select do |a| - if a.is_a?(DRCBothEdges) - if !self.data.is_a?(RBA::EdgePairs) - raise("'both' keyword is only available for edge pair layers") - end - f = :with_#{f}_both - false - else - true - end + f = :with_angle + absolute = self.data.is_a?(RBA::Region) ? nil : false + + args = args.select do |a| + if a.is_a?(DRCBothEdges) + if !self.data.is_a?(RBA::EdgePairs) + raise("'both' keyword is only available for edge pair layers") end - else - requires_edge_pairs - end - - result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) - elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) - if self.data.is_a?(RBA::Region) - raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers") - end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect})) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect})) + f = :with_angle_both + false + elsif a.is_a?(DRCAbsoluteMode) + if self.data.is_a?(RBA::Region) + raise("'absolute' keyword is only available for edge or edge pair layers") end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect})) + absolute = a.value + false else - raise("Invalid number of range arguments (1 or 2 expected)") + true end + end + result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs + if args.size == 1 + a = args[0] + if a.is_a?(Range) + args = [ a.begin, a.end, #{inv.inspect} ] + if absolute != nil + args += [ true, false, absolute ] + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) + if self.data.is_a?(RBA::Region) + raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers") + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect})) + else + args = [ @engine._make_numeric_value(a), #{inv.inspect} ] + if absolute != nil + args += [ absolute ] + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + end + elsif args.size == 2 + args = [ @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect} ] + if absolute != nil + args += [ true, false, absolute ] + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + else + raise("Invalid number of range arguments (1 or 2 expected)") end end -CODE + end +CODE + end + + [true, false].each do |inv| + mn = (inv ? "without" : "with") + "_internal_angle" + eval <<"CODE" + def #{mn}(*args) + + @engine._context("#{mn}") do + + f = :with_internal_angle + + requires_edge_pairs + + result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) + elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) + if self.data.is_a?(RBA::Region) + raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers") + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(a), #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect})) + else + raise("Invalid number of range arguments (1 or 2 expected)") + end + + end + + end +CODE end # %DRC% @@ -1231,8 +1290,11 @@ CODE # # This method produces markers on the corners of the polygons. An angle criterion can be given which # selects corners based on the angle of the connecting edges. Positive angles indicate a left turn - # while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles + # while negative angles indicate a right turn. + # Since polygons are oriented clockwise, positive angles # indicate concave (inner) corners while negative ones indicate convex (outer) corners + # The 'absolute' option allows turning this off and considering both left and right turns + # positive angles. # # The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default. # @@ -1243,6 +1305,8 @@ CODE # @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li # @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge # is the incoming edge to the corner, the second edge the outgoing edge. @/li + # @li @b absolute @/b: with this option, left and right turns will both be considered positive angles @/li + # @li @b negative @/b: with this option, all corners @b not @/b matching the angle criterion are selected @/li # @/ul # # The following images show the effect of this method: @@ -1264,6 +1328,8 @@ CODE output_mode = :boxes amin = -180.0 amax = 180.0 + absolute = false + inverse = false args.each do |a| if a.is_a?(Range) @@ -1277,21 +1343,35 @@ CODE amax = a.to_f elsif a.is_a?(DRCOutputMode) output_mode = a.value + elsif a.is_a?(DRCAbsoluteMode) + absolute = a.value + elsif a.is_a?(DRCNegative) + inverse = true else raise("Invalid argument #{a.inspect}") end end - f = :corners - cls = RBA::Region + args = [ amin, amax ] + if output_mode == :edges || output_mode == :dots f = :corners_dots cls = RBA::Edges elsif output_mode == :edge_pairs f = :corners_edge_pairs cls = RBA::EdgePairs + else + f = :corners + cls = RBA::Region + args << 1 # 2x2 DBU boxes end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax)) + + args << true # include amin + args << true # include amax + args << inverse + args << absolute + + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, *args)) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 482f18db8..11d826943 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -123,6 +123,16 @@ module DRC end end + # A wrapper for the "absolute" flag for + # some DRC functions. The purpose of this class + # is to identify the value by the class. + class DRCAbsoluteMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for a rectangle error filter mode # The purpose of this wrapper is to identify the error filter mode class DRCRectangleErrorFilter diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 1c8180a8d..495074f48 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1673,6 +1673,16 @@ TEST(92_issue1594_dual_top) compare_netlists (_this, output, au); } +TEST(93_withAngle) +{ + run_test (_this, "93", false); +} + +TEST(93d_withAngle) +{ + run_test (_this, "93", true); +} + TEST(100_edge_interaction_with_count) { run_test (_this, "100", false); diff --git a/testdata/drc/drcGenericTests_12.drc b/testdata/drc/drcGenericTests_12.drc index 9993793f2..159721a62 100644 --- a/testdata/drc/drcGenericTests_12.drc +++ b/testdata/drc/drcGenericTests_12.drc @@ -27,4 +27,6 @@ l1.drc(angle < 0.0).output(111, 0) l1.drc(primary.angle > 0.0).output(112, 0) l1.drc(primary.edges.angle == 90).output(113, 0) l1.drc((angle == 0.0) + (angle == 90)).output(114, 0) +l1.drc(angle == 45).output(115, 0) +l1.drc(angle(absolute) == 45).output(116, 0) diff --git a/testdata/drc/drcGenericTests_6.drc b/testdata/drc/drcGenericTests_6.drc index 4b49eab6d..80da9f09d 100644 --- a/testdata/drc/drcGenericTests_6.drc +++ b/testdata/drc/drcGenericTests_6.drc @@ -23,6 +23,8 @@ l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners l1.drc(corners(as_boxes) <= -90).output(104, 0) l1.drc(corners(as_edge_pairs) == 90).output(105, 0) +l1.drc(corners(as_boxes, absolute) == 90).output(106, 0) # inner and outer corners +l1.drc(corners(as_boxes) != 90).output(107, 0) # not inner corners l1.drc(middle).output(110, 0) l1.drc(middle(as_dots)).output(111, 0) diff --git a/testdata/drc/drcGenericTests_au12.gds b/testdata/drc/drcGenericTests_au12.gds index 64cea5f5cd9d98285a0c08f4a59076ad579681cc..832d5dcfa05085550708d662b5819259daf44aaf 100644 GIT binary patch delta 1088 zcmZ`&&rcIk5MJuC{Xtu{EoHmB-EIq>j6%~iYc;6Va8MLW6Az{b>A{0BfFycV8xuoJ zPzm2n1-Y3R67=Rp>A`ps?b>(Tm!Dz~Ic=w(7w{GxOfQnfd1XzP3)^nXW3D;`3cp ztUjOro1!X1N>T|biNA9t;M2~XIi)B=`}&Wcym$EQyJzhqQ?DjAA6G9ZR{wwNUREz_ zD5V7KfVmcMkuz*gaVw^w>CfYhR=_2H5nJ?R&~KudP|;4B*bSPP4;1k!P{5XDV~1)= z6nBFHdQio5Fpsx!AAQvlBi>yzVZ#a23L1Vya-&X-uoC@@lAePe(O{VdZVw7H2MawYU!npl^!pNJctzAk1twx9GI5tE zo2NS$n~sg|2|??-u(ASQ;~iKA?(1dDW^Ejc+i2+pYzh_UV|fyQAsVu*5h8|TISGy1 zFf@+2gi9+3JRG`Witsx(k7W~l9Vy9XE7D3RA#5kEsmp9aj;xOzez##;ykq y*khht+cc}|Qm*m#=UL2y_vbq5u3Z$s`4A>qDRUA delta 1074 zcmZ`%&ref95PsCh>n}=Qq2+bEeQjy+rlDzGo|Pb=@t}w)Mh}J<(t`&>0`dn)FeV-} zsHES~7<)4@D0nek6i&t%QO-t9FW!vy9{`-$EzygI&CJfs?tC-fcW1J7ZpN^TK;Wv8 z?Fs~c7+prh=r=0H(SLm-6tJewpD~Q&k?yh6_fA}R_pE*L?B;Ou@yrz?+x_2nuV$`W zXbTB9BpM+f6IqvBiH|`a`x%LNP@+!$M#04Ql0$caLZ69|&>SB2No-N=^CVjVJ7(f# zTHt0x!XA>!){7?I_c_=JOWX?kxKFvAut3|jP|rBJCTOBC$Tb{yu@sfqj?AGRwb1G* z<96yWW}?~e;B!o3A}y(ov2adMZ57Ap>_$Dz<|K~CF5z`TBHtsh5w@@(9rbUBuySlQ zW8oU%T8ic|ImA|Aw_o>J>HUi7l!N6s|LfvguTU74;}WB`htZ^~a*KAAcHnhzI(T5$ z6#i4RmwIQW;Y1Csv_-hfYCkQzf<@aW-v!tGO!z4DO689weZuS`l=Nt>D)jEk9js+F z2V;Gdyo4tEv8qWiUlM_4Ya-i#q$6j;lSD!vY~j7MwaOC!8lpPbB2|>N}9vDM2 zUnPA@tmpI&<_Gf_$yZ1R60w|YxbyO5n{G#oWa^oh}Y=;mZZlj{MxK3)W h7k#A$W`BOLrr2QgvpKI3GVvu(K*LEZXc(qx`~ijnOho_y diff --git a/testdata/drc/drcGenericTests_au12d.gds b/testdata/drc/drcGenericTests_au12d.gds index b4412a56ef6ca568d92087b0e10dc8fe9eb009a7..62293afe45dc7003ed4aae29d8c0759f3d88375c 100644 GIT binary patch delta 357 zcmbQ`xyLJtfsKKQDS|{L4=vr&aujG18Sojxzz0vH_>m_j3sC+ N5l#^{pvPEP7yw$?HqZb7 delta 323 zcmdnvHOn)KfsKKQDS|m0S!A9GkTmr!$J`+5hDs*SRW*?`XKGm$RY|p#{TC}MX&fJU zN)?w<&cVZO_wDIx?{Kp9IP9INYUE%4*_@NA^-pY delta 347 zcmZ|HJxjw-7zN;)Xg=CB^@F4eHfddoASft}aS8F_BGEx`kPME+)um+Y(8c+TT?9d0 zB@i+?`6s#wLPw{P(NpN);&8e5p7%U&@laf+LKmufE82=`d<#u@VuL66`xd6EUtUNd zoE0rPdpt=#Uw=-|%fqKhdL!D}zwSM~(^2UW=7vORL?}#&x6NbhH?I)#AK1(r%T)F)};CkXcC|&{ZaT_6YMj=j>NC&bc?_U9dl-%NBW)*5tA1n3ap2)&cbO zZa!Z7T1BrVaqo<<=SFyOV+>r0nG<8?M$dzm!exC-wi2R#zO$WZ=yeD@0-wMq@CiHu SpP;h@-6h!lQ1a=~RQm;|du@dP diff --git a/testdata/drc/drcGenericTests_au6d.gds b/testdata/drc/drcGenericTests_au6d.gds index de48275c70e1a78d6b30e1d5c3d0223ea0652bae..4cfe2bc6abc1edf13d4a966141ebf2cb318ee290 100644 GIT binary patch delta 705 zcmaiwy-LGS6vt1J=EGDgNQ+Csp>hxrq?5Q9q8%)Du%#5q;F!S-;sc1YqhxS!ak_ng zEIxtHAh=%~gHPbO|8#JolK%@en94%TJ9`fUL=zsH#NNZG1+bMn*`9@ToRC+Hy)>({}hki9ERIT I)f;^JKN;4hu>b%7 delta 321 zcmexR_#rTgfsKKQDS|{L4=vr&aujG18Sojxzz0KF!XN^KN(|D-Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+ zDuh9X6}LV%Ha2Gl1{Mwm1~y(M21W)pJ|+eR1{DEjAh{EWzXBztfH;hSfw=|3hl;ZZ zV(QZoVE+IA=N|?J#t9Ib>DK@Me>X8Oz-X8_;{*n+4Eh{4^#!omOmzPRA<*APPPB z9r6E$kmJS|=K-Sa!I`<;$)(%V!qmd$_{mW+0e!(=dFRQipnxE_oeltOMcS46h$|(t z2J?qXy$>kG=`K**TtW++f)K>!UO_*!K!i}jD(lznuIYK#$zwZsQzRW_P`y%Y_ z_luEI4>8?CFLF}Y1K}N_@FCYz$OQ<50Nk(>7_H{ZwBgGq!!IM((|*3N1!Nc-#RoN_ zfZW6nBiAhfoBt-w02*!y9k^}&h&@?$MnAjm^Y;2##$@N>t;!aU}hEM(+*?wAJhS5slmU&H0cwD$Rgmk2qP9TrIj(I01!w~#K$A0)fKIm&sLLk{y;XDE zylZm|%gFUrYCx3#NUR2Q`Z?{5oq{p{DWDK@*f@NQT-PB)|9Jvi06lCtL?#0k-)!jb L4PG1!Jw$#0XjpSc delta 109 zcmaD?@hm5bfsKKQDS|{L4=vr&au{L4=vr&au6@Y*vm bUIpp73es~GtneyW36Kp`a&_|>{(a&AlqgfU delta 108 zcmaDA`^qnhfsKKQDS|{L4=vr&au{L4=vr&au{L4=vr&auEb?5D*3_ QZvZN7;J~G7@+FxY0AZIY2LJ#7 diff --git a/testdata/drc/drcSimpleTests_au93.gds b/testdata/drc/drcSimpleTests_au93.gds new file mode 100644 index 0000000000000000000000000000000000000000..986937a596571b5ad1f48f90c703f10736017f31 GIT binary patch literal 9958 zcmc(l&uSDw5XLLBo5^mB8X?DA1Q7(mn_rsxkb`*gpq_jH^^~iZynrB{BEEr7 zAW76W@bArILSXB)wyEfs|;nO{&P|; z_+QJYD6e0=BC;}=zjXP@xofXq?4F;zI`jPL?KN4QCtO*s-MA?-9Ep^N3z?JhNFgF8 zmIw9UmB`%7`XBFz%rA-zcHHOb^Wm{puBv{X5*c5ts&88&OV8ZrrI9@sz&U&4L(bmP z17}Y^tG#+{<4!LRs_J+B;>E^C^Q&g3t359!nEyVh`Sq&OwLDu@KQ@}xo)_zw zSNr2lXMb_m*|*i6k2eGUZqI$5;WGEj<17wY@wwOdiTBy_S$%zx}B(t&%9In|5lWL7_zGkY0&3*OH-UCo}^k8~PvNT=>O_EY!YbZok| zHPUsfJ>{G4cX7z!)UO10ztLx>dNspk=8?yB%A<#kpQb~%nm%>a_l@!);5y_{z`38B zRTo=1<;_paW9(gl<@Z7BLn+FgCiFDppI%Z_d?v{C(4PoxB1_xy>*nHat}VaH+fxT z_cMOHAVoD#sBq@Z-3_97VvA@VQdIMV(me#4hg4rh=^P0%$QdEb8B07W= z)ghsX4hcncNGPI1NKqXUis%qhREG%)=5Lp6oyakWYRcPtx{Z+K@f^7LAnwVgjNwmL6K6VYxOs%>u%h2@5Y^babf>} z`1uElpF5Xs6a*#SJDKa`-sC*V1knyNnNH@(J?A|i_e>-cV{XbgF$-TLm1X-lDd(#1 zB{$jC%NruggW>u02WPH4fBg0A#b>71@u@lfZoEW03*<|5faBZ*{(jEJl*4eaZl zeSWhiSL}03WUy2IKm9yCQs+)B4T|E&PmyG|JevKY`1M4@AN}Xau2lJ3J=eWG&e`Yf@^<=TWM{y6KRfu>$T@ztj<-Ls^Uln_x#Ds6|8d@rdi)#WP_eVjla6r+ zxH;m`$aRRry`FmhYwrHMb}k5Zhun6D)iqn^2g?5Na(eyjgb^e8Vb>*W&vD(NsXMe$ zagLO^-%eF4)>r71dZk+!^|49+X{Vwt*olgJUf`E%VO%)@^;-7}EPSI=paxKV)z5D_S{8B0O3n<^O%Dxjs`CTu+fP$)2DXdDM!hNutUt!meY8lo74d3_D5GcqE zmBQQr3jF$@`vp-}DU32u(0f1X7DBfH3j9(j^b07+-WJNwEK&9X1yTN_YY1wn6siFf zMEP?s3snj&1PUyCpo?Zgry#6)b#^Kh@L6Q`gU+c=3pjD96sipr zsQuR`6UcLQTB!Xxs?KRDg=zo=YN-1@#tW#XjGa!cDusRl1%9b}aqa#BRSGQxs>#BZDahUl+Prq% znmH25j=Fosi<5iAm+a2_wCs)bGx@RTOtzBP?=Oyr8{78i{_ItX;`?!teCN-1b9;Sv Y*QsfR+PTUaBgai9wGQi8f74Wc1F!_4h5!Hn literal 0 HcmV?d00001 diff --git a/testdata/drc/drctest.gds b/testdata/drc/drctest.gds index 4cea4d52302add64b23a3de7b37ecf9bd0dabf3a..9c26cc88dc7c4aed3e128c1fbe5e00b8964b1445 100644 GIT binary patch delta 364 zcmew%(I6GYz{bGD6u}_F$i)7FfrUYYL5P8gK@6GAz`?}kegW2hkutNFPWY zh<@t;WM6>LQ1jJ+7BR30GBB{PGvM>cNIgGm(mp>=};BsbWVPOCO>db&< delta 227 zcmZpW`XLd;z{bGD6u}_F$i)7Hft5j=L5RVS!2+4hz`?}k5U_#Bta=c^8kfG#>LOi}Cd0)Z4&a&CJ5W F000@IB>4aU From ac5b3073f1a4176c356aae7d03950685a72ef471 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 20 Apr 2024 23:31:54 +0200 Subject: [PATCH 06/41] Some enhancements on rulers: pasted ruler is selected now, 'duplicate_interactive' now working on rulers too --- src/ant/ant/antService.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 11b0d2fb7..b42ae5012 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -2198,15 +2198,29 @@ Service::paste () } } + std::vector new_objects; + for (db::Clipboard::iterator c = db::Clipboard::instance ().begin (); c != db::Clipboard::instance ().end (); ++c) { const db::ClipboardValue *value = dynamic_cast *> (*c); if (value) { ant::Object *ruler = new ant::Object (value->get ()); ruler->id (++idmax); - mp_view->annotation_shapes ().insert (db::DUserObject (ruler)); + new_objects.push_back (&mp_view->annotation_shapes ().insert (db::DUserObject (ruler))); } } + // make new objects selected + + if (! new_objects.empty ()) { + + for (auto r = new_objects.begin (); r != new_objects.end (); ++r) { + m_selected.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (*r), 0)); + } + + selection_to_view (); + + } + } } From 753f2cd1643d3a54669229b00ed35eeb65c18d52 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 20 Apr 2024 23:35:38 +0200 Subject: [PATCH 07/41] Fixed one unit test (remove file path reference) --- src/db/unit_tests/dbNetlistReaderTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index c3b67b343..47dfbee3e 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -893,7 +893,7 @@ TEST(24_recursive_calls) reader.read (is, nl); EXPECT_EQ (false, true); } catch (tl::Exception &ex) { - EXPECT_EQ (ex.msg (), "Subcircuit 'C1' called recursively in /home/matthias/klayout/master/testdata/algo/nreader24.cir, line 8"); + EXPECT_EQ (ex.msg ().find ("Subcircuit 'C1' called recursively in"), size_t (0)); } } From f68fd4f8d057e20863a6be6304d2d53adb9cce22 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 21 Apr 2024 00:17:05 +0200 Subject: [PATCH 08/41] Refactoring of EdgePairs/Edges API to avoid ambiguities --- src/db/db/gsiDeclDbEdgePairs.cc | 101 ++++++++++++++++------ src/db/db/gsiDeclDbEdges.cc | 51 +++++++---- src/drc/drc/built-in-macros/_drc_layer.rb | 30 +++---- testdata/ruby/dbEdgePairsTest.rb | 11 +++ testdata/ruby/dbEdgesTest.rb | 7 ++ 5 files changed, 138 insertions(+), 62 deletions(-) diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 819ea1b0a..d5edc7cec 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -430,16 +430,30 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian return r->filtered (ef); } -static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse, bool absolute) +static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, inverse, absolute); + db::EdgeOrientationFilter f (a, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return r->filtered (ef); } -static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) +static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_abs_angle1 (const db::EdgePairs *r, double a, bool inverse) +{ + db::EdgeOrientationFilter f (a, inverse, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return r->filtered (ef); } @@ -451,16 +465,30 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta return r->filtered (ef); } -static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse, bool absolute) +static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, inverse, absolute); + db::EdgeOrientationFilter f (a, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return r->filtered (ef); } -static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) +static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_abs_angle_both1 (const db::EdgePairs *r, double a, bool inverse) +{ + db::EdgeOrientationFilter f (a, inverse, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return r->filtered (ef); } @@ -926,16 +954,12 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with at least one edge having the given angle to the x-axis are returned. If \"inverse\" is true, " "edge pairs not fulfilling this criterion are returned.\n" "\n" - "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " - "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " - "'absolute_angle' set to true, the angles are always positive.\n" - "\n" "This will filter edge pairs with at least one horizontal edge:\n" "\n" "@code\n" @@ -950,9 +974,9 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle_both(0, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" + "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with at least one edge having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " @@ -961,10 +985,6 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" - "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " - "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " - "'absolute_angle' set to true, the angles are always positive.\n" - "\n" "Note that the inverse @b result @/b of \\with_angle is delivered by \\with_angle_both with the inverse flag set as edge pairs are unselected when both edges fail to meet the criterion.\n" "I.e\n" "\n" @@ -973,7 +993,23 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle_both(0, 45, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "\n" + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Filter the edge pairs by orientation of their edges\n" + "\n" + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" ) + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" @@ -994,7 +1030,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + - method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with both edges having the given angle to the x-axis are returned. If \"inverse\" is true, " @@ -1016,7 +1052,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), + method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " "edge pairs with both edges having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " @@ -1025,10 +1061,6 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" - "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " - "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " - "'absolute_angle' set to true, the angles are always positive.\n" - "\n" "Note that the inverse @b result @/b of \\with_angle_both is delivered by \\with_angle with the inverse flag set as edge pairs are unselected when one edge fails to meet the criterion.\n" "I.e\n" "\n" @@ -1037,7 +1069,22 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "others = edge_pairs.with_angle(0, 45, true)\n" "@/code\n" "\n" - "This method has been added in version 0.27.1. 'absolute_angle' has been introduced in version 0.29.1.\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_abs_angle_both", with_abs_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of both of their edges\n" + "\n" + "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("with_abs_angle_both", with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "\n" + "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" ) + method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 7b2fe4ae2..8e983164a 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -438,15 +438,27 @@ static db::Edges with_length2 (const db::Edges *r, const tl::Variant &min, const return r->filtered (f); } -static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse, bool absolute) +static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, inverse, absolute); + db::EdgeOrientationFilter f (a, inverse, false); return r->filtered (f); } -static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax, bool absolute) +static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, absolute); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); + return r->filtered (f); +} + +static db::Edges with_abs_angle1 (const db::Edges *r, double a, bool inverse) +{ + db::EdgeOrientationFilter f (a, inverse, true); + return r->filtered (f); +} + +static db::Edges with_abs_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); return r->filtered (f); } @@ -901,25 +913,19 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + - method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), gsi::arg ("absolute_angle", false), + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" "\n" - "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " - "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " - "'absolute_angle' set to true, the angles are always positive.\n" - "\n" "This will select horizontal edges:\n" "\n" "@code\n" "horizontal = edges.with_angle(0, false)\n" "@/code\n" - "\n" - "'absolute_angle' has been introduced in version 0.29.1." ) + - method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), gsi::arg ("absolute_angle", false), + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have an angle to the x-axis larger or equal to \"min_angle\" (depending on \"include_min_angle\") and equal or less than \"max_angle\" (depending on \"include_max_angle\") are " @@ -929,12 +935,23 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" - "With 'absolute_angle' set to false (the default), the angle against the x axis can be negative or positive. " - "Edges pointing 'down' make negative angles while edges pointing 'up' make positive angles. With " - "'absolute_angle' set to true, the angles are always positive.\n" + "The two \"include..\" arguments have been added in version 0.27.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edges by orientation\n" "\n" - "The two \"include..\" arguments have been added in version 0.27. " - "'absolute_angle' has been introduced in version 0.29.1." + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Filter the edges by orientation\n" + "\n" + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" ) + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filters the edges by orientation type\n" diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index c5aa2a64b..c3525d090 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1007,15 +1007,15 @@ CODE self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer") - f = :with_angle - absolute = self.data.is_a?(RBA::Region) ? nil : false + absolute = false + both = false args = args.select do |a| if a.is_a?(DRCBothEdges) if !self.data.is_a?(RBA::EdgePairs) raise("'both' keyword is only available for edge pair layers") end - f = :with_angle_both + both = true false elsif a.is_a?(DRCAbsoluteMode) if self.data.is_a?(RBA::Region) @@ -1028,33 +1028,27 @@ CODE end end + if both + f = absolute ? :with_abs_angle_both : :with_angle_both + else + f = absolute ? :with_abs_angle : :with_angle + end + result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs if args.size == 1 a = args[0] if a.is_a?(Range) - args = [ a.begin, a.end, #{inv.inspect} ] - if absolute != nil - args += [ true, false, absolute ] - end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) if self.data.is_a?(RBA::Region) raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers") end DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect})) else - args = [ @engine._make_numeric_value(a), #{inv.inspect} ] - if absolute != nil - args += [ absolute ] - end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(a), #{inv.inspect})) end elsif args.size == 2 - args = [ @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect} ] - if absolute != nil - args += [ true, false, absolute ] - end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, *args)) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect})) else raise("Invalid number of range arguments (1 or 2 expected)") end diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index f9b3289be..c5bf4c4fe 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -294,6 +294,7 @@ class DBEdgePairs_TestClass < TestBase ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 10)) r1 = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ]) + assert_equal(r1.with_angle(0, 90, false).to_s, "") # @@@ assert_equal(r1.with_distance(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_distance(5, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") @@ -310,15 +311,25 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r1.with_length_both(10, true).to_s, "(0,0;0,20)/(10,20;10,0)") assert_equal(r1.with_angle(0, false).to_s, "") + assert_equal(r1.with_abs_angle(0, false).to_s, "") assert_equal(r1.with_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle(0, 90, false).to_s, "") + assert_equal(r1.with_abs_angle(0, 90, false).to_s, "") assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(0, false).to_s, "") + assert_equal(r1.with_abs_angle_both(0, false).to_s, "") assert_equal(r1.with_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(0, 90, false).to_s, "") + assert_equal(r1.with_abs_angle_both(0, 90, false).to_s, "") assert_equal(r1.with_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.with_abs_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_area(0, false).to_s, "(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_area(150, false).to_s, "(0,0;0,10)/(10,20;10,0)") diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index dc4ae88bb..e3e9f17f1 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -585,12 +585,19 @@ class DBEdges_TestClass < TestBase r.insert(RBA::Edge::new(0, 0, 100, 0)) r.insert(RBA::Edge::new(100, 0, 100, 50)) assert_equal(r.with_angle(0, false).to_s, "(0,0;100,0)") + assert_equal(r.with_abs_angle(0, false).to_s, "(0,0;100,0)") assert_equal(r.with_angle(0, true).to_s, "(100,0;100,50)") + assert_equal(r.with_abs_angle(0, true).to_s, "(100,0;100,50)") assert_equal(r.with_angle(90, false).to_s, "(100,0;100,50)") + assert_equal(r.with_abs_angle(90, false).to_s, "(100,0;100,50)") assert_equal(r.with_angle(90, true).to_s, "(0,0;100,0)") + assert_equal(r.with_abs_angle(90, true).to_s, "(0,0;100,0)") assert_equal(r.with_angle(-10, 10, false).to_s, "(0,0;100,0)") + assert_equal(r.with_abs_angle(-10, 10, false).to_s, "(0,0;100,0)") assert_equal(r.with_angle(-10, 10, true).to_s, "(100,0;100,50)") + assert_equal(r.with_abs_angle(-10, 10, true).to_s, "(100,0;100,50)") assert_equal(r.with_angle(80, 100, false).to_s, "(100,0;100,50)") + assert_equal(r.with_abs_angle(80, 100, false).to_s, "(100,0;100,50)") assert_equal(r.with_length(100, false).to_s, "(0,0;100,0)") assert_equal(r.with_length(100, true).to_s, "(100,0;100,50)") assert_equal(r.with_length(50, false).to_s, "(100,0;100,50)") From bb17250d94561e600e9cc6b20636af8c14b7e786 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 21 Apr 2024 18:13:54 +0200 Subject: [PATCH 09/41] Implemented a fix for issue #1691 The root cause was that the terminal identity could not be derived in the presence of soft connections inside the device abstract cell. The solution is to join soft connected clusters in the device abstracts with a warning. --- src/db/db/dbHierNetworkProcessor.cc | 6 +- src/db/db/dbHierNetworkProcessor.h | 17 +++ src/db/db/dbNetlistExtractor.cc | 95 +++++++++--- src/db/db/dbNetlistExtractor.h | 2 +- src/lvs/unit_tests/lvsSimpleTests.cc | 2 + testdata/lvs/soft_connect1a.cir | 13 ++ testdata/lvs/soft_connect1a.l2n | 218 +++++++++++++++++++++++++++ testdata/lvs/soft_connect1a.lvs | 92 +++++++++++ 8 files changed, 417 insertions(+), 28 deletions(-) create mode 100644 testdata/lvs/soft_connect1a.cir create mode 100644 testdata/lvs/soft_connect1a.l2n create mode 100644 testdata/lvs/soft_connect1a.lvs diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index bbb463faa..bd7dbd903 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1216,11 +1216,13 @@ struct cluster_building_receiver db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first); for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) { + bool soft = (g->second != 0); + typename std::map::iterator>::iterator icg = m_global_to_clusters.find (g->first); if (icg == m_global_to_clusters.end ()) { - if (g->second != 0) { + if (soft) { // soft connection to a new global cluster m_clusters.push_back (cluster_value ()); @@ -1240,7 +1242,7 @@ struct cluster_building_receiver } else if (ic->second != icg->second) { - if (g->second != 0) { + if (soft) { register_soft_connection (ic->second, icg->second, g->second); diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 08ec80718..ad97c7fad 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -513,6 +513,7 @@ public: typedef db::box_tree, local_cluster_box_convert > tree_type; typedef typename tree_type::touching_iterator touching_iterator; typedef typename tree_type::const_iterator const_iterator; + typedef typename tree_type::iterator iterator; /** * @brief Creates an empty collection @@ -566,6 +567,22 @@ public: return m_clusters.end (); } + /** + * @brief Gets the clusters (begin iterator) + */ + iterator begin () + { + return m_clusters.begin (); + } + + /** + * @brief Gets the clusters (end iterator) + */ + iterator end () + { + return m_clusters.end (); + } + /** * @brief Gets a value indicating whether the cluster set is empty */ diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index dd4ac0c23..cfd253135 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -228,8 +228,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo const db::Cell &cell = mp_layout->cell (*cid); - const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); - if (clusters.empty ()) { + connected_clusters_type &per_cell_clusters = mp_clusters->clusters_per_cell (*cid); + if (per_cell_clusters.empty ()) { bool any_good = false; @@ -251,7 +251,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo if (dm) { // This is a device abstract cell: // make the terminal to cluster ID connections for the device abstract from the device cells - make_device_abstract_connections (dm, clusters); + make_device_abstract_connections (dm, per_cell_clusters); continue; } @@ -284,12 +284,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo } - for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + for (connected_clusters_type::all_iterator c = per_cell_clusters.begin_all (); ! c.at_end (); ++c) { - const db::local_cluster &lc = clusters.cluster_by_id (*c); - const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c); - const std::set &sc_up = clusters.upward_soft_connections (*c); - const std::set &sc_down = clusters.downward_soft_connections (*c); + const db::local_cluster &lc = per_cell_clusters.cluster_by_id (*c); + const connected_clusters_type::connections_type &cc = per_cell_clusters.connections_for_cluster (*c); + const std::set &sc_up = per_cell_clusters.upward_soft_connections (*c); + const std::set &sc_down = per_cell_clusters.downward_soft_connections (*c); if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) { // this is an entirely empty cluster so we skip it. // Such clusters are left over when joining clusters. @@ -301,14 +301,14 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo circuit->add_net (net); // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters - make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); + make_and_connect_subcircuits (circuit, per_cell_clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); // connect devices - connect_devices (circuit, clusters, *c, net); + connect_devices (circuit, per_cell_clusters, *c, net); // collect labels to net names std::set net_names; - collect_labels (clusters, *c, net_names); + collect_labels (per_cell_clusters, *c, net_names); // add the global names as second priority if (net_names.empty ()) { @@ -323,7 +323,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo // made to satisfy the subcircuit's pin, but not to make a physical connection. // Don't know whether this is a good idea, so this code is disabled for now. - if (net_names.empty () && clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) { + if (net_names.empty () && per_cell_clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) { // in the case of a dummy connection (partially connected subcircuits) create a // new name indicating the subcircuit and the subcircuit net name - this makes subcircuit // net names available (the net is pseudo-root inside in the subcircuit) @@ -337,7 +337,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo assign_net_names (net, net_names); - if (! clusters.is_root (*c)) { + if (! per_cell_clusters.is_root (*c)) { // a non-root cluster makes a pin size_t pin_id = make_pin (circuit, net); c2p.insert (std::make_pair (*c, pin_id)); @@ -364,28 +364,73 @@ NetlistExtractor::assign_net_names (db::Net *net, const std::set &n net->set_name (nn); } +static void +collect_soft_connected_clusters (size_t from_id, const NetlistExtractor::connected_clusters_type &clusters, std::set &ids) +{ + if (ids.find (from_id) != ids.end ()) { + return; + } + + ids.insert (from_id); + + auto upward = clusters.upward_soft_connections (from_id); + for (auto i = upward.begin (); i != upward.end (); ++i) { + collect_soft_connected_clusters (*i, clusters, ids); + } + + auto downward = clusters.downward_soft_connections (from_id); + for (auto i = downward.begin (); i != downward.end (); ++i) { + collect_soft_connected_clusters (*i, clusters, ids); + } +} + void -NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters) +NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters) { // make the terminal to cluster ID connections for the device abstract from the device cells if (m_terminal_annot_name_id.first) { - for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { + for (connected_clusters_type::iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { - for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { + std::set ids; + collect_soft_connected_clusters (dc->id (), clusters, ids); + + for (auto id = ids.begin (); id != ids.end (); ++id) { + + const local_cluster_type &lc = clusters.cluster_by_id (*id); + bool join = false; + + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + if (! db::is_prop_id_attr (*a)) { + continue; + } + + db::properties_id_type pi = db::prop_id_from_attr (*a); + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (j->first == m_terminal_annot_name_id.second) { + + size_t terminal_id = j->second.to (); + if (*id != dc->id ()) { + tl::warn << tl::sprintf (tl::to_string (tr ("Ignoring soft connection at device terminal %s for device %s")), dm->device_class ()->terminal_definition (terminal_id)->name (), dm->device_class ()->name ()); + join = true; + } + + dm->set_cluster_id_for_terminal (terminal_id, dc->id ()); + + } + + } - if (! db::is_prop_id_attr (*a)) { - continue; } - db::properties_id_type pi = db::prop_id_from_attr (*a); - - const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (j->first == m_terminal_annot_name_id.second) { - dm->set_cluster_id_for_terminal (j->second.to (), dc->id ()); - } + if (join) { + // copy the terminal attributes and shapes so we attach the terminal here in the device connection step + clusters.join_cluster_with (dc->id (), *id); } } diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 18d7fed01..e06c29475 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -233,7 +233,7 @@ private: /** * @brief Makes the terminal to cluster ID connections of the device abstract */ - void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters); + void make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters); }; diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index ac141d640..1f06fefcf 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -305,6 +305,8 @@ TEST(40_DeviceExtractorErrors) TEST(50_BasicSoftConnection) { run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); + // issue #1691 + run_test (_this, "soft_connect1a", "soft_connect1.gds", true, false /*no LVS*/); } // No errors diff --git a/testdata/lvs/soft_connect1a.cir b/testdata/lvs/soft_connect1a.cir new file mode 100644 index 000000000..61ac75956 --- /dev/null +++ b/testdata/lvs/soft_connect1a.cir @@ -0,0 +1,13 @@ +* Extracted by KLayout + +.SUBCKT TOP A Q VDD SUBSTRATE|VSS +X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV +X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV +.ENDS TOP + +.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE +M$1 \$2 \$4 \$5 \$3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/soft_connect1a.l2n b/testdata/lvs/soft_connect1a.l2n new file mode 100644 index 000000000..c65a7d112 --- /dev/null +++ b/testdata/lvs/soft_connect1a.l2n @@ -0,0 +1,218 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l3 '1/0') +L(l4 '3/0') +L(l15 '3/1') +L(l8 '4/0') +L(l11 '5/0') +L(l12 '6/0') +L(l16 '6/1') +L(l13 '7/0') +L(l14 '8/0') +L(l17 '8/1') +L(l7) +L(l10) +L(l2) +L(l9) +L(l6) +C(l3 l3 l10) +C(l4 l4 l15 l11) +C(l15 l4 l15) +C(l8 l8 l12 l10 l2 l9 l6) +CS(l8 l10 l2 l9 l6) +C(l11 l4 l11 l12) +CS(l11 l4) +C(l12 l8 l11 l12 l16 l13) +C(l16 l12 l16) +C(l13 l12 l13 l14) +C(l14 l13 l14 l17) +C(l17 l14 l17) +C(l7 l7) +C(l10 l3 l8 l10) +CS(l10 l3) +C(l2 l8 l2) +C(l9 l8 l9) +C(l6 l8 l6) +G(l7 SUBSTRATE) +GS(l7 SUBSTRATE) +G(l9 SUBSTRATE) +GS(l9 SUBSTRATE) +H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check')) +H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(0.6,3.95;0.6,4.85;4.5,4.85;4.5,3.95)')) +H(B('\tPartial net #2: TOP - $I4') C(TOP) Q('(5.1,3.95;5.1,4.85;9,4.85;9,3.95)')) +H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check')) +H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(0.6,1.15;0.6,2.05;4.5,2.05;4.5,1.15)')) +H(B('\tPartial net #2: TOP - $I1') C(TOP) Q('(5.1,1.15;5.1,2.05;9,2.05;9,1.15)')) +K(PMOS MOS4) +K(NMOS MOS4) +D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + T(B + R(l3 (-125 -475) (250 950)) + ) +) +D(D$NMOS NMOS + T(S + R(l6 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l6 (125 -475) (775 950)) + ) + T(B + R(l7 (-125 -475) (250 950)) + ) +) +X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l8 (290 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l14 (-2025 -775) (3000 900)) + R(l6 (-1375 -925) (775 950)) + ) + N(2 + R(l8 (290 2490) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l14 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-1500 1800) (3000 2000)) + ) + N(4 + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + N(5 + R(l8 (-510 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (-220 2180) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -3530) (360 2840)) + R(l12 (-360 -2800) (360 760)) + R(l12 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l6 (-775 -3750) (775 950)) + ) + N(6 I(SUBSTRATE)) + P(1) + P(2) + P(3) + P(4) + P(5) + P(6 I(SUBSTRATE)) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 5) + T(G 4) + T(D 2) + T(B 3) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 5) + T(G 4) + T(D 1) + T(B 6) + ) +) +X(TOP + R((600 800) (8880 4600)) + N(1 + R(l4 (2920 2600) (2880 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-300 -300) (690 400)) + ) + N(2 I(A) + R(l4 (6600 2600) (2880 400)) + R(l15 (-2380 -200) (0 0)) + ) + N(3 I(Q) + R(l12 (1810 2600) (690 400)) + R(l16 (-400 -200) (0 0)) + ) + N(4 I(VDD) + R(l3 (4000 3400) (1600 2000)) + R(l3 (-5000 -2000) (1000 2000)) + R(l3 (6400 -2000) (1000 2000)) + R(l8 (-8000 -900) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (7200 200) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-7900 -350) (800 900)) + R(l12 (6600 -900) (800 900)) + R(l13 (-7900 -350) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l13 (7200 200) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-8000 -350) (1000 900)) + R(l14 (6400 -900) (1000 900)) + R(l17 (-4800 -450) (0 0)) + ) + N(5 I('SUBSTRATE,VSS') + R(l8 (1000 1700) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (7200 200) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-7900 -350) (800 900)) + R(l12 (6600 -900) (800 900)) + R(l13 (-7900 -350) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l13 (7200 200) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-8000 -350) (1000 900)) + R(l14 (6400 -900) (1000 900)) + R(l17 (-4800 -550) (0 0)) + ) + P(2 I(A)) + P(3 I(Q)) + P(4 I(VDD)) + P(5 I('SUBSTRATE,VSS')) + X(1 INV Y(3000 1600) + P(0 5) + P(1 4) + P(2 4) + P(3 1) + P(4 3) + P(5 5) + ) + X(2 INV Y(6600 1600) + P(0 5) + P(1 4) + P(2 4) + P(3 2) + P(4 1) + P(5 5) + ) +) diff --git a/testdata/lvs/soft_connect1a.lvs b/testdata/lvs/soft_connect1a.lvs new file mode 100644 index 000000000..4063000dc --- /dev/null +++ b/testdata/lvs/soft_connect1a.lvs @@ -0,0 +1,92 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_l2n + report_netlist($lvs_test_target_l2n) +else + report_netlist +end + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +nplus = input(2, 1) +pplus = input(2, 2) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell & pplus +ntie = active_in_nwell & nplus +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell & nplus +ptie = active_outside_nwell & pplus +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer + +soft_connect(diff_cont, psd) +soft_connect(diff_cont, nsd) +soft_connect(diff_cont, ptie) +soft_connect(diff_cont, ntie) +soft_connect(ntie, nwell) +soft_connect(poly_cont, poly) + +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +soft_connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +netlist + +netlist.simplify + + From 0a88349277e42b64280e7ba84324abca7326a89e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 25 Apr 2024 23:41:40 +0200 Subject: [PATCH 10/41] WIP --- src/rdb/rdb/rdb.cc | 194 +++++++++++++++++++++++++++++++++++++++++++++ src/rdb/rdb/rdb.h | 23 +++++- 2 files changed, 216 insertions(+), 1 deletion(-) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 0a532328f..2d77e5c46 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -403,6 +403,55 @@ Values::operator= (const Values &d) return *this; } +bool +Values::compare (const Values &other, const std::map &tag_map, const std::map &rev_tag_map) const +{ + Values::const_iterator a = begin (), b = other.begin (); + while (a != end () && b != other.end ()) { + + id_type t12 = 0; + while (a != end () && a->tag_id () != 0) { + auto j = tag_map.find (a->tag_id ()); + if (j != tag_map.end ()) { + t12 = j->second; + break; + } + ++a; + } + + id_type t2 = 0; + while (b != end () && b->tag_id () != 0) { + auto j = rev_tag_map.find (b->tag_id ()); + if (j != rev_tag_map.end ()) { + t2 = j->first; + break; + } + ++b; + } + + if (a == end () || b == other.end ()) { + return b != other.end (); + } + + if (t12 != t2) { + return t12 < t2; + } + + if (a->get () && b->get ()) { + if (a->get ()->compare (b->get ())) { + return true; + } else if (b->get ()->compare (a->get ())) { + return false; + } + } else if ((a->get () != 0) != (b->get () != 0)) { + return (a->get () != 0) < (b->get () != 0); + } + + } + + return false; +} + std::string Values::to_string (const Database *rdb) const { @@ -1659,6 +1708,151 @@ Database::load (const std::string &fn) } } +namespace +{ + class ValueMapEntryCompare + { + public: + ValueMapEntryCompare (const std::map &tag2tag, const std::map &rev_tag2tag) + { + mp_tag2tag = &tag2tag; + mp_rev_tag2tag = &rev_tag2tag; + } + + bool operator() (const Item *a, const Item *b) const + { + return a->values ().compare (b->values (), *mp_tag2tag, *mp_rev_tag2tag); + } + + private: + const std::map *mp_tag2tag; + const std::map *mp_rev_tag2tag; + }; + + class ValueMapEntry + { + public: + ValueMapEntry () + : mp_tag2tag (0), mp_rev_tag2tag (0) + { } + + void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::map &tag2tag, const std::map &rev_tag2tag) + { + mp_tag2tag = &tag2tag; + mp_rev_tag2tag = &rev_tag2tag; + + auto i2i = rdb.items_by_cell_and_category (cell_id, cat_id); + + size_t n = 0; + for (auto i = i2i.first; i != i2i.second; ++i) { + ++n; + } + m_items.reserve (n); + + for (auto i = i2i.first; i != i2i.second; ++i) { + m_items.push_back ((*i).operator-> ()); + } + + ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag); + std::sort (m_items.begin (), m_items.end (), cmp); + } + + const Item *find (const rdb::Item &item) const + { + ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag); + + auto i = std::lower_bound (m_items.begin (), m_items.end (), &item, cmp); + if (i == m_items.end ()) { + return 0; + } + + if (cmp (&item, *i) || cmp (*i, &item)) { + return 0; + } else { + return *i; + } + } + + public: + std::vector m_items; + const std::map *mp_tag2tag; + const std::map *mp_rev_tag2tag; + }; +} + +void +Database::apply (const rdb::Database &other) +{ + std::map cell2cell; + std::map cat2cat; + std::map tag2tag; + std::map rev_tag2tag; + + for (auto c = other.cells ().begin (); c != other.cells ().end (); ++c) { + // TODO: do we have a consistent scheme of naming variants? What requirements + // exist towards detecting variant specific waivers + const rdb::Cell *this_cell = cell_by_qname (c->qname ()); + if (this_cell) { + cell2cell.insert (std::make_pair (this_cell->id (), c->id ())); + } + } + + for (auto c = other.categories ().begin (); c != other.categories ().end (); ++c) { + const rdb::Category *this_cat = category_by_name (c->name ()); + if (this_cat) { + cat2cat.insert (std::make_pair (this_cat->id (), c->id ())); + } + } + + std::map tags_by_name; + for (auto c = tags ().begin_tags (); c != tags ().end_tags (); ++c) { + tags_by_name.insert (std::make_pair (c->name (), c->id ())); + } + + for (auto c = other.tags ().begin_tags (); c != other.tags ().end_tags (); ++c) { + auto t = tags_by_name.find (c->name ()); + if (t != tags_by_name.end ()) { + tag2tag.insert (std::make_pair (t->second, c->id ())); + rev_tag2tag.insert (std::make_pair (c->id (), t->second)); + } + } + + std::map, ValueMapEntry> value_map; + + for (Items::iterator i = items_non_const ().begin (); i != items_non_const ().end (); ++i) { + + auto icell = cell2cell.find (i->cell_id ()); + if (icell == cell2cell.end ()) { + continue; + } + + auto icat = cat2cat.find (i->category_id ()); + if (icat == cat2cat.end ()) { + continue; + } + + // build a cache of value vs. value + auto vmap = value_map.find (std::make_pair (icell->second, icat->second)); + if (vmap == value_map.end ()) { + vmap = value_map.insert (std::make_pair (std::make_pair (icell->second, icat->second), ValueMapEntry ())).first; + vmap->second.build (other, icell->second, icat->second, tag2tag, rev_tag2tag); + } + + // find a value in the reference DB + const rdb::Item *other = vmap->second.find (*i); + if (other) { + + // actually transfer the attributes here + + // TODO: this has some optimization potential in terms of performance ... + i->set_image_str (other->image_str ()); + i->set_tag_str (other->tag_str ()); + + } + + } +} + void Database::scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) { diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index b15558a96..936ab28e6 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -679,6 +679,19 @@ public: */ Values &operator= (const Values &d); + /** + * @brief Compare two value sets (less operator) + * + * This compare function will use the tag mapping provided by tag map ("this" tag id to "other" tag id). + * Values with tags not listed in the tag map will not be compared. + * Untagged values (tag_id 0) will be compared always. + * + * "rev_tag_map" needs to be the reverse of "tag_map". + * + * The order of the values matters. + */ + bool compare (const Values &other, const std::map &tag_map, const std::map &rev_tag_map) const; + /** * @brief The const iterator (begin) */ @@ -2381,10 +2394,18 @@ public: /** * @brief Load the database from a file * - * @brief This clears the existing database. + * Note: This clears the existing database. */ void load (const std::string &filename); + /** + * @brief Applies the attributes from a different database + * + * Attributes are waived flags, images etc. + * The attributes are applied to markers with identical value(s), category and cell context. + */ + void apply (const rdb::Database &other); + /** * @brief Scans a layout into this RDB * From 9b1fd345738d67378259b27f571cb50433a3ebc7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Apr 2024 22:12:21 +0200 Subject: [PATCH 11/41] WIP: debugging and tests --- src/rdb/rdb/rdb.cc | 42 +++++-- src/rdb/rdb/rdb.h | 8 ++ src/rdb/unit_tests/rdbTests.cc | 223 +++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 11 deletions(-) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 2d77e5c46..807d0c668 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -60,16 +60,18 @@ namespace rdb // type index specializations template <> RDB_PUBLIC int type_index_of () { return 0; } -template <> RDB_PUBLIC int type_index_of () { return 1; } -template <> RDB_PUBLIC int type_index_of () { return 2; } -template <> RDB_PUBLIC int type_index_of () { return 3; } -template <> RDB_PUBLIC int type_index_of () { return 4; } -template <> RDB_PUBLIC int type_index_of () { return 5; } -template <> RDB_PUBLIC int type_index_of () { return 6; } -template <> RDB_PUBLIC int type_index_of () { return 7; } +template <> RDB_PUBLIC int type_index_of () { return 1; } +template <> RDB_PUBLIC int type_index_of () { return 2; } +template <> RDB_PUBLIC int type_index_of () { return 3; } +template <> RDB_PUBLIC int type_index_of () { return 4; } +template <> RDB_PUBLIC int type_index_of () { return 5; } +template <> RDB_PUBLIC int type_index_of () { return 6; } +template <> RDB_PUBLIC int type_index_of () { return 7; } +template <> RDB_PUBLIC int type_index_of () { return 8; } // Explicit instantiations to make VC++ happy in debug mode template class RDB_PUBLIC Value; +template class RDB_PUBLIC Value; template class RDB_PUBLIC Value; template class RDB_PUBLIC Value; template class RDB_PUBLIC Value; @@ -85,6 +87,11 @@ template <> RDB_PUBLIC std::string Value::to_string () const return "float: " + tl::to_string (m_value); } +template <> RDB_PUBLIC std::string Value::to_string () const +{ + return "int: " + tl::to_string (m_value); +} + template <> RDB_PUBLIC std::string Value::to_string () const { return "text: " + tl::to_word_or_quoted_string (m_value); @@ -127,6 +134,11 @@ template <> RDB_PUBLIC std::string Value::to_display_string () const return tl::to_string (m_value); } +template <> RDB_PUBLIC std::string Value::to_display_string () const +{ + return tl::to_string (m_value); +} + template <> RDB_PUBLIC std::string Value::to_display_string () const { return m_value; @@ -169,6 +181,11 @@ template <> RDB_PUBLIC bool Value::is_shape () const return false; } +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return false; +} + template <> RDB_PUBLIC bool Value::is_shape () const { return false; @@ -420,7 +437,7 @@ Values::compare (const Values &other, const std::map &tag_map, } id_type t2 = 0; - while (b != end () && b->tag_id () != 0) { + while (b != other.end () && b->tag_id () != 0) { auto j = rev_tag_map.find (b->tag_id ()); if (j != rev_tag_map.end ()) { t2 = j->first; @@ -438,18 +455,21 @@ Values::compare (const Values &other, const std::map &tag_map, } if (a->get () && b->get ()) { - if (a->get ()->compare (b->get ())) { + if (rdb::ValueBase::compare (a->get (), b->get ())) { return true; - } else if (b->get ()->compare (a->get ())) { + } else if (rdb::ValueBase::compare (b->get (), a->get ())) { return false; } } else if ((a->get () != 0) != (b->get () != 0)) { return (a->get () != 0) < (b->get () != 0); } + ++a; + ++b; + } - return false; + return b != other.end (); } std::string diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 936ab28e6..3541e6768 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -752,6 +752,14 @@ public: m_values.swap (other.m_values); } + /** + * @brief Clears the values + */ + void clear () + { + m_values.clear (); + } + /** * @brief Convert the values collection to a string */ diff --git a/src/rdb/unit_tests/rdbTests.cc b/src/rdb/unit_tests/rdbTests.cc index 55e78c1f9..5665aa522 100644 --- a/src/rdb/unit_tests/rdbTests.cc +++ b/src/rdb/unit_tests/rdbTests.cc @@ -598,3 +598,226 @@ TEST(7) #endif } + + +TEST(8_ApplyBasicEmptyValue) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("cat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("cell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + i2->add_tag (tag2); + + EXPECT_EQ (i2->tag_str (), "tag2"); + EXPECT_EQ (i1->tag_str (), ""); + + // empty value apply + db1.apply (db2); + + EXPECT_EQ (i1->tag_str (), "tag2"); +} + +TEST(9_ApplyBasicSomeValue) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + i1->add_value (std::string ("abc")); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("cat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("cell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + + db2.tags ().tag ("dummy_tag"); + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + i2->add_tag (tag2); + + EXPECT_EQ (i2->tag_str (), "tag2"); + EXPECT_EQ (i1->tag_str (), ""); + + // empty value apply + db1.apply (db2); + + // not applied (different value) + EXPECT_EQ (i1->tag_str (), ""); + + // incorrect value + i2->add_value (17); + + db1.apply (db2); + + // still not applied + EXPECT_EQ (i1->tag_str (), ""); + + // correct value + i2->values ().clear (); + i2->add_value (std::string ("abc")); + + db1.apply (db2); + + // now, the tag is applied + EXPECT_EQ (i1->tag_str (), "tag2"); + + // too many values + i1->remove_tags (); + i2->add_value (17); + + db1.apply (db2); + + // not applied + EXPECT_EQ (i1->tag_str (), ""); +} + +TEST(10_ApplyTaggedValue) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + rdb::id_type vtag11 = db1.tags ().tag ("vtag1").id (); + rdb::id_type vtag12 = db1.tags ().tag ("vtag2").id (); + i1->add_value (std::string ("abc")); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("cat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("cell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + db2.tags ().tag ("dummy_tag"); + + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + rdb::id_type vtag21 = db2.tags ().tag ("vtag1").id (); + i2->add_tag (tag2); + i2->add_value (std::string ("abc"), vtag21); + + // empty tag vs. vtag1 + db1.apply (db2); + + // not applied (empty tag vs. tagged) + EXPECT_EQ (i1->tag_str (), ""); + + // vtag2 vs. vtag1 + i1->values ().clear (); + i1->add_value (std::string ("abc"), vtag12); + + db1.apply (db2); + + // not applied (different tags) + EXPECT_EQ (i1->tag_str (), ""); + + // vtag1 vs. vtag1 + i1->values ().clear (); + i1->add_value (std::string ("abc"), vtag11); + + db1.apply (db2); + + // this time it is applied (same tag) + EXPECT_EQ (i1->tag_str (), "tag2"); +} + +TEST(11_ApplyWrongCat) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("xcat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("cell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + i2->add_tag (tag2); + + EXPECT_EQ (i2->tag_str (), "tag2"); + EXPECT_EQ (i1->tag_str (), ""); + + // empty value apply + db1.apply (db2); + + EXPECT_EQ (i1->tag_str (), ""); +} + +TEST(12_ApplyWrongCell) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("cat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("xcell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + i2->add_tag (tag2); + + EXPECT_EQ (i2->tag_str (), "tag2"); + EXPECT_EQ (i1->tag_str (), ""); + + // empty value apply + db1.apply (db2); + + EXPECT_EQ (i1->tag_str (), ""); +} + +TEST(13_ApplyIgnoreUnknownTag) +{ + rdb::Database db1; + rdb::Category *cat1 = db1.create_category ("cat_name"); + rdb::Cell *c1 = db1.create_cell ("cell"); + rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ()); + rdb::id_type vtag11 = db1.tags ().tag ("vtag1").id (); + i1->add_value (std::string ("abc"), vtag11); + + rdb::Database db2; + db2.create_category ("dummy_cat"); + rdb::Category *cat2 = db2.create_category ("cat_name"); + db2.create_cell ("dummy_cell"); + rdb::Cell *c2 = db2.create_cell ("cell"); + rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ()); + db2.tags ().tag ("dummy_tag"); + + rdb::id_type tag2 = db2.tags ().tag ("tag2").id (); + rdb::id_type vtag21 = db2.tags ().tag ("vtag1").id (); + rdb::id_type vtag22 = db2.tags ().tag ("vtag2").id (); + i2->add_tag (tag2); + + // same tags, different values + i2->add_value (std::string ("xyz"), vtag21); + + db1.apply (db2); + + // not applied + EXPECT_EQ (i1->tag_str (), ""); + + // different tags without mapping + i2->values ().clear (); + i2->add_value (std::string ("xyz"), vtag22); + + // values with incompatible tags are ignored -> tag2 is applied + db1.apply (db2); + + EXPECT_EQ (i1->tag_str (), "tag2"); +} + From 1939fd2909fbfadca2a6e618188585a8d035db90 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Apr 2024 22:38:10 +0200 Subject: [PATCH 12/41] GSI binding of RdbDatabase#apply --- src/rdb/rdb/gsiDeclRdb.cc | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 2b467f2ed..e47297b92 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -911,6 +911,10 @@ Class decl_RdbItem ("rdb", "RdbItem", "@brief Remove the tag with the given id from the item\n" "If a tag with that ID does not exists on this item, this method does nothing." ) + + gsi::method ("remove_tags", &rdb::Item::remove_tags, + "@brief Removes all tags from the item\n" + "This method has been introduced in version 0.29.1." + ) + gsi::method ("has_tag?", &rdb::Item::has_tag, gsi::arg ("tag_id"), "@brief Returns a value indicating whether the item has a tag with the given ID\n" "@return True, if the item has a tag with the given ID\n" @@ -1562,6 +1566,36 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param trans The transformation to apply\n" "@param edge_pairs The list of edge_pairs for which the items are created\n" ) + + gsi::method ("apply", &rdb::Database::apply, gsi::arg ("other"), + "@brief Transfers item attributes from one database to another for identical items\n" + "This method will identify items that are identical between the two databases and transfer " + "item attributes from the 'other' database to this database. Transferable attributes are:\n" + "\n" + "@ul\n" + "@li Images @/li\n" + "@li Item tags @/li\n" + "@/ul\n" + "\n" + "Existing attributes in this database are overwritten.\n" + "\n" + "Items are identical if\n" + "\n" + "@ul\n" + "@li They belong to the same cell (by qname) @/li\n" + "@li They belong to the same category (by name) @/li\n" + "@li Their values are identical @/li\n" + "@/ul\n" + "\n" + "Values are identical if their individual values and (optional) value tags are identical. " + "Values tagged with a tag unknown to the other database are ignored. " + "The order of values matters during the compare. So the value pair (17.0, 'abc') is different from ('abc', 17.0).\n" + "\n" + "The intended application for this method is use for error waiving: as the waived attribute is a transferable " + "attribute, it is possible to apply the waived flag from from a waiver database (the 'other' database) using this " + "method.\n" + "\n" + "This method has been added in version 0.29.1." + ) + gsi::method ("is_modified?", &rdb::Database::is_modified, "@brief Returns a value indicating whether the database has been modified\n" ) + From 369f85561e007bc6455990b041b810c7ee19ba04 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Apr 2024 22:47:36 +0200 Subject: [PATCH 13/41] [consider merging] escape category descriptions for marker browser info box --- src/layui/layui/rdbMarkerBrowserPage.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 38877fef2..c39c6dac4 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -1976,11 +1976,11 @@ MarkerBrowserPage::update_info_text () info += "

    "; if (category && n_category == 1) { - info += category->name (); + tl::escape_to_html (info, category->name ()); } if (cell && n_cell == 1 && ! cell->name ().empty ()) { - info += " [" + cell->name () + "]"; + tl::escape_to_html (info, std::string (" [") + cell->name () + "]"); } info += "

    "; From aba5362e8200cbeedf0b4d1cf187c42a12bdc057 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 00:55:50 +0200 Subject: [PATCH 14/41] Displaying waived marker count --- src/layui/layui/rdbMarkerBrowserPage.cc | 204 ++++++++++++++++++++---- src/layui/layui/rdbMarkerBrowserPage.h | 1 + 2 files changed, 173 insertions(+), 32 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index c39c6dac4..52827e234 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -103,13 +103,13 @@ class MarkerBrowserTreeViewModelCacheEntry { public: MarkerBrowserTreeViewModelCacheEntry () - : mp_parent (0), m_id (0), m_row (0), m_count (0) + : mp_parent (0), m_id (0), m_row (0), m_count (0), m_waived_count (0) { // .. nothing yet .. } MarkerBrowserTreeViewModelCacheEntry (rdb::id_type id, unsigned int branch) - : mp_parent (0), m_id ((id << 3) + (branch << 1)), m_row (0), m_count (0) + : mp_parent (0), m_id ((id << 3) + (branch << 1)), m_row (0), m_count (0), m_waived_count (0) { // .. nothing yet .. } @@ -233,6 +233,25 @@ public: m_count = c; } + size_t waived_count () const + { + return m_waived_count; + } + + void set_waived_count (size_t c) + { + m_waived_count = c; + } + + void waive_or_unwaive (bool w) + { + if (w) { + ++m_waived_count; + } else { + --m_waived_count; + } + } + void sort_by_key_name (bool ascending, const rdb::Database *database) { std::sort (m_ids.begin (), m_ids.end (), SortByKeyCompareFunc (ascending, database)); @@ -257,7 +276,7 @@ private: MarkerBrowserTreeViewModelCacheEntry *mp_parent; rdb::id_type m_id; unsigned int m_row; - size_t m_count; + size_t m_count, m_waived_count; std::vector m_ids; }; @@ -338,14 +357,15 @@ public: }; MarkerBrowserTreeViewModel () - : mp_database (0), m_show_empty_ones (true) + : mp_database (0), m_show_empty_ones (true), m_waived_tag_id (0) { - // .. nothing yet .. + // .. nothing yet .. } void set_database (const rdb::Database *db) { mp_database = db; + m_waived_tag_id = mp_database ? mp_database->tags ().tag ("waived").id () : 0; invalidate (); } @@ -384,6 +404,23 @@ public: } } + void waived_changed (const rdb::Item *item, bool waived) + { + const rdb::Category *cat = mp_database->category_by_id (item->category_id ()); + while (cat) { + waive_or_unwaive (0, cat->id (), waived); + if (item->cell_id () != 0) { + waive_or_unwaive (item->cell_id (), cat->id (), waived); + } + cat = cat->parent (); + } + + waive_or_unwaive (0, 0, waived); + if (item->cell_id () != 0) { + waive_or_unwaive (item->cell_id (), 0, waived); + } + } + int columnCount (const QModelIndex & /*parent*/) const { return 2; @@ -496,8 +533,12 @@ public: if (node->count () > 0) { size_t visited = node->visited_count (mp_database); + size_t waived = node->waived_count (); +// @@@ +return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (tr ("%lu (%lu) - %lu")), node->count (), node->count () - visited, waived))); +// @@@ if (visited < node->count ()) { - return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("%lu (%lu)")), node->count (), node->count () - visited))); + return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (tr ("%lu (%lu)")), node->count (), node->count () - visited))); } else { return QVariant ((unsigned int) node->count ()); } @@ -725,22 +766,85 @@ public: private: const rdb::Database *mp_database; mutable MarkerBrowserTreeViewModelCacheEntry m_cache; + mutable std::multimap, MarkerBrowserTreeViewModelCacheEntry *> m_cache_by_ids; bool m_show_empty_ones; + id_type m_waived_tag_id; + + void waive_or_unwaive (rdb::id_type cell_id, rdb::id_type cat_id, bool waived) + { + auto k = std::make_pair (cell_id, cat_id); + auto c = m_cache_by_ids.find (k); + while (c != m_cache_by_ids.end () && c->first == k) { + c->second->waive_or_unwaive (waived); + ++c; + } + } + + size_t num_waived () const + { + size_t n = 0; + for (auto i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { + if (i->has_tag (m_waived_tag_id)) { + ++n; + } + } + return n; + } + + size_t num_waived_per_cat (id_type cat_id) const + { + auto ii = mp_database->items_by_category (cat_id); + size_t n = 0; + for (auto i = ii.first; i != ii.second; ++i) { + if ((*i)->has_tag (m_waived_tag_id)) { + ++n; + } + } + return n; + } + + size_t num_waived_per_cell_and_cat (id_type cell_id, id_type cat_id) const + { + auto ii = mp_database->items_by_cell_and_category (cell_id, cat_id); + size_t n = 0; + for (auto i = ii.first; i != ii.second; ++i) { + if ((*i)->has_tag (m_waived_tag_id)) { + ++n; + } + } + return n; + } + + size_t num_waived_per_cell (id_type cell_id) const + { + auto ii = mp_database->items_by_cell (cell_id); + size_t n = 0; + for (auto i = ii.first; i != ii.second; ++i) { + if ((*i)->has_tag (m_waived_tag_id)) { + ++n; + } + } + return n; + } void invalidate () { beginResetModel (); m_cache.clear (); + m_cache_by_ids.clear (); MarkerBrowserTreeViewModelCacheEntry *by_cell_node = new MarkerBrowserTreeViewModelCacheEntry(0, 0); m_cache.add_child (by_cell_node); + m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), by_cell_node)); MarkerBrowserTreeViewModelCacheEntry *by_category_node = new MarkerBrowserTreeViewModelCacheEntry(0, 1); m_cache.add_child (by_category_node); + m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), by_category_node)); MarkerBrowserTreeViewModelCacheEntry *all_node = new MarkerBrowserTreeViewModelCacheEntry(0, 2); m_cache.add_child (all_node); + m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), all_node)); m_cache.set_cache_valid (true); @@ -760,18 +864,22 @@ private: { const rdb::Category *category = mp_database->category_by_id (node->id ()); if (category) { + for (rdb::Categories::const_iterator c = category->sub_categories ().begin (); c != category->sub_categories ().end (); ++c) { node->set_cache_valid (true); MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), node->branch ()); + m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), c->id ()), child)); node->add_child (child); child->set_count (mp_database->category_by_id (c->id ())->num_items ()); + child->set_waived_count (num_waived_per_cat (c->id ())); add_sub_categories (child); } + } } @@ -781,19 +889,24 @@ private: const rdb::Category *category = mp_database->category_by_id (node->id ()); if (category) { + for (rdb::Categories::const_iterator c = category->sub_categories ().begin (); c != category->sub_categories ().end (); ++c) { + if (partial_tree.find (c->id ()) != partial_tree.end ()) { MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), node->branch ()); + m_cache_by_ids.insert (std::make_pair (std::make_pair (cell_id, c->id ()), child)); node->add_child (child); - size_t n = mp_database->num_items (cell_id, c->id ()); - child->set_count (n); + child->set_count (mp_database->num_items (cell_id, c->id ())); + child->set_waived_count (num_waived_per_cell_and_cat (cell_id, c->id ())); add_sub_categories (cell_id, child, partial_tree); } + } + } } @@ -813,27 +926,44 @@ private: if (branch == 0) { for (rdb::Database::const_cell_iterator c = mp_database->cells ().begin (); c != mp_database->cells ().end (); ++c) { + if (mp_database->cell_by_id (c->id ()) && (m_show_empty_ones || mp_database->cell_by_id (c->id ())->num_items () != 0)) { + MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch); + m_cache_by_ids.insert (std::make_pair (std::make_pair (c->id (), rdb::id_type (0)), child)); + child->set_count (mp_database->cell_by_id (c->id ())->num_items ()); + child->set_waived_count (num_waived_per_cell (c->id ())); + node->add_child (child); + } + } } else if (branch == 1) { for (rdb::Categories::const_iterator c = mp_database->categories ().begin (); c != mp_database->categories ().end (); ++c) { + if (mp_database->category_by_id (c->id ()) && (m_show_empty_ones || mp_database->category_by_id (c->id ())->num_items () != 0)) { + MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch); + m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), c->id ()), child)); + child->set_count (mp_database->category_by_id (c->id ())->num_items ()); + child->set_waived_count (num_waived_per_cat (c->id ())); + node->add_child (child); add_sub_categories (child); + } + } } node->set_count (mp_database->num_items ()); + node->set_waived_count (num_waived ()); } else if (branch == 0) { @@ -869,9 +999,14 @@ private: MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch); size_t n = mp_database->num_items (id, c->id ()); + if (m_show_empty_ones || n != 0) { + m_cache_by_ids.insert (std::make_pair (std::make_pair (id, c->id ()), child)); + child->set_count (n); + child->set_waived_count (num_waived_per_cell_and_cat (id, c->id ())); + node->add_child (child); add_sub_categories (id, child, category_ids); @@ -903,8 +1038,14 @@ private: size_t n = mp_database->num_items (*c, id); if (m_show_empty_ones || n != 0) { + + m_cache_by_ids.insert (std::make_pair (std::make_pair (*c, id), child)); + child->set_count (n); + child->set_waived_count (num_waived_per_cell_and_cat (*c, id)); + node->add_child (child); + } else { delete child; } @@ -3099,32 +3240,17 @@ MarkerBrowserPage::mark_visited (bool f) void MarkerBrowserPage::waive () { - if (! mp_database) { - return; - } - - MarkerBrowserListViewModel *list_model = dynamic_cast (markers_list->model ()); - if (! list_model) { - return; - } - - id_type waived_tag_id = mp_database->tags ().tag ("waived").id (); - - QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { - if (selected_item->column () == 0) { - const rdb::Item *i = list_model->item (selected_item->row ()); - if (i) { - mp_database->add_item_tag (i, waived_tag_id); - } - } - } - - list_model->mark_data_changed (); + waive_or_unwaive (true); } -void +void MarkerBrowserPage::unwaive () +{ + waive_or_unwaive (false); +} + +void +MarkerBrowserPage::waive_or_unwaive (bool w) { if (! mp_database) { return; @@ -3135,6 +3261,11 @@ MarkerBrowserPage::unwaive () return; } + MarkerBrowserTreeViewModel *tree_model = dynamic_cast (directory_tree->model ()); + if (! tree_model) { + return; + } + id_type waived_tag_id = mp_database->tags ().tag ("waived").id (); QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); @@ -3142,12 +3273,21 @@ MarkerBrowserPage::unwaive () if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { - mp_database->remove_item_tag (i, waived_tag_id); + bool was_waived = i->has_tag (waived_tag_id); + if (w != was_waived) { + if (w) { + mp_database->add_item_tag (i, waived_tag_id); + } else { + mp_database->remove_item_tag (i, waived_tag_id); + } + tree_model->waived_changed (i, w); + } } } } list_model->mark_data_changed (); + tree_model->mark_data_changed (); } void diff --git a/src/layui/layui/rdbMarkerBrowserPage.h b/src/layui/layui/rdbMarkerBrowserPage.h index 0c50bdde4..58cd27ecc 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.h +++ b/src/layui/layui/rdbMarkerBrowserPage.h @@ -238,6 +238,7 @@ private: void do_update_markers (); void update_info_text (); void rerun_macro (); + void waive_or_unwaive (bool w); }; } // namespace rdb From aadb745fb9c4db74d30d95500b865b4dd97a59a2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 01:14:14 +0200 Subject: [PATCH 15/41] Enhanced display of waived count --- src/layui/layui/rdbMarkerBrowserPage.cc | 37 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 52827e234..e7cd38241 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -473,13 +473,14 @@ public: return true; } - bool no_errors (const QModelIndex &index) const + bool no_errors (const QModelIndex &index, bool include_waived = false) const { MarkerBrowserTreeViewModelCacheEntry *node = (MarkerBrowserTreeViewModelCacheEntry *)(index.internalPointer ()); if (node && mp_database) { rdb::id_type id = node->id (); bool none = false; + size_t thr = include_waived ? node->waived_count () : 0; const rdb::Cell *cell = mp_database->cell_by_id (id); const rdb::Category *category = mp_database->category_by_id (id); @@ -501,13 +502,13 @@ public: } if (cell == 0 && category == 0) { - none = (mp_database->num_items () == 0); + none = (mp_database->num_items () <= thr); } else if (category == 0) { - none = (cell->num_items () == 0); + none = (cell->num_items () <= thr); } else if (cell == 0) { - none = (category->num_items () == 0); + none = (category->num_items () <= thr); } else { - none = (mp_database->num_items (cell->id (), category->id ()) == 0); + none = (mp_database->num_items (cell->id (), category->id ()) <= thr); } return none; @@ -531,21 +532,31 @@ public: if (index.column () == 1) { + std::string s; + if (node->count () > 0) { + size_t visited = node->visited_count (mp_database); size_t waived = node->waived_count (); -// @@@ -return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (tr ("%lu (%lu) - %lu")), node->count (), node->count () - visited, waived))); -// @@@ + if (visited < node->count ()) { - return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (tr ("%lu (%lu)")), node->count (), node->count () - visited))); + s = tl::sprintf (tl::to_string (tr ("%lu (%lu)")), node->count (), node->count () - visited); } else { - return QVariant ((unsigned int) node->count ()); + s = tl::sprintf (tl::to_string (tr ("%lu")), node->count ()); } - } else { - return QVariant (QString::fromUtf8 ("")); + + if (waived > 0) { + if (waived == node->count ()) { + s += tl::to_string (tr (" - all waived")); + } else { + s += tl::sprintf (tl::to_string (tr (" - %lu")), waived); + } + } + } + return QVariant (tl::to_qstring (s)); + } else if (index.column () == 0) { rdb::id_type id = node->id (); @@ -608,7 +619,7 @@ return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (tr ("%lu (%lu) - %l } // Green color if no errors are present - if (no_errors (index)) { + if (no_errors (index, true)) { return QVariant (QColor (0, 192, 0)); } From 2ff06ede071c4e3e6101f7880f70630b9445882c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 17:57:06 +0200 Subject: [PATCH 16/41] UI enhancments - marker list is sorted by waived flag by default showing waived items at the end - "unwaive_all" works now - waiving items now makes them visited --- src/layui/layui/rdbMarkerBrowserPage.cc | 38 +++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index e7cd38241..7b10974b1 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -432,7 +432,7 @@ public: if (section == 0) { return QVariant (QObject::tr ("Cell / Category")); } else if (section == 1) { - return QVariant (QObject::tr ("Count (Not Visited)")); + return QVariant (QObject::tr ("Count (Not Visited) - Waived")); } } @@ -1148,6 +1148,16 @@ public: m_sorting_order = sorting_order; } + int sorting () const + { + return m_sorting; + } + + bool sorting_order () const + { + return m_sorting_order; + } + template bool set_items (const std::vector > &be_vector, size_t max_marker_count) { @@ -1897,6 +1907,9 @@ MarkerBrowserPage::set_rdb (rdb::Database *database) QAbstractItemModel *list_model = markers_list->model (); MarkerBrowserListViewModel *new_list_model = new MarkerBrowserListViewModel (); + // default sorting is by waived flag + new_list_model->set_sorting (2, true); + markers_list->header ()->setSortIndicator (new_list_model->sorting (), new_list_model->sorting_order () ? Qt::AscendingOrder : Qt::DescendingOrder); new_list_model->set_database (database); markers_list->setModel (new_list_model); connect (markers_list->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (markers_selection_changed (const QItemSelection &, const QItemSelection &))); @@ -3055,7 +3068,12 @@ MarkerBrowserPage::unwaive_all () return; } - QMessageBox msgbox (QMessageBox::Question, + MarkerBrowserTreeViewModel *tree_model = dynamic_cast (directory_tree->model ()); + if (! tree_model) { + return; + } + + QMessageBox msgbox (QMessageBox::Question, QObject::tr ("Remove All Waived"), QObject::tr ("Are you sure to remove the waived flags from all markers?"), QMessageBox::Yes | QMessageBox::No); @@ -3064,7 +3082,10 @@ MarkerBrowserPage::unwaive_all () id_type waived_tag_id = mp_database->tags ().tag ("waived").id (); for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { - mp_database->remove_item_tag (&*i, waived_tag_id); + if (i->has_tag (waived_tag_id)) { + mp_database->remove_item_tag (i.operator-> (), waived_tag_id); + tree_model->waived_changed (i.operator-> (), false); + } } list_model->mark_data_changed (); @@ -3085,7 +3106,7 @@ MarkerBrowserPage::revisit_all () } for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { - mp_database->set_item_visited (&*i, false); + mp_database->set_item_visited (i.operator-> (), false); } list_model->mark_data_changed (); @@ -3112,7 +3133,7 @@ MarkerBrowserPage::revisit_non_waived () for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { if (! i->has_tag (waived_tag_id)) { - mp_database->set_item_visited (&*i, false); + mp_database->set_item_visited (i.operator-> (), false); } } @@ -3140,7 +3161,7 @@ MarkerBrowserPage::revisit_important () for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { if (i->has_tag (important_tag_id)) { - mp_database->set_item_visited (&*i, false); + mp_database->set_item_visited (i.operator-> (), false); } } @@ -3291,6 +3312,11 @@ MarkerBrowserPage::waive_or_unwaive (bool w) } else { mp_database->remove_item_tag (i, waived_tag_id); } + if (w) { + // waiving an item makes it visited (rationale: once waived, an item is no + // longer of interest) + mp_database->set_item_visited (i, true); + } tree_model->waived_changed (i, w); } } From 9ef51ca9815bb09ff0887a1c8311a399028a81db Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 19:56:39 +0200 Subject: [PATCH 17/41] RdbCell#layout_name and RdbItem#comment with tests and GSI binding --- src/rdb/rdb/gsiDeclRdb.cc | 33 +++++++++++++++--- src/rdb/rdb/rdb.cc | 22 ++++++++---- src/rdb/rdb/rdb.h | 62 ++++++++++++++++++++++++++++++---- src/rdb/rdb/rdbFile.cc | 4 ++- src/rdb/unit_tests/rdbTests.cc | 7 ++-- testdata/ruby/rdbTest.rb | 22 ++++++++---- 6 files changed, 121 insertions(+), 29 deletions(-) diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index e47297b92..36e134da3 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -265,17 +265,25 @@ Class decl_RdbCell ("rdb", "RdbCell", "The cell name is an string that identifies the category in the database. " "Additionally, a cell may carry a variant identifier which is a string that uniquely identifies a cell " "in the context of its variants. The \"qualified name\" contains both the cell name and the variant name. " - "Cell names are also used to identify report database cell's with layout cells. " + "Cell names are also used to identify report database cells with layout cells. For variants, the layout cell name " + "can be specified explicitly with the \\layout_name attribute (see \\RdbDatabase#create_cell). The latter is available " + "since version 0.29.1.\n" "@return The cell name\n" ) + gsi::method ("variant", &rdb::Cell::variant, "@brief Gets the cell variant name\n" "A variant name additionally identifies the cell when multiple cells with the same name are present. " - "A variant name is either assigned automatically or set when creating a cell. " + "A variant name is either assigned automatically or set when creating a cell.\n" "@return The cell variant name\n" ) + - gsi::method ("qname", &rdb::Cell::qname, - "@brief Gets the cell's qualified name\n" + gsi::method ("layout_name", &rdb::Cell::layout_name, + "@brief Gets the name of the layout cell\n" + "For variants, this string is the name of the actual layout cell. If empty, the cell is assume to be called 'name'.\n" + "@return The layout cell name\n" + "This read-only attribute has been added in version 0.29.1.\n" + ) + + gsi::method ("qname", &rdb::Cell::qname, + "@brief Gets the qualified name of the cell\n" "The qualified name is a combination of the cell name and optionally the variant name. " "It is used to identify the cell by name in a unique way.\n" "@return The qualified name\n" @@ -932,6 +940,19 @@ Class decl_RdbItem ("rdb", "RdbItem", "See \\image_str how to obtain the image.\n\n" "This method has been introduced in version 0.28.\n" ) + + gsi::method ("comment", &rdb::Item::comment, + "@brief Gets the common associated with this item as a string\n" + "@return The comment string\n" + "The comment string is an arbitrary string added by the user to the item.\n" + "\n" + "This attribute has been added in version 0.29.1.\n" + ) + + gsi::method ("comment=", &rdb::Item::set_comment, gsi::arg ("comment"), + "@brief Sets the common associated with this item as a string\n" + "See \\comment for a description of that attribute.\n" + "\n" + "This attribute has been added in version 0.29.1.\n" + ) + gsi::method ("image_str", &rdb::Item::image_str, "@brief Gets the image associated with this item as a string\n" "@return A base64-encoded image file (in PNG format)\n" @@ -1348,10 +1369,12 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@brief Creates a new cell\n" "@param name The name of the cell\n" ) + - gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), gsi::arg ("variant"), + gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &, const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), gsi::arg ("variant"), gsi::arg ("layout_name", std::string ()), "@brief Creates a new cell, potentially as a variant for a cell with the same name\n" "@param name The name of the cell\n" "@param variant The variant name of the cell\n" + "@param layout_name For variants, this is the name of the layout cell. If empty, 'name' is used for the layout cell name.\n" + "The 'layout_name' argument has been added in version 0.29.1.\n" ) + gsi::method ("variants", &rdb::Database::variants, gsi::arg ("name"), "@brief Gets the variants for a given cell name\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 807d0c668..a2e352144 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -516,7 +516,7 @@ Cells::import_cell (const Cell &c) { Cell *cell; if (mp_database) { - cell = mp_database->create_cell (c.name (), c.variant ()); + cell = mp_database->create_cell (c.name (), c.variant (), c.layout_name ()); } else { cell = new Cell (0, c.name ()); add_cell (cell); @@ -548,8 +548,8 @@ Cell::Cell (id_type id, const std::string &name) // .. nothing yet .. } -Cell::Cell (id_type id, const std::string &name, const std::string &variant) - : m_id (id), m_name (name), m_variant (variant), m_num_items (0), m_num_items_visited (0), mp_database (0) +Cell::Cell (id_type id, const std::string &name, const std::string &variant, const std::string &layout_name = std::string ()) + : m_id (id), m_name (name), m_variant (variant), m_layout_name (layout_name), m_num_items (0), m_num_items_visited (0), mp_database (0) { // .. nothing yet .. } @@ -939,6 +939,7 @@ Item &Item::operator= (const Item &d) m_category_id = d.m_category_id; m_visited = d.m_visited; m_multiplicity = d.m_multiplicity; + m_comment = d.m_comment; m_tag_ids = d.m_tag_ids; m_image_str = d.m_image_str; } @@ -1321,7 +1322,7 @@ Database::category_by_id_non_const (id_type id) } Cell * -Database::create_cell (const std::string &name, const std::string &variant) +Database::create_cell (const std::string &name, const std::string &variant, const std::string &layout_name) { set_modified (); @@ -1358,13 +1359,13 @@ Database::create_cell (const std::string &name, const std::string &variant) } } - new_cell = new Cell (++m_next_id, name, tl::to_string (variant_index + 1)); + new_cell = new Cell (++m_next_id, name, tl::to_string (variant_index + 1), layout_name); variant->second.push_back (new_cell->id ()); } else { - new_cell = new Cell (++m_next_id, name); + new_cell = new Cell (++m_next_id, name, std::string (), layout_name); } @@ -1374,7 +1375,7 @@ Database::create_cell (const std::string &name, const std::string &variant) } else { - new_cell = new Cell (++m_next_id, name, variant); + new_cell = new Cell (++m_next_id, name, variant, layout_name); m_cells.add_cell (new_cell); m_cells_by_id.insert (std::make_pair (new_cell->id (), new_cell)); m_cells_by_qname.insert (std::make_pair (new_cell->qname (), new_cell)); @@ -1468,6 +1469,13 @@ Database::remove_item_tag (const Item *item, id_type tag) const_cast (item)->remove_tag (tag); } +void +Database::set_item_comment (const Item *item, const std::string &comment) +{ + set_modified (); + const_cast (item)->set_comment (comment); +} + #if defined(HAVE_QT) void Database::set_item_image (const Item *item, const QImage &image) diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 3541e6768..a2d0f0b5a 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -1014,6 +1014,26 @@ public: */ void set_image_str (const std::string &s); + /** + * @brief Gets the item comment + * + * The comment string is an arbitrary string attached to the item. + */ + const std::string &comment () const + { + return m_comment; + } + + /** + * @brief Sets the item comment + * + * The comment string is an arbitrary string attached to the item. + */ + void set_comment (const std::string &s) + { + m_comment = s; + } + /** * @brief Get the database reference */ @@ -1038,6 +1058,7 @@ private: id_type m_cell_id; id_type m_category_id; size_t m_multiplicity; + std::string m_comment; bool m_visited; std::vector m_tag_ids; Database *mp_database; @@ -1462,7 +1483,7 @@ public: * * This method is provided for persistency application only. It should not be used otherwise. */ - Cell (id_type id, const std::string &name, const std::string &variant); + Cell (id_type id, const std::string &name, const std::string &variant, const std::string &layout_name); /** * @brief Cell destructor @@ -1488,7 +1509,7 @@ public: } /** - * @brief Get the cell name + * @brief Gets the cell name */ const std::string &name () const { @@ -1496,7 +1517,7 @@ public: } /** - * @brief Set the name string (setter) + * @brief Sets the name string * * This method must not be used for items in the database to keep the database consistent. */ @@ -1506,7 +1527,7 @@ public: } /** - * @brief Get the variant id + * @brief Gets the variant id */ const std::string &variant () const { @@ -1514,7 +1535,7 @@ public: } /** - * @brief Set the variant string (setter) + * @brief Sets the variant string * * This method must not be used for items in the database to keep the database consistent. */ @@ -1523,6 +1544,24 @@ public: m_variant = v; } + /** + * @brief Gets the layout cell name + */ + const std::string &layout_name () const + { + return m_layout_name; + } + + /** + * @brief Sets the layout cell string + * + * This method must not be used for items in the database to keep the database consistent. + */ + void set_layout_name (const std::string &s) + { + m_layout_name = s; + } + /** * @brief Get the qualified name (name plus optionally the variant id separated by a colon) */ @@ -1600,6 +1639,7 @@ private: id_type m_id; std::string m_name; std::string m_variant; + std::string m_layout_name; size_t m_num_items; size_t m_num_items_visited; References m_references; @@ -2203,7 +2243,7 @@ public: */ Cell *create_cell (const std::string &name) { - return create_cell (name, std::string ()); + return create_cell (name, std::string (), std::string ()); } /** @@ -2212,8 +2252,11 @@ public: * A cell with name name/variant combination must not exist already. * If the variant string is empty, this method behaves the same as the * method without variant. + * + * "layout_name" is the name of the cell in the layout. If empty, the layout + * cell is assumed to be identical to "name". */ - Cell *create_cell (const std::string &name, const std::string &variant); + Cell *create_cell (const std::string &name, const std::string &variant, const std::string &layout_name); /** * @brief Get all variants registered for a given cell name (not qname!) @@ -2307,6 +2350,11 @@ public: */ void remove_item_tag (const Item *item, id_type tag); + /** + * @brief Sets the comment string of the item + */ + void set_item_comment (const Item *item, const std::string &comment); + #if defined(HAVE_QT) /** * @brief Set the image of an item diff --git a/src/rdb/rdb/rdbFile.cc b/src/rdb/rdb/rdbFile.cc index b845cfb92..d7575e839 100644 --- a/src/rdb/rdb/rdbFile.cc +++ b/src/rdb/rdb/rdbFile.cc @@ -91,7 +91,8 @@ make_rdb_structure (rdb::Database *rdb) tl::make_element_with_parent_ref (&rdb::Cells::begin, &rdb::Cells::end, &rdb::Cells::import_cell, "cell", tl::make_member (&rdb::Cell::name, &rdb::Cell::set_name, "name") + tl::make_member (&rdb::Cell::variant, &rdb::Cell::set_variant, "variant") + - tl::make_element_with_parent_ref (&rdb::Cell::references, &rdb::Cell::import_references, "references", + tl::make_member (&rdb::Cell::layout_name, &rdb::Cell::set_layout_name, "layout-name") + + tl::make_element_with_parent_ref (&rdb::Cell::references, &rdb::Cell::import_references, "references", tl::make_element_with_parent_ref (&rdb::References::begin, &rdb::References::end, &rdb::References::insert, "ref", tl::make_member (&rdb::Reference::parent_cell_qname, &rdb::Reference::set_parent_cell_qname, "parent") + tl::make_member (&rdb::Reference::trans_str, &rdb::Reference::set_trans_str, "trans") @@ -106,6 +107,7 @@ make_rdb_structure (rdb::Database *rdb) tl::make_member (&rdb::Item::cell_qname, &rdb::Item::set_cell_qname, "cell") + tl::make_member (&rdb::Item::visited, &rdb::Item::set_visited, "visited") + tl::make_member (&rdb::Item::multiplicity, &rdb::Item::set_multiplicity, "multiplicity") + + tl::make_member (&rdb::Item::comment, &rdb::Item::set_comment, "comment") + tl::make_member (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") + tl::make_element (&rdb::Item::values, &rdb::Item::set_values, "values", tl::make_member (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb)) diff --git a/src/rdb/unit_tests/rdbTests.cc b/src/rdb/unit_tests/rdbTests.cc index 5665aa522..88b48676e 100644 --- a/src/rdb/unit_tests/rdbTests.cc +++ b/src/rdb/unit_tests/rdbTests.cc @@ -521,11 +521,11 @@ TEST(6) EXPECT_EQ (db.variants ("c1")[0], c1->id ()); EXPECT_EQ (db.variants ("c1")[1], c1a->id ()); - rdb::Cell *c1b = db.create_cell ("c1", "var"); + rdb::Cell *c1b = db.create_cell ("c1", "var", std::string ()); EXPECT_EQ (c1b->qname (), "c1:var") EXPECT_EQ (db.variants ("c1").size (), size_t (3)); - rdb::Cell *c2 = db.create_cell ("c2", "1027"); + rdb::Cell *c2 = db.create_cell ("c2", "1027", std::string ()); EXPECT_EQ (c2->qname (), "c2:1027"); EXPECT_EQ (db.variants ("c2").size (), size_t (1)); @@ -534,8 +534,9 @@ TEST(6) EXPECT_EQ (c2->qname (), "c2:1027") EXPECT_EQ (db.variants ("c2").size (), size_t (2)); - rdb::Cell *c2b = db.create_cell ("c2", "var"); + rdb::Cell *c2b = db.create_cell ("c2", "var", "c2$1"); EXPECT_EQ (c2b->qname (), "c2:var") + EXPECT_EQ (c2b->layout_name (), "c2$1") rdb::Cell *c2c = db.create_cell ("c2"); EXPECT_EQ (c2c->qname (), "c2:2"); diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index abc40519f..eb99bd859 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -76,16 +76,22 @@ class RDB_TestClass < TestBase assert_equal(cell.name, "cell_name") assert_equal(cell.rdb_id, 1) - cell2 = db.create_cell("cell_name", "var1") + cell2 = db.create_cell("new_cell", "var1") + assert_equal(cell2.name, "new_cell") + assert_equal(cell2.layout_name, "") + assert_equal(cell2.qname, "new_cell:var1") + + cell2 = db.create_cell("cell_name", "var1", "cell_name$1") assert_equal(cell.name, "cell_name") assert_equal(cell.qname, "cell_name:1") assert_equal(db.cell_by_qname("cell_name:1").rdb_id, cell.rdb_id) assert_equal(db.cell_by_id(cell.rdb_id).rdb_id, cell.rdb_id) assert_equal(cell2.name, "cell_name") + assert_equal(cell2.layout_name, "cell_name$1") assert_equal(cell2.qname, "cell_name:var1") assert_equal(db.cell_by_qname("cell_name:var1").rdb_id, cell2.rdb_id) assert_equal(db.cell_by_id(cell2.rdb_id).rdb_id, cell2.rdb_id) - assert_equal(cell2.rdb_id, 2) + assert_equal(cell2.rdb_id, 3) assert_equal(cell.num_items, 0) assert_equal(cell2.num_items, 0) assert_equal(cell.num_items_visited, 0) @@ -98,19 +104,19 @@ class RDB_TestClass < TestBase cc = [] db.each_cell { |c| cc.push(c) } - assert_equal(cc.size, 2) + assert_equal(cc.size, 3) assert_equal(cc[0].rdb_id, cell.rdb_id) - assert_equal(cc[1].rdb_id, cell2.rdb_id) + assert_equal(cc[2].rdb_id, cell2.rdb_id) cat = db.create_category("cat") assert_equal(cat.database.inspect, db.inspect) assert_equal(cat.name, "cat") - assert_equal(cat.rdb_id, 3) + assert_equal(cat.rdb_id, 4) assert_equal(cat.path, "cat") cats = db.create_category(cat, "subcat") assert_equal(cats.name, "subcat") - assert_equal(cats.rdb_id, 4) + assert_equal(cats.rdb_id, 5) assert_equal(cats.path, "cat.subcat") assert_equal(cats.parent.rdb_id, cat.rdb_id) @@ -425,6 +431,10 @@ class RDB_TestClass < TestBase assert_equal(item.image_str, "") assert_equal(item.has_image?, false) + assert_equal(item.comment, "") + item.comment = "abc" + assert_equal(item.comment, "abc") + # can actually by any string, but only base64-encoded PNG images make sense is="iVBORw0KGgoAAAANSUhEUgAAACoAAAA0CAIAAABzfT3nAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAA0SAAANOgHo3ZneAAAA3UlEQVRYhe2WwQ3DIAxFoco8XaGZIaeO43FyYgZYgYXcQ6SWuDGgBhWq/qccIvGCEd9SbAwAAPSGaW2lFR2rfWDpXrPpSe2SP10fvnn/PZHZH9IwbKFVZZ/Z6wMtZcjW02Bn2FVpZYdWdkr2nvh23S2FyDNJuVITpwmRjTGbNr0v20U5byNtJuuJt/fO2f93+UlbEJl5UjVPr3Y71EQ/PoPPlU+lDJtWlCt3GwCMG33BuJGAcWMEMG6c1jBudCyf/nzV8nbZPRohclFLHdGbZ8eNSjN1fmf0AACA1jwA4hKxu4C6P7EAAAAASUVORK5CYII=" item.image_str=is From 0b5473c1533144a75ea7f6444d04f4380723a056 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 21:09:14 +0200 Subject: [PATCH 18/41] Editor feature for RDB item comments, bindkeys for waive and edit --- src/layui/layui/MarkerBrowserPage.ui | 138 ++++++++++++++---------- src/layui/layui/rdbMarkerBrowserPage.cc | 71 +++++++++++- src/layui/layui/rdbMarkerBrowserPage.h | 1 + 3 files changed, 150 insertions(+), 60 deletions(-) diff --git a/src/layui/layui/MarkerBrowserPage.ui b/src/layui/layui/MarkerBrowserPage.ui index 18afcbe7f..577e9cebc 100644 --- a/src/layui/layui/MarkerBrowserPage.ui +++ b/src/layui/layui/MarkerBrowserPage.ui @@ -480,51 +480,7 @@ 6 - - - - Waive - - - W - - - - :/waived_16px.png:/waived_16px.png - - - - - - - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Remove snapshot</p></body></html> - - - ... - - - - :/nophoto_16px.png:/nophoto_16px.png - - - - - - - Set or reset flag - - - Flag - - - QToolButton::MenuButtonPopup - - - - + Add snapshot @@ -538,28 +494,24 @@ p, li { white-space: pre-wrap; } - - + + - Important + Waive - Imp + W - :/important_16px.png:/important_16px.png + :/waived_16px.png:/waived_16px.png + + + Ctrl+W - - - - - - - - + @@ -605,7 +557,38 @@ p, li { white-space: pre-wrap; } - + + + + Important + + + Imp + + + + :/important_16px.png:/important_16px.png + + + + + + + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Remove snapshot</p></body></html> + + + ... + + + + :/nophoto_16px.png:/nophoto_16px.png + + + + @@ -662,6 +645,43 @@ p, li { white-space: pre-wrap; } + + + + + + + + + + + Set or reset flag + + + Flag + + + QToolButton::MenuButtonPopup + + + + + + + Edit Comment + + + ... + + + + :/edit_16px.png:/edit_16px.png + + + F2 + + + diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 7b10974b1..657f52a14 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -39,6 +39,7 @@ #include #include #include +#include namespace rdb { @@ -1690,6 +1691,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/) connect (list_down_pb, SIGNAL (clicked ()), this, SLOT (list_down_clicked ())); connect (flags_pb, SIGNAL (clicked ()), this, SLOT (flag_button_clicked ())); connect (important_pb, SIGNAL (clicked ()), this, SLOT (important_button_clicked ())); + connect (edit_pb, SIGNAL (clicked ()), this, SLOT (edit_button_clicked ())); connect (waive_pb, SIGNAL (clicked ()), this, SLOT (waived_button_clicked ())); connect (photo_pb, SIGNAL (clicked ()), this, SLOT (snapshot_button_clicked ())); connect (nophoto_pb, SIGNAL (clicked ()), this, SLOT (remove_snapshot_button_clicked ())); @@ -2104,6 +2106,8 @@ MarkerBrowserPage::update_info_text () size_t n_category = 0; const rdb::Item *item = 0; size_t n_item = 0; + std::string comment; + size_t n_comment = 0; for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { @@ -2115,6 +2119,11 @@ MarkerBrowserPage::update_info_text () item = i; ++n_item; + if (! item->comment ().empty () && item->comment () != comment) { + comment = item->comment (); + ++n_comment; + } + const rdb::Cell *c = mp_database->cell_by_id (item->cell_id ()); if (c && c != cell) { cell = c; @@ -2162,6 +2171,12 @@ MarkerBrowserPage::update_info_text () info += "

    "; } + if (! comment.empty () && n_comment == 1) { + info += "

    "; + tl::escape_to_html (info, comment); + info += "

    "; + } + info += "

    "; if (item && n_item == 1) { @@ -2901,7 +2916,61 @@ MarkerBrowserPage::flag_menu_selected () } } -void +void +MarkerBrowserPage::edit_button_clicked () +{ + if (! mp_database) { + return; + } + + MarkerBrowserListViewModel *list_model = dynamic_cast (markers_list->model ()); + if (! list_model) { + return; + } + + std::string str; + + QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); + for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + if (selected_item->column () == 0) { + const rdb::Item *i = list_model->item (selected_item->row ()); + if (! i->comment ().empty ()) { + if (str.empty ()) { + str = i->comment (); + } else if (str != i->comment ()) { + str.clear (); + break; + } + } + } + } + + bool ok = false; + +#if QT_VERSION >= 0x50200 + QString new_text = QInputDialog::getMultiLineText (this, QObject::tr ("Edit Marker Comment"), QObject::tr ("Comment"), tl::to_qstring (str), &ok); + str = tl::to_string (new_text); +#else + QString new_text = QInputDialog::getText (this, QObject::tr ("Edit Marker Comment"), QObject::tr ("Comment"), QLineEdit::Normal, tl::to_qstring (tl::escape_string (str)), &ok); + str = tl::unescape_string (tl::to_string (new_text)); +#endif + + if (ok) { + + QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); + for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + if (selected_item->column () == 0) { + const rdb::Item *i = list_model->item (selected_item->row ()); + mp_database->set_item_comment (i, str); + } + } + + update_info_text (); + + } +} + +void MarkerBrowserPage::waived_button_clicked () { if (! mp_database) { diff --git a/src/layui/layui/rdbMarkerBrowserPage.h b/src/layui/layui/rdbMarkerBrowserPage.h index 58cd27ecc..7a916b1f6 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.h +++ b/src/layui/layui/rdbMarkerBrowserPage.h @@ -179,6 +179,7 @@ public slots: void flag_menu_selected (); void important_button_clicked (); void waived_button_clicked (); + void edit_button_clicked (); void snapshot_button_clicked (); void remove_snapshot_button_clicked (); void revisit_non_waived (); From bab4d7b5c7ffedda195703d00f1b02eb7398dcc3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 21:14:02 +0200 Subject: [PATCH 19/41] Cleanup --- src/layui/layui/rdbMarkerBrowserPage.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 657f52a14..e6c87b117 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -2265,8 +2265,6 @@ MarkerBrowserPage::do_update_markers () item_index = size_t (selected_item->row ()); ++n_item; - std::string info; - const rdb::Cell *c = mp_database->cell_by_id (item->cell_id ()); if (c && c != cell) { cell = c; @@ -2931,7 +2929,7 @@ MarkerBrowserPage::edit_button_clicked () std::string str; QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (! i->comment ().empty ()) { @@ -2958,7 +2956,7 @@ MarkerBrowserPage::edit_button_clicked () if (ok) { QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); mp_database->set_item_comment (i, str); @@ -2988,7 +2986,7 @@ MarkerBrowserPage::waived_button_clicked () size_t nno = 0; QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3026,7 +3024,7 @@ MarkerBrowserPage::important_button_clicked () size_t nno = 0; QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3065,7 +3063,7 @@ MarkerBrowserPage::remove_snapshot_button_clicked () if (msgbox.exec () == QMessageBox::Yes) { QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3092,7 +3090,7 @@ MarkerBrowserPage::snapshot_button_clicked () } QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3257,7 +3255,7 @@ MarkerBrowserPage::mark_important () id_type important_tag_id = mp_database->tags ().tag ("important").id (); QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3284,7 +3282,7 @@ MarkerBrowserPage::mark_unimportant () id_type important_tag_id = mp_database->tags ().tag ("important").id (); QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3321,7 +3319,7 @@ MarkerBrowserPage::mark_visited (bool f) } QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { @@ -3370,7 +3368,7 @@ MarkerBrowserPage::waive_or_unwaive (bool w) id_type waived_tag_id = mp_database->tags ().tag ("waived").id (); QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes (); - for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { + for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) { if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { From 4b967fcc516cbdb5c1a4561620630155d48ffb8b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 21:41:04 +0200 Subject: [PATCH 20/41] Show filename in RDB drop-down, 'save' button, bindkeys for reload, save, open --- src/layui/layui/MarkerBrowserDialog.ui | 68 +++++++++++++++++++ src/layui/layui/rdbMarkerBrowserDialog.cc | 80 ++++++++++++++++------- src/layui/layui/rdbMarkerBrowserDialog.h | 7 +- 3 files changed, 125 insertions(+), 30 deletions(-) diff --git a/src/layui/layui/MarkerBrowserDialog.ui b/src/layui/layui/MarkerBrowserDialog.ui index a65ca1385..7aee07fe1 100644 --- a/src/layui/layui/MarkerBrowserDialog.ui +++ b/src/layui/layui/MarkerBrowserDialog.ui @@ -292,6 +292,74 @@ to load a marker database + + + Open + + + Ctrl+O + + + QAction::NoRole + + + + + Save As + + + QAction::NoRole + + + + + Reload + + + F5 + + + QAction::NoRole + + + + + Save + + + Ctrl+S + + + QAction::NoRole + + + + + Export To Layout + + + QAction::NoRole + + + + + Unload + + + QAction::NoRole + + + + + Unload All + + + Unload All + + + QAction::NoRole + + diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 800025a13..b94829ed8 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -84,34 +84,29 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView view ()->rdb_list_changed_event.add (this, &MarkerBrowserDialog::rdbs_changed); } - m_open_action = new QAction (QObject::tr ("Open"), mp_ui->file_menu); - m_saveas_action = new QAction (QObject::tr ("Save As"), mp_ui->file_menu); - m_export_action = new QAction (QObject::tr ("Export To Layout"), mp_ui->file_menu); - m_reload_action = new QAction (QObject::tr ("Reload"), mp_ui->file_menu); - m_unload_action = new QAction (QObject::tr ("Unload"), mp_ui->file_menu); - m_unload_all_action = new QAction (QObject::tr ("Unload All"), mp_ui->file_menu); + connect (mp_ui->open_action, SIGNAL (triggered ()), this, SLOT (open_clicked ())); + connect (mp_ui->save_action, SIGNAL (triggered ()), this, SLOT (save_clicked ())); + connect (mp_ui->saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ())); + connect (mp_ui->export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ())); + connect (mp_ui->reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ())); + connect (mp_ui->unload_action, SIGNAL (triggered ()), this, SLOT (unload_clicked ())); + connect (mp_ui->unload_all_action, SIGNAL (triggered ()), this, SLOT (unload_all_clicked ())); - connect (m_open_action, SIGNAL (triggered ()), this, SLOT (open_clicked ())); - connect (m_saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ())); - connect (m_export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ())); - connect (m_reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ())); - connect (m_unload_action, SIGNAL (triggered ()), this, SLOT (unload_clicked ())); - connect (m_unload_all_action, SIGNAL (triggered ()), this, SLOT (unload_all_clicked ())); - - mp_ui->file_menu->addAction (m_open_action); - mp_ui->file_menu->addAction (m_saveas_action); + mp_ui->file_menu->addAction (mp_ui->open_action); + mp_ui->file_menu->addAction (mp_ui->save_action); + mp_ui->file_menu->addAction (mp_ui->saveas_action); QAction *sep0 = new QAction (mp_ui->file_menu); sep0->setSeparator (true); - mp_ui->file_menu->addAction (m_export_action); + mp_ui->file_menu->addAction (mp_ui->export_action); QAction *sep1 = new QAction (mp_ui->file_menu); sep1->setSeparator (true); mp_ui->file_menu->addAction (sep1); - mp_ui->file_menu->addAction (m_reload_action); + mp_ui->file_menu->addAction (mp_ui->reload_action); QAction *sep2 = new QAction (mp_ui->file_menu); sep2->setSeparator (true); mp_ui->file_menu->addAction (sep2); - mp_ui->file_menu->addAction (m_unload_action); - mp_ui->file_menu->addAction (m_unload_all_action); + mp_ui->file_menu->addAction (mp_ui->unload_action); + mp_ui->file_menu->addAction (mp_ui->unload_all_action); connect (mp_ui->layout_cb, SIGNAL (activated (int)), this, SLOT (cv_index_changed (int))); connect (mp_ui->rdb_cb, SIGNAL (activated (int)), this, SLOT (rdb_index_changed (int))); @@ -367,6 +362,34 @@ BEGIN_PROTECTED END_PROTECTED } +void +MarkerBrowserDialog::save_clicked () +{ +BEGIN_PROTECTED + + if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) { + + rdb::Database *rdb = view ()->get_rdb (m_rdb_index); + if (rdb) { + + if (rdb->filename ().empty ()) { + + saveas_clicked (); + + } else { + + rdb->save (rdb->filename ()); + rdb->reset_modified (); + + } + + } + + } + +END_PROTECTED +} + void MarkerBrowserDialog::saveas_clicked () { @@ -385,6 +408,9 @@ BEGIN_PROTECTED rdb->save (fn); rdb->reset_modified (); + // update the RDB title strings + rdbs_changed (); + } } @@ -439,6 +465,7 @@ BEGIN_PROTECTED int rdb_index = view ()->add_rdb (db.release ()); mp_ui->rdb_cb->setCurrentIndex (rdb_index); + // it looks like the setCurrentIndex does not issue this signal: rdb_index_changed (rdb_index); @@ -595,6 +622,10 @@ MarkerBrowserDialog::rdbs_changed () text += rdb->description (); text += ")"; } + if (! rdb->filename ().empty () && rdb->name () != rdb->filename ()) { + text += " - "; + text += rdb->filename (); + } mp_ui->rdb_cb->addItem (tl::to_qstring (text)); if (rdb->name () == m_rdb_name) { rdb_index = i; @@ -688,11 +719,12 @@ MarkerBrowserDialog::update_content () mp_ui->central_stack->setCurrentIndex (1); } - m_saveas_action->setEnabled (rdb != 0); - m_export_action->setEnabled (rdb != 0); - m_unload_action->setEnabled (rdb != 0); - m_unload_all_action->setEnabled (rdb != 0); - m_reload_action->setEnabled (rdb != 0); + mp_ui->save_action->setEnabled (rdb != 0); + mp_ui->saveas_action->setEnabled (rdb != 0); + mp_ui->export_action->setEnabled (rdb != 0); + mp_ui->unload_action->setEnabled (rdb != 0); + mp_ui->unload_all_action->setEnabled (rdb != 0); + mp_ui->reload_action->setEnabled (rdb != 0); mp_ui->browser_frame->enable_updates (false); // Avoid building the internal lists several times ... mp_ui->browser_frame->set_rdb (0); // force update diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index 482913f6a..a42005566 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -74,6 +74,7 @@ private: public slots: void cv_index_changed (int); void rdb_index_changed (int); + void save_clicked (); void saveas_clicked (); void export_clicked (); void reload_clicked (); @@ -98,12 +99,6 @@ private: std::string m_rdb_name; int m_rdb_index; std::string m_open_filename; - QAction *m_open_action; - QAction *m_saveas_action; - QAction *m_export_action; - QAction *m_unload_action; - QAction *m_unload_all_action; - QAction *m_reload_action; void update_content (); void scan_layer (); From ed64d4a59bf10ae5b0f16394d3812aa1c15a969b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 23:02:54 +0200 Subject: [PATCH 21/41] DRC enhancements: - Shapes are added to existing categories rather than creating a new category with the same name - The category name in "output" can be an array creating a hierarchy of categories. --- src/drc/drc/built-in-macros/_drc_engine.rb | 18 +++++++++++++++++- src/drc/drc/built-in-macros/_drc_layer.rb | 6 +++++- src/rdb/rdb/gsiDeclRdb.cc | 1 + src/rdb/rdb/rdb.cc | 4 ++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 81e325ff3..0d198f1f8 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -3226,7 +3226,23 @@ CODE output_rdb = channel.rdb output_cell = channel.cell - cat = output_rdb.create_category(args[0].to_s) + categories = args[0] + if !categories.is_a?(Array) + categories = [ categories.to_s ] + end + + cat = nil + categories.each do |c| + ccat = nil + if cat + ccat = cat.each_sub_category.find { |i| i.name == c } + else + ccat = output_rdb.each_category.find { |i| i.name == c } + end + cat = ccat ? ccat : output_rdb.create_category(cat, c) + end + cat ||= output_rdb.create_category("default") + args[1] && cat.description = args[1] cat.scan_collection(output_cell, RBA::CplxTrans::new(self.dbu), data) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 870591330..2d514d687 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -5111,7 +5111,11 @@ CODE # This method will copy the content of the layer to the specified output. # # If a report database is selected for the output, the specification has to include a - # category name and optionally a category description. + # category name and optionally a category description. The category name can be an + # array of strings - in that case, a hierarchy of categories is created + # with the first array item being the top level category name. + # Shapes are added to an existing category, if a category with the given + # name already exists. # # If the layout is selected for the output, the specification can consist of # one to three parameters: a layer number, a data type (optional, default is 0) diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 36e134da3..920334d43 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -1342,6 +1342,7 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@brief Creates a new sub-category\n" "@param parent The category under which the category should be created\n" "@param name The name of the category\n" + "Since version 0.29.1, 'parent' can be nil. In that case, a top-level category is created." ) + gsi::method ("category_by_path", &rdb::Database::category_by_name, gsi::arg ("path"), "@brief Gets a category by path\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index a2e352144..df19f88dc 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1275,6 +1275,10 @@ Database::import_cells (const Cells &cells) Category * Database::create_category (Category *parent, const std::string &name) { + if (! parent) { + return create_category (name); + } + set_modified (); Category *cat = create_category (&parent->sub_categories (), name); From 3152be5c3d0e847b9d829d046a087cb4e5431e3b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 23:36:27 +0200 Subject: [PATCH 22/41] Original file name is written into DRC marker db now --- src/drc/drc/built-in-macros/_drc_engine.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 0d198f1f8..157154698 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -3349,7 +3349,7 @@ CODE else output_rdb = RBA::ReportDatabase::new(name) end - + cn = cellname && cellname.to_s cn ||= @def_cell && @def_cell.name cn ||= @def_source && @def_source.cell_obj && @def_source.cell_obj.name @@ -3359,7 +3359,8 @@ CODE output_rdb.generator = self._generator output_rdb.top_cell_name = cn output_rdb.description = description - + output_rdb.original_file = @def_path + RDBOutputChannel::new(self, output_rdb, output_rdb_index, cn, output_rdb_file) end From aacdc1a4b71c198fff66478c450094d9dfa127d4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Apr 2024 23:36:48 +0200 Subject: [PATCH 23/41] Info feature for marker browser --- src/layui/layui/MarkerBrowserDialog.ui | 8 +++++ src/layui/layui/rdbMarkerBrowserDialog.cc | 42 ++++++++++++++++++++++- src/layui/layui/rdbMarkerBrowserDialog.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/layui/layui/MarkerBrowserDialog.ui b/src/layui/layui/MarkerBrowserDialog.ui index 7aee07fe1..65a398b74 100644 --- a/src/layui/layui/MarkerBrowserDialog.ui +++ b/src/layui/layui/MarkerBrowserDialog.ui @@ -360,6 +360,14 @@ to load a marker database QAction::NoRole + + + Info + + + QAction::NoRole + + diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index b94829ed8..6f7fb1f14 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -33,6 +33,7 @@ #include "layConverters.h" #include "layQtTools.h" #include "layConfigurationDialog.h" +#include "layBrowserDialog.h" #include "dbLayoutUtils.h" #include "dbRecursiveShapeIterator.h" #include "dbStream.h" @@ -89,6 +90,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView connect (mp_ui->saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ())); connect (mp_ui->export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ())); connect (mp_ui->reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ())); + connect (mp_ui->info_action, SIGNAL (triggered ()), this, SLOT (info_clicked ())); connect (mp_ui->unload_action, SIGNAL (triggered ()), this, SLOT (unload_clicked ())); connect (mp_ui->unload_all_action, SIGNAL (triggered ()), this, SLOT (unload_all_clicked ())); @@ -102,6 +104,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView sep1->setSeparator (true); mp_ui->file_menu->addAction (sep1); mp_ui->file_menu->addAction (mp_ui->reload_action); + mp_ui->file_menu->addAction (mp_ui->info_action); QAction *sep2 = new QAction (mp_ui->file_menu); sep2->setSeparator (true); mp_ui->file_menu->addAction (sep2); @@ -420,6 +423,42 @@ BEGIN_PROTECTED END_PROTECTED } +void +MarkerBrowserDialog::info_clicked () +{ + rdb::Database *rdb = 0; + if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) { + rdb = view ()->get_rdb (m_rdb_index); + } + if (! rdb) { + return; + } + + std::string html; + html = "\n"; + html += "

    " + tl::escaped_to_html (rdb->name ()) + "

    \n"; + if (! rdb->description ().empty ()) { + html += "" + tl::to_string (tr ("Description: ")) + "" + tl::escaped_to_html (tl::escape_string (rdb->description ())) + "
    \n"; + } + if (! rdb->filename ().empty ()) { + html += "" + tl::to_string (tr ("File: ")) + "" + tl::escaped_to_html (rdb->filename ()) + "
    \n"; + } + if (! rdb->original_file ().empty ()) { + html += "" + tl::to_string (tr ("Original File: ")) + "" + tl::escaped_to_html (rdb->original_file ()) + "
    \n"; + } + if (! rdb->top_cell_name ().empty ()) { + html += "" + tl::to_string (tr ("Top Cell: ")) + "" + tl::escaped_to_html (rdb->top_cell_name ()) + "
    \n"; + } + if (! rdb->generator ().empty ()) { + html += "" + tl::to_string (tr ("Generator: ")) + "" + tl::escaped_to_html (rdb->generator ()) + "
    \n"; + } + html += ""; + + std::unique_ptr info_dialog (new lay::BrowserDialog (this, html)); + info_dialog->setWindowTitle (QObject::tr ("Marker Database Info")); + info_dialog->exec (); +} + void MarkerBrowserDialog::reload_clicked () { @@ -619,7 +658,7 @@ MarkerBrowserDialog::rdbs_changed () std::string text = rdb->name (); if (! rdb->description ().empty ()) { text += " ("; - text += rdb->description (); + text += tl::escape_string (rdb->description ()); text += ")"; } if (! rdb->filename ().empty () && rdb->name () != rdb->filename ()) { @@ -725,6 +764,7 @@ MarkerBrowserDialog::update_content () mp_ui->unload_action->setEnabled (rdb != 0); mp_ui->unload_all_action->setEnabled (rdb != 0); mp_ui->reload_action->setEnabled (rdb != 0); + mp_ui->info_action->setEnabled (rdb != 0); mp_ui->browser_frame->enable_updates (false); // Avoid building the internal lists several times ... mp_ui->browser_frame->set_rdb (0); // force update diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index a42005566..3d651b3fb 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -74,6 +74,7 @@ private: public slots: void cv_index_changed (int); void rdb_index_changed (int); + void info_clicked (); void save_clicked (); void saveas_clicked (); void export_clicked (); From 6e3eb568c342b81c386dfa03294ee3d1c5ff69ce Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 15:30:29 +0200 Subject: [PATCH 24/41] Implementing variant reporting Instead of reporting working layout cell names, the original cell names plus a variant indicator is reported. Also, the variant information from the RDB is filled now. Also, the marker browser now shows the qname of the cells instead of the name alone. --- src/db/db/dbCellVariants.cc | 20 ++++++++---- src/db/db/dbDeepShapeStore.cc | 35 +++++++++++++++++++-- src/db/db/dbHierarchyBuilder.cc | 42 ++++++++++++++++++++++++- src/db/db/dbHierarchyBuilder.h | 18 ++++++++++- src/db/db/dbLayout.cc | 3 ++ src/db/db/dbLayout.h | 20 ++++++++++++ src/layui/layui/rdbMarkerBrowserPage.cc | 4 +-- src/rdb/rdb/rdbUtils.cc | 22 +++++++++++-- 8 files changed, 149 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index d1135c30a..af549ad8a 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -288,6 +288,7 @@ VariantsCollectorBase::separate_variants (std::mapempty ()); for (db::Layout::bottom_up_const_iterator c = mp_layout->begin_bottom_up (); c != mp_layout->end_bottom_up (); ++c) { @@ -311,6 +312,9 @@ VariantsCollectorBase::separate_variants (std::mapsecond.begin (); v != vc->second.end (); ++v, ++index) { @@ -333,6 +337,8 @@ VariantsCollectorBase::separate_variants (std::mapfirst.is_unity ()) { - std::set &tv = m_variants [*c]; - tv.clear (); - tv.insert (vt.begin ()->first); - } else { - m_variants.erase (*c); + if (original_cell_is_variant) { + if (! original_cell_variant.is_unity ()) { + std::set &tv = m_variants [*c]; + tv.clear (); + tv.insert (original_cell_variant); + } else { + m_variants.erase (*c); + } } } else { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 02a33fcaa..db052be3b 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -298,28 +298,57 @@ struct DeepShapeStore::LayoutHolder { public: VariantsCreatedListener (DeepShapeStore::LayoutHolder *lh, db::Layout *ly) - : mp_lh (lh) + : mp_lh (lh), m_dbu (ly->dbu ()) { ly->variants_created_event.add (this, &VariantsCreatedListener::variants_created); } private: + std::string var_desc (const db::ICplxTrans &t) + { + std::string s; + if (t.is_mirror ()) { + s += "m"; + s += tl::to_string (t.angle () * 0.5); + } else { + s += "r"; + s += tl::to_string (t.angle ()); + } + if (t.is_mag ()) { + s += tl::sprintf ("*%.9g", t.mag ()); + } + if (t.disp () != db::Vector ()) { + s += tl::sprintf ("(%.12g,%.12g)", t.disp ().x () * m_dbu, t.disp ().y () * m_dbu); + } + return s; + } + void variants_created (const std::map > *var_map) { for (std::map >::const_iterator i = var_map->begin (); i != var_map->end (); ++i) { for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { - mp_lh->builder.register_variant (i->first, j->second); + if (i->first != j->second) { + mp_lh->builder.register_variant (i->first, j->second, var_desc (j->first)); + } + } + // NOTE: variant conversion events are registered after variant formation events, so we can + // base the formed variants (first pass) on the originals. + for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + if (i->first == j->second) { + mp_lh->builder.register_variant (i->first, j->second, var_desc (j->first)); + } } } } DeepShapeStore::LayoutHolder *mp_lh; + double m_dbu; }; LayoutHolder (const db::ICplxTrans &trans) : refs (0), layout (false), builder (&layout, trans), variants_created (this, &layout) { - // .. nothing yet .. + layout.set_hierarchy_builder (&builder); } void add_layer_ref (unsigned int layer) diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index bbfd1d68e..f8476730d 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -182,14 +182,24 @@ HierarchyBuilder::reset () m_cells_to_be_filled.clear (); m_cell_map.clear (); + m_variants_of_sources_map.clear (); m_cells_seen.clear (); m_cell_stack.clear (); m_cm_entry = null_iterator; m_cm_new_entry = false; } +const std::pair & +HierarchyBuilder::variant_of_source (db::cell_index_type target) const +{ + static std::pair def (std::numeric_limits::max (), std::string ()); + + auto vs = m_variants_of_sources_map.find (target); + return vs != m_variants_of_sources_map.end () ? vs->second : def; +} + void -HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var) +HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var, const std::string &description) { // non_var (despite its name) may be a variant created previously. variant_to_original_target_map_type::const_iterator v = m_variants_to_original_target_map.find (non_var); @@ -199,11 +209,23 @@ HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_ m_original_targets_to_variants_map [non_var].push_back (var); m_variants_to_original_target_map.insert (std::make_pair (var, non_var)); + + auto vs = m_variants_of_sources_map.find (non_var); + if (vs != m_variants_of_sources_map.end ()) { + std::string new_description = vs->second.second; + if (! new_description.empty ()) { + new_description += ";"; + } + new_description += description; + m_variants_of_sources_map [var] = std::make_pair (vs->second.first, new_description); + } } void HierarchyBuilder::unregister_variant (db::cell_index_type var) { + m_variants_of_sources_map.erase (var); + variant_to_original_target_map_type::iterator v = m_variants_to_original_target_map.find (var); if (v == m_variants_to_original_target_map.end ()) { return; @@ -257,6 +279,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) if (m_cm_entry == m_cell_map.end ()) { db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.original_cell)); m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; + m_variants_of_sources_map.insert (std::make_pair (new_top_index, std::make_pair (key.original_cell, std::string ()))); } db::Cell &new_top = mp_target->cell (m_cm_entry->second); @@ -324,17 +347,34 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co if (m_cm_entry == m_cell_map.end ()) { std::string cn = cell_name; + std::string description; if (! key.clip_region.empty ()) { cn += "$CLIP_VAR"; + description += "CLIP"; + } if (key.inactive) { cn += "$DIS"; + if (! description.empty ()) { + description += "/"; + } + description += "DISABLED"; } + new_cell = mp_target->add_cell (cn.c_str ()); + + std::string new_name = mp_target->cell_name (new_cell); + if (new_name.size () > cn.size ()) { + // use cell name extension to uniquify the description + description += new_name.c_str () + cn.size (); + } + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; m_cm_new_entry = true; m_cells_to_be_filled.insert (new_cell); + m_variants_of_sources_map.insert (std::make_pair (new_cell, std::make_pair (key.original_cell, description))); + } else { new_cell = m_cm_entry->second; } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 70c76b2fe..4cd0caf65 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -389,7 +389,7 @@ public: * The first cell is either the original, non-variant target cell or itself a variant. * The second cell will be registered as a variant of the first one. */ - void register_variant (db::cell_index_type non_var, db::cell_index_type var); + void register_variant (db::cell_index_type non_var, db::cell_index_type var, const std::string &description); /** * @brief Unregisters a cell as a variant @@ -404,8 +404,23 @@ public: return m_variants_to_original_target_map.find (ci) != m_variants_to_original_target_map.end (); } + /** + * @brief Gets the information about the target/source + variant relationship + * + * For a target cell, returns a pointer to a pair with the cell index of the source cell and + * a string describing the variant this cell forms of the source cell. + * + * The description string is empty if the cell is not a variant. + */ + const std::pair &variant_of_source (db::cell_index_type target) const; + /** * @brief Gets the original target for a variant cell + * + * The original cell is the first-generation target-space cell that variants have been created for. + * This still can mean that the original cell is a clip variant of a source cell. + * + * Target-to-source variants can be derived with "variant_of_source". */ db::cell_index_type original_target_for_variant (db::cell_index_type ci) const; @@ -419,6 +434,7 @@ private: cell_map_type m_cell_map; original_target_to_variants_map_type m_original_targets_to_variants_map; variant_to_original_target_map_type m_variants_to_original_target_map; + std::map > m_variants_of_sources_map; std::set m_cells_seen; std::set m_cells_to_be_filled; diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 4e29d27ef..319f10bae 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -439,6 +439,7 @@ LayoutOrCellContextInfo::has_meta_info () const Layout::Layout (db::Manager *manager) : db::Object (manager), mp_library (0), + mp_builder (0), m_cells_size (0), m_invalid (0), m_top_cells (0), @@ -454,6 +455,7 @@ Layout::Layout (db::Manager *manager) Layout::Layout (bool editable, db::Manager *manager) : db::Object (manager), mp_library (0), + mp_builder (0), m_cells_size (0), m_invalid (0), m_top_cells (0), @@ -473,6 +475,7 @@ Layout::Layout (const db::Layout &layout) tl::Object (), tl::UniqueId (), mp_library (0), + mp_builder (0), m_cells_size (0), m_invalid (0), m_top_cells (0), diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 59a4ad19f..4ab44a12e 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -72,6 +72,7 @@ class Technology; class CellMapping; class LayerMapping; class VariantsCollectorBase; +class HierarchyBuilder; template class generic_repository; typedef generic_repository GenericRepository; @@ -2113,6 +2114,24 @@ public: */ const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const; + /** + * @brief Sets the hierarchy builder reference + * Used internally + */ + void set_hierarchy_builder (db::HierarchyBuilder *builder) + { + mp_builder = builder; + } + + /** + * @brief Gets the hierarchy builder + * Used internally + */ + db::HierarchyBuilder *builder () const + { + return mp_builder; + } + /** * @brief This event is triggered when the technology changes */ @@ -2138,6 +2157,7 @@ protected: private: db::Library *mp_library; + db::HierarchyBuilder *mp_builder; cell_list m_cells; size_t m_cells_size; cell_ptr_vector m_cell_ptrs; diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index e6c87b117..380e327e7 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -287,7 +287,7 @@ SortByKeyCompareFunc::operator() (MarkerBrowserTreeViewModelCacheEntry *a, Marke const rdb::Cell *ca = mp_rdb->cell_by_id (a->id ()); const rdb::Cell *cb = mp_rdb->cell_by_id (b->id ()); if (ca && cb) { - return m_ascending ? ca->name () < cb->name () : cb->name () < ca->name (); + return m_ascending ? ca->qname () < cb->qname () : cb->qname () < ca->qname (); } const rdb::Category *xa = mp_rdb->category_by_id (a->id ()); @@ -582,7 +582,7 @@ public: if (cell->name ().empty ()) { return QObject::tr ("All Cells"); } else { - return QVariant (QString::fromUtf8 ("[") + tl::to_qstring (cell->name ()) + QString::fromUtf8 ("]")); + return QVariant (QString::fromUtf8 ("[") + tl::to_qstring (cell->qname ()) + QString::fromUtf8 ("]")); } } diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 766d43183..6b17dd753 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -174,10 +174,28 @@ public: const rdb::Cell *cell_for_id (const db::Layout *layout, db::cell_index_type ci) { tl_assert (layout != 0); + std::string cn = layout->cell_name (ci); - const rdb::Cell *rdb_cell = mp_rdb->cell_by_qname (cn); + std::string layout_cn = cn; + std::string qname = cn; + std::string var; + + // resolve references to original cells in deep mode and determine variant + if (layout->builder ()) { + const db::Layout *source = layout->builder ()->source ().layout (); + if (source) { + const std::pair &vs = layout->builder ()->variant_of_source (ci); + if (! vs.second.empty () && source->is_valid_cell_index (vs.first)) { + var = vs.second; + cn = source->cell_name (vs.first); + qname = cn + ":" + var; + } + } + } + + const rdb::Cell *rdb_cell = mp_rdb->cell_by_qname (qname); if (! rdb_cell) { - return mp_rdb->create_cell (cn); + return mp_rdb->create_cell (cn, var, layout_cn); } else { return rdb_cell; } From 1c367f3757d3e1452531b8bd0017073c22d61bfb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 16:16:21 +0200 Subject: [PATCH 25/41] Updating test data --- src/rdb/unit_tests/rdbRVEReaderTests.cc | 6 +++--- testdata/drc/drcSimpleTests_au14b.lyrdb | 6 ++++++ testdata/drc/drcSimpleTests_au14b_2.lyrdb | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/rdb/unit_tests/rdbRVEReaderTests.cc b/src/rdb/unit_tests/rdbRVEReaderTests.cc index 4a22a8e2e..66368ae81 100644 --- a/src/rdb/unit_tests/rdbRVEReaderTests.cc +++ b/src/rdb/unit_tests/rdbRVEReaderTests.cc @@ -69,15 +69,15 @@ void run_rve_test (tl::TestBase *_this, const std::string &fn_rve, const std::st TEST(1) { - run_rve_test (_this, "rve1.db", "rve1_au.txt"); + run_rve_test (_this, "rve1.db", "rve1_au_2.txt"); } TEST(2) { - run_rve_test (_this, "rve2.db", "rve2_au.txt"); + run_rve_test (_this, "rve2.db", "rve2_au_2.txt"); } TEST(3) { - run_rve_test (_this, "rve3.db", "rve3_au.txt"); + run_rve_test (_this, "rve3.db", "rve3_au_2.txt"); } diff --git a/testdata/drc/drcSimpleTests_au14b.lyrdb b/testdata/drc/drcSimpleTests_au14b.lyrdb index 0b6c00a9d..e025e989e 100644 --- a/testdata/drc/drcSimpleTests_au14b.lyrdb +++ b/testdata/drc/drcSimpleTests_au14b.lyrdb @@ -24,6 +24,7 @@ TOP + @@ -35,6 +36,7 @@ TOP false 1 + edge-pair: (-0.2,0.7;1,0.7)|(1,1.1;-0.2,1.1) @@ -46,6 +48,7 @@ TOP false 1 + edge-pair: (0,0;0,0.9)|(0.3,0.9;0.3,0) @@ -57,6 +60,7 @@ TOP false 1 + edge-pair: (0.3,0;0,0)|(0,0.9;0.3,0.9) @@ -68,6 +72,7 @@ TOP false 1 + edge-pair: (0.5,0;0.5,0.9)|(0.8,0.9;0.8,0) @@ -79,6 +84,7 @@ TOP false 1 + edge-pair: (0.8,0;0.5,0)|(0.5,0.9;0.8,0.9) diff --git a/testdata/drc/drcSimpleTests_au14b_2.lyrdb b/testdata/drc/drcSimpleTests_au14b_2.lyrdb index f3430532f..71743b100 100644 --- a/testdata/drc/drcSimpleTests_au14b_2.lyrdb +++ b/testdata/drc/drcSimpleTests_au14b_2.lyrdb @@ -18,6 +18,7 @@ TOP + @@ -29,6 +30,7 @@ TOP false 1 + edge-pair: (0,0.9;0.3,0.9)/(1,1.1;-0.2,1.1) @@ -40,6 +42,7 @@ TOP false 1 + edge-pair: (0.5,0.9;0.8,0.9)/(1,1.1;-0.2,1.1) From fbc336dde2a463da206561ca7be1f0f58f73c93e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 17:37:58 +0200 Subject: [PATCH 26/41] Added tests --- src/drc/unit_tests/drcSimpleTests.cc | 71 +++ testdata/drc/drcSimpleTests_110.drc | 16 + testdata/drc/drcSimpleTests_110.gds | Bin 0 -> 472 bytes testdata/drc/drcSimpleTests_111.drc | 14 + testdata/drc/drcSimpleTests_111.gds | Bin 0 -> 472 bytes testdata/drc/drcSimpleTests_au110.lyrdb | 585 ++++++++++++++++++++++++ testdata/drc/drcSimpleTests_au111.lyrdb | 382 ++++++++++++++++ 7 files changed, 1068 insertions(+) create mode 100644 testdata/drc/drcSimpleTests_110.drc create mode 100644 testdata/drc/drcSimpleTests_110.gds create mode 100644 testdata/drc/drcSimpleTests_111.drc create mode 100644 testdata/drc/drcSimpleTests_111.gds create mode 100644 testdata/drc/drcSimpleTests_au110.lyrdb create mode 100644 testdata/drc/drcSimpleTests_au111.lyrdb diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 1c8180a8d..e08cd0a2c 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1702,3 +1702,74 @@ TEST(102d_edge_modes) { run_test (_this, "102", true); } + +TEST(110_RDBVariantAssignment) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_110.drc"; + + // apart from that it's a variant of 14b ... + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_110.gds"; + + std::string au_report = tl::testdata (); + au_report += "/drc/drcSimpleTests_au110.lyrdb"; + + std::string report = this->tmp_file ("tmp.lydrc"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_force_gc = true\n" + "$drc_test_source = '%s'\n" + "$drc_test_report = '%s'\n" + , input, report) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + compare_text_files (report, au_report); +} + +TEST(111_RDBCategoryHierarchy) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_111.drc"; + + // apart from that it's a variant of 14b ... + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_111.gds"; + + std::string au_report = tl::testdata (); + au_report += "/drc/drcSimpleTests_au111.lyrdb"; + + std::string report = this->tmp_file ("tmp.lydrc"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_force_gc = true\n" + "$drc_test_source = '%s'\n" + "$drc_test_report = '%s'\n" + , input, report) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + compare_text_files (report, au_report); +} + diff --git a/testdata/drc/drcSimpleTests_110.drc b/testdata/drc/drcSimpleTests_110.drc new file mode 100644 index 000000000..f087e932e --- /dev/null +++ b/testdata/drc/drcSimpleTests_110.drc @@ -0,0 +1,16 @@ + +source($drc_test_source) +report("Report comment with\nanother line", $drc_test_report) + +deep + +l1 = input(1, 0) +l1 = l1.sized(0.1, 0.0) + +l1.output("l1", "L1 (1/0 sized by x=100nm,y=0)") +l1.drc(width < 1.0.um).output("w1um", "w < 1µm\nFrom sized input") + +l1s = l1.snapped(200.nm) +l1s.output("l1_snapped", "L1 snapped to 200nm") +l1s.drc(width < 1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") + diff --git a/testdata/drc/drcSimpleTests_110.gds b/testdata/drc/drcSimpleTests_110.gds new file mode 100644 index 0000000000000000000000000000000000000000..131ac19daee529fd226dc3d77eec1571b8db524f GIT binary patch literal 472 zcmZQzV_;&6V31*CVt>KF!XU%I4}_Y?Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+ zDuh9X6}LV%Ha15F1{Mwm1~y(M21W)pJ|+eR1|0!rAc+Ln9Dtk?Kt)g*Ns2`fsGpqy zuPq#GY$5&uKy_R|BZNRU@_=k$`~no(zyLOZ8z#dh#RxQ=M~2l|-~q%M4q=eX7#Uu#b7f~O$V9`HyzD?HXxJuZZI(L+b}TjdI0GKKz<1WgWwNf5CR=4tOCSu7#IXH sAmRdN;OfCPp_vB`FOC@u4BR^y7(g_i00RTp2Lufh2hyKF!XU%I4}_Y?Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+ zDuh9X6}LV%Ha15F1{Mwm1~y(M21W)pJ|+eR1|0!rAc+Ln9Dtk?Kt)g*Ns2`fsGpqy zuPq#GY$5&uKy_R|BZNRU@_=k$`~no(zyLOZ8z#dh#RxQ=M~2l|-~q%M4q=eX7#Uu#b7f~O$V9`HyzD?HXxJuZZI(L+b}TjdI0GKKz<1WgWwNf5CR=4tOCSu7#IXH sAmRdN;OfCPp_vB`FOC@u4BR^y7(g_i00RTp2Lufh2hy + + Report comment with +another line + + drc: script='.drc' + TOP + + + + + l1 + L1 (1/0 sized by x=100nm,y=0) + + + + + w1um + w < 1µm +From sized input + + + + + l1_snapped + L1 snapped to 200nm + + + + + w1um_snapped + w < 1µm +From snapped input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + A + r0;r0(-0.1,0) + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r0;r0 + A$VAR1$1 + + + TOP + r0 *1 1.2,1.2 + + + + + + + + l1 + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + l1 + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + l1 + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + l1 + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + + l1_snapped + TOP + false + 1 + + + + polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) + + + + + l1_snapped + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;3,3.6;3,3) + + + + + l1_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + l1_snapped + A:r90 + false + 1 + + + + polygon: (0,0;0,1.8;0.2,1.8;0.2,0) + + + + + l1_snapped + A:r0;r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) + + + + + w1um_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um_snapped + A:r90 + false + 1 + + + + edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) + + + + + w1um_snapped + A:r0;r0 + false + 1 + + + + edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) + + + + diff --git a/testdata/drc/drcSimpleTests_au111.lyrdb b/testdata/drc/drcSimpleTests_au111.lyrdb new file mode 100644 index 000000000..63a47e6f0 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au111.lyrdb @@ -0,0 +1,382 @@ + + + Report comment with +another line + + drc: script='.drc' + TOP + + + + + input + + + + L1 + Original layer + + + + + L1S + Sized original layer + + + + + + + w1um + w < 1µm +From sized input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + + + + input.L1 + TOP + false + 1 + + + + polygon: (3.8,3.9;3.4,4;3.7,4.6;5.2,4.9;5.1,4.2;3.7,4.3) + + + + + input.L1 + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;2.8,3.6;2.8,3) + + + + + input.L1 + A:r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.2,1.6;0.2,0) + + + + + input.L1 + A:r90 + false + 1 + + + + polygon: (0,0;0,1.6;0.2,1.6;0.2,0) + + + + + input.L1S + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + input.L1S + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + input.L1S + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + input.L1S + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + From 4f96914b3c1aedc32fe517b35673d39579824afd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 17:52:51 +0200 Subject: [PATCH 27/41] Also copy comments on RDB#apply --- src/rdb/rdb/rdb.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index df19f88dc..7771727b3 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1876,6 +1876,7 @@ Database::apply (const rdb::Database &other) // actually transfer the attributes here + i->set_comment (other->comment ()); // TODO: this has some optimization potential in terms of performance ... i->set_image_str (other->image_str ()); i->set_tag_str (other->tag_str ()); From daa406d5189884a7f08f82927d9916b73c9a2d4c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 17:53:10 +0200 Subject: [PATCH 28/41] Auto-apply *.w files on RDB output in DRC --- src/drc/drc/built-in-macros/_drc_engine.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 157154698..df4592a7e 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -115,11 +115,29 @@ module DRC end def write + if @file_name + rdb_file = @engine._make_path(@file_name) + + # Apply waive DB if possible + wdb_file = rdb_file + ".w" + if File.exists?(wdb_file) + begin + wdb = RBA::ReportDatabase::new + wdb.load(wdb_file) + @engine.info("Applying waive database: #{wdb_file} ..") + @rdb.apply(wdb) + wdb._destroy + rescue + end + end + @engine.info("Writing report database: #{rdb_file} ..") @rdb.save(rdb_file) + end + end def rdb From 9619081f8471ec9a9cb1e7cf57e3ace8e479e02f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 18:50:33 +0200 Subject: [PATCH 29/41] Use fuzzy compare for RDB#apply --- src/rdb/rdb/rdb.cc | 62 +++++++++++++++++++++++++++++++++++++++++++--- src/rdb/rdb/rdb.h | 5 +--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 7771727b3..b3ea21a41 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -174,6 +174,62 @@ template <> RDB_PUBLIC std::string Value::to_display_string () const return to_string (); } +// compare implementations + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value < other->m_value - db::epsilon; +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value < other->m_value; +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value < other->m_value; +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + +template <> RDB_PUBLIC bool Value::compare (const ValueBase *o) const +{ + const Value *other = static_cast *> (o); + return m_value.less (other->m_value); +} + // is_shape implementations template <> RDB_PUBLIC bool Value::is_shape () const @@ -881,14 +937,14 @@ Tags::tag (const std::string &name, bool user_tag) const Tag & Tags::tag (id_type id) const { - tl_assert (id - 1 < m_tags.size () && id > 0); + tl_assert (id < m_tags.size () + 1 && id > 0); return m_tags [id - 1]; } Tag & Tags::tag (id_type id) { - tl_assert (id - 1 < m_tags.size () && id > 0); + tl_assert (id < m_tags.size () + 1 && id > 0); return m_tags [id - 1]; } @@ -1830,7 +1886,7 @@ Database::apply (const rdb::Database &other) } for (auto c = other.categories ().begin (); c != other.categories ().end (); ++c) { - const rdb::Category *this_cat = category_by_name (c->name ()); + const rdb::Category *this_cat = category_by_name (c->path ()); if (this_cat) { cat2cat.insert (std::make_pair (this_cat->id (), c->id ())); } diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index a2d0f0b5a..d8f6c3d18 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -506,10 +506,7 @@ public: return type_index_of (); } - bool compare (const ValueBase *other) const - { - return m_value < static_cast *> (other)->m_value; - } + bool compare (const ValueBase *other) const; bool is_shape () const; From 9fcd9d76161df9d900a65be5cf27aa6ed8c8aff5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 18:50:53 +0200 Subject: [PATCH 30/41] RDB browser: mark waived items as unvisited initially --- src/layui/layui/rdbMarkerBrowserPage.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 380e327e7..173f20739 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -367,6 +367,7 @@ public: { mp_database = db; m_waived_tag_id = mp_database ? mp_database->tags ().tag ("waived").id () : 0; + invalidate (); } @@ -1888,6 +1889,16 @@ MarkerBrowserPage::set_rdb (rdb::Database *database) rerun_button->setToolTip (QString ()); } + // mark items visited that carry the waived flag + if (mp_database) { + id_type waived_tag_id = mp_database->tags ().tag ("waived").id (); + for (auto i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) { + if (i->has_tag (waived_tag_id)) { + mp_database->set_item_visited (i.operator-> (), true); + } + } + } + QAbstractItemModel *tree_model = directory_tree->model (); MarkerBrowserTreeViewModel *new_model = new MarkerBrowserTreeViewModel (); From 86a2a6dd40e1d2b2b7e5b628c0b95ff5ef08e384 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 19:05:39 +0200 Subject: [PATCH 31/41] 'Save As Waiver DB' implemented --- src/layui/layui/MarkerBrowserDialog.ui | 8 ++++++++ src/layui/layui/rdbMarkerBrowserDialog.cc | 25 +++++++++++++++++++++++ src/layui/layui/rdbMarkerBrowserDialog.h | 1 + src/rdb/rdb/rdb.h | 7 +++++++ src/rdb/rdb/rdbFile.cc | 16 +++++++++++---- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/layui/layui/MarkerBrowserDialog.ui b/src/layui/layui/MarkerBrowserDialog.ui index 65a398b74..298ac92a3 100644 --- a/src/layui/layui/MarkerBrowserDialog.ui +++ b/src/layui/layui/MarkerBrowserDialog.ui @@ -368,6 +368,14 @@ to load a marker database QAction::NoRole + + + Save As Waiver DB + + + QAction::NoRole + + diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 6f7fb1f14..e388ef7da 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -88,6 +88,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView connect (mp_ui->open_action, SIGNAL (triggered ()), this, SLOT (open_clicked ())); connect (mp_ui->save_action, SIGNAL (triggered ()), this, SLOT (save_clicked ())); connect (mp_ui->saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ())); + connect (mp_ui->saveas_waiver_db_action, SIGNAL (triggered ()), this, SLOT (saveas_waiver_db_clicked ())); connect (mp_ui->export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ())); connect (mp_ui->reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ())); connect (mp_ui->info_action, SIGNAL (triggered ()), this, SLOT (info_clicked ())); @@ -97,6 +98,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView mp_ui->file_menu->addAction (mp_ui->open_action); mp_ui->file_menu->addAction (mp_ui->save_action); mp_ui->file_menu->addAction (mp_ui->saveas_action); + mp_ui->file_menu->addAction (mp_ui->saveas_waiver_db_action); QAction *sep0 = new QAction (mp_ui->file_menu); sep0->setSeparator (true); mp_ui->file_menu->addAction (mp_ui->export_action); @@ -393,6 +395,28 @@ BEGIN_PROTECTED END_PROTECTED } +void +MarkerBrowserDialog::saveas_waiver_db_clicked () +{ +BEGIN_PROTECTED + + rdb::Database *rdb = 0; + if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) { + rdb = view ()->get_rdb (m_rdb_index); + } + if (! rdb) { + return; + } + + if (rdb->filename ().empty ()) { + throw tl::Exception (tl::to_string (tr ("The current report database is not saved.\nSave it to some file with 'Save As', before saving it as waiver DB."))); + } + + rdb->write (rdb->filename () + ".w"); + +END_PROTECTED +} + void MarkerBrowserDialog::saveas_clicked () { @@ -760,6 +784,7 @@ MarkerBrowserDialog::update_content () mp_ui->save_action->setEnabled (rdb != 0); mp_ui->saveas_action->setEnabled (rdb != 0); + mp_ui->saveas_waiver_db_action->setEnabled (rdb != 0); mp_ui->export_action->setEnabled (rdb != 0); mp_ui->unload_action->setEnabled (rdb != 0); mp_ui->unload_all_action->setEnabled (rdb != 0); diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index 3d651b3fb..a7844414e 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -77,6 +77,7 @@ public slots: void info_clicked (); void save_clicked (); void saveas_clicked (); + void saveas_waiver_db_clicked (); void export_clicked (); void reload_clicked (); void open_clicked (); diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index d8f6c3d18..d368b5332 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -2444,6 +2444,13 @@ public: */ void save (const std::string &filename); + /** + * @brief Write the database to a file + * + * This function is like "save", but does not update the file name attribute. + */ + void write (const std::string &filename); + /** * @brief Load the database from a file * diff --git a/src/rdb/rdb/rdbFile.cc b/src/rdb/rdb/rdbFile.cc index d7575e839..e6159b6a4 100644 --- a/src/rdb/rdb/rdbFile.cc +++ b/src/rdb/rdb/rdbFile.cc @@ -118,17 +118,25 @@ make_rdb_structure (rdb::Database *rdb) } // ------------------------------------------------------------- -// Implementation of rdb::Database::save +// Implementation of rdb::Database::save and write // TODO: move this somewhere else - with generalized functionality void rdb::Database::save (const std::string &fn) { - tl::OutputStream os (fn, tl::OutputStream::OM_Auto); - make_rdb_structure (this).write (os, *this); + write (fn); set_filename (fn); +} - tl::log << "Saved RDB to " << fn; +void +rdb::Database::write (const std::string &fn) +{ + tl::OutputStream os (fn, tl::OutputStream::OM_Auto); + make_rdb_structure (this).write (os, *this); + + if (tl::verbosity () >= 10) { + tl::log << "Saved RDB to " << fn; + } } // ------------------------------------------------------------- From 7e323442870e34a8023b8977dc8cf7de86fa9ca5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 19:23:44 +0200 Subject: [PATCH 32/41] Including sub-categories in RDB::apply, fixing handling of waived count in a category hierarchy in marker browser --- src/layui/layui/rdbMarkerBrowserPage.cc | 22 ++++++++++++++++++++-- src/rdb/rdb/rdb.cc | 17 +++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 173f20739..7d9625053 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -806,25 +806,43 @@ private: size_t num_waived_per_cat (id_type cat_id) const { - auto ii = mp_database->items_by_category (cat_id); size_t n = 0; + + auto ii = mp_database->items_by_category (cat_id); for (auto i = ii.first; i != ii.second; ++i) { if ((*i)->has_tag (m_waived_tag_id)) { ++n; } } + + // include sub-categories + const rdb::Category *cat = mp_database->category_by_id (cat_id); + tl_assert (cat != 0); + for (auto c = cat->sub_categories ().begin (); c != cat->sub_categories ().end (); ++c) { + n += num_waived_per_cat (c->id ()); + } + return n; } size_t num_waived_per_cell_and_cat (id_type cell_id, id_type cat_id) const { - auto ii = mp_database->items_by_cell_and_category (cell_id, cat_id); size_t n = 0; + + auto ii = mp_database->items_by_cell_and_category (cell_id, cat_id); for (auto i = ii.first; i != ii.second; ++i) { if ((*i)->has_tag (m_waived_tag_id)) { ++n; } } + + // include sub-categories + const rdb::Category *cat = mp_database->category_by_id (cat_id); + tl_assert (cat != 0); + for (auto c = cat->sub_categories ().begin (); c != cat->sub_categories ().end (); ++c) { + n += num_waived_per_cell_and_cat (cell_id, c->id ()); + } + return n; } diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index b3ea21a41..b1e41866c 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1868,6 +1868,18 @@ namespace }; } +static void map_category (const rdb::Category &cat, const rdb::Database &db, std::map &cat2cat) +{ + const rdb::Category *this_cat = db.category_by_name (cat.path ()); + if (this_cat) { + cat2cat.insert (std::make_pair (this_cat->id (), cat.id ())); + } + + for (auto c = cat.sub_categories ().begin (); c != cat.sub_categories ().end (); ++c) { + map_category (*c, db, cat2cat); + } +} + void Database::apply (const rdb::Database &other) { @@ -1886,10 +1898,7 @@ Database::apply (const rdb::Database &other) } for (auto c = other.categories ().begin (); c != other.categories ().end (); ++c) { - const rdb::Category *this_cat = category_by_name (c->path ()); - if (this_cat) { - cat2cat.insert (std::make_pair (this_cat->id (), c->id ())); - } + map_category (*c, *this, cat2cat); } std::map tags_by_name; From 92a0c38d96ab98d4962fc138e6c91252a0554670 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 19:32:54 +0200 Subject: [PATCH 33/41] Tests for waiving flow --- src/drc/unit_tests/drcSimpleTests.cc | 44 ++ testdata/drc/drcSimpleTests_112.drc | 16 + testdata/drc/drcSimpleTests_112.gds | Bin 0 -> 472 bytes testdata/drc/drcSimpleTests_au112.lyrdb | 596 +++++++++++++++++++++ testdata/drc/drcSimpleTests_au112.lyrdb.w | 616 ++++++++++++++++++++++ 5 files changed, 1272 insertions(+) create mode 100644 testdata/drc/drcSimpleTests_112.drc create mode 100644 testdata/drc/drcSimpleTests_112.gds create mode 100644 testdata/drc/drcSimpleTests_au112.lyrdb create mode 100644 testdata/drc/drcSimpleTests_au112.lyrdb.w diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index e08cd0a2c..db764bd02 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -25,6 +25,7 @@ #include "dbTestSupport.h" #include "dbNetlist.h" #include "dbNetlistSpiceReader.h" +#include "rdb.h" #include "lymMacro.h" #include "tlFileUtils.h" @@ -1773,3 +1774,46 @@ TEST(111_RDBCategoryHierarchy) compare_text_files (report, au_report); } +TEST(112_Waiving) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_112.drc"; + + // apart from that it's a variant of 14b ... + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_112.gds"; + + std::string au_report = tl::testdata (); + au_report += "/drc/drcSimpleTests_au112.lyrdb"; + + std::string report = this->tmp_file ("tmp.lydrc"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_force_gc = true\n" + "$drc_test_source = '%s'\n" + "$drc_test_report = '%s'\n" + , input, report) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + // prepare a waiver db + { + std::string report_w = this->tmp_file ("tmp.lydrc.w"); + rdb::Database rdb_w; + rdb_w.load (au_report + ".w"); + rdb_w.write (report_w); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + compare_text_files (report, au_report); +} + diff --git a/testdata/drc/drcSimpleTests_112.drc b/testdata/drc/drcSimpleTests_112.drc new file mode 100644 index 000000000..7c21b7f48 --- /dev/null +++ b/testdata/drc/drcSimpleTests_112.drc @@ -0,0 +1,16 @@ + +source($drc_test_source) +report("Report comment with\nanother line", $drc_test_report) + +deep + +l1 = input(1, 0) +l1 = l1.sized(0.1, 0.0) + +l1.output(["inputs", "l1"], "L1 (1/0 sized by x=100nm,y=0)") +l1.drc(width < 1.0.um).output("w1um", "w < 1µm\nAnother line") + +l1s = l1.snapped(200.nm) +l1s.output([ "inputs", "l1_snapped" ], "L1 snapped to 200nm") +l1s.drc(width < 1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") + diff --git a/testdata/drc/drcSimpleTests_112.gds b/testdata/drc/drcSimpleTests_112.gds new file mode 100644 index 0000000000000000000000000000000000000000..131ac19daee529fd226dc3d77eec1571b8db524f GIT binary patch literal 472 zcmZQzV_;&6V31*CVt>KF!XU%I4}_Y?Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+ zDuh9X6}LV%Ha15F1{Mwm1~y(M21W)pJ|+eR1|0!rAc+Ln9Dtk?Kt)g*Ns2`fsGpqy zuPq#GY$5&uKy_R|BZNRU@_=k$`~no(zyLOZ8z#dh#RxQ=M~2l|-~q%M4q=eX7#Uu#b7f~O$V9`HyzD?HXxJuZZI(L+b}TjdI0GKKz<1WgWwNf5CR=4tOCSu7#IXH sAmRdN;OfCPp_vB`FOC@u4BR^y7(g_i00RTp2Lufh2hy + + Report comment with +another line + + drc: script='.drc' + TOP + + + waived + + + + + + inputs + + + + l1 + L1 (1/0 sized by x=100nm,y=0) + + + + + l1_snapped + L1 snapped to 200nm + + + + + + + w1um + w < 1µm +Another line + + + + + w1um_snapped + w < 1µm +From snapped input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + A + r0;r0(-0.1,0) + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r0;r0 + A$VAR1$1 + + + TOP + r0 *1 1.2,1.2 + + + + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + inputs.l1 + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1 + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + waived + w1um + TOP + false + 1 + First item von w1um + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + waived + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;3,3.6;3,3) + + + + waived + inputs.l1_snapped + A:r0;r0(-0.1,0) + false + 1 + A comment on the only waived input shape + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1_snapped + A:r90 + false + 1 + + + + polygon: (0,0;0,1.8;0.2,1.8;0.2,0) + + + + + inputs.l1_snapped + A:r0;r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + waived + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + waived + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) + + + + + w1um_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um_snapped + A:r90 + false + 1 + + + + edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) + + + + + w1um_snapped + A:r0;r0 + false + 1 + A comment +With two lines + + + edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) + + + + diff --git a/testdata/drc/drcSimpleTests_au112.lyrdb.w b/testdata/drc/drcSimpleTests_au112.lyrdb.w new file mode 100644 index 000000000..c4b5fd756 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au112.lyrdb.w @@ -0,0 +1,616 @@ + + + waiving +another line + /home/matthias/klayout/testdata/waiving.gds + drc: script='/home/matthias/.klayout/drc/waiving.lydrc' + TOP + + + waived + + + + red + + + + green + + + + blue + + + + yellow + + + + important + + + + + + inputs + + + + l1 + L1 (1/0 sized by x=100nm,y=0) + + + + + l1_snapped + L1 snapped to 200nm + + + + + + + w1um + w < 1µm +Another line + + + + + w1um_snapped + w < 1µm +From snapped input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + A + r0;r0(-0.1,0) + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r0;r0 + A$VAR1$1 + + + TOP + r0 *1 1.2,1.2 + + + + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + inputs.l1 + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1 + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + waived + w1um + TOP + true + 1 + First item von w1um + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + true + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + waived + w1um + A:r0 + true + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um + A:r90 + true + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;3,3.6;3,3) + + + + waived + inputs.l1_snapped + A:r0;r0(-0.1,0) + true + 1 + A comment on the only waived input shape + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1_snapped + A:r90 + false + 1 + + + + polygon: (0,0;0,1.8;0.2,1.8;0.2,0) + + + + + inputs.l1_snapped + A:r0;r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + waived + w1um_snapped + TOP + true + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + waived + w1um_snapped + TOP + true + 1 + + + + edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) + + + + + w1um_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um_snapped + A:r90 + true + 1 + + + + edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) + + + + + w1um_snapped + A:r0;r0 + false + 1 + A comment +With two lines + + + edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) + + + + From 405ac5109a5ce95a2ec553c1727b3958da5a6a69 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 19:57:10 +0200 Subject: [PATCH 34/41] Update DRC documentation, explaining the waive flow. --- src/doc/doc/manual/drc_basic.xml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/doc/doc/manual/drc_basic.xml b/src/doc/doc/manual/drc_basic.xml index 4fe88fc7a..a5f0cc756 100644 --- a/src/doc/doc/manual/drc_basic.xml +++ b/src/doc/doc/manual/drc_basic.xml @@ -234,6 +234,49 @@ gate.width(min_width).output("gate_width", "Gate width violations")
    Hence such files can only be run from the macro IDE.

    +

    DRC waiving flow

    + +

    + DRC waiving is a process of signing off DRC violations that cannot be avoided. + Usually DRC waiving is not encouraged, as manufacturability of the device cannot + be guaranteed if DRC violations are present. Even worse, giving a non-clean layout + into manufacturing may create a contamination risk that manufacturers will try + to avoid. Hence, non-DRC-clean layouts are usually rejected. +

    + +

    + Still there are some legit reasons for DRC waiving. Sometimes DRC rules do not apply + because a specific technology option is not used by the device and corresponding + DRC rules do not apply. Or, a certain DRC rule may be a recommended rule, and violating + it is not forbidden. In that case, a DRC violation can be waived at your own risk. + Waiving is not a formal process. Usually, the manufacturer will ask for a confirmation + if DRC violations are present. KLayout can help documenting violations and copying + the waivers from one DRC run to the next. +

    + +

    + The DRC waiving flow of KLayout is the following: +

    + +
      +
    • In the initial step, a RDB database is created by the DRC run, using the "report" command with a destination file.
    • +
    • This report is inspected in the marker browser. You can add comments, set flags and add screenshots. This is + also the time to waive DRC violations that you deem necessary to be waived. The marker browser has a "waive" + button which sets or resets the waived flag of the selected markers.
    • +
    • When finished, save the edited database to a 'waiver DB' using the marker browser's 'Save As Waiver DB' feature from + the File menu. Technically, this will write the report database to a second file. This second file is named + like the original one, with a ".w" added to the file name.
    • +
    • When you run the DRC again, KLayout will find this waiver DB file and apply attributes from the waiver DB + to the current report database. These attributes include the flags, images and comments. + This will - among the other annotations - apply the waived flag to the report database items.
    • +
    + +

    + In the waiving step, KLayout will apply attributes to items with the same value (i.e. shape), category and cell. + This specifically means, that when you rename a cell, waivers will no longer be applied, or - in the worst case - + be applied to the wrong cell. Hence, waiving should be done late in the process, when cell names are unlikely to change. +

    +

    Using KLayout as a standalone DRC engine

    From bc2028cedb544fc62041897083a43a3e4f097bc3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 28 Apr 2024 22:59:21 +0200 Subject: [PATCH 35/41] Providing test data variants --- ...110.lyrdb => drcSimpleTests_au110.lyrdb.1} | 0 testdata/drc/drcSimpleTests_au110.lyrdb.2 | 585 +++++++++++++++++ ...111.lyrdb => drcSimpleTests_au111.lyrdb.1} | 0 testdata/drc/drcSimpleTests_au111.lyrdb.2 | 382 +++++++++++ ...112.lyrdb => drcSimpleTests_au112.lyrdb.1} | 0 testdata/drc/drcSimpleTests_au112.lyrdb.2 | 596 ++++++++++++++++++ 6 files changed, 1563 insertions(+) rename testdata/drc/{drcSimpleTests_au110.lyrdb => drcSimpleTests_au110.lyrdb.1} (100%) create mode 100644 testdata/drc/drcSimpleTests_au110.lyrdb.2 rename testdata/drc/{drcSimpleTests_au111.lyrdb => drcSimpleTests_au111.lyrdb.1} (100%) create mode 100644 testdata/drc/drcSimpleTests_au111.lyrdb.2 rename testdata/drc/{drcSimpleTests_au112.lyrdb => drcSimpleTests_au112.lyrdb.1} (100%) create mode 100644 testdata/drc/drcSimpleTests_au112.lyrdb.2 diff --git a/testdata/drc/drcSimpleTests_au110.lyrdb b/testdata/drc/drcSimpleTests_au110.lyrdb.1 similarity index 100% rename from testdata/drc/drcSimpleTests_au110.lyrdb rename to testdata/drc/drcSimpleTests_au110.lyrdb.1 diff --git a/testdata/drc/drcSimpleTests_au110.lyrdb.2 b/testdata/drc/drcSimpleTests_au110.lyrdb.2 new file mode 100644 index 000000000..439de7517 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au110.lyrdb.2 @@ -0,0 +1,585 @@ + + + Report comment with +another line + + drc: script='.drc' + TOP + + + + + l1 + L1 (1/0 sized by x=100nm,y=0) + + + + + w1um + w < 1µm +From sized input + + + + + l1_snapped + L1 snapped to 200nm + + + + + w1um_snapped + w < 1µm +From snapped input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + A + r0;r0(-0.1,0) + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r0;r0 + A$VAR1$1 + + + TOP + r0 *1 1.2,1.2 + + + + + + + + l1 + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + l1 + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + l1 + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + l1 + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + + l1_snapped + TOP + false + 1 + + + + polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) + + + + + l1_snapped + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;3,3.6;3,3) + + + + + l1_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + l1_snapped + A:r90 + false + 1 + + + + polygon: (0,0;0,1.8;0.2,1.8;0.2,0) + + + + + l1_snapped + A:r0;r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) + + + + + w1um_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um_snapped + A:r90 + false + 1 + + + + edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) + + + + + w1um_snapped + A:r0;r0 + false + 1 + + + + edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) + + + + diff --git a/testdata/drc/drcSimpleTests_au111.lyrdb b/testdata/drc/drcSimpleTests_au111.lyrdb.1 similarity index 100% rename from testdata/drc/drcSimpleTests_au111.lyrdb rename to testdata/drc/drcSimpleTests_au111.lyrdb.1 diff --git a/testdata/drc/drcSimpleTests_au111.lyrdb.2 b/testdata/drc/drcSimpleTests_au111.lyrdb.2 new file mode 100644 index 000000000..20d7799e5 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au111.lyrdb.2 @@ -0,0 +1,382 @@ + + + Report comment with +another line + + drc: script='.drc' + TOP + + + + + input + + + + L1 + Original layer + + + + + L1S + Sized original layer + + + + + + + w1um + w < 1µm +From sized input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + + + + input.L1 + TOP + false + 1 + + + + polygon: (3.8,3.9;3.4,4;3.7,4.6;5.2,4.9;5.1,4.2;3.7,4.3) + + + + + input.L1 + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;2.8,3.6;2.8,3) + + + + + input.L1 + A:r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.2,1.6;0.2,0) + + + + + input.L1 + A:r90 + false + 1 + + + + polygon: (0,0;0,1.6;0.2,1.6;0.2,0) + + + + + input.L1S + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + input.L1S + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + input.L1S + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + input.L1S + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + diff --git a/testdata/drc/drcSimpleTests_au112.lyrdb b/testdata/drc/drcSimpleTests_au112.lyrdb.1 similarity index 100% rename from testdata/drc/drcSimpleTests_au112.lyrdb rename to testdata/drc/drcSimpleTests_au112.lyrdb.1 diff --git a/testdata/drc/drcSimpleTests_au112.lyrdb.2 b/testdata/drc/drcSimpleTests_au112.lyrdb.2 new file mode 100644 index 000000000..5c9191829 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au112.lyrdb.2 @@ -0,0 +1,596 @@ + + + Report comment with +another line + + drc: script='.drc' + TOP + + + waived + + + + + + inputs + + + + l1 + L1 (1/0 sized by x=100nm,y=0) + + + + + l1_snapped + L1 snapped to 200nm + + + + + + + w1um + w < 1µm +Another line + + + + + w1um_snapped + w < 1µm +From snapped input + + + + + + + TOP + + + + + + + A + r0 + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r90 + A$VAR1 + + + TOP + r270 *1 2,2 + + + + + A + r0;r0(-0.1,0) + A + + + TOP + r0 *1 0.5,1.2 + + + + + A + r0;r0 + A$VAR1$1 + + + TOP + r0 *1 1.2,1.2 + + + + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) + + + + + inputs.l1 + TOP + false + 1 + + + + polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) + + + + + inputs.l1 + A:r0 + false + 1 + + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1 + A:r90 + false + 1 + + + + polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + waived + w1um + TOP + false + 1 + First item von w1um + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + + + waived + w1um + A:r0 + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um + A:r90 + false + 1 + + + + edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) + + + + + inputs.l1_snapped + TOP + false + 1 + + + + polygon: (2.2,3;2.2,3.6;3,3.6;3,3) + + + + waived + inputs.l1_snapped + A:r0;r0(-0.1,0) + false + 1 + A comment on the only waived input shape + + + polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) + + + + + inputs.l1_snapped + A:r90 + false + 1 + + + + polygon: (0,0;0,1.8;0.2,1.8;0.2,0) + + + + + inputs.l1_snapped + A:r0;r0 + false + 1 + + + + polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + waived + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + waived + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) + + + + + w1um_snapped + A:r0;r0(-0.1,0) + false + 1 + + + + edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) + + + + waived + w1um_snapped + A:r90 + false + 1 + + + + edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) + + + + + w1um_snapped + A:r0;r0 + false + 1 + A comment +With two lines + + + edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) + + + + From a7f0f3ba8e2e0a93b52a80414ef449eccbb2bc66 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 00:10:57 +0200 Subject: [PATCH 36/41] Compatibility with old Ruby versions --- src/drc/drc/built-in-macros/_drc_engine.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index df4592a7e..5f17a8c8f 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -3253,9 +3253,19 @@ CODE categories.each do |c| ccat = nil if cat - ccat = cat.each_sub_category.find { |i| i.name == c } + cat.each_sub_category do |i| + if i.name == c + ccat = i + break + end + end else - ccat = output_rdb.each_category.find { |i| i.name == c } + output_rdb.each_category do |i| + if i.name == c + ccat = i + break + end + end end cat = ccat ? ccat : output_rdb.create_category(cat, c) end From 4a2143d234d4ab1ed1978ce9bc16450e5ae73f1d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 00:46:58 +0200 Subject: [PATCH 37/41] Using plain DRC functions for better test reproducibility --- testdata/drc/drcSimpleTests_110.drc | 4 +- testdata/drc/drcSimpleTests_111.drc | 2 +- testdata/drc/drcSimpleTests_112.drc | 4 +- ...110.lyrdb.1 => drcSimpleTests_au110.lyrdb} | 412 ++++++------ testdata/drc/drcSimpleTests_au110.lyrdb.2 | 585 ----------------- ...111.lyrdb.1 => drcSimpleTests_au111.lyrdb} | 242 +++---- testdata/drc/drcSimpleTests_au111.lyrdb.2 | 382 ----------- ...112.lyrdb.1 => drcSimpleTests_au112.lyrdb} | 412 ++++++------ testdata/drc/drcSimpleTests_au112.lyrdb.2 | 596 ------------------ 9 files changed, 538 insertions(+), 2101 deletions(-) rename testdata/drc/{drcSimpleTests_au110.lyrdb.1 => drcSimpleTests_au110.lyrdb} (100%) delete mode 100644 testdata/drc/drcSimpleTests_au110.lyrdb.2 rename testdata/drc/{drcSimpleTests_au111.lyrdb.1 => drcSimpleTests_au111.lyrdb} (100%) delete mode 100644 testdata/drc/drcSimpleTests_au111.lyrdb.2 rename testdata/drc/{drcSimpleTests_au112.lyrdb.1 => drcSimpleTests_au112.lyrdb} (100%) delete mode 100644 testdata/drc/drcSimpleTests_au112.lyrdb.2 diff --git a/testdata/drc/drcSimpleTests_110.drc b/testdata/drc/drcSimpleTests_110.drc index f087e932e..6fb126755 100644 --- a/testdata/drc/drcSimpleTests_110.drc +++ b/testdata/drc/drcSimpleTests_110.drc @@ -8,9 +8,9 @@ l1 = input(1, 0) l1 = l1.sized(0.1, 0.0) l1.output("l1", "L1 (1/0 sized by x=100nm,y=0)") -l1.drc(width < 1.0.um).output("w1um", "w < 1µm\nFrom sized input") +l1.width(1.0.um).output("w1um", "w < 1µm\nFrom sized input") l1s = l1.snapped(200.nm) l1s.output("l1_snapped", "L1 snapped to 200nm") -l1s.drc(width < 1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") +l1s.width(1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") diff --git a/testdata/drc/drcSimpleTests_111.drc b/testdata/drc/drcSimpleTests_111.drc index 8ba7378dd..763b98b93 100644 --- a/testdata/drc/drcSimpleTests_111.drc +++ b/testdata/drc/drcSimpleTests_111.drc @@ -10,5 +10,5 @@ l1s = l1.sized(0.1, 0.0) l1.output([ "input", "L1" ], "Original layer") l1s.output([ "input", "L1S" ], "Sized original layer") -l1s.drc(width < 1.0.um).output("w1um", "w < 1µm\nFrom sized input") +l1s.width(1.0.um).output("w1um", "w < 1µm\nFrom sized input") diff --git a/testdata/drc/drcSimpleTests_112.drc b/testdata/drc/drcSimpleTests_112.drc index 7c21b7f48..fe79de298 100644 --- a/testdata/drc/drcSimpleTests_112.drc +++ b/testdata/drc/drcSimpleTests_112.drc @@ -8,9 +8,9 @@ l1 = input(1, 0) l1 = l1.sized(0.1, 0.0) l1.output(["inputs", "l1"], "L1 (1/0 sized by x=100nm,y=0)") -l1.drc(width < 1.0.um).output("w1um", "w < 1µm\nAnother line") +l1.width(1.0.um).output("w1um", "w < 1µm\nAnother line") l1s = l1.snapped(200.nm) l1s.output([ "inputs", "l1_snapped" ], "L1 snapped to 200nm") -l1s.drc(width < 1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") +l1s.width(1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input") diff --git a/testdata/drc/drcSimpleTests_au110.lyrdb.1 b/testdata/drc/drcSimpleTests_au110.lyrdb similarity index 100% rename from testdata/drc/drcSimpleTests_au110.lyrdb.1 rename to testdata/drc/drcSimpleTests_au110.lyrdb index 793d828a3..77ef871b3 100644 --- a/testdata/drc/drcSimpleTests_au110.lyrdb.1 +++ b/testdata/drc/drcSimpleTests_au110.lyrdb @@ -137,78 +137,6 @@ From snapped input polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - - w1um - TOP - false - 1 - - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - w1um @@ -221,18 +149,6 @@ From snapped input edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - w1um @@ -245,42 +161,6 @@ From snapped input edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - w1um @@ -302,7 +182,31 @@ From snapped input - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) @@ -317,6 +221,18 @@ From snapped input edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + w1um @@ -329,6 +245,90 @@ From snapped input edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + w1um @@ -413,6 +413,90 @@ From snapped input polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + w1um_snapped @@ -446,79 +530,7 @@ From snapped input - edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) @@ -533,18 +545,6 @@ From snapped input edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) - - w1um_snapped diff --git a/testdata/drc/drcSimpleTests_au110.lyrdb.2 b/testdata/drc/drcSimpleTests_au110.lyrdb.2 deleted file mode 100644 index 439de7517..000000000 --- a/testdata/drc/drcSimpleTests_au110.lyrdb.2 +++ /dev/null @@ -1,585 +0,0 @@ - - - Report comment with -another line - - drc: script='.drc' - TOP - - - - - l1 - L1 (1/0 sized by x=100nm,y=0) - - - - - w1um - w < 1µm -From sized input - - - - - l1_snapped - L1 snapped to 200nm - - - - - w1um_snapped - w < 1µm -From snapped input - - - - - - - TOP - - - - - - - A - r0 - A - - - TOP - r0 *1 0.5,1.2 - - - - - A - r90 - A$VAR1 - - - TOP - r270 *1 2,2 - - - - - A - r0;r0(-0.1,0) - A - - - TOP - r0 *1 0.5,1.2 - - - - - A - r0;r0 - A$VAR1$1 - - - TOP - r0 *1 1.2,1.2 - - - - - - - - l1 - TOP - false - 1 - - - - polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) - - - - - l1 - TOP - false - 1 - - - - polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) - - - - - l1 - A:r0 - false - 1 - - - - polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) - - - - - l1 - A:r90 - false - 1 - - - - polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - - w1um - A:r0 - false - 1 - - - - edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) - - - - - w1um - A:r90 - false - 1 - - - - edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) - - - - - l1_snapped - TOP - false - 1 - - - - polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) - - - - - l1_snapped - TOP - false - 1 - - - - polygon: (2.2,3;2.2,3.6;3,3.6;3,3) - - - - - l1_snapped - A:r0;r0(-0.1,0) - false - 1 - - - - polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) - - - - - l1_snapped - A:r90 - false - 1 - - - - polygon: (0,0;0,1.8;0.2,1.8;0.2,0) - - - - - l1_snapped - A:r0;r0 - false - 1 - - - - polygon: (0,0;0,1.6;0.4,1.6;0.4,0) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) - - - - - w1um_snapped - A:r0;r0(-0.1,0) - false - 1 - - - - edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) - - - - - w1um_snapped - A:r90 - false - 1 - - - - edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) - - - - - w1um_snapped - A:r0;r0 - false - 1 - - - - edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) - - - - diff --git a/testdata/drc/drcSimpleTests_au111.lyrdb.1 b/testdata/drc/drcSimpleTests_au111.lyrdb similarity index 100% rename from testdata/drc/drcSimpleTests_au111.lyrdb.1 rename to testdata/drc/drcSimpleTests_au111.lyrdb index 63a47e6f0..37d05841c 100644 --- a/testdata/drc/drcSimpleTests_au111.lyrdb.1 +++ b/testdata/drc/drcSimpleTests_au111.lyrdb @@ -162,78 +162,6 @@ From sized input polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - - w1um - TOP - false - 1 - - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - w1um @@ -246,18 +174,6 @@ From sized input edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - w1um @@ -270,42 +186,6 @@ From sized input edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - w1um @@ -327,7 +207,31 @@ From sized input - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) @@ -342,6 +246,18 @@ From sized input edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + w1um @@ -354,6 +270,90 @@ From sized input edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + w1um diff --git a/testdata/drc/drcSimpleTests_au111.lyrdb.2 b/testdata/drc/drcSimpleTests_au111.lyrdb.2 deleted file mode 100644 index 20d7799e5..000000000 --- a/testdata/drc/drcSimpleTests_au111.lyrdb.2 +++ /dev/null @@ -1,382 +0,0 @@ - - - Report comment with -another line - - drc: script='.drc' - TOP - - - - - input - - - - L1 - Original layer - - - - - L1S - Sized original layer - - - - - - - w1um - w < 1µm -From sized input - - - - - - - TOP - - - - - - - A - r0 - A - - - TOP - r0 *1 0.5,1.2 - - - - - A - r90 - A$VAR1 - - - TOP - r270 *1 2,2 - - - - - - - - input.L1 - TOP - false - 1 - - - - polygon: (3.8,3.9;3.4,4;3.7,4.6;5.2,4.9;5.1,4.2;3.7,4.3) - - - - - input.L1 - TOP - false - 1 - - - - polygon: (2.2,3;2.2,3.6;2.8,3.6;2.8,3) - - - - - input.L1 - A:r0 - false - 1 - - - - polygon: (0,0;0,1.6;0.2,1.6;0.2,0) - - - - - input.L1 - A:r90 - false - 1 - - - - polygon: (0,0;0,1.6;0.2,1.6;0.2,0) - - - - - input.L1S - TOP - false - 1 - - - - polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) - - - - - input.L1S - TOP - false - 1 - - - - polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) - - - - - input.L1S - A:r0 - false - 1 - - - - polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) - - - - - input.L1S - A:r90 - false - 1 - - - - polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - - w1um - A:r0 - false - 1 - - - - edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) - - - - - w1um - A:r90 - false - 1 - - - - edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) - - - - diff --git a/testdata/drc/drcSimpleTests_au112.lyrdb.1 b/testdata/drc/drcSimpleTests_au112.lyrdb similarity index 100% rename from testdata/drc/drcSimpleTests_au112.lyrdb.1 rename to testdata/drc/drcSimpleTests_au112.lyrdb index 5a2297a78..33b59bf2b 100644 --- a/testdata/drc/drcSimpleTests_au112.lyrdb.1 +++ b/testdata/drc/drcSimpleTests_au112.lyrdb @@ -147,78 +147,6 @@ From snapped input polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - waived - w1um - TOP - false - 1 - First item von w1um - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - waived w1um @@ -231,18 +159,6 @@ From snapped input edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - waived w1um @@ -255,42 +171,6 @@ From snapped input edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - waived - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - waived w1um @@ -312,7 +192,31 @@ From snapped input - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) @@ -327,6 +231,18 @@ From snapped input edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) + + waived w1um @@ -339,6 +255,90 @@ From snapped input edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) + + + + waived + w1um + TOP + false + 1 + First item von w1um + + + edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) + + + + waived + w1um + TOP + false + 1 + + + + edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) + + waived w1um @@ -423,6 +423,90 @@ From snapped input polygon: (0,0;0,1.6;0.4,1.6;0.4,0) + + waived + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) + + + + + w1um_snapped + TOP + false + 1 + + + + edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) + + w1um_snapped @@ -456,79 +540,7 @@ From snapped input - edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) - - - - waived - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) + edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) @@ -543,18 +555,6 @@ From snapped input edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) - - w1um_snapped diff --git a/testdata/drc/drcSimpleTests_au112.lyrdb.2 b/testdata/drc/drcSimpleTests_au112.lyrdb.2 deleted file mode 100644 index 5c9191829..000000000 --- a/testdata/drc/drcSimpleTests_au112.lyrdb.2 +++ /dev/null @@ -1,596 +0,0 @@ - - - Report comment with -another line - - drc: script='.drc' - TOP - - - waived - - - - - - inputs - - - - l1 - L1 (1/0 sized by x=100nm,y=0) - - - - - l1_snapped - L1 snapped to 200nm - - - - - - - w1um - w < 1µm -Another line - - - - - w1um_snapped - w < 1µm -From snapped input - - - - - - - TOP - - - - - - - A - r0 - A - - - TOP - r0 *1 0.5,1.2 - - - - - A - r90 - A$VAR1 - - - TOP - r270 *1 2,2 - - - - - A - r0;r0(-0.1,0) - A - - - TOP - r0 *1 0.5,1.2 - - - - - A - r0;r0 - A$VAR1$1 - - - TOP - r0 *1 1.2,1.2 - - - - - - - - inputs.l1 - TOP - false - 1 - - - - polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9) - - - - - inputs.l1 - TOP - false - 1 - - - - polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3) - - - - - inputs.l1 - A:r0 - false - 1 - - - - polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) - - - - - inputs.l1 - A:r90 - false - 1 - - - - polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762) - - - - waived - w1um - TOP - false - 1 - First item von w1um - - - edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3) - - - - waived - w1um - TOP - false - 1 - - - - edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6) - - - - waived - w1um - A:r0 - false - 1 - - - - edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) - - - - waived - w1um - A:r90 - false - 1 - - - - edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1) - - - - - inputs.l1_snapped - TOP - false - 1 - - - - polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4) - - - - - inputs.l1_snapped - TOP - false - 1 - - - - polygon: (2.2,3;2.2,3.6;3,3.6;3,3) - - - - waived - inputs.l1_snapped - A:r0;r0(-0.1,0) - false - 1 - A comment on the only waived input shape - - - polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0) - - - - - inputs.l1_snapped - A:r90 - false - 1 - - - - polygon: (0,0;0,1.8;0.2,1.8;0.2,0) - - - - - inputs.l1_snapped - A:r0;r0 - false - 1 - - - - polygon: (0,0;0,1.6;0.4,1.6;0.4,0) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4) - - - - waived - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2) - - - - waived - w1um_snapped - TOP - false - 1 - - - - edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6) - - - - - w1um_snapped - TOP - false - 1 - - - - edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3) - - - - - w1um_snapped - A:r0;r0(-0.1,0) - false - 1 - - - - edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0) - - - - waived - w1um_snapped - A:r90 - false - 1 - - - - edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0) - - - - - w1um_snapped - A:r0;r0 - false - 1 - A comment -With two lines - - - edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0) - - - - From db0e9efe3107606dbcb3edfb3afc76a1b9a2c020 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 18:51:31 +0200 Subject: [PATCH 38/41] Bugfix: reload was not working properly --- src/rdb/rdb/rdb.cc | 2 +- src/rdb/rdb/rdb.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index b1e41866c..64f8192f4 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1762,7 +1762,7 @@ read_db_from_layout (rdb::Database *db, tl::InputStream &is) } void -Database::load (const std::string &fn) +Database::load (std::string fn) { tl::log << "Loading RDB from " << fn; diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index d368b5332..808af53ba 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -2455,8 +2455,10 @@ public: * @brief Load the database from a file * * Note: This clears the existing database. + * The argument intentionally is a copy, so we can call + * "load (this->filename ())" for reloading. */ - void load (const std::string &filename); + void load (std::string filename); /** * @brief Applies the attributes from a different database From 14f9d579c963fd50fb5f907c6d152852d80f39ea Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 18:56:32 +0200 Subject: [PATCH 39/41] 'apply waiver DB' feature in marker browser --- src/layui/layui/MarkerBrowserDialog.ui | 8 ++++ src/layui/layui/rdbMarkerBrowserDialog.cc | 47 +++++++++++++++++++++++ src/layui/layui/rdbMarkerBrowserDialog.h | 1 + 3 files changed, 56 insertions(+) diff --git a/src/layui/layui/MarkerBrowserDialog.ui b/src/layui/layui/MarkerBrowserDialog.ui index 298ac92a3..dc2e8e58a 100644 --- a/src/layui/layui/MarkerBrowserDialog.ui +++ b/src/layui/layui/MarkerBrowserDialog.ui @@ -376,6 +376,14 @@ to load a marker database QAction::NoRole + + + Apply Waiver DB + + + QAction::NoRole + + diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index e388ef7da..9f4b3d410 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -37,6 +37,7 @@ #include "dbLayoutUtils.h" #include "dbRecursiveShapeIterator.h" #include "dbStream.h" +#include "tlFileUtils.h" #include "ui_MarkerBrowserDialog.h" @@ -89,6 +90,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView connect (mp_ui->save_action, SIGNAL (triggered ()), this, SLOT (save_clicked ())); connect (mp_ui->saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ())); connect (mp_ui->saveas_waiver_db_action, SIGNAL (triggered ()), this, SLOT (saveas_waiver_db_clicked ())); + connect (mp_ui->apply_waiver_db_action, SIGNAL (triggered ()), this, SLOT (apply_waiver_db_clicked ())); connect (mp_ui->export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ())); connect (mp_ui->reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ())); connect (mp_ui->info_action, SIGNAL (triggered ()), this, SLOT (info_clicked ())); @@ -99,6 +101,7 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView mp_ui->file_menu->addAction (mp_ui->save_action); mp_ui->file_menu->addAction (mp_ui->saveas_action); mp_ui->file_menu->addAction (mp_ui->saveas_waiver_db_action); + mp_ui->file_menu->addAction (mp_ui->apply_waiver_db_action); QAction *sep0 = new QAction (mp_ui->file_menu); sep0->setSeparator (true); mp_ui->file_menu->addAction (mp_ui->export_action); @@ -395,6 +398,49 @@ BEGIN_PROTECTED END_PROTECTED } +void +MarkerBrowserDialog::apply_waiver_db_clicked () +{ +BEGIN_PROTECTED + + rdb::Database *rdb = 0; + if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) { + rdb = view ()->get_rdb (m_rdb_index); + } + if (! rdb) { + return; + } + + std::string wdb_filename; + if (! rdb->filename ().empty () && tl::file_exists (rdb->filename () + ".w")) { + + wdb_filename = rdb->filename () + ".w"; + + } else { + + // prepare and open the file dialog + lay::FileDialog open_dialog (this, tl::to_string (QObject::tr ("Apply Waiver DB File")), "Waiver DB files (*.w)"); + + if (! rdb->filename ().empty ()) { + wdb_filename = rdb->filename () + ".w"; + } + + if (! open_dialog.get_open (wdb_filename)) { + return; + } + + } + + rdb::Database wdb; + wdb.load (wdb_filename); + + mp_ui->browser_frame->set_rdb (0); + rdb->apply (wdb); + mp_ui->browser_frame->set_rdb (rdb); + +END_PROTECTED +} + void MarkerBrowserDialog::saveas_waiver_db_clicked () { @@ -785,6 +831,7 @@ MarkerBrowserDialog::update_content () mp_ui->save_action->setEnabled (rdb != 0); mp_ui->saveas_action->setEnabled (rdb != 0); mp_ui->saveas_waiver_db_action->setEnabled (rdb != 0); + mp_ui->apply_waiver_db_action->setEnabled (rdb != 0); mp_ui->export_action->setEnabled (rdb != 0); mp_ui->unload_action->setEnabled (rdb != 0); mp_ui->unload_all_action->setEnabled (rdb != 0); diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index a7844414e..a833d993d 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -78,6 +78,7 @@ public slots: void save_clicked (); void saveas_clicked (); void saveas_waiver_db_clicked (); + void apply_waiver_db_clicked (); void export_clicked (); void reload_clicked (); void open_clicked (); From de535c711fb126fcd76608470cce64943dc745c7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 18:58:04 +0200 Subject: [PATCH 40/41] Fixed problem by creating a default device class from an empty template string --- src/db/db/dbLayoutToNetlistReader.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 1f310c806..127cbc4b7 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -395,12 +395,18 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo throw tl::Exception (tl::to_string (tr ("Duplicate definition of device class: ")) + class_name); } - db::DeviceClassTemplateBase *dct = db::DeviceClassTemplateBase::template_by_name (templ_name); - if (! dct) { - throw tl::Exception (tl::to_string (tr ("Invalid device class template: ")) + templ_name); + db::DeviceClass *dc; + if (templ_name.empty ()) { + // generic device class (issue #1696) + dc = new db::DeviceClass (); + } else { + db::DeviceClassTemplateBase *dct = db::DeviceClassTemplateBase::template_by_name (templ_name); + if (! dct) { + throw tl::Exception (tl::to_string (tr ("Invalid device class template: ")) + templ_name); + } + dc = dct->create (); } - db::DeviceClass *dc = dct->create (); dc->set_name (class_name); netlist->add_device_class (dc); From a10356fd859aa994a7fd99a510951b5407b96435 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Apr 2024 19:18:39 +0200 Subject: [PATCH 41/41] Added tests --- .../dbLayoutToNetlistReaderTests.cc | 24 + testdata/algo/l2n_reader_7.l2n | 1100 ++++++++++++++++ testdata/algo/l2n_reader_au_7.l2n | 1126 +++++++++++++++++ 3 files changed, 2250 insertions(+) create mode 100644 testdata/algo/l2n_reader_7.l2n create mode 100644 testdata/algo/l2n_reader_au_7.l2n diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 4b04f724e..047f6a1f1 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -539,3 +539,27 @@ TEST(6_ReaderLog) compare_text_files (path, au_path); } +// issue #1696 +TEST(7_CustomDevice) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_7.l2n"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_au_7.l2n"); + + compare_text_files (path, au_path); +} diff --git a/testdata/algo/l2n_reader_7.l2n b/testdata/algo/l2n_reader_7.l2n new file mode 100644 index 000000000..0ceaa3d44 --- /dev/null +++ b/testdata/algo/l2n_reader_7.l2n @@ -0,0 +1,1100 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l3 '66/20') +L(l11 '66/44') +L(l12 '67/20') +L(l13 '67/5') +L(l14 '67/44') +L(l15 '68/20') +L(l16 '68/5') +L(l4) +L(l5) +C(l3 l3 l11) +C(l11 l3 l11 l12 l5) +C(l12 l11 l12 l13 l14) +C(l13 l12 l13) +C(l14 l12 l14 l15) +C(l15 l14 l15 l16) +C(l16 l15 l16) +C(l4 l4) +C(l5 l11 l5) +G(l4 SUBSTRATE) +K(MOSCAPN '' + E(W 1 0) + E(L 1 0) + E(N 1 1) + T(G) + T(D) + T(B) +) +K(PMOS MOS4) +K(NMOS MOS4) +D(D$MOSCAPN MOSCAPN + T(G + R(l3 (-8600 -12000) (17200 24000)) + ) + T(D + R(l5 (-8600 -12000) (17200 24000)) + ) + T(B + R(l4 (-8600 -12000) (17200 24000)) + ) +) +D(D$MOSCAPN$1 MOSCAPN + T(G + R(l3 (-8600 -7000) (17200 14000)) + ) + T(D + R(l5 (-8600 -7000) (17200 14000)) + ) + T(B + R(l4 (-8600 -7000) (17200 14000)) + ) +) +D(D$NMOS NMOS + T(S + R(l5 (-490 -5000) (290 10000)) + ) + T(G + R(l3 (-200 -5000) (400 10000)) + ) + T(D + R(l5 (200 -5000) (290 10000)) + ) + T(B + R(l4 (-200 -5000) (400 10000)) + ) +) +D(D$NMOS$1 NMOS + T(S + R(l5 (-490 -5000) (290 10000)) + ) + T(G + R(l3 (-200 -5000) (400 10000)) + ) + T(D + R(l5 (200 -5000) (290 10000)) + ) + T(B + R(l4 (-200 -5000) (400 10000)) + ) +) +D(D$NMOS$2 NMOS + T(S + R(l5 (-490 -5000) (290 10000)) + ) + T(G + R(l3 (-200 -5000) (400 10000)) + ) + T(D + R(l5 (200 -5000) (290 10000)) + ) + T(B + R(l4 (-200 -5000) (400 10000)) + ) +) +X(TOP + R((-9185 0) (84745 24700)) + N(1 I(C2) + R(l3 (39920 5050) (17200 14600)) + R(l3 (490 -14600) (17200 14600)) + R(l11 (-18390 -14550) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (33500 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l12 (-1290 -190) (700 210)) + R(l12 (-17690 -210) (16990 210)) + R(l12 (700 -210) (16990 210)) + R(l14 (-17780 -190) (170 170)) + R(l14 (190 -170) (170 170)) + R(l14 (190 -170) (170 170)) + R(l15 (-980 -270) (1080 370)) + R(l16 (-540 -190) (0 0)) + ) + N(2 I(C1) + R(l3 (750 50) (17200 24600)) + R(l3 (490 -24600) (17200 24600)) + R(l11 (-18390 -24550) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (33500 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l11 (-510 -170) (170 170)) + R(l12 (-1290 -190) (700 210)) + R(l12 (-17690 -210) (16990 210)) + R(l12 (700 -210) (16990 210)) + R(l14 (-17780 -190) (170 170)) + R(l14 (190 -170) (170 170)) + R(l14 (190 -170) (170 170)) + R(l15 (-980 -270) (1080 370)) + R(l16 (-540 -190) (0 0)) + ) + N(3 + R(l11 (-5830 13120) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -2210) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (1210 8330) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -2550) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (1210 9350) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -2550) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (77870 14780) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 170) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 12410) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 170) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-21650 5950) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2890) (170 170)) + R(l11 (-170 2210) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 -4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -9010) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 9010) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -4250) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2890) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -8670) (170 170)) + R(l11 (-170 7990) (170 170)) + R(l11 (-170 14450) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 8670) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2210) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -3910) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 7990) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -9350) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (17520 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2890) (170 170)) + R(l11 (-170 2210) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 -4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -9010) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 9010) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -4250) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5610) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2890) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -8670) (170 170)) + R(l11 (-170 7990) (170 170)) + R(l11 (-170 14450) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-17860 8670) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 2210) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 4250) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -3910) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 7990) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -9350) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -5270) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l12 (-6480 -9155) (290 10000)) + R(l12 (1090 -10000) (290 10000)) + R(l12 (1090 -10000) (290 10000)) + R(l12 (77780 -7595) (230 13000)) + R(l12 (-17860 -750) (17800 500)) + R(l12 (-17860 -12750) (230 13000)) + R(l12 (-170 -2750) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17860 -750) (230 13000)) + R(l12 (-17860 -750) (17800 500)) + R(l12 (-17860 -12750) (230 13000)) + R(l12 (-170 -2750) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-21650 -5750) (230 23000)) + R(l12 (-17860 -1750) (17800 500)) + R(l12 (-17860 -21750) (230 23000)) + R(l12 (-170 -3750) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17860 -1750) (230 23000)) + R(l12 (-17860 -1750) (17800 500)) + R(l12 (-17860 -21750) (230 23000)) + R(l12 (-170 -3750) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l12 (-17800 -2500) (17800 500)) + R(l14 (-21320 8810) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-1550 550) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-1550 550) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (6080 550) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (39000 550) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l14 (-170 -530) (170 170)) + R(l15 (-45655 -235) (45720 1010)) + R(l5 (-45715 -8190) (290 10000)) + R(l5 (1090 -10000) (290 10000)) + R(l5 (1090 -10000) (290 10000)) + R(l5 (77650 -8095) (400 14000)) + R(l5 (-18000 -14000) (400 14000)) + R(l5 (-490 -14000) (400 14000)) + R(l5 (-18000 -14000) (400 14000)) + R(l5 (-4280 -19000) (400 24000)) + R(l5 (-18000 -24000) (400 24000)) + R(l5 (-490 -24000) (400 24000)) + R(l5 (-18000 -24000) (400 24000)) + ) + N(4 I(VSS) + R(l11 (-5140 13120) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (1210 9350) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -2210) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -2210) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (1210 9350) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -850) (170 170)) + R(l11 (-170 850) (170 170)) + R(l11 (-170 -2210) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1870) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (-170 510) (170 170)) + R(l11 (-170 -1190) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -2550) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -2550) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (-170 -1530) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l12 (-2990 -325) (290 10000)) + R(l12 (1090 -10000) (290 10000)) + R(l12 (1090 -10000) (290 10000)) + R(l14 (-2990 -4735) (170 170)) + R(l14 (-170 190) (170 170)) + R(l14 (-170 190) (170 170)) + R(l14 (1210 -890) (170 170)) + R(l14 (-170 190) (170 170)) + R(l14 (-170 190) (170 170)) + R(l14 (1210 -890) (170 170)) + R(l14 (-170 190) (170 170)) + R(l14 (-170 190) (170 170)) + R(l15 (-6975 -945) (7040 1010)) + R(l16 (-5360 -520) (0 0)) + R(l5 (2305 -5700) (290 10000)) + R(l5 (1090 -10000) (290 10000)) + R(l5 (1090 -10000) (290 10000)) + ) + N(5 I(DOWN) + R(l3 (-5600 3225) (400 90)) + R(l3 (-400 -700) (400 610)) + R(l3 (-400 90) (400 10260)) + R(l3 (290 -10350) (400 90)) + R(l3 (-400 0) (400 10260)) + R(l3 (-400 -10960) (400 610)) + R(l3 (290 0) (400 90)) + R(l3 (-400 -700) (400 610)) + R(l3 (-400 90) (400 10260)) + R(l3 (290 -10260) (400 10260)) + R(l3 (-400 -10350) (400 90)) + R(l3 (-400 -700) (400 610)) + R(l3 (290 -610) (400 610)) + R(l3 (-400 0) (400 90)) + R(l3 (-400 0) (400 10260)) + R(l11 (-3045 -10910) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (520 -510) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (520 -510) (170 170)) + R(l11 (-170 170) (170 170)) + R(l11 (520 -170) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l11 (520 170) (170 170)) + R(l11 (-170 -510) (170 170)) + R(l12 (-3045 -220) (3160 610)) + R(l13 (-1575 -310) (0 0)) + ) + N(6 I(SUBSTRATE)) + P(1 I(C2)) + P(2 I(C1)) + P(4 I(VSS)) + P(5 I(DOWN)) + P(6 I(SUBSTRATE)) + D(1 D$NMOS + D(D$NMOS$1 Y(690 0)) + D(D$NMOS$1 Y(1380 0)) + D(D$NMOS$1 Y(2070 0)) + D(D$NMOS$2 Y(2760 0)) + C(0 S S) + C(1 S D) + C(2 S S) + C(3 S D) + C(4 S S) + C(0 G G) + C(1 G G) + C(2 G G) + C(3 G G) + C(4 G G) + C(0 D D) + C(1 D S) + C(2 D D) + C(3 D S) + C(4 D D) + C(0 B B) + C(1 B B) + C(2 B B) + C(3 B B) + C(4 B B) + M Y(-5400 8445) + E(L 0.4) + E(W 50) + E(AS 8.7) + E(AD 8.7) + E(PS 61.74) + E(PD 61.74) + T(S 3) + T(G 5) + T(D 4) + T(B 6) + ) + D(6 D$MOSCAPN$1 + D(D$MOSCAPN$1 Y(-17690 0)) + O(180) Y(48520 12350) + E(W 14) + E(L 17.2) + E(N 2) + T(G 1) + T(D 3) + T(B 6) + ) + D(8 D$MOSCAPN + D(D$MOSCAPN Y(-17690 0)) + O(180) Y(9350 12350) + E(W 24) + E(L 17.2) + E(N 2) + T(G 2) + T(D 3) + T(B 6) + ) +) diff --git a/testdata/algo/l2n_reader_au_7.l2n b/testdata/algo/l2n_reader_au_7.l2n new file mode 100644 index 000000000..e4a9f1a1b --- /dev/null +++ b/testdata/algo/l2n_reader_au_7.l2n @@ -0,0 +1,1126 @@ +#%l2n-klayout +top(TOP) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers +layer(l3 '66/20') +layer(l11 '66/44') +layer(l12 '67/20') +layer(l13 '67/5') +layer(l14 '67/44') +layer(l15 '68/20') +layer(l16 '68/5') +layer(l4) +layer(l5) + +# Mask layer connectivity +connect(l3 l3 l11) +connect(l11 l3 l11 l12 l5) +connect(l12 l11 l12 l13 l14) +connect(l13 l12 l13) +connect(l14 l12 l14 l15) +connect(l15 l14 l15 l16) +connect(l16 l15 l16) +connect(l4 l4) +connect(l5 l11 l5) + +# Global nets and connectivity +global(l4 SUBSTRATE) + +# Device class section +class(MOSCAPN '' + param(W 1 0) + param(L 1 0) + param(N 1 1) + terminal(G) + terminal(D) + terminal(B) +) +class(PMOS MOS4) +class(NMOS MOS4) + +# Device abstracts section +# Device abstracts list the pin shapes of the devices. +device(D$MOSCAPN MOSCAPN + terminal(G + rect(l3 (-8600 -12000) (17200 24000)) + ) + terminal(D + rect(l5 (-8600 -12000) (17200 24000)) + ) + terminal(B + rect(l4 (-8600 -12000) (17200 24000)) + ) +) +device(D$MOSCAPN$1 MOSCAPN + terminal(G + rect(l3 (-8600 -7000) (17200 14000)) + ) + terminal(D + rect(l5 (-8600 -7000) (17200 14000)) + ) + terminal(B + rect(l4 (-8600 -7000) (17200 14000)) + ) +) +device(D$NMOS NMOS + terminal(S + rect(l5 (-490 -5000) (290 10000)) + ) + terminal(G + rect(l3 (-200 -5000) (400 10000)) + ) + terminal(D + rect(l5 (200 -5000) (290 10000)) + ) + terminal(B + rect(l4 (-200 -5000) (400 10000)) + ) +) +device(D$NMOS$1 NMOS + terminal(S + rect(l5 (-490 -5000) (290 10000)) + ) + terminal(G + rect(l3 (-200 -5000) (400 10000)) + ) + terminal(D + rect(l5 (200 -5000) (290 10000)) + ) + terminal(B + rect(l4 (-200 -5000) (400 10000)) + ) +) +device(D$NMOS$2 NMOS + terminal(S + rect(l5 (-490 -5000) (290 10000)) + ) + terminal(G + rect(l3 (-200 -5000) (400 10000)) + ) + terminal(D + rect(l5 (200 -5000) (290 10000)) + ) + terminal(B + rect(l4 (-200 -5000) (400 10000)) + ) +) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. +circuit(TOP + + # Circuit boundary + rect((-9185 0) (84745 24700)) + + # Nets with their geometries + net(1 name(C2) + rect(l3 (39920 5050) (17200 14600)) + rect(l3 (490 -14600) (17200 14600)) + rect(l11 (-18390 -14550) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (33500 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l12 (-1290 -190) (700 210)) + rect(l12 (-17690 -210) (16990 210)) + rect(l12 (700 -210) (16990 210)) + rect(l14 (-17780 -190) (170 170)) + rect(l14 (190 -170) (170 170)) + rect(l14 (190 -170) (170 170)) + rect(l15 (-980 -270) (1080 370)) + rect(l16 (-540 -190) (0 0)) + ) + net(2 name(C1) + rect(l3 (750 50) (17200 24600)) + rect(l3 (490 -24600) (17200 24600)) + rect(l11 (-18390 -24550) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (33500 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l11 (-510 -170) (170 170)) + rect(l12 (-1290 -190) (700 210)) + rect(l12 (-17690 -210) (16990 210)) + rect(l12 (700 -210) (16990 210)) + rect(l14 (-17780 -190) (170 170)) + rect(l14 (190 -170) (170 170)) + rect(l14 (190 -170) (170 170)) + rect(l15 (-980 -270) (1080 370)) + rect(l16 (-540 -190) (0 0)) + ) + net(3 + rect(l11 (-5830 13120) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -2210) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (1210 8330) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -2550) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (1210 9350) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -2550) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (77870 14780) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 170) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 12410) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 170) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-21650 5950) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2890) (170 170)) + rect(l11 (-170 2210) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 -4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -9010) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 9010) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -4250) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2890) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -8670) (170 170)) + rect(l11 (-170 7990) (170 170)) + rect(l11 (-170 14450) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 8670) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2210) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -3910) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 7990) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -9350) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (17520 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2890) (170 170)) + rect(l11 (-170 2210) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 -4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -9010) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 9010) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -4250) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5610) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2890) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -8670) (170 170)) + rect(l11 (-170 7990) (170 170)) + rect(l11 (-170 14450) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-17860 8670) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 2210) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 4250) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -3910) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 7990) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -9350) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -5270) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l12 (-6480 -9155) (290 10000)) + rect(l12 (1090 -10000) (290 10000)) + rect(l12 (1090 -10000) (290 10000)) + rect(l12 (77780 -7595) (230 13000)) + rect(l12 (-17860 -750) (17800 500)) + rect(l12 (-17860 -12750) (230 13000)) + rect(l12 (-170 -2750) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17860 -750) (230 13000)) + rect(l12 (-17860 -750) (17800 500)) + rect(l12 (-17860 -12750) (230 13000)) + rect(l12 (-170 -2750) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-21650 -5750) (230 23000)) + rect(l12 (-17860 -1750) (17800 500)) + rect(l12 (-17860 -21750) (230 23000)) + rect(l12 (-170 -3750) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17860 -1750) (230 23000)) + rect(l12 (-17860 -1750) (17800 500)) + rect(l12 (-17860 -21750) (230 23000)) + rect(l12 (-170 -3750) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l12 (-17800 -2500) (17800 500)) + rect(l14 (-21320 8810) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-1550 550) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-1550 550) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (6080 550) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (39000 550) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l14 (-170 -530) (170 170)) + rect(l15 (-45655 -235) (45720 1010)) + rect(l5 (-45715 -8190) (290 10000)) + rect(l5 (1090 -10000) (290 10000)) + rect(l5 (1090 -10000) (290 10000)) + rect(l5 (77650 -8095) (400 14000)) + rect(l5 (-18000 -14000) (400 14000)) + rect(l5 (-490 -14000) (400 14000)) + rect(l5 (-18000 -14000) (400 14000)) + rect(l5 (-4280 -19000) (400 24000)) + rect(l5 (-18000 -24000) (400 24000)) + rect(l5 (-490 -24000) (400 24000)) + rect(l5 (-18000 -24000) (400 24000)) + ) + net(4 name(VSS) + rect(l11 (-5140 13120) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (1210 9350) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -2210) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -2210) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (1210 9350) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -850) (170 170)) + rect(l11 (-170 850) (170 170)) + rect(l11 (-170 -2210) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1870) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (-170 510) (170 170)) + rect(l11 (-170 -1190) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -2550) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -2550) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (-170 -1530) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l12 (-2990 -325) (290 10000)) + rect(l12 (1090 -10000) (290 10000)) + rect(l12 (1090 -10000) (290 10000)) + rect(l14 (-2990 -4735) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l14 (1210 -890) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l14 (1210 -890) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l14 (-170 190) (170 170)) + rect(l15 (-6975 -945) (7040 1010)) + rect(l16 (-5360 -520) (0 0)) + rect(l5 (2305 -5700) (290 10000)) + rect(l5 (1090 -10000) (290 10000)) + rect(l5 (1090 -10000) (290 10000)) + ) + net(5 name(DOWN) + rect(l3 (-5600 3225) (400 90)) + rect(l3 (-400 -700) (400 610)) + rect(l3 (-400 90) (400 10260)) + rect(l3 (290 -10350) (400 90)) + rect(l3 (-400 0) (400 10260)) + rect(l3 (-400 -10960) (400 610)) + rect(l3 (290 0) (400 90)) + rect(l3 (-400 -700) (400 610)) + rect(l3 (-400 90) (400 10260)) + rect(l3 (290 -10260) (400 10260)) + rect(l3 (-400 -10350) (400 90)) + rect(l3 (-400 -700) (400 610)) + rect(l3 (290 -610) (400 610)) + rect(l3 (-400 0) (400 90)) + rect(l3 (-400 0) (400 10260)) + rect(l11 (-3045 -10910) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (520 -510) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (520 -510) (170 170)) + rect(l11 (-170 170) (170 170)) + rect(l11 (520 -170) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l11 (520 170) (170 170)) + rect(l11 (-170 -510) (170 170)) + rect(l12 (-3045 -220) (3160 610)) + rect(l13 (-1575 -310) (0 0)) + ) + net(6 name(SUBSTRATE)) + + # Outgoing pins and their connections to nets + pin(1 name(C2)) + pin(2 name(C1)) + pin(4 name(VSS)) + pin(5 name(DOWN)) + pin(6 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$NMOS + device(D$NMOS$1 location(690 0)) + device(D$NMOS$1 location(1380 0)) + device(D$NMOS$1 location(2070 0)) + device(D$NMOS$2 location(2760 0)) + connect(0 S S) + connect(1 S D) + connect(2 S S) + connect(3 S D) + connect(4 S S) + connect(0 G G) + connect(1 G G) + connect(2 G G) + connect(3 G G) + connect(4 G G) + connect(0 D D) + connect(1 D S) + connect(2 D D) + connect(3 D S) + connect(4 D D) + connect(0 B B) + connect(1 B B) + connect(2 B B) + connect(3 B B) + connect(4 B B) + mirror location(-5400 8445) + param(L 0.4) + param(W 50) + param(AS 8.7) + param(AD 8.7) + param(PS 61.74) + param(PD 61.74) + terminal(S 3) + terminal(G 5) + terminal(D 4) + terminal(B 6) + ) + device(2 D$MOSCAPN$1 + device(D$MOSCAPN$1 location(-17690 0)) + rotation(180) location(48520 12350) + param(W 14) + param(L 17.2) + param(N 2) + terminal(G 1) + terminal(D 3) + terminal(B 6) + ) + device(3 D$MOSCAPN + device(D$MOSCAPN location(-17690 0)) + rotation(180) location(9350 12350) + param(W 24) + param(L 17.2) + param(N 2) + terminal(G 2) + terminal(D 3) + terminal(B 6) + ) + +)