Corner detection feature: tests, bug fixes, enhancements (inclusive/exclusive angle constraints)

This commit is contained in:
Matthias Koefferlein 2021-01-09 23:10:17 +01:00
parent bc63f98622
commit ddf36290fa
11 changed files with 106 additions and 68 deletions

View File

@ -30,11 +30,14 @@ namespace db
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// CornerDetectorCore implementation // CornerDetectorCore implementation
CornerDetectorCore::CornerDetectorCore (double angle_start, double angle_end) CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
{ {
m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ());
m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ());
m_include_start = include_angle_start;
m_include_end = include_angle_end;
m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0; m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0;
m_all = (angle_end - angle_start - db::epsilon) > 360.0; m_all = (angle_end - angle_start - db::epsilon) > 360.0;
} }
@ -64,15 +67,18 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
db::DVector v1 = m_t_start * vin; db::DVector v1 = m_t_start * vin;
db::DVector v2 = m_t_end * vin; db::DVector v2 = m_t_end * vin;
bool vp1 = db::vprod_sign (v1, vout) >= 0; int vps1 = db::vprod_sign (v1, vout);
bool vp2 = db::vprod_sign (v2, vout) <= 0; int vps2 = db::vprod_sign (v2, vout);
bool opp1 = vps1 == 0 && (db::sprod_sign (v1, vout) < 0);
bool opp2 = vps2 == 0 && (db::sprod_sign (v2, vout) < 0);
bool vp1 = !opp1 && (m_include_start ? (db::vprod_sign (v1, vout) >= 0) : (db::vprod_sign (v1, vout) > 0));
bool vp2 = !opp2 && (m_include_end ? (db::vprod_sign (v2, vout) <= 0) : (db::vprod_sign (v2, vout) < 0));
if (m_big_angle && (vp1 || vp2)) { if (m_big_angle && (vp1 || vp2)) {
delivery.make_point (pt); delivery.make_point (pt);
} else if (! m_big_angle && vp1 && vp2) { } else if (! m_big_angle && vp1 && vp2) {
if (db::sprod_sign (v1, vout) > 0 && db::sprod_sign (v2, vout) > 0) { delivery.make_point (pt);
delivery.make_point (pt);
}
} }
} }

View File

@ -91,13 +91,14 @@ private:
class DB_PUBLIC CornerDetectorCore class DB_PUBLIC CornerDetectorCore
{ {
public: public:
CornerDetectorCore (double angle_start, double angle_end); CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end);
virtual ~CornerDetectorCore () { } virtual ~CornerDetectorCore () { }
void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const; void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const;
private: private:
db::CplxTrans m_t_start, m_t_end; db::CplxTrans m_t_start, m_t_end;
bool m_include_start, m_include_end;
bool m_big_angle, m_all; bool m_big_angle, m_all;
}; };
@ -108,8 +109,8 @@ class DB_PUBLIC CornersAsRectangles
: public db::PolygonProcessorBase, private CornerDetectorCore : public db::PolygonProcessorBase, private CornerDetectorCore
{ {
public: public:
CornersAsRectangles (double angle_start, double angle_end, db::Coord dim = 1) CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1)
: CornerDetectorCore (angle_start, angle_end), m_dim (dim) : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end), m_dim (dim)
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -137,8 +138,8 @@ class DB_PUBLIC CornersAsDots
: public db::PolygonToEdgeProcessorBase, private CornerDetectorCore : public db::PolygonToEdgeProcessorBase, private CornerDetectorCore
{ {
public: public:
CornersAsDots (double angle_start, double angle_end) CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, angle_end) : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
{ {
// .. nothing yet .. // .. nothing yet ..
} }

View File

@ -214,16 +214,16 @@ static db::CompoundRegionOperationNode *new_count_filter (db::CompoundRegionOper
return new db::CompoundRegionCountFilterNode (input, invert, min_count, max_count); return new db::CompoundRegionCountFilterNode (input, invert, min_count, max_count);
} }
static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, double 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 = 1)
{ {
check_non_null (input, "input"); check_non_null (input, "input");
return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, 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, dim), input, true /*processor is owned*/, dim /*dist adder*/);
} }
static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, double 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)
{ {
check_non_null (input, "input"); check_non_null (input, "input");
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, angle_end), input, true /*processor is owned*/); return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
} }
static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e)
@ -584,10 +584,10 @@ 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 ()), 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" "@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_start"), gsi::arg ("angle_end"), 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"),
"@brief Creates a node turning corners into rectangles.\n" "@brief Creates a node turning corners into rectangles.\n"
) + ) +
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_start"), gsi::arg ("angle_end"), 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"),
"@brief Creates a node turning corners into dots (single-point edges).\n" "@brief Creates a node turning corners into dots (single-point edges).\n"
) + ) +
gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0), gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0),

