Merge pull request #1690 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2024-04-30 15:26:59 +02:00 committed by GitHub
commit d5a67080c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 810 additions and 291 deletions

View File

@ -2198,15 +2198,29 @@ Service::paste ()
}
}
std::vector<const db::DUserObject *> new_objects;
for (db::Clipboard::iterator c = db::Clipboard::instance ().begin (); c != db::Clipboard::instance ().end (); ++c) {
const db::ClipboardValue<ant::Object> *value = dynamic_cast<const db::ClipboardValue<ant::Object> *> (*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 ();
}
}
}

View File

@ -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);

View File

@ -759,6 +759,11 @@ public:
*/
void blank ();
/**
* @brief Gets a value indicating whether the circuit is empty
*/
bool is_empty () const;
/**
* @brief Generate memory statistics
*/

View File

@ -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);
}
}

View File

@ -215,8 +215,13 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector<db::Edge> &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

View File

@ -142,22 +142,23 @@ private:
class DB_PUBLIC EdgeAngleChecker
{
public:
EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end);
EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute);
bool operator() (const db::Edge &a, const db::Edge &b) const
{
return m_all || check (a.d (), b.d ());
return (m_all || check (a.d (), b.d ()) || (m_absolute && check (b.d (), a.d ()))) != m_inverse;
}
bool operator() (const db::Vector &a, const db::Vector &b) const
{
return m_all || check (a, b);
return (m_all || check (a, b) || (m_absolute && check (b, a))) != m_inverse;
}
private:
db::CplxTrans m_t_start, m_t_end;
bool m_include_start, m_include_end;
bool m_big_angle, m_all;
bool m_inverse, m_absolute;
bool check (const db::Vector &a, const db::Vector &b) const;
};
@ -181,22 +182,24 @@ struct DB_PUBLIC EdgeOrientationFilter
* @param amin The minimum angle (measured against the x axis)
* @param amax The maximum angle (measured against the x axis)
* @param inverse If set to true, only edges not matching this criterion will be filtered
* @param absolute Angles are always positive
*
* This filter will filter out all edges whose angle against x axis
* is larger or equal to amin and less than amax.
*/
EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse);
EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse, bool absolute);
/**
* @brief Constructor
*
* @param a The angle (measured against the x axis)
* @param inverse If set to true, only edges not matching this criterion will be filtered
* @param absolute Angles are always positive
*
* This filter will filter out all edges whose angle against x axis
* is equal to a.
*/
EdgeOrientationFilter (double a, bool inverse);
EdgeOrientationFilter (double a, bool inverse, bool absolute);
/**
* @brief Returns true if the edge orientation matches the criterion

View File

@ -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<std::map<std::string, db::Net *> > 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;
}

View File

@ -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 ..
}

View File

@ -114,7 +114,7 @@ private:
class DB_PUBLIC CornerDetectorCore
{
public:
CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end);
CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute);
virtual ~CornerDetectorCore () { }
void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const;
@ -130,8 +130,8 @@ class DB_PUBLIC CornersAsRectangles
: public db::PolygonProcessorBase, private CornerDetectorCore
{
public:
CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end), m_dim (dim)
CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute, db::Coord dim = 1)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), m_dim (dim)
{
// .. nothing yet ..
}
@ -159,8 +159,8 @@ class DB_PUBLIC CornersAsDots
: public db::PolygonToEdgeProcessorBase, private CornerDetectorCore
{
public:
CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)
{
// .. nothing yet ..
}
@ -184,8 +184,8 @@ class DB_PUBLIC CornersAsEdgePairs
: public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
{
public:
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)
{
// .. nothing yet ..
}

View File

@ -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<db::CompoundRegionOperationNode> 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<size_t>::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<db::CompoundRegionOperationNode> 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<db::Edge::distance_type>::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"

View File

@ -432,14 +432,28 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian
static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
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)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
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);
}
@ -453,14 +467,28 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta
static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
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)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
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);
}
@ -967,6 +995,22 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\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"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
@ -1027,6 +1071,21 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\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"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "

View File

@ -440,13 +440,25 @@ static db::Edges with_length2 (const db::Edges *r, const tl::Variant &min, const
static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
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)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
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);
}
@ -923,7 +935,23 @@ Class<db::Edges> 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."
"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"
"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"

View File

@ -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<db::Region> 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<db::Region> 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"

View File

@ -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<db::Polygon> (0, 0)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor<db::Polygon> (1000, 2000)));

View File

@ -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);

View File

@ -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));

View File

@ -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<db::Polygon> (0, 0)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor<db::Polygon> (1000, 2000)));

View File

@ -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);
}

View File

@ -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 (), "");
}
}

View File

@ -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 ().find ("Subcircuit 'C1' called recursively in"), size_t (0));
}
}
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<std::string, tl::Variant> vars;

View File

@ -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<db::Polygon> (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<db::Polygon> (10, 20)), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)"), true);

View File

@ -164,7 +164,7 @@ out = in.drc((width &lt; 0.3).edges - secondary(waive))
This operation selects edges by their angle, measured against the horizontal
axis in the mathematical sense.
</p><p>
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).
</p><p>
@ -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.
</p><p>
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:
</p><p>
<pre>
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
</pre>
</p><p>
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:
</p><p>
<pre>
out = in.drc(angle(absolute) == 45)
</pre>
</p><p>
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 <a href="/about/drc_ref_drc.xml#rectilinear">DRC#rectilinear</a> for
example to detect shapes with non-manhattan geometry instead of using angle checks.
</p><p>
The "angle" method is available as a plain function or as a method on <a href="/about/drc_ref_drc.xml">DRC</a> expressions.
The plain function is equivalent to "primary.angle".
The plain function is equivalent to the method call "primary.angle".
</p>
<a name="area"/><h2>"area" - Selects the primary shape if the area is meeting the condition</h2>
<keyword name="area"/>
@ -363,8 +371,7 @@ See <a href="/about/drc_ref_layer.xml#centers">layer#centers</a> for details abo
<p>Usage:</p>
<ul>
<li><tt>expression.corners</tt></li>
<li><tt>expression.corners(as_dots)</tt></li>
<li><tt>expression.corners(as_boxes)</tt></li>
<li><tt>expression.corners([ options ])</tt></li>
</ul>
<p>
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.
</p><p>
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.
</p><p>
Another option is "absolute" which selects the corners by absolute angle - i.e left
and right turns will both be considered positive angles.
</p><p>
Examples for use of the options are:
</p><p>
<pre>
corners(as_dots)
corners(as_boxes, absolute)
</pre>
</p><p>
The following example selects all corners:
</p><p>
<pre>

View File

@ -264,8 +264,11 @@ deliver objects that can be converted into polygons. Such objects are of class <
<p>
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.
</p><p>
The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
</p><p>
@ -276,6 +279,8 @@ The options available are:
<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>
</p><p>
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
<keyword name="with_angle"/>
<p>Usage:</p>
<ul>
<li><tt>layer.with_angle(min .. max)</tt></li>
<li><tt>layer.with_angle(value)</tt></li>
<li><tt>layer.with_angle(min, max)</tt></li>
<li><tt>layer.with_angle(min .. max [, absolute])</tt></li>
<li><tt>layer.with_angle(value [, absolute])</tt></li>
<li><tt>layer.with_angle(min, max [, absolute])</tt></li>
<li><tt>layer.with_angle(ortho)</tt></li>
<li><tt>layer.with_angle(diagonal)</tt></li>
<li><tt>layer.with_angle(diagonal_only)</tt></li>
<li><tt>edge_pair_layer.with_angle(... [, both])</tt></li>
<li><tt>edge_pair_layer.with_angle(... [, both] [, absolute])</tt></li>
</ul>
<p>
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.
</p><p>
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.
</p><p>
Here are examples for "with_angle" on edge pair layers:
</p><p>
<pre>
@ -4006,21 +4016,25 @@ This method is available for polygon layers only.
<keyword name="without_angle"/>
<p>Usage:</p>
<ul>
<li><tt>layer.without_angle(min .. max)</tt></li>
<li><tt>layer.without_angle(value)</tt></li>
<li><tt>layer.without_angle(min, max)</tt></li>
<li><tt>layer.without_angle(min .. max [, absolute])</tt></li>
<li><tt>layer.without_angle(value [, absolute])</tt></li>
<li><tt>layer.without_angle(min, max [, absolute])</tt></li>
<li><tt>layer.without_angle(ortho)</tt></li>
<li><tt>layer.without_angle(diagonal)</tt></li>
<li><tt>layer.without_angle(diagonal_only)</tt></li>
<li><tt>edge_pair_layer.without_angle(... [, both])</tt></li>
<li><tt>edge_pair_layer.without_angle(... [, both] [, absolute])</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_angle">with_angle</a>. 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).
</p><p>
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.
</p><p>
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.
</p><p>
A note on the "both" modifier (without_angle called on edge pairs): "both" means that
both edges need to be "without_angle". For example

View File

@ -282,6 +282,7 @@ var x = 3; x = x + 1; x
<tr><td><tt>combine(</tt>x<tt>,</tt>y<tt>)</tt></td><td>String</td><td>String</td><td>Combines the path components x and y using the system specific separator</td></tr>
<tr><td><tt>cosh(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Hyperbolic cosine function</td></tr>
<tr><td><tt>cos(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Cosine function</td></tr>
<tr><td><tt>downcase(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Converts the given string to lower case</td></tr>
<tr><td><tt>env(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Access an environment variable</td></tr>
<tr><td><tt>error(</tt>x<tt>)</tt></td><td>String</td><td></td><td>Raise an error</td></tr>
<tr><td><tt>exp(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Exponential function</td></tr>
@ -318,6 +319,7 @@ var x = 3; x = x + 1; x
<tr><td><tt>to_f(</tt>x<tt>)</tt></td><td>Any</td><td>Numeric</td><td>Convert argument to numeric if possible</td></tr>
<tr><td><tt>to_i(</tt>x<tt>)</tt></td><td>Any</td><td>Numeric (integer)</td><td>Convert argument to numeric (32 bit integer)</td></tr>
<tr><td><tt>to_s(</tt>x<tt>)</tt></td><td>Any</td><td>String</td><td>Convert argument to string</td></tr>
<tr><td><tt>upcase(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Converts the given string to upper case</td></tr>
</table>
</doc>

View File

@ -691,7 +691,7 @@ CODE
# This operation selects edges by their angle, measured against the horizontal
# axis in the mathematical sense.
#
# For this measurement edges are considered without their direction and straight lines.
# For this measurement edges are considered without their direction.
# A horizontal edge has an angle of zero degree. A vertical one has
# an angle of 90 degrees. The angle range is from -90 (exclusive) to 90 degree (inclusive).
#
@ -699,23 +699,43 @@ CODE
# before the angle test is made.
#
# For example, the following code selects all edges from the primary shape which are 45 degree
# (up) or 135 degree (down). The "+" will join the results:
# (up) or -45 degree (down). The "+" operator will join the results:
#
# @code
# out = in.drc((angle == 45) + (angle == 135))
# out = in.drc((primary.angle == 45) + (primary.angle == 135)) # equivalent
# out = in.drc((angle == 45) + (angle == -45))
# out = in.drc((primary.angle == 45) + (primary.angle == -45)) # equivalent
# @/code
#
# Note that angle checks usually imply the need to rotation variant formation as cells which
# You can avoid using both 45 and -45 degree checks with the 'absolute' option.
# With this option, angles are not signed and the value is the absolute angle
# the edge encloses with the x axis:
#
# @code
# out = in.drc(angle(absolute) == 45)
# @/code
#
# Note that angle checks usually imply the need for rotation variant formation as cells which
# are placed non-rotated and rotated by 90 degree cannot be considered identical. This imposes
# a performance penalty in hierarchical mode. If possible, consider using \DRC#rectilinear for
# example to detect shapes with non-manhattan geometry instead of using angle checks.
#
# The "angle" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.angle".
# The plain function is equivalent to the method call "primary.angle".
def angle
DRCOpNodeEdgeOrientationFilter::new(@engine, self)
def angle(*args)
filter = DRCOpNodeEdgeOrientationFilter::new(@engine, self)
args.each do |a|
if a.is_a?(DRCAbsoluteMode)
filter.absolute = a.value
else
raise("Invalid argument (#{a.inspect}) for 'angle' method")
end
end
filter
end
# %DRC%
@ -758,8 +778,7 @@ CODE
# @name corners
# @brief Selects corners of polygons
# @synopsis expression.corners
# @synopsis expression.corners(as_dots)
# @synopsis expression.corners(as_boxes)
# @synopsis expression.corners([ options ])
#
# This operation acts on polygons and selects the corners of the polygons.
# It can be put into a condition to select corners by their angles. The angle of
@ -768,9 +787,19 @@ CODE
# (inner) corners, negative ones indicate convex (outer) corners.
# Angles take values between -180 and 180 degree.
#
# When using "as_dots" for the argument, the operation will return single-point edges at
# When using "as_dots" for an option, the operation will return single-point edges at
# the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be
# produced at each selected corner.
#
# Another option is "absolute" which selects the corners by absolute angle - i.e left
# and right turns will both be considered positive angles.
#
# Examples for use of the options are:
#
# @code
# corners(as_dots)
# corners(as_boxes, absolute)
# @/code
#
# The following example selects all corners:
#
@ -789,14 +818,20 @@ CODE
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.corners".
def corners(output_mode = DRCOutputMode::new(:dots))
def corners(*args)
@engine._context("corners") do
if output_mode.is_a?(DRCOutputMode)
output_mode = output_mode.value
else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
output_mode = :as_boxes
absolute = false
args.each do |a|
if a.is_a?(DRCOutputMode)
output_mode = a.value
elsif a.is_a?(DRCAbsoluteMode)
absolute = a.value
else
raise("Invalid argument (#{a.inspect}) for 'corners' method")
end
end
DRCOpNodeCornersFilter::new(@engine, output_mode, self)
DRCOpNodeCornersFilter::new(@engine, output_mode, absolute, self)
end
end
@ -1721,6 +1756,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
attr_accessor :absolute
def initialize(engine, input)
super(engine)
@ -1748,6 +1784,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
args << (self.gt ? false : true)
args << (self.lt ? self.lt : (self.le ? self.le + angle_delta : 180.0))
args << (self.lt ? false : true)
args << self.absolute
RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args)
@ -2037,12 +2074,16 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :output_mode
attr_accessor :inverse
attr_accessor :absolute
def initialize(engine, output_mode, input)
def initialize(engine, output_mode, absolute, input)
super(engine)
self.output_mode = output_mode
self.input = input
self.description = "corners"
self.inverse = false
self.absolute = absolute
end
def do_create_node(cache)
@ -2052,14 +2093,26 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
args << (self.lt ? false : true)
if self.output_mode == :dots || self.output_mode == :edges
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
elsif self.output_mode == :edge_pairs
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
else
args << 2 # dimension is 2x2 DBU
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
end
end
def inverted
res = self.dup
res.inverse = !res.inverse
return res
end
end

View File

@ -770,17 +770,7 @@ CODE
# \DRC# expressions (see \Layer#drc and \DRC#length for more details). In this context,
# the operation acts similar to \Layer#with_length.
# %DRC%
# @name angle
# @brief In universal DRC context: selects edges based on their orientation
# @synopsis angle (in condition)
#
# "angle" represents the edge orientation filter on the primary shape edges in
# \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context,
# the operation acts similar to \Layer#with_angle.
%w(
angle
area
holes
hulls
@ -798,6 +788,15 @@ CODE
CODE
end
# %DRC%
# @name angle
# @brief In universal DRC context: selects edges based on their orientation
# @synopsis angle (in condition)
#
# "angle" represents the edge orientation filter on the primary shape edges in
# \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context,
# the operation acts similar to \Layer#with_angle.
# %DRC%
# @name corners
# @brief Selects corners of polygons
@ -817,11 +816,6 @@ CODE
# The "corners" operator can be put into a condition which means it's
# applied to corners meeting a particular angle constraint.
def _cop_corners(output_mode = DRCOutputMode::new(:boxes))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
return primary.corners(output_mode)
end
# %DRC%
# @name extent_refs
# @brief Returns partial references to the boundings boxes of the polygons
@ -897,6 +891,8 @@ CODE
rounded_corners
sized
smoothed
corners
angle
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"

View File

@ -551,6 +551,10 @@ module DRC
end
end
def absolute
DRCAbsoluteMode::new(true)
end
def as_dots
DRCOutputMode::new(:dots)
end

View File

@ -861,13 +861,13 @@ CODE
# %DRC%
# @name with_angle
# @brief Selects edges by their angle
# @synopsis layer.with_angle(min .. max)
# @synopsis layer.with_angle(value)
# @synopsis layer.with_angle(min, max)
# @synopsis layer.with_angle(min .. max [, absolute])
# @synopsis layer.with_angle(value [, absolute])
# @synopsis layer.with_angle(min, max [, absolute])
# @synopsis layer.with_angle(ortho)
# @synopsis layer.with_angle(diagonal)
# @synopsis layer.with_angle(diagonal_only)
# @synopsis edge_pair_layer.with_angle(... [, both])
# @synopsis edge_pair_layer.with_angle(... [, both] [, absolute])
#
# When called on an edge layer, the method selects edges by their angle,
# measured against the horizontal axis in the mathematical sense.
@ -886,6 +886,11 @@ CODE
# either "both" (plain word) to indicate that both edges have to be within the given interval.
# Without this argument, it is sufficient for one edge to meet the criterion.
#
# The "absolute" option is available for edge or edge pair layers.
# Without the "absolute" option, edges sloping down are assigned a negative angle while edges sloping up are assigned
# a positive angle (vertical edges are always 90 degree). With the "absolute" option,
# edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
#
# Here are examples for "with_angle" on edge pair layers:
#
# @code
@ -931,21 +936,25 @@ CODE
# %DRC%
# @name without_angle
# @brief Selects edges by the their angle
# @synopsis layer.without_angle(min .. max)
# @synopsis layer.without_angle(value)
# @synopsis layer.without_angle(min, max)
# @synopsis layer.without_angle(min .. max [, absolute])
# @synopsis layer.without_angle(value [, absolute])
# @synopsis layer.without_angle(min, max [, absolute])
# @synopsis layer.without_angle(ortho)
# @synopsis layer.without_angle(diagonal)
# @synopsis layer.without_angle(diagonal_only)
# @synopsis edge_pair_layer.without_angle(... [, both])
# @synopsis edge_pair_layer.without_angle(... [, both] [, absolute])
#
# The method basically is the inverse of \with_angle. It selects all edges
# of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
# is not inside the given interval (first and third form) or of the given type (other forms).
#
# When called on edge pairs, it selects
# edge pairs by the angles of their edges.
# When called on edge pairs, it selects edge pairs by the angles of their edges.
#
# The "absolute" option is available for edge or edge pair layers. Without the "absolute" option,
# edges sloping down are assigned a negative angle while edges sloping up are assigned
# a positive angle (vertical edges are always 90 degree). With the "absolute" option,
# edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
#
# A note on the "both" modifier (without_angle called on edge pairs): "both" means that
# both edges need to be "without_angle". For example
#
@ -989,57 +998,101 @@ CODE
# The method basically is the inverse of \with_internal_angle. It selects all
# edge pairs by the angle enclosed by their edges, applying the opposite criterion than \with_internal_angle.
%w(angle internal_angle).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_angle"
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
@engine._context("#{mn}") do
f = :with_#{f}
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer")
if "#{f}" == "angle"
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer")
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword is only available for edge pair layers")
end
f = :with_#{f}_both
false
else
true
end
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
else
requires_edge_pairs
end
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Region)
raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect}))
both = true
false
elsif a.is_a?(DRCAbsoluteMode)
if self.data.is_a?(RBA::Region)
raise("'absolute' keyword is only available for edge or edge pair layers")
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect}))
absolute = a.value
false
else
raise("Invalid number of range arguments (1 or 2 expected)")
true
end
end
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)
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
CODE
end
CODE
end
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_internal_angle"
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
f = :with_internal_angle
requires_edge_pairs
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Region)
raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
end
CODE
end
# %DRC%
@ -1231,8 +1284,11 @@ CODE
#
# This method produces markers on the corners of the polygons. An angle criterion can be given which
# selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
# while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles
# while negative angles indicate a right turn.
# Since polygons are oriented clockwise, positive angles
# indicate concave (inner) corners while negative ones indicate convex (outer) corners
# The 'absolute' option allows turning this off and considering both left and right turns
# positive angles.
#
# The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
#
@ -1243,6 +1299,8 @@ CODE
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
# @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge
# is the incoming edge to the corner, the second edge the outgoing edge. @/li
# @li @b absolute @/b: with this option, left and right turns will both be considered positive angles @/li
# @li @b negative @/b: with this option, all corners @b not @/b matching the angle criterion are selected @/li
# @/ul
#
# The following images show the effect of this method:
@ -1264,6 +1322,8 @@ CODE
output_mode = :boxes
amin = -180.0
amax = 180.0
absolute = false
inverse = false
args.each do |a|
if a.is_a?(Range)
@ -1277,21 +1337,35 @@ CODE
amax = a.to_f
elsif a.is_a?(DRCOutputMode)
output_mode = a.value
elsif a.is_a?(DRCAbsoluteMode)
absolute = a.value
elsif a.is_a?(DRCNegative)
inverse = true
else
raise("Invalid argument #{a.inspect}")
end
end
f = :corners
cls = RBA::Region
args = [ amin, amax ]
if output_mode == :edges || output_mode == :dots
f = :corners_dots
cls = RBA::Edges
elsif output_mode == :edge_pairs
f = :corners_edge_pairs
cls = RBA::EdgePairs
else
f = :corners
cls = RBA::Region
args << 1 # 2x2 DBU boxes
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax))
args << true # include amin
args << true # include amax
args << inverse
args << absolute
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, *args))
end

View File

@ -123,6 +123,16 @@ module DRC
end
end
# A wrapper for the "absolute" flag for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
class DRCAbsoluteMode
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for a rectangle error filter mode
# The purpose of this wrapper is to identify the error filter mode
class DRCRectangleErrorFilter

View File

@ -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);

View File

@ -24,6 +24,7 @@
#include <QDialog>
#include <QApplication>
#include <QCloseEvent>
#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 ();
}
}

View File

@ -145,14 +145,14 @@ ExpressionParserContext::where () const
// ----------------------------------------------------------------------------
// Utilities for evaluation
static double to_double (const ExpressionParserContext &context, const tl::Variant &v)
static double to_double (const ExpressionParserContext &context, const tl::Variant &v, unsigned int narg)
{
if (v.can_convert_to_double ()) {
return v.to_double ();
} else if (v.is_list ()) {
return v.get_list ().size ();
} else {
throw EvalError (tl::to_string (tr ("Double precision floating point value expected")), context);
throw EvalError (tl::to_string (tr ("Double precision floating point value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
@ -162,50 +162,50 @@ static double to_double (const ExpressionParserContext &context, const std::vect
throw EvalError (tl::to_string (tr ("Function expects a single numeric argument")), context);
}
return to_double (context, v [0]);
return to_double (context, v [0], 0);
}
static long to_long (const ExpressionParserContext &context, const tl::Variant &v)
static long to_long (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_long ()) {
return v.to_long ();
} else if (v.is_list ()) {
return long (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Integer value expected")), context);
throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v)
static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_ulong ()) {
return v.to_ulong ();
} else if (v.is_list ()) {
return (unsigned long) (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context);
throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v)
static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_longlong ()) {
return v.to_longlong ();
} else if (v.is_list ()) {
return long (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Integer value expected")), context);
throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v)
static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_ulonglong ()) {
return v.to_ulong ();
} else if (v.is_list ()) {
return (unsigned long long) (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context);
throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
@ -1089,13 +1089,13 @@ public:
v.swap (o);
} else if (v->is_longlong ()) {
v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b)));
v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b, 1)));
} else if (v->is_ulonglong ()) {
v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b)));
v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b, 1)));
} else if (v->is_ulong ()) {
v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b)));
v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) << to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) << to_long (m_context, *b, 1)));
}
}
};
@ -1145,13 +1145,13 @@ public:
v.swap (o);
} else if (v->is_longlong ()) {
v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b)));
v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b, 1)));
} else if (v->is_ulonglong ()) {
v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b)));
v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b, 1)));
} else if (v->is_ulong ()) {
v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b)));
v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) >> to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) >> to_long (m_context, *b, 1)));
}
}
};
@ -1203,17 +1203,17 @@ public:
} else if (v->is_a_string () || b->is_a_string ()) {
v.set (tl::Variant (std::string (v->to_string ()) + b->to_string ()));
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) + to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) + to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) + to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) + to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) + to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) + to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) + to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) + to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1)));
}
}
};
@ -1263,17 +1263,17 @@ public:
v.swap (o);
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) - to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) - to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) - to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) - to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) - to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) - to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) - to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) - to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1)));
}
}
};
@ -1324,7 +1324,7 @@ public:
} else if (v->is_a_string ()) {
long x = to_long (m_context, *b);
long x = to_long (m_context, *b, 1);
if (x < 0) {
throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context);
}
@ -1339,7 +1339,7 @@ public:
} else if (b->is_a_string ()) {
long x = to_long (m_context, *v);
long x = to_long (m_context, *v, 0);
if (x < 0) {
throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context);
}
@ -1353,17 +1353,17 @@ public:
v.set (tl::Variant (s));
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) * to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) * to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) * to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) * to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) * to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) * to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) * to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) * to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1)));
}
}
};
@ -1413,41 +1413,41 @@ public:
v.swap (o);
} else if (v->is_double () || b->is_double ()) {
double d = to_double (m_context, *b);
double d = to_double (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_double (m_context, *v) / d));
v.set (tl::Variant (to_double (m_context, *v, 0) / d));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
unsigned long long d = to_ulonglong (m_context, *b);
unsigned long long d = to_ulonglong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_ulonglong (m_context, *v) / d));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) / d));
} else if (v->is_longlong () || b->is_longlong ()) {
long long d = to_longlong (m_context, *b);
long long d = to_longlong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_longlong (m_context, *v) / d));
v.set (tl::Variant (to_longlong (m_context, *v, 0) / d));
} else if (v->is_ulong () || b->is_ulong ()) {
unsigned long d = to_ulong (m_context, *b);
unsigned long d = to_ulong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_ulong (m_context, *v) / d));
v.set (tl::Variant (to_ulong (m_context, *v, 0) / d));
} else if (v->is_long () || b->is_long ()) {
long d = to_long (m_context, *b);
long d = to_long (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_long (m_context, *v) / d));
v.set (tl::Variant (to_long (m_context, *v, 0) / d));
} else {
double d = to_double (m_context, *b);
double d = to_double (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_double (m_context, *v) / d));
v.set (tl::Variant (to_double (m_context, *v, 0) / d));
}
}
};
@ -1497,29 +1497,29 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
unsigned long long d = to_ulonglong (m_context, *b);
unsigned long long d = to_ulonglong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_ulonglong (m_context, *v) % d));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) % d));
} else if (v->is_longlong () || b->is_longlong ()) {
long long d = to_longlong (m_context, *b);
long long d = to_longlong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_longlong (m_context, *v) % d));
v.set (tl::Variant (to_longlong (m_context, *v, 0) % d));
} else if (v->is_ulong () || b->is_ulong ()) {
unsigned long d = to_ulong (m_context, *b);
unsigned long d = to_ulong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_ulong (m_context, *v) % d));
v.set (tl::Variant (to_ulong (m_context, *v, 0) % d));
} else {
long d = to_long (m_context, *b);
long d = to_long (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_long (m_context, *v) % d));
v.set (tl::Variant (to_long (m_context, *v, 0) % d));
}
}
};
@ -1569,13 +1569,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) & to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) & to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) & to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) & to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) & to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) & to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) & to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) & to_long (m_context, *b, 1)));
}
}
};
@ -1625,13 +1625,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) | to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) | to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) | to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) | to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) | to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) | to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) | to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) | to_long (m_context, *b, 1)));
}
}
};
@ -1681,13 +1681,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) ^ to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) ^ to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) ^ to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) ^ to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) ^ to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) ^ to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) ^ to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) ^ to_long (m_context, *b, 1)));
}
}
};
@ -1826,7 +1826,7 @@ public:
} else if (v->is_ulonglong ()) {
v.set (-(long long)(v->to_ulonglong ()));
} else {
v.set (-to_double (m_context, *v));
v.set (-to_double (m_context, *v, 0));
}
}
};
@ -1881,7 +1881,7 @@ public:
} else if (v->is_ulonglong ()) {
v.set (~v->to_ulonglong ());
} else {
v.set (~to_long (m_context, *v));
v.set (~to_long (m_context, *v, 0));
}
}
};
@ -2388,7 +2388,7 @@ abs_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect
} else if (v[0].is_double ()) {
out = fabs (v[0].to_double ());
} else {
out = labs (to_long (context, v[0]));
out = labs (to_long (context, v[0], 0));
}
}
@ -2463,7 +2463,7 @@ pow_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect
throw EvalError (tl::to_string (tr ("'pow' function expects exactly two arguments")), context);
}
out = pow (to_double (context, vv [0]), to_double (context, vv [1]));
out = pow (to_double (context, vv [0], 0), to_double (context, vv [1], 1));
}
static void
@ -2473,7 +2473,7 @@ atan2_f (const ExpressionParserContext &context, tl::Variant &out, const std::ve
throw EvalError (tl::to_string (tr ("'atan2' function expects exactly two arguments")), context);
}
out = atan2 (to_double (context, vv [0]), to_double (context, vv [1]));
out = atan2 (to_double (context, vv [0], 0), to_double (context, vv [1], 1));
}
static void
@ -2690,10 +2690,10 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v
long len = -1;
if (vv.size () > 2) {
len = std::max (long (0), to_long (context, vv [2]));
len = std::max (long (0), to_long (context, vv [2], 2));
}
long l = to_long (context, vv [1]);
long l = to_long (context, vv [1], 1);
if (l < 0) {
l = long (s.size ()) + l;
if (l < 0) {
@ -2713,6 +2713,26 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v
}
}
static void
upcase_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector <tl::Variant> &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 <tl::Variant> &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 <tl::Variant> &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

View File

@ -27,6 +27,7 @@
#include "tlDeferredExecution.h"
#include "tlObject.h"
#include "tlTimer.h"
#include "tlSleep.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
@ -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) {

View File

@ -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

14
testdata/algo/nreader24.cir vendored Normal file
View File

@ -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

11
testdata/algo/nreader25.cir vendored Normal file
View File

@ -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

View File

@ -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)

View File

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -14,10 +14,12 @@ end
a1 = input(1)
b1 = input(2)
c1 = input(3)
e1 = input(5)
a1.output(1, 0)
b1.output(2, 0)
c1.output(3, 0)
e1.output(5, 0)
c1.rounded_corners(0.5, 0.5, 16).output(1010, 0)
c1.smoothed(1.5).output(1011, 0)
@ -70,6 +72,11 @@ a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
e1.corners(90.0, as_boxes).sized(0.05).output(1066, 0)
e1.corners(90.0, absolute, as_boxes).sized(0.05).output(1067, 0)
e1.corners(90.0, negative, as_boxes).sized(0.05).output(1068, 0)
e1.corners(44.0 .. 46.0, absolute, as_boxes).sized(0.05).output(1069, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
a1.collect_to_region { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1102, 0)

View File

@ -18,9 +18,13 @@ ep.without_distance(0.25, nil).polygons(0).output(121, 0)
ep.with_angle(45.0).polygons(0).output(200, 0)
ep.with_angle(0.0).polygons(0).output(201, 0)
ep.with_angle(45.0..91.0).polygons(0).output(202, 0)
ep.with_angle(45.0, absolute).polygons(0).output(203, 0)
ep.with_angle(45.0..91.0, absolute).polygons(0).output(204, 0)
ep.with_angle(45.0, 91.0, absolute).polygons(0).output(205, 0)
ep.with_angle(45.0, both).polygons(0).output(210, 0)
ep.with_angle(0.0, both).polygons(0).output(211, 0)
ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0)
ep.with_angle(45.0..91.0, absolute, both).polygons(0).output(213, 0)
ep.without_angle(45.0).polygons(0).output(220, 0)
ep.without_angle(0.0).polygons(0).output(221, 0)

35
testdata/drc/drcSimpleTests_93.drc vendored Normal file
View File

@ -0,0 +1,35 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
r = input(1, 0)
e = r.edges
r.output(1, 0)
r.with_angle(45.0).polygons(0).output(100, 0)
r.with_angle(90.0).polygons(0).output(101, 0)
r.with_angle(91.0..100.0).polygons(0).output(102, 0)
r.without_angle(45.0).polygons(0).output(120, 0)
r.without_angle(90.0).polygons(0).output(121, 0)
r.without_angle(45.0..100.0).polygons(0).output(122, 0)
e.with_angle(45.0).output(200, 0)
e.with_angle(0.0).output(201, 0)
e.with_angle(45.0..91.0).output(202, 0)
e.with_angle(45.0, absolute).output(203, 0)
e.with_angle(45.0..91.0, absolute).output(204, 0)
e.with_angle(45.0, 91.0, absolute).output(205, 0)
e.without_angle(45.0).output(220, 0)
e.without_angle(0.0).output(221, 0)
e.without_angle(45.0..91.0).output(222, 0)
e.without_angle(45.0, absolute).output(223, 0)
e.without_angle(45.0..91.0, absolute).output(224, 0)
e.without_angle(45.0, 91.0, absolute).output(225, 0)

BIN
testdata/drc/drcSimpleTests_93.gds vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au93.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au93d.gds vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@ -294,6 +294,7 @@ class DBEdgePairs_TestClass < TestBase
ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 10))
r1 = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ])
assert_equal(r1.with_angle(0, 90, false).to_s, "") # @@@
assert_equal(r1.with_distance(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_distance(5, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
@ -310,15 +311,25 @@ class DBEdgePairs_TestClass < TestBase
assert_equal(r1.with_length_both(10, true).to_s, "(0,0;0,20)/(10,20;10,0)")
assert_equal(r1.with_angle(0, false).to_s, "")
assert_equal(r1.with_abs_angle(0, false).to_s, "")
assert_equal(r1.with_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(0, 90, false).to_s, "")
assert_equal(r1.with_abs_angle(0, 90, false).to_s, "")
assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, false).to_s, "")
assert_equal(r1.with_abs_angle_both(0, false).to_s, "")
assert_equal(r1.with_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, 90, false).to_s, "")
assert_equal(r1.with_abs_angle_both(0, 90, false).to_s, "")
assert_equal(r1.with_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(0, false).to_s, "(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(150, false).to_s, "(0,0;0,10)/(10,20;10,0)")

View File

@ -585,12 +585,19 @@ class DBEdges_TestClass < TestBase
r.insert(RBA::Edge::new(0, 0, 100, 0))
r.insert(RBA::Edge::new(100, 0, 100, 50))
assert_equal(r.with_angle(0, false).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(0, false).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(0, true).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(0, true).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(90, false).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(90, false).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(90, true).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(90, true).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(-10, 10, false).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(-10, 10, false).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(-10, 10, true).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(-10, 10, true).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(80, 100, false).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(80, 100, false).to_s, "(100,0;100,50)")
assert_equal(r.with_length(100, false).to_s, "(0,0;100,0)")
assert_equal(r.with_length(100, true).to_s, "(100,0;100,50)")
assert_equal(r.with_length(50, false).to_s, "(100,0;100,50)")