Enhanced intersections algorithm so that the generated points won't overlay with finite edges from the AND part

This commit is contained in:
Matthias Koefferlein 2019-11-19 21:19:36 +01:00
parent 9af662a512
commit 6c7ceb74dc
12 changed files with 306 additions and 46 deletions

View File

@ -533,7 +533,7 @@ EdgesDelegate *
AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const
{
std::auto_ptr<FlatEdges> output (new FlatEdges (true));
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&output->raw_edges (), op);
EdgeBooleanClusterCollectorToShapes cluster_collector (&output->raw_edges (), op);
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (size () + (other ? other->size () : 0));

View File

@ -137,6 +137,21 @@ struct box_scanner_receiver
* terminate the scan process early if the outcome is known.
*/
bool stop () const { return false; }
/**
* @brief Pre-scanning operations
*
* This method is called before the scanning starts.
*/
void initialize () { }
/**
* @brief Post-scanning operations
*
* This method is called after the scan has finished (without exception). The argument is the
* return value (false if "stop" stopped the process).
*/
void finalize (bool) { }
};
/**
@ -265,6 +280,22 @@ public:
*/
template <class Rec, class BoxConvert>
bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bc);
rec.finalize (ret);
return ret;
}
private:
container_type m_pp;
double m_fill_factor;
size_t m_scanner_thr;
bool m_report_progress;
std::string m_progress_desc;
template <class Rec, class BoxConvert>
bool do_process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
{
typedef typename BoxConvert::box_type box_type;
typedef typename box_type::coord_type coord_type;
@ -420,13 +451,6 @@ public:
return true;
}
private:
container_type m_pp;
double m_fill_factor;
size_t m_scanner_thr;
bool m_report_progress;
std::string m_progress_desc;
};
/**
@ -469,6 +493,21 @@ struct box_scanner_receiver2
* terminate the scan process early if the outcome is known.
*/
bool stop () const { return false; }
/**
* @brief Pre-scanning operations
*
* This method is called before the scanning starts.
*/
void initialize () { }
/**
* @brief Post-scanning operations
*
* This method is called after the scan has finished (without exception). The argument is the
* return value (false if "stop" stopped the process).
*/
void finalize (bool) { }
};
/**
@ -613,6 +652,23 @@ public:
*/
template <class Rec, class BoxConvert1, class BoxConvert2>
bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bc1, bc2);
rec.finalize (ret);
return ret;
}
private:
container_type1 m_pp1;
container_type2 m_pp2;
double m_fill_factor;
size_t m_scanner_thr;
bool m_report_progress;
std::string m_progress_desc;
template <class Rec, class BoxConvert1, class BoxConvert2>
bool do_process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ())
{
typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type
typedef typename box_type::coord_type coord_type;
@ -845,14 +901,6 @@ public:
return true;
}
private:
container_type1 m_pp1;
container_type2 m_pp2;
double m_fill_factor;
size_t m_scanner_thr;
bool m_report_progress;
std::string m_progress_desc;
};
/**

View File

@ -349,7 +349,7 @@ public:
// .. and run the merge operation
s->second.clear ();
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&s->second, EdgeOr);
EdgeBooleanClusterCollectorToShapes cluster_collector (&s->second, EdgeOr);
m_scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
return s->second;

View File

@ -26,6 +26,7 @@
#include "dbEdge.h"
#include "dbHash.h"
#include "dbBoxScanner.h"
#include "dbShapes.h"
#include "tlIntervalMap.h"
@ -222,14 +223,14 @@ struct EdgeBooleanClusterCollector
{
EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op)
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/),
mp_intersections (op == EdgeIntersections ? output : 0)
mp_output (output), mp_intersections (op == EdgeIntersections ? output : 0)
{
// .. nothing yet ..
}
EdgeBooleanClusterCollector (OutputContainer *output, OutputContainer *intersections, EdgeBoolOp op)
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, op), op != EdgeAnd /*report single*/),
mp_intersections (intersections)
mp_output (output), mp_intersections (intersections)
{
// .. nothing yet ..
}
@ -252,16 +253,168 @@ struct EdgeBooleanClusterCollector
} else if (mp_intersections && p1 != p2) {
std::pair<bool, db::Point> ip = o1->intersect_point (*o2);
if (ip.first && m_seen_intersections.insert (ip.second).second) {
mp_intersections->insert (db::Edge (ip.second, ip.second));
if (ip.first) {
m_intersections.insert (ip.second);
}
}
}
/**
* @brief A receiver for the reducer which removes points that are on the edges
*/
struct RemovePointsOnEdges
: public db::box_scanner_receiver2<db::Edge, size_t, db::Point, size_t>
{
public:
RemovePointsOnEdges (std::set<db::Point> &points_to_remove)
: mp_points_to_remove (&points_to_remove)
{ }
void add (const db::Edge *e, const size_t &, const db::Point *pt, const size_t &)
{
if (e->contains (*pt)) {
mp_points_to_remove->insert (*pt);
}
}
private:
std::set<db::Point> *mp_points_to_remove;
};
/**
* @brief An inserter to produce degenerated edges from points
*/
struct PointInserter
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
typedef db::Point value_type;
PointInserter (OutputContainer *output)
: mp_output (output)
{ }
PointInserter &operator= (const db::Point &pt)
{
mp_output->insert (db::Edge (pt, pt));
return *this;
}
PointInserter &operator* () { return *this; }
PointInserter &operator++ () { return *this; }
PointInserter &operator++ (int) { return *this; }
private:
OutputContainer *mp_output;
};
/**
* @brief Finalizes the implementation for "EdgeIntersections"
* This method pushes those points which don't interact with the edges to the output container
* as degenerate edges. It needs to be called after the pass has been made.
*/
void finalize (bool)
{
if (m_intersections.empty ()) {
return;
}
db::box_scanner2<db::Edge, size_t, db::Point, size_t> intersections_to_edge_scanner;
for (typename OutputContainer::const_iterator e = mp_output->begin (); e != mp_output->end (); ++e) {
intersections_to_edge_scanner.insert1 (e.operator-> (), 0);
}
for (std::set<db::Point>::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) {
intersections_to_edge_scanner.insert2 (p.operator-> (), 0);
}
std::set<db::Point> points_to_remove;
RemovePointsOnEdges rpoe (points_to_remove);
intersections_to_edge_scanner.process (rpoe, 1, db::box_convert<db::Edge> (), db::box_convert<db::Point> ());
std::set_difference (m_intersections.begin (), m_intersections.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (mp_intersections));
}
private:
OutputContainer *mp_output;
OutputContainer *mp_intersections;
std::unordered_set<db::Point> m_seen_intersections;
std::set<db::Point> m_intersections;
};
/**
* @brief A helper class to use db::Shapes as container for EdgeBooleanClusterCollector
*/
struct DB_PUBLIC ShapesToOutputContainerAdaptor
{
public:
struct Iterator
: public db::Shapes::shape_iterator
{
Iterator (const db::Shapes::shape_iterator &iter)
: db::Shapes::shape_iterator (iter)
{ }
Iterator ()
: db::Shapes::shape_iterator ()
{ }
const db::Edge *operator-> () const
{
return (db::Shapes::shape_iterator::operator* ()).basic_ptr (db::Edge::tag ());
}
bool operator!= (const Iterator &other) const
{
// only for testing whether at end:
return at_end () != other.at_end ();
}
bool operator== (const Iterator &other) const
{
// only for testing whether at end:
return at_end () == other.at_end ();
}
};
typedef Iterator const_iterator;
ShapesToOutputContainerAdaptor (db::Shapes &shapes)
: mp_shapes (&shapes)
{
// .. nothing yet ..
}
const_iterator begin ()
{
return Iterator (mp_shapes->begin (db::ShapeIterator::Edges));
}
const_iterator end ()
{
return Iterator ();
}
void insert (const db::Edge &edge)
{
mp_shapes->insert (edge);
}
private:
db::Shapes *mp_shapes;
};
/**
* @brief A specialization of the EdgeBooleanClusterCollector for a Shapes output container
*/
struct DB_PUBLIC EdgeBooleanClusterCollectorToShapes
: EdgeBooleanClusterCollector<ShapesToOutputContainerAdaptor>
{
EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op)
: EdgeBooleanClusterCollector<ShapesToOutputContainerAdaptor> (&m_adaptor, op), m_adaptor (*output)
{
}
private:
ShapesToOutputContainerAdaptor m_adaptor;
};
}