View File

@ -112,14 +112,14 @@ 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)); 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) static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
{ {
return r->processed (db::CornersAsDots (angle_start, angle_end)); return r->processed (db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end));
} }
static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim) 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)
{ {
return r->processed (db::CornersAsRectangles (angle_start, angle_end, dim)); return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
} }
static db::Region *new_si (const db::RecursiveShapeIterator &si) static db::Region *new_si (const db::RecursiveShapeIterator &si)
@ -1121,11 +1121,14 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@hide\n" "@hide\n"
"This method is provided for DRC implementation.\n" "This method is provided for DRC implementation.\n"
) + ) +
method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("dim", 1), 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),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n" "\n"
"The angle values specify a range of angles: all corners whose attached edges form an angle " "The angle values specify a range of angles: all corners whose attached edges form an angle "
"between angle_start and angle_end will be reported boxes with 2*dim x 2*dim dimension. The default dimension is 2x2 DBU.\n" "between angle_min and angle_max will be reported boxes with 2*dim x 2*dim dimension. The default dimension is 2x2 DBU.\n"
"\n"
"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" "\n"
"The angle is measured " "The angle is measured "
"between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left " "between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left "
@ -1134,14 +1137,14 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n" "\n"
"A similar function that reports corners as point-like edges is \\corners_dots.\n" "A similar function that reports corners as point-like edges is \\corners_dots.\n"
"\n" "\n"
"This function has been introduced in version 0.25.\n" "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
) + ) +
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), 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),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n" "\n"
"This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n"
"\n" "\n"
"This function has been introduced in version 0.25.\n" "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
) + ) +
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge, method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
"@brief Merge the region\n" "@brief Merge the region\n"

View File

@ -835,10 +835,10 @@ void run_test15 (tl::TestBase *_this, bool deep)
db::CompoundRegionOperationPrimaryNode *primary = new db::CompoundRegionOperationPrimaryNode (); db::CompoundRegionOperationPrimaryNode *primary = new db::CompoundRegionOperationPrimaryNode ();
db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, 180.0, 1), primary, true /*processor is owned*/); db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, true, 180.0, true, 1), primary, true /*processor is owned*/);
db::CompoundRegionCountFilterNode count1 (corners1, false, 5, 10000); db::CompoundRegionCountFilterNode count1 (corners1, false, 5, 10000);
db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, 180.0), primary, true /*processor is owned*/); db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, true, 180.0, true), primary, true /*processor is owned*/);
db::CompoundRegionCountFilterNode count2 (corners2, true, 5, 10000); db::CompoundRegionCountFilterNode count2 (corners2, true, 5, 10000);
EXPECT_EQ (count1.result_type () == db::CompoundRegionJoinOperationNode::Region, true); EXPECT_EQ (count1.result_type () == db::CompoundRegionJoinOperationNode::Region, true);

View File

