From 18eadb119e275d8f7dc12032638b156fab95b7b2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 22:31:46 +0100 Subject: [PATCH 1/8] 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 edfbf4a267..b5e3bebc32 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 2/8] 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 019caeb910..c8c03a572a 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 3/8] 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 aaca632c8f..c9c4d2153e 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 1e0f0a9568..abcb742749 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -759,6 +759,11 @@ class DB_PUBLIC Circuit */ 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 88632b71a5..05976791d8 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 19858f26df..c3b67b343d 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 0000000000..c42a4ff511 --- /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 0000000000..9026cfc701 --- /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 4/8] 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 f7e405c48c..d7f83622fc 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 dea3d086a1..0db330f5c7 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 @@ class TL_PUBLIC ShiftLeftExpressionNode 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 @@ class TL_PUBLIC ShiftRightExpressionNode 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 @@ class TL_PUBLIC PlusExpressionNode } 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 @@ class TL_PUBLIC MinusExpressionNode 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 @@ class TL_PUBLIC StarExpressionNode } 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 @@ class TL_PUBLIC StarExpressionNode } 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 @@ class TL_PUBLIC StarExpressionNode 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 @@ class TL_PUBLIC SlashExpressionNode 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 @@ class TL_PUBLIC PercentExpressionNode 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 @@ class TL_PUBLIC AmpersandExpressionNode 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 @@ class TL_PUBLIC PipeExpressionNode 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 @@ class TL_PUBLIC AcuteExpressionNode 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 @@ class TL_PUBLIC UnaryMinusExpressionNode } 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 @@ class TL_PUBLIC UnaryTildeExpressionNode } 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 a547d7c7de..3d87199008 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 5/8] 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 | 182 +++++++++++++----- 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, 518 insertions(+), 220 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 8e5aa30085..602c56b904 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 56d345b2a9..fb9407609f 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 531a18fcaa..ed856e57a6 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -142,22 +142,23 @@ struct DB_PUBLIC EdgeLengthFilter 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 b61dd2eaf2..f432aec21a 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 de017aa7c4..9593ec7823 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -114,7 +114,7 @@ class DB_PUBLIC CornerEdgePairDelivery 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 0ee676e203..cc8f93eff1 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 b4f6b5447a..819ea1b0a3 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 5fc83ec496..7b2fe4ae29 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 013c431545..cfd5205e20 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 e966e65975..55d4590005 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 95b790268f..f64cce6eef 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 381ae4fc7c..b078fc8084 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 9212846a56..8826f1f8d6 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 1e42dc5746..b91972ec8c 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 d9cb665898..56a21d00f7 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 70ac453b20..eaaee9e8cb 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 19c63f8b94..a4ad2a239e 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 858e64abc3..3d68b9eda3 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 3f093df457..e472906852 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 @@ def length_sum # 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 @@ def length_sum # 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 @@ def smoothed(d, keep_hv = false) # @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 @@ def smoothed(d, keep_hv = false) # (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 @@ def smoothed(d, keep_hv = false) # 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 @@ def do_create_node(cache) 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 @@ def do_create_node(cache) 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 8bf358bf0a..72d73f44fe 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 @@ def #{f} # \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 @@ def _cop_#{f} 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 @@ def _cop_#{f} # 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 @@ def _cop_corners(output_mode = DRCOutputMode::new(:boxes)) 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 81e325ff3c..15e9dc8240 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 @@ def text(p) 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 8705913307..c5aa2a64b2 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 @@ def #{mn}(*args) # %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 @@ def #{mn}(*args) # 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 @@ def #{mn}(*args) # %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 @@ def #{mn}(*args) # 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} - - 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 + 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 + + 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 + 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 + absolute = a.value + false else - requires_edge_pairs + 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) - 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})) + 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 - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect})) + 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 - raise("Invalid number of range arguments (1 or 2 expected)") + 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 + + 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 @@ def _texts_impl(invert, *args) # # 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 @@ def _texts_impl(invert, *args) # @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 @@ def corners(*args) 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 @@ def corners(*args) 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 482f18db80..11d8269438 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 @@ def initialize(v) 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 1c8180a8db..495074f48c 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 9993793f27..159721a62e 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 4b49eab6df..80da9f09d3 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 6/8] 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 11b0d2fb7b..b42ae50122 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 7/8] 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 c3b67b343d..47dfbee3ee 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 8/8] 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 819ea1b0a3..d5edc7cecb 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 7b2fe4ae29..8e983164a8 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" + "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" - "The two \"include..\" arguments have been added in version 0.27. " - "'absolute_angle' has been introduced in version 0.29.1." + "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 c5aa2a64b2..c3525d090c 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 @@ def #{mn}(*args) 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 @@ def #{mn}(*args) 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 f9b3289bec..c5bf4c4fe8 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -294,6 +294,7 @@ def test_5 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 @@ def test_5 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 dc4ae88bb8..e3e9f17f1c 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -585,12 +585,19 @@ def test_5 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)")