View File

@ -112,7 +112,7 @@ FlatEdges::ensure_merged_edges_valid () const
m_merged_edges.clear ();
db::Shapes tmp (false);
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&tmp, EdgeOr);
EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr);
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (m_edges.size ());

View File

@ -1228,11 +1228,6 @@ public:
add_pair (*c1, *i2, p, t);
}
bool stop () const
{
return false;
}
/**
* @brief Finally join the clusters in the join set
*
@ -1275,6 +1270,11 @@ public:
}
}
// needs explicit implementation because we have two base classes:
bool stop () const { return false; }
void initialize () { }
void finalize (bool) { }
private:
struct InteractionKeyForClustersType
: public InstanceToInstanceInteraction

View File

@ -253,7 +253,7 @@ OriginalLayerEdges::ensure_merged_edges_valid () const
m_merged_edges.clear ();
db::Shapes tmp (false);
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&tmp, EdgeOr);
EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr);
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (size ());

View File

@ -36,6 +36,8 @@ struct BoxScannerTestRecorder
}
bool stop () const { return false; }
void initialize () { str += "[i]"; }
void finalize (bool) { str += "[f]"; }
void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2)
{
@ -54,6 +56,8 @@ struct BoxScannerTestRecorderStopping
}
bool stop () const { return do_stop; }
void initialize () { str += "[i]"; }
void finalize (bool s) { str += s ? "[f+]" : "[f-]"; }
void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2)
{
@ -70,6 +74,8 @@ struct BoxScannerTestRecorder2
void finish (const db::Box *, size_t) { }
bool stop () const { return false; }
void initialize () { }
void finalize (bool) { }
void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2)
{
@ -91,6 +97,8 @@ struct BoxScannerTestRecorderTwo
}
bool stop () const { return false; }
void initialize () { str += "[i]"; }
void finalize (bool) { str += "[f]"; }
void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2)
{
@ -113,6 +121,8 @@ struct BoxScannerTestRecorderTwoStopping
}
bool stop () const { return do_stop; }
void initialize () { str += "[i]"; }
void finalize (bool s) { str += s ? "[f+]" : "[f-]"; }
void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2)
{
@ -130,6 +140,8 @@ struct BoxScannerTestRecorder2Two
void finish2 (const db::SimplePolygon *, int) { }
bool stop () const { return false; }
void initialize () { }
void finalize (bool) { }
void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2)
{
@ -159,11 +171,11 @@ TEST(1)
db::box_convert<db::Box> bc;
bs.set_scanner_threshold (0);
EXPECT_EQ (bs.process (tr, 1, bc), true);
EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>");
EXPECT_EQ (tr.str, "[i](4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>[f]");
BoxScannerTestRecorderStopping trstop;
EXPECT_EQ (bs.process (trstop, 1, bc), false);
EXPECT_EQ (trstop.str, "(4-2)");
EXPECT_EQ (trstop.str, "[i](4-2)[f-]");
}
TEST(1a)
@ -182,7 +194,7 @@ TEST(1a)
db::box_convert<db::Box> bc;
bs.set_scanner_threshold (0);
bs.process (tr, 1, bc);
EXPECT_EQ (tr.str, "(1-0)<0><1>");
EXPECT_EQ (tr.str, "[i](1-0)<0><1>[f]");
}
TEST(1b)
@ -204,7 +216,7 @@ TEST(1b)
db::box_convert<db::Box> bc;
bs.set_scanner_threshold (0);
bs.process (tr, 1, bc);
EXPECT_EQ (tr.str, "(3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>");
EXPECT_EQ (tr.str, "[i](3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>[f]");
}
TEST(1c)
@ -226,7 +238,7 @@ TEST(1c)
db::box_convert<db::Box> bc;
bs.set_scanner_threshold (0);
bs.process (tr, 1, bc);
EXPECT_EQ (tr.str, "(3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>");
EXPECT_EQ (tr.str, "[i](3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>[f]");
}
TEST(1d)
@ -248,7 +260,7 @@ TEST(1d)
db::box_convert<db::Box> bc;
bs.set_scanner_threshold (0);
bs.process (tr, 0, bc);
EXPECT_EQ (tr.str, "(3-0)<0><3><1><4><2>");
EXPECT_EQ (tr.str, "[i](3-0)<0><3><1><4><2>[f]");
}
TEST(1e)
@ -269,7 +281,7 @@ TEST(1e)
bs.set_fill_factor (0.0);
db::box_convert<db::Box> bc;
bs.process (tr, 0, bc);
EXPECT_EQ (tr.str, "(0-3)<0><1><2><3><4>");
EXPECT_EQ (tr.str, "[i](0-3)<0><1><2><3><4>[f]");
}
TEST(1f)
@ -280,7 +292,7 @@ TEST(1f)
bs.set_fill_factor (0.0);
db::box_convert<db::Box> bc;
bs.process (tr, 0, bc);
EXPECT_EQ (tr.str, "");
EXPECT_EQ (tr.str, "[i][f]");
}
TEST(1g)
@ -302,7 +314,7 @@ TEST(1g)
bs.set_fill_factor (0.0);
db::box_convert<db::Box> bc;
bs.process (tr, 0, bc);
EXPECT_EQ (tr.str, "<2><4>(0-3)<0><1><3>");
EXPECT_EQ (tr.str, "[i]<2><4>(0-3)<0><1><3>[f]");
}
void run_test2 (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true)
@ -927,7 +939,7 @@ TEST(two_1)
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.process (tr, 1, bc1, bc2);
EXPECT_EQ (tr.str, "(2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>");
EXPECT_EQ (tr.str, "[i](2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>[f]");
}
TEST(two_1a)
@ -963,7 +975,7 @@ TEST(two_1a)
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.process (tr, 1, bc1, bc2);
EXPECT_EQ (tr.str, "(2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>");
EXPECT_EQ (tr.str, "[i](2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>[f]");
}
TEST(two_1b)
@ -999,12 +1011,12 @@ TEST(two_1b)
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true);
EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>");
EXPECT_EQ (tr.str, "[i](1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>[f]");
BoxScannerTestRecorderTwoStopping trstop;
EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false);
EXPECT_EQ (trstop.str, "(1-12)");
EXPECT_EQ (trstop.str, "[i](1-12)[f-]");
}
TEST(two_1c)
@ -1035,7 +1047,7 @@ TEST(two_1c)
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true);
EXPECT_EQ (tr.str, "<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>");
EXPECT_EQ (tr.str, "[i]<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>[f]");
}
void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true)

View File

@ -131,6 +131,7 @@ TEST(3_Edge2EdgeBooleans)
db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss);
db::Region r2and3 = r2 & r3;
db::Edges e2 = r2.edges ();
db::Edges e3 = r3.edges ();
db::Edges e2and3 = r2and3.edges ();
@ -144,6 +145,8 @@ TEST(3_Edge2EdgeBooleans)
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 - e2and3);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 ^ e2and3);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.intersections(e2and3));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), e3.intersections(e2));
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au3.gds");

View File

@ -852,9 +852,37 @@ TEST(22)
ee.insert (db::Edge (4000,-2000,-2000,-2000));
EXPECT_EQ ((e & ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)");
EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;400,0);(-2000,0;-2000,0);(1000,0;1000,0);(4000,0;4000,0);(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)");
// no particular new points with intersections - just endpoints of original edges
EXPECT_EQ (e.intersections (ee).merged ().to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)");
EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)");
// Edge/edge intersections
ee.clear ();
e.clear ();
e.insert (db::Edge (0, -100, 0, 150));
ee.insert (db::Edge (-50, 50, 50, 50));
ee.insert (db::Edge (-50, 100, 50, 100));
EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points
EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,100;0,100)");
// Edge is intersected by pair with connection point on this line
ee.clear ();
e.clear ();
e.insert (db::Edge (0, -100, 0, 150));
ee.insert (db::Edge (-50, 50, 0, 50));
ee.insert (db::Edge (0, 60, 50, 60));
ee.insert (db::Edge (-50, 100, 0, 100));
ee.insert (db::Edge (0, 100, 50, 100));
EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points
EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,60;0,60);(0,100;0,100)");
// Coincident edges are crossed by another one
ee.clear ();
e.clear ();
e.insert (db::Edge (0, -100, 0, 250));
ee.insert (db::Edge (0, 0, 0, 150));
ee.insert (db::Edge (-50, 100, 50, 100));
ee.insert (db::Edge (-50, 200, 50, 200));
EXPECT_EQ ((e & ee).to_string (), "(0,0;0,150)");
EXPECT_EQ (e.intersections (ee).to_string (), "(0,0;0,150);(0,200;0,200)");
}
TEST(23)

Binary file not shown.

View File

@ -589,6 +589,22 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
p = c.edges.pull_interacting(b.edges)
p.output(lb + 5, dm)
lb += 10 #440
message "--- edge booleans, intersections #{lb}"
p = c.edges - b.edges
p.output(lb, dm)
p = b.edges - c.edges
p.output(lb + 1, dm)
p = c.edges & b.edges
p.output(lb + 2, dm)
p = c.edges ^ b.edges
p.output(lb + 3, dm)
p = c.edges.intersections(b.edges)
p.output(lb + 4, dm)
p = b.edges.intersections(c.edges)
p.output(lb + 5, dm)
end
if $drc_test_mode == 1