@ -1220,13 +1220,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 (1, 0)), r1);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, 180.0))); 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, 180.0))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true)));
db::Region ext; db::Region ext;
r1.processed (db::CornersAsDots (0.0, 180.0)).extended (ext, 1000, 1000, 2000, 2000); r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).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 (12, 0)), ext);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, 180.0, 2000))); 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, 180.0, 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 (20, 0)), r1.processed (db::extents_processor<db::Polygon> (0, 0))); 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))); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor<db::Polygon> (1000, 2000)));

View File

@ -1979,13 +1979,17 @@ TEST(100_Processors)
r.insert (db::Box (db::Point (0, 300), db::Point (200, 400))); r.insert (db::Box (db::Point (0, 300), db::Point (200, 400)));
r.insert (db::Box (db::Point (100, 300), db::Point (200, 500))); r.insert (db::Box (db::Point (100, 300), db::Point (200, 500)));
EXPECT_EQ (r.processed (db::CornersAsDots (-180.0, 180.0)).to_string (), "(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)"); EXPECT_EQ (r.processed (db::CornersAsDots (-180.0, true, 180.0, true)).to_string (), "(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)");
EXPECT_EQ (r.processed (db::CornersAsDots (0.0, 180.0)).to_string (), "(100,400;100,400)"); EXPECT_EQ (r.processed (db::CornersAsDots (0.0, true, 180.0, true)).to_string (), "(100,400;100,400)");
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, true, 90.0, true)).to_string (), "(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)");
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, true)).to_string (), "(100,400;100,400)");
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, true, 90.0, false)).to_string (), "(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)");
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, false)).to_string (), "");
db::Region ext; db::Region ext;
r.processed (db::CornersAsDots (0.0, 180.0)).extended (ext, 10, 10, 20, 20); r.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 10, 10, 20, 20);
EXPECT_EQ (ext.to_string (), "(90,380;90,420;110,420;110,380)"); EXPECT_EQ (ext.to_string (), "(90,380;90,420;110,420;110,380)");
EXPECT_EQ (r.processed (db::CornersAsRectangles (-180.0, 180.0, 2)).to_string (), "(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)"); EXPECT_EQ (r.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2)).to_string (), "(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)");
EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, 180.0, 2)).to_string (), "(98,398;98,402;102,402;102,398)"); 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 (r.processed (db::extents_processor<db::Polygon> (0, 0)).to_string (), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)"); EXPECT_EQ (r.processed (db::extents_processor<db::Polygon> (0, 0)).to_string (), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)");
EXPECT_EQ (r.processed (db::extents_processor<db::Polygon> (10, 20)).to_string (), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)"); EXPECT_EQ (r.processed (db::extents_processor<db::Polygon> (10, 20)).to_string (), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)");

View File

@ -455,7 +455,7 @@ CODE
else else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
end end
DRCOpNodeCornersFilter::new(@engine, self, as_dots) DRCOpNodeCornersFilter::new(@engine, as_dots, self)
end end
end end
@ -536,12 +536,12 @@ CODE
end end
if as_edges if as_edges
return DRCOpNodeRelativeExtents::new(self, true, *f) return DRCOpNodeRelativeExtents::new(@engine, true, self, *f)
else else
# add oversize for point- and edge-like regions # add oversize for point- and edge-like regions
zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7 zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7
f += [ zero_area ? 1 : 0 ] * 2 f += [ zero_area ? 1 : 0 ] * 2
return DRCOpNodeRelativeExtents::new(self, false, *f) return DRCOpNodeRelativeExtents::new(@engine, false, self, *f)
end end
end end
@ -1207,7 +1207,7 @@ class DRCOpNodeCountFilter < DRCOpNodeWithCompare
args = [ self.input.create_node(cache), self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? @engine._make_numeric_value(self.gt) + 1 : (self.ge ? @engine._make_numeric_value(self.ge) : 0)) args << (self.gt ? @engine._make_numeric_value(self.gt) + 1 : (self.ge ? @engine._make_numeric_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? @engine._make_numeric_value(self.lt) : @engine._make_numeric_value(self.le) - 1) args << (self.lt ? @engine._make_numeric_value(self.lt) : @engine._make_numeric_value(self.le) + 1)
end end
RBA::CompoundRegionOperationNode::new_count_filter(*args) RBA::CompoundRegionOperationNode::new_count_filter(*args)
end end
@ -1240,7 +1240,7 @@ class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
args = [ self.input.create_node(cache), self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? @engine._make_area_value(self.gt) + 1 : (self.ge ? @engine._make_area_value(self.ge) : 0)) args << (self.gt ? @engine._make_area_value(self.gt) + 1 : (self.ge ? @engine._make_area_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? @engine._make_area_value(self.lt) : @engine._make_area_value(self.le) - 1) args << (self.lt ? @engine._make_area_value(self.lt) : @engine._make_area_value(self.le) + 1)
end end
RBA::CompoundRegionOperationNode::new_area_filter(*args) RBA::CompoundRegionOperationNode::new_area_filter(*args)
end end
@ -1281,7 +1281,7 @@ class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
args = [ n, self.inverse ] args = [ n, self.inverse ]
args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0)) args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) - 1) args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) + 1)
end end
RBA::CompoundRegionOperationNode::new_edge_length_filter(*args) RBA::CompoundRegionOperationNode::new_edge_length_filter(*args)
@ -1324,7 +1324,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
args = [ n, self.inverse ] args = [ n, self.inverse ]
angle_delta = 1e-6 angle_delta = 1e-6
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0)) args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0)) args << (self.lt ? self.lt : (self.le ? self.le + angle_delta : 180.0))
RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args) RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args)
@ -1358,7 +1358,7 @@ class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
args = [ self.input.create_node(cache), self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0)) args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) - 1) args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) + 1)
end end
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args) RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
end end
@ -1390,7 +1390,7 @@ class DRCOpNodeInteractingWithCount < DRCOpNodeWithCompare
args = [ self.a.create_node(cache), self.b.create_node(cache), self.inverse ] args = [ self.a.create_node(cache), self.b.create_node(cache), self.inverse ]
args << (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0)) args << (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? self.lt : self.le - 1) args << (self.lt ? self.lt : self.le + 1)
end end
factory = { :covering => :new_enclosing, factory = { :covering => :new_enclosing,
:overlapping => :new_overlapping, :overlapping => :new_overlapping,
@ -1549,7 +1549,7 @@ class DRCOpNodeBBoxParameterFilter < DRCOpNodeWithCompare
args = [ self.input.create_node(cache), self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0)) args << (self.gt ? @engine._make_value(self.gt) + 1 : (self.ge ? @engine._make_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) - 1) args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) + 1)
end end
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args) RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
end end
@ -1565,8 +1565,7 @@ end
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input attr_accessor :input
attr_accessor :parameter attr_accessor :as_dots
attr_accessor :inverse
def initialize(engine, as_dots, input) def initialize(engine, as_dots, input)
super(engine) super(engine)
@ -1577,14 +1576,15 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
def do_create_node(cache) def do_create_node(cache)
args = [ self.input.create_node(cache) ] args = [ self.input.create_node(cache) ]
angle_delta = 1e-6 args << (self.gt ? self.gt : (self.ge ? self.ge : -180.0))
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0)) args << (self.gt ? false : true)
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0)) args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
args << (self.lt ? false : true)
if self.as_dots if self.as_dots
RBA::CompoundRegionOperationNode::new_corners_as_dots_node(*args) RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
else else
args << 2 # dimension is 2x2 DBU args << 2 # dimension is 2x2 DBU
RBA::CompoundRegionOperationNode::new_corners_as_rectangles_node(*args) RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
end end
end end
@ -1595,11 +1595,17 @@ class DRCOpNodeRelativeExtents < DRCOpNode
attr_accessor :input attr_accessor :input
attr_accessor :as_edges, :fx1, :fx2, :fy1, :fy2, :dx, :dy attr_accessor :as_edges, :fx1, :fx2, :fy1, :fy2, :dx, :dy
def initialize(engine, input, as_edges, fx1, fx2, fy1, fy2, dx = 0, dy = 0) def initialize(engine, as_edges, input, fx1, fx2, fy1, fy2, dx = 0, dy = 0)
super(engine) super(engine)
self.input = input self.input = input
self.as_edges = as_edges self.as_edges = as_edges
self.description = "extents" self.description = "extents"
self.fx1 = fx1
self.fx2 = fx2
self.fy1 = fy1
self.fy2 = fy2
self.dx = dx
self.dy = dy
end end
def dump(indent) def dump(indent)
@ -1611,10 +1617,11 @@ class DRCOpNodeRelativeExtents < DRCOpNode
end end
def do_create_node(cache) def do_create_node(cache)
node = self.input.create_node(cache)
if !self.as_edges if !self.as_edges
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy) RBA::CompoundRegionOperationNode::new_relative_extents(node, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy)
else else
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2) RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(node, self.fx1, self.fx2, self.fy1, self.fy2)
end end
end end

View File

@ -429,13 +429,20 @@ CODE
# %DRC% # %DRC%
# @name corners # @name corners
# @brief Selects all polygons which are rectilinear # @brief Selects all polygons which are rectilinear
# @synopsis corners (in condition) # @synopsis corners([ options ]) (in condition)
# @synopsis corners(layer) # @synopsis corners(layer [, options ])
# #
# This function can be used with a layer argument in which case it # This function can be used with a layer argument in which case it
# is equivalent to "layer.corners" (see \Layer#corners). Without a layer # is equivalent to "layer.corners" (see \Layer#corners). Without a layer
# argument, "corners" represents the corner generator/filter in primary shapes for # argument, "corners" represents the corner generator/filter in primary shapes for
# \DRC# expressions (see \Layer#drc and \DRC#corners for more details). # \DRC# expressions (see \Layer#drc and \DRC#corners for more details).
#
# Like the layer-based version, the "corners" operator accepts the
# output type option: "as_dots" for dot-like edges and "as_boxes" for
# small (2x2 DBU) box markers.
#
# The "corners" operator can be put into a condition which means it's
# applied to coners meeting a particular angle constraint.
def _cop_corners(as_dots = DRCAsDots::new(false)) def _cop_corners(as_dots = DRCAsDots::new(false))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.

View File

@ -118,3 +118,13 @@ TEST(5d)
{ {
run_test (_this, "5", true); run_test (_this, "5", true);
} }
TEST(6)
{
run_test (_this, "6", false);
}
TEST(6d)
{
run_test (_this, "6", true);
}

View File

@ -470,13 +470,13 @@ class DBRegion_TestClass < TestBase
assert_equal(r3a.separation_check(r1, 15, false, RBA::Region::Projection, nil, 380, 500).to_s, "") assert_equal(r3a.separation_check(r1, 15, false, RBA::Region::Projection, nil, 380, 500).to_s, "")
assert_equal(r3a.separation_check(r1, 15, false, RBA::Region::Projection, nil, 0, 300).to_s, "(-10,10;-10,0)/(0,0;0,10)") assert_equal(r3a.separation_check(r1, 15, false, RBA::Region::Projection, nil, 0, 300).to_s, "(-10,10;-10,0)/(0,0;0,10)")
assert_equal(r3b.overlap_check(r1, 15).to_s, "(10,10;10,-10)/(0,0;0,21);(-10,10;10,10)/(21,0;0,0)") assert_equal(r3b.overlap_check(r1, 15).to_s, "(-10,10;10,10)/(21,0;0,0);(10,10;10,-10)/(0,0;0,21)")
assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, nil, nil).to_s, "(10,10;10,0)/(0,0;0,10);(0,10;10,10)/(10,0;0,0)") assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, nil, nil).to_s, "(0,10;10,10)/(10,0;0,0);(10,10;10,0)/(0,0;0,10)")
assert_equal(r3b.overlap_check(r1, 15, true, RBA::Region::Projection, nil, nil, nil).to_s, "(10,10;10,-10)/(0,0;0,200);(-10,10;10,10)/(100,0;0,0)") assert_equal(r3b.overlap_check(r1, 15, true, RBA::Region::Projection, nil, nil, nil).to_s, "(-10,10;10,10)/(100,0;0,0);(10,10;10,-10)/(0,0;0,200)")
assert_equal(r3b.overlap_check(r1, 15, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "") assert_equal(r3b.overlap_check(r1, 15, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "")
assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 0, 500).to_s, "(10,10;10,0)/(0,0;0,10);(0,10;10,10)/(10,0;0,0)") assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 0, 500).to_s, "(0,10;10,10)/(10,0;0,0);(10,10;10,0)/(0,0;0,10)")
assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 380, 500).to_s, "") assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 380, 500).to_s, "")
assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 0, 300).to_s, "(10,10;10,0)/(0,0;0,10);(0,10;10,10)/(10,0;0,0)") assert_equal(r3b.overlap_check(r1, 15, false, RBA::Region::Projection, nil, 0, 300).to_s, "(0,10;10,10)/(10,0;0,0);(10,10;10,0)/(0,0;0,10)")
assert_equal((r1 | r2).merged.isolated_check(25).to_s, "(120,20;120,380)/(100,395;100,5)") assert_equal((r1 | r2).merged.isolated_check(25).to_s, "(120,20;120,380)/(100,395;100,5)")
assert_equal((r1 | r2).merged.isolated_check(25, false, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(100,380;100,20)") assert_equal((r1 | r2).merged.isolated_check(25, false, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(100,380;100,20)")
@ -497,9 +497,9 @@ class DBRegion_TestClass < TestBase
assert_equal((r1 | r2).merged.notch_check(25, true, RBA::Region::Projection, nil, 40, 50).to_s, "(0,200;50,200)/(50,220;10,220)") assert_equal((r1 | r2).merged.notch_check(25, true, RBA::Region::Projection, nil, 40, 50).to_s, "(0,200;50,200)/(50,220;10,220)")
assert_equal((r1 | r2).merged.notch_check(25, true, RBA::Region::Projection, nil, nil, 40).to_s, "") assert_equal((r1 | r2).merged.notch_check(25, true, RBA::Region::Projection, nil, nil, 40).to_s, "")
assert_equal((r1 | r2).merged.space_check(25).to_s, "(0,200;50,200)/(50,220;10,220);(100,395;100,5)/(120,20;120,380)") assert_equal((r1 | r2).merged.space_check(25).to_s, "(100,395;100,5)/(120,20;120,380);(0,200;50,200)/(50,220;10,220)")
assert_equal((r1 | r2).merged.space_check(25, false, RBA::Region::Projection, nil, nil, nil).to_s, "(10,200;50,200)/(50,220;10,220);(100,380;100,20)/(120,20;120,380)") assert_equal((r1 | r2).merged.space_check(25, false, RBA::Region::Projection, nil, nil, nil).to_s, "(100,380;100,20)/(120,20;120,380);(10,200;50,200)/(50,220;10,220)")
assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, nil, nil).to_s, "(0,200;50,200)/(50,220;10,220);(100,400;100,0)/(120,20;120,380)") assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, nil, nil).to_s, "(100,400;100,0)/(120,20;120,380);(0,200;50,200)/(50,220;10,220)")
assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "") assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "")
assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, 50, nil).to_s, "(100,400;100,0)/(120,20;120,380)") assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, 50, nil).to_s, "(100,400;100,0)/(120,20;120,380)")
assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, nil, 50).to_s, "(0,200;50,200)/(50,220;10,220)") assert_equal((r1 | r2).merged.space_check(25, true, RBA::Region::Projection, nil, nil, 50).to_s, "(0,200;50,200)/(50,220;10,220)")