From 5187ddbfc0f7a6f0caff7caf3690cb19fab851f7 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sat, 17 Feb 2024 21:20:24 +0100
Subject: [PATCH 01/79] Do not insert the same point twice into edge set in
EdgeProcessor - this improves performance in the case of manifold
intersecions in one point. Also: added edge count API
---
src/db/db/dbEdgeProcessor.cc | 13 +++++++++++++
src/db/db/dbEdgeProcessor.h | 5 +++++
src/db/db/dbShapeProcessor.cc | 6 ++++++
src/db/db/dbShapeProcessor.h | 5 +++++
4 files changed, 29 insertions(+)
diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc
index 9f1c3f11f..4f0534ae8 100644
--- a/src/db/db/dbEdgeProcessor.cc
+++ b/src/db/db/dbEdgeProcessor.cc
@@ -251,6 +251,13 @@ struct CutPoints
}
+ // do not insert points twice
+ for (auto c = cut_points.begin (); c != cut_points.end (); ++c) {
+ if (*c == p) {
+ return;
+ }
+ }
+
cut_points.push_back (p);
}
@@ -1057,6 +1064,12 @@ EdgeProcessor::reserve (size_t n)
mp_work_edges->reserve (n);
}
+size_t
+EdgeProcessor::count () const
+{
+ return mp_work_edges->size ();
+}
+
void
EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p)
{
diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h
index a6fb2a680..30de5b35d 100644
--- a/src/db/db/dbEdgeProcessor.h
+++ b/src/db/db/dbEdgeProcessor.h
@@ -695,6 +695,11 @@ public:
*/
void reserve (size_t n);
+ /**
+ * @brief Reports the number of edges stored in the processor
+ */
+ size_t count () const;
+
/**
* @brief Insert an edge
*/
diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc
index 518aaa9e7..974fd469b 100644
--- a/src/db/db/dbShapeProcessor.cc
+++ b/src/db/db/dbShapeProcessor.cc
@@ -53,6 +53,12 @@ ShapeProcessor::reserve (size_t n)
m_processor.reserve (n);
}
+size_t
+ShapeProcessor::count () const
+{
+ return m_processor.count ();
+}
+
void
ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
{
diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h
index a7afde10c..333c53b69 100644
--- a/src/db/db/dbShapeProcessor.h
+++ b/src/db/db/dbShapeProcessor.h
@@ -196,6 +196,11 @@ public:
*/
void reserve (size_t n);
+ /**
+ * @brief Reports the number of edges stored in the processor
+ */
+ size_t count () const;
+
/**
* @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details)
*/
From 8947c9992fb534c12e7c1a354552717245d22c4f Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sat, 17 Feb 2024 21:20:52 +0100
Subject: [PATCH 02/79] Added configuration options for XOR tool to switch
between with merge-before and without.
---
.../tools/xor/lay_plugin/layXORToolDialog.cc | 169 ++++++++++--------
1 file changed, 92 insertions(+), 77 deletions(-)
diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc
index d78cbc9cf..08558adc4 100644
--- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc
+++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc
@@ -52,6 +52,12 @@
namespace lay
{
+bool merge_before_bool ()
+{
+ // $KLAYOUT_XOR_MERGE_BEFORE_BOOLEAN
+ return tl::app_flag ("xor-merge-before-boolean");
+}
+
std::string cfg_xor_input_mode ("xor-input-mode");
std::string cfg_xor_output_mode ("xor-output-mode");
std::string cfg_xor_nworkers ("xor-num-workers");
@@ -864,107 +870,116 @@ XORWorker::do_perform_tiled (const XORTask *xor_task)
if (! mp_job->has_tiles ()) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part");
-#if 0
- // Straightforward implementation
- sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la,
- mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb,
- xor_results_cell.shapes (0), op, true, false, true);
-#else
- // This implementation is faster when a lot of overlapping shapes are involved
- db::Layout merge_helper;
- db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ());
- merge_helper.insert_layer (0);
- merge_helper.insert_layer (1);
- if (!la.empty ()) {
- sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la,
- merge_helper_cell.shapes (0), true, 0, false, true);
+ if (! merge_before_bool ()) {
+#
+ // Straightforward implementation
+ sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la,
+ mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb,
+ xor_results_cell.shapes (0), mp_job->op (), true, false, true);
+
+ } else {
+
+ // This implementation is faster when a lot of overlapping shapes are involved
+ db::Layout merge_helper;
+ db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ());
+ merge_helper.insert_layer (0);
+ merge_helper.insert_layer (1);
+
+ if (!la.empty ()) {
+ sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la,
+ merge_helper_cell.shapes (0), true, 0, false, true);
+ }
+ if (!lb.empty ()) {
+ sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb,
+ merge_helper_cell.shapes (1), true, 0, false, true);
+ }
+ sp.boolean (merge_helper, merge_helper_cell, 0,
+ merge_helper, merge_helper_cell, 1,
+ xor_results_cell.shapes (0), mp_job->op (), true, false, true);
+
}
- if (!lb.empty ()) {
- sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb,
- merge_helper_cell.shapes (1), true, 0, false, true);
- }
- sp.boolean (merge_helper, merge_helper_cell, 0,
- merge_helper, merge_helper_cell, 1,
- xor_results_cell.shapes (0), mp_job->op (), true, false, true);
-#endif
} else {
tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part");
size_t n;
-#if 0
- // Straightforward implementation
- sp.clear ();
-
- db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ());
- db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ());
-
- n = 0;
- for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), mp_job->cva ().cell (), la, region_a); ! s.at_end (); ++s, ++n) {
- sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2);
- }
-
- n = 0;
- for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), mp_job->cvb ().cell (), lb, region_b); ! s.at_end (); ++s, ++n) {
- sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1);
- }
-
- db::BooleanOp bool_op (mp_job->op ());
- db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/);
- db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/);
- sp.process (out, mp_job->op ());
-#else
- // This implementation is faster when a lot of overlapping shapes are involved
- db::Layout merge_helper;
- merge_helper.dbu (mp_job->dbu ());
- db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ());
- merge_helper.insert_layer (0);
- merge_helper.insert_layer (1);
-
- // This implementation is faster when a lot of overlapping shapes are involved
- if (!la.empty ()) {
+ if (! merge_before_bool ()) {
+ // Straightforward implementation
sp.clear ();
- db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ());
+ db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ());
+ db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ());
n = 0;
for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) {
- sp.insert (s.shape (), dbu_scale * s.trans (), n);
+ sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2);
}
- db::MergeOp op (0);
- db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/);
- db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/);
- sp.process (out, op);
-
- }
-
- if (!lb.empty ()) {
-
- sp.clear ();
-
- db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ());
-
n = 0;
for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) {
- sp.insert (s.shape (), dbu_scale * s.trans (), n);
+ sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1);
}
- db::MergeOp op (0);
- db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/);
+ db::BooleanOp bool_op (mp_job->op ());
+ db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/);
db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/);
- sp.process (out, op);
+ sp.process (out, bool_op);
+
+ } else {
+
+ // This implementation is faster when a lot of overlapping shapes are involved
+ db::Layout merge_helper;
+ merge_helper.dbu (mp_job->dbu ());
+ db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ());
+ merge_helper.insert_layer (0);
+ merge_helper.insert_layer (1);
+
+ // This implementation is faster when a lot of overlapping shapes are involved
+ if (!la.empty ()) {
+
+ sp.clear ();
+
+ db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ());
+
+ n = 0;
+ for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) {
+ sp.insert (s.shape (), dbu_scale * s.trans (), n);
+ }
+
+ db::MergeOp op (0);
+ db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/);
+ db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/);
+ sp.process (out, op);
+
+ }
+
+ if (!lb.empty ()) {
+
+ sp.clear ();
+
+ db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ());
+
+ n = 0;
+ for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) {
+ sp.insert (s.shape (), dbu_scale * s.trans (), n);
+ }
+
+ db::MergeOp op (0);
+ db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/);
+ db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/);
+ sp.process (out, op);
+
+ }
+
+ sp.boolean (merge_helper, merge_helper_cell, 0,
+ merge_helper, merge_helper_cell, 1,
+ xor_results_cell.shapes (0), mp_job->op (), true, false, true);
}
- sp.boolean (merge_helper, merge_helper_cell, 0,
- merge_helper, merge_helper_cell, 1,
- xor_results_cell.shapes (0), mp_job->op (), true, false, true);
-#endif
-
}
} else if (mp_job->op () == db::BooleanOp::Xor ||
From f7411b52d2956fe1b0851534fe2daf03804f2fbe Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 21 Feb 2024 22:17:24 +0100
Subject: [PATCH 03/79] Fixed a typo in DRC doc of 'corners'
---
src/doc/doc/about/drc_ref.xml | 2 +-
src/doc/doc/about/drc_ref_drc.xml | 14 ++++++++------
src/doc/doc/about/drc_ref_global.xml | 7 +------
src/doc/doc/about/drc_ref_layer.xml | 7 ++-----
src/doc/doc/about/drc_ref_netter.xml | 2 +-
src/doc/doc/about/drc_ref_source.xml | 2 +-
src/doc/doc/about/lvs_ref.xml | 2 +-
src/doc/doc/about/lvs_ref_global.xml | 2 +-
src/doc/doc/about/lvs_ref_netter.xml | 2 +-
src/drc/drc/built-in-macros/_drc_complex_ops.rb | 12 +++++++-----
src/drc/drc/built-in-macros/_drc_layer.rb | 2 +-
11 files changed, 25 insertions(+), 29 deletions(-)
diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml
index 889c3fedc..203941a1d 100644
--- a/src/doc/doc/about/drc_ref.xml
+++ b/src/doc/doc/about/drc_ref.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml
index 7ee678eca..7f121c94a 100644
--- a/src/doc/doc/about/drc_ref_drc.xml
+++ b/src/doc/doc/about/drc_ref_drc.xml
@@ -1,7 +1,7 @@
-
+
@@ -358,7 +358,7 @@ The plain function is equivalent to "primary.bbox_width".
This method acts on edge expressions and delivers a specific part of each edge.
See layer#centers for details about this functionality.
-"corners" - Applies smoothing
+"corners" - Selects corners of polygons
Usage:
@@ -369,8 +369,10 @@ See layer#centers for details abo
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
-a corner is positive for a turn to the left if walking a polygon counterclockwise
-and negative for the turn to the right. Angles take values between -180 and 180 degree.
+a corner is positive for a turn to the left if walking a polygon clockwise
+and negative for the turn to the right. Hence positive angles indicate concave
+(inner) corners, negative ones indicate convex (outer) corners.
+Angles take values between -180 and 180 degree.
When using "as_dots" for the argument, the operation will return single-point edges at
the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be
@@ -386,8 +388,8 @@ out = in.drc(primary.corners) # equivalent
The following example selects all inner corners:
-out = in.drc(corners < 0)
-out = in.drc(primary.corners < 0) # equivalent
+out = in.drc(corners > 0)
+out = in.drc(primary.corners > 0) # equivalent
The "corners" method is available as a plain function or as a method on DRC expressions.
diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml
index 7e8b6ba82..670651628 100644
--- a/src/doc/doc/about/drc_ref_global.xml
+++ b/src/doc/doc/about/drc_ref_global.xml
@@ -1,17 +1,12 @@
-
+
DRC Reference: Global Functions
-
-Some functions are available on global level and can be used without any object.
-Most of them are convenience functions that basically act on some default object
-or provide function-like alternatives for the methods.
-
"angle" - In universal DRC context: selects edges based on their orientation
diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml
index ce8c3aa4a..35af6a5b4 100644
--- a/src/doc/doc/about/drc_ref_layer.xml
+++ b/src/doc/doc/about/drc_ref_layer.xml
@@ -1,15 +1,12 @@
-
+
DRC Reference: Layer Object
-
-The layer object represents a collection of polygons, edges or edge pairs.
-
"&" - Boolean AND operation
@@ -268,7 +265,7 @@ deliver objects that can be converted into polygons. Such objects are of class <
This method produces markers on the corners of the polygons. An angle criterion can be given which
selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles
-indicate concave corners while negative ones indicate convex corners.
+indicate concave (inner) corners while negative ones indicate convex (outer) corners
The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml
index f03149944..8fd41f6f5 100644
--- a/src/doc/doc/about/drc_ref_netter.xml
+++ b/src/doc/doc/about/drc_ref_netter.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml
index a9ab3eaab..74d119bcb 100644
--- a/src/doc/doc/about/drc_ref_source.xml
+++ b/src/doc/doc/about/drc_ref_source.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml
index 4a38dd6db..58168f012 100644
--- a/src/doc/doc/about/lvs_ref.xml
+++ b/src/doc/doc/about/lvs_ref.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml
index c262d6815..c1d2ad914 100644
--- a/src/doc/doc/about/lvs_ref_global.xml
+++ b/src/doc/doc/about/lvs_ref_global.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml
index f5f851203..9feef786f 100644
--- a/src/doc/doc/about/lvs_ref_netter.xml
+++ b/src/doc/doc/about/lvs_ref_netter.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
index c3674e3d2..aa9e9e7d3 100644
--- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb
+++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
@@ -756,15 +756,17 @@ CODE
# %DRC%
# @name corners
- # @brief Applies smoothing
+ # @brief Selects corners of polygons
# @synopsis expression.corners
# @synopsis expression.corners(as_dots)
# @synopsis expression.corners(as_boxes)
#
# 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
- # a corner is positive for a turn to the left if walking a polygon counterclockwise
- # and negative for the turn to the right. Angles take values between -180 and 180 degree.
+ # a corner is positive for a turn to the left if walking a polygon clockwise
+ # and negative for the turn to the right. Hence positive angles indicate concave
+ # (inner) corners, negative ones indicate convex (outer) corners.
+ # Angles take values between -180 and 180 degree.
#
# When using "as_dots" for the argument, the operation will return single-point edges at
# the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be
@@ -780,8 +782,8 @@ CODE
# The following example selects all inner corners:
#
# @code
- # out = in.drc(corners < 0)
- # out = in.drc(primary.corners < 0) # equivalent
+ # out = in.drc(corners > 0)
+ # out = in.drc(primary.corners > 0) # equivalent
# @/code
#
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb
index ffa80b943..24c2cbb2e 100644
--- a/src/drc/drc/built-in-macros/_drc_layer.rb
+++ b/src/drc/drc/built-in-macros/_drc_layer.rb
@@ -1232,7 +1232,7 @@ 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
- # indicate concave corners while negative ones indicate convex corners.
+ # indicate concave (inner) corners while negative ones indicate convex (outer) corners
#
# The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
#
From 012447c31bfcd6a1d45543c32bfd229aa87de45d Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Wed, 28 Feb 2024 22:41:09 +0100
Subject: [PATCH 04/79] Fixed a small flaw: pipe stream did not report source
in errors
---
src/tl/tl/tlStream.cc | 6 +++---
src/tl/tl/tlStream.h | 5 ++---
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc
index dc57b3a98..dc1425537 100644
--- a/src/tl/tl/tlStream.cc
+++ b/src/tl/tl/tlStream.cc
@@ -1334,11 +1334,11 @@ OutputPipe::write (const char *b, size_t n)
// ---------------------------------------------------------------
// InputPipe delegate implementation
-InputPipe::InputPipe (const std::string &path)
+InputPipe::InputPipe (const std::string &source)
: m_file (NULL)
{
- m_source = path;
- m_file = popen (tl::string_to_system (path).c_str (), "r");
+ m_source = source;
+ m_file = popen (tl::string_to_system (source).c_str (), "r");
if (m_file == NULL) {
throw FilePOpenErrorException (m_source, errno);
}
diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h
index 2412c4663..087e54f45 100644
--- a/src/tl/tl/tlStream.h
+++ b/src/tl/tl/tlStream.h
@@ -313,7 +313,7 @@ public:
* @param cmd The command to execute
* @param read True, if the file should be read, false on write.
*/
- InputPipe (const std::string &path);
+ InputPipe (const std::string &source);
/**
* @brief Close the pipe
@@ -348,8 +348,7 @@ public:
*/
virtual std::string source () const
{
- // No source (in the sense of a file name) is available ..
- return std::string ();
+ return m_source;
}
virtual std::string absolute_path () const
From 9548f5109a608cc176dc0a022b072d8d9c1b38f5 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 29 Feb 2024 00:30:34 +0100
Subject: [PATCH 05/79] Implementing automatic .gz support for https and pipe
URIs
---
src/tl/tl/tlHttpStream.h | 16 ++-
src/tl/tl/tlStream.cc | 146 ++++++++++++++++++++++++-
src/tl/tl/tlStream.h | 83 +++++++++++++-
src/tl/unit_tests/tlHttpStreamTests.cc | 28 +++++
src/tl/unit_tests/tlStreamTests.cc | 9 ++
5 files changed, 272 insertions(+), 10 deletions(-)
diff --git a/src/tl/tl/tlHttpStream.h b/src/tl/tl/tlHttpStream.h
index a85dc3348..529e81d85 100644
--- a/src/tl/tl/tlHttpStream.h
+++ b/src/tl/tl/tlHttpStream.h
@@ -112,7 +112,6 @@ public:
* @brief Polling: call this function regularly to explicitly establish polling
* (in the Qt framework, this is done automatically within the event loop)
* May throw a tl::CancelException to stop.
- * Returns true if a message has arrived.
*/
void tick ();
@@ -209,6 +208,21 @@ private:
InputHttpStreamCallback *mp_callback;
};
+/**
+ * @brief A HTTP stream with .gz support
+ */
+class TL_PUBLIC InflatingInputHttpStream
+ : public inflating_input_stream
+{
+public:
+ /**
+ * @brief Open a stream with the given URL
+ */
+ InflatingInputHttpStream (const std::string &url)
+ : inflating_input_stream (new InputHttpStream (url))
+ { }
+};
+
}
#endif
diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc
index dc1425537..4ffc2d43e 100644
--- a/src/tl/tl/tlStream.cc
+++ b/src/tl/tl/tlStream.cc
@@ -140,6 +140,125 @@ public:
gzFile zs;
};
+// ---------------------------------------------------------------
+// inflating_input_stream implementation
+
+/**
+ * @brief A wrapper that adds generic .gz support
+ */
+template
+inflating_input_stream::inflating_input_stream (Base *delegate)
+ : m_inflating_stream (delegate), m_is_compressed (false), mp_delegate (delegate)
+{
+ enter_inflate ();
+}
+
+template
+size_t
+inflating_input_stream::read (char *b, size_t n)
+{
+ // TODO: this is somewhat inefficient, but we only use it for pipe and HTTP streams
+ size_t i = 0;
+ while (i < n) {
+
+ if (m_is_compressed || m_inflating_stream.blen () == 0) {
+
+ const char *read = m_inflating_stream.get (1);
+ if (! read) {
+ break;
+ }
+ *b++ = *read;
+ i += 1;
+
+ } else {
+
+ size_t nn = std::min (n - i, m_inflating_stream.blen ());
+ const char *read = m_inflating_stream.get (nn);
+ tl_assert (read != 0);
+ memcpy (b, read, nn);
+ b += nn;
+ i += nn;
+
+ }
+
+ }
+ return i;
+}
+
+template
+void
+inflating_input_stream::enter_inflate ()
+{
+ // identify and skip header for .gz file
+ if (auto_detect_gz ()) {
+ m_is_compressed = true;
+ m_inflating_stream.inflate (true /* stop after inflated block */);
+ } else {
+ m_inflating_stream.unget (m_inflating_stream.pos ());
+ }
+}
+
+template
+bool
+inflating_input_stream::auto_detect_gz ()
+{
+ std::string header = m_inflating_stream.read_all (10);
+ if (header.size () < 10) {
+ return false;
+ }
+
+ const unsigned char *header_data = (const unsigned char *) header.c_str ();
+ unsigned char flags = header_data[3];
+ if (header_data[0] != 0x1f || header_data[1] != 0x8b || header_data[2] != 0x08 || (flags & 0xe0) != 0) {
+ return false;
+ }
+
+ // .gz signature found
+
+ bool has_fhcrc = (flags & 0x02) != 0;
+ bool has_extra = (flags & 0x04) != 0;
+ bool has_fname = (flags & 0x08) != 0;
+ bool has_comment = (flags & 0x10) != 0;
+
+ if (has_extra) {
+ const unsigned char *xlen = (const unsigned char *) m_inflating_stream.get (2);
+ if (! xlen) {
+ throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing XLEN field")));
+ }
+ const char *xdata = m_inflating_stream.get (size_t (xlen[0]) + (size_t (xlen[1]) << 8));
+ if (! xdata) {
+ throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing EXTRA data")));
+ }
+ }
+
+ if (has_fname) {
+ const char *c;
+ while ((c = m_inflating_stream.get (1)) != 0 && *c)
+ ;
+ if (! c) {
+ throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing FNAME data trailing zero byte")));
+ }
+ }
+
+ if (has_comment) {
+ const char *c;
+ while ((c = m_inflating_stream.get (1)) != 0 && *c)
+ ;
+ if (! c) {
+ throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing COMMENT data trailing zero byte")));
+ }
+ }
+
+ if (has_fhcrc) {
+ const char *crc16 = m_inflating_stream.get (2);
+ if (! crc16) {
+ throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing CRC16 data")));
+ }
+ }
+
+ return true;
+}
+
// ---------------------------------------------------------------
// InputStream implementation
@@ -175,7 +294,7 @@ public:
}
InputStream::InputStream (InputStreamBase &delegate)
- : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false)
+ : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false)
{
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
@@ -183,7 +302,7 @@ InputStream::InputStream (InputStreamBase &delegate)
}
InputStream::InputStream (InputStreamBase *delegate)
- : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false)
+ : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false)
{
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
@@ -191,7 +310,7 @@ InputStream::InputStream (InputStreamBase *delegate)
}
InputStream::InputStream (const std::string &abstract_path)
- : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false)
+ : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false)
{
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
@@ -252,7 +371,7 @@ InputStream::InputStream (const std::string &abstract_path)
} else if (ex.test ("pipe:")) {
- mp_delegate = new InputPipe (ex.get ());
+ mp_delegate = new InflatingInputPipe (ex.get ());
} else {
@@ -260,7 +379,7 @@ InputStream::InputStream (const std::string &abstract_path)
if (uri.scheme () == "http" || uri.scheme () == "https") {
#if defined(HAVE_CURL) || defined(HAVE_QT)
- mp_delegate = new InputHttpStream (abstract_path);
+ mp_delegate = new InflatingInputHttpStream (abstract_path);
#else
throw tl::Exception (tl::to_string (tr ("HTTP support not enabled - HTTP/HTTPS paths are not available")));
#endif
@@ -337,9 +456,16 @@ InputStream::get (size_t n, bool bypass_inflate)
tl_assert (r != 0); // since deflate did not report at_end()
return r;
+ } else if (m_stop_after_inflate) {
+
+ // report EOF after the inflator has finished
+ return 0;
+
} else {
+
delete mp_inflate;
mp_inflate = 0;
+
}
}
@@ -384,9 +510,16 @@ InputStream::get (size_t n, bool bypass_inflate)
void
InputStream::unget (size_t n)
{
+ if (n == 0) {
+ return;
+ }
+
if (mp_inflate) {
+ // TODO: this will not work if mp_inflate just got destroyed
+ // (no unget into previous compressed block)
mp_inflate->unget (n);
} else {
+ tl_assert (mp_buffer + n <= mp_bptr);
mp_bptr -= n;
m_blen += n;
m_pos -= n;
@@ -476,10 +609,11 @@ void InputStream::copy_to (tl::OutputStream &os)
}
void
-InputStream::inflate ()
+InputStream::inflate (bool stop_after)
{
tl_assert (mp_inflate == 0);
mp_inflate = new tl::InflateFilter (*this);
+ m_stop_after_inflate = stop_after;
}
void
diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h
index 087e54f45..4e7cfdcf6 100644
--- a/src/tl/tl/tlStream.h
+++ b/src/tl/tl/tlStream.h
@@ -310,8 +310,7 @@ public:
* an error occurs - commonly if the command cannot be executed.
* This implementation is based on popen ().
*
- * @param cmd The command to execute
- * @param read True, if the file should be read, false on write.
+ * @param source The command to execute
*/
InputPipe (const std::string &source);
@@ -473,8 +472,11 @@ public:
* the uncompressed data rather than the raw data, until the
* compressed block is finished.
* The stream must not be in inflate state yet.
+ *
+ * If "stop_after" is true, the stream will stop after the inflated
+ * block has finished.
*/
- void inflate ();
+ void inflate (bool stop_after = false);
/**
* @brief Enables "inflate" right from the beginning
@@ -577,12 +579,87 @@ private:
// inflate support
InflateFilter *mp_inflate;
bool m_inflate_always;
+ bool m_stop_after_inflate;
// No copying currently
InputStream (const InputStream &);
InputStream &operator= (const InputStream &);
};
+/**
+ * @brief A wrapper that adds generic .gz support
+ */
+template
+class TL_PUBLIC_TEMPLATE inflating_input_stream
+ : public InputStreamBase
+{
+public:
+ inflating_input_stream (Base *delegate);
+
+ Base *delegate ()
+ {
+ return mp_delegate;
+ }
+
+ virtual size_t read (char *b, size_t n);
+
+ virtual void reset ()
+ {
+ m_inflating_stream.reset ();
+ enter_inflate ();
+ }
+
+ virtual void close ()
+ {
+ m_inflating_stream.close ();
+ }
+
+ virtual std::string source () const
+ {
+ return m_inflating_stream.source ();
+ }
+
+ virtual std::string absolute_path () const
+ {
+ return m_inflating_stream.absolute_path ();
+ }
+
+ virtual std::string filename () const
+ {
+ return m_inflating_stream.filename ();
+ }
+
+private:
+ tl::InputStream m_inflating_stream;
+ bool m_is_compressed;
+ Base *mp_delegate;
+
+ void enter_inflate ();
+ bool auto_detect_gz ();
+};
+
+/**
+ * @brief A pipe stream with .gz support
+ */
+class TL_PUBLIC InflatingInputPipe
+ : public inflating_input_stream
+{
+public:
+ /**
+ * @brief Open a stream by connecting with the stdout of a given command
+ *
+ * Opening a pipe is a prerequisite for reading from the
+ * object. open() will throw a FilePOpenErrorException if
+ * an error occurs - commonly if the command cannot be executed.
+ * This implementation is based on popen ().
+ *
+ * @param source The command to execute
+ */
+ InflatingInputPipe (const std::string &source)
+ : inflating_input_stream (new InputPipe (source))
+ { }
+};
+
// ---------------------------------------------------------------------------------
/**
diff --git a/src/tl/unit_tests/tlHttpStreamTests.cc b/src/tl/unit_tests/tlHttpStreamTests.cc
index e05c7e5fe..4acf10f3c 100644
--- a/src/tl/unit_tests/tlHttpStreamTests.cc
+++ b/src/tl/unit_tests/tlHttpStreamTests.cc
@@ -24,8 +24,10 @@
#include "tlHttpStream.h"
#include "tlUnitTest.h"
#include "tlTimer.h"
+#include "tlStream.h"
static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
+static std::string test_url1_gz ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text.gz");
static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1");
TEST(1)
@@ -125,3 +127,29 @@ TEST(3)
EXPECT_EQ (res, "hello, world.\n");
}
+// tl::Stream embedding
+TEST(4)
+{
+ if (! tl::InputHttpStream::is_available ()) {
+ throw tl::CancelException ();
+ }
+
+ tl::InputStream stream (test_url1);
+
+ std::string res = stream.read_all ();
+ EXPECT_EQ (res, "hello, world.\n");
+}
+
+// tl::Stream embedding with automatic unzip
+TEST(5)
+{
+ if (! tl::InputHttpStream::is_available ()) {
+ throw tl::CancelException ();
+ }
+
+ tl::InputStream stream (test_url1_gz);
+
+ std::string res = stream.read_all ();
+ EXPECT_EQ (res, "hello, world.\n");
+}
+
diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc
index e51a7d4c4..517d71615 100644
--- a/src/tl/unit_tests/tlStreamTests.cc
+++ b/src/tl/unit_tests/tlStreamTests.cc
@@ -52,6 +52,13 @@ TEST(InputPipe2)
EXPECT_NE (ret, 0);
}
+TEST(InputPipe3)
+{
+ tl::InputStream str ("pipe:echo HELLOWORLD");
+ tl::TextInputStream tstr (str);
+ EXPECT_EQ (tstr.get_line (), "HELLOWORLD");
+}
+
TEST(OutputPipe1)
{
std::string tf = tmp_file ("pipe_out");
@@ -455,3 +462,5 @@ TEST(Backups)
}
}
+
+
From 26fc81624ca29a225669bcc7e188f7e8209725c4 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 29 Feb 2024 21:45:56 +0100
Subject: [PATCH 06/79] Updating tests
---
src/tl/unit_tests/tlWebDAVTests.cc | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/tl/unit_tests/tlWebDAVTests.cc b/src/tl/unit_tests/tlWebDAVTests.cc
index 6d2948e7f..67598d832 100644
--- a/src/tl/unit_tests/tlWebDAVTests.cc
+++ b/src/tl/unit_tests/tlWebDAVTests.cc
@@ -70,6 +70,7 @@ TEST(1)
"[dir] dir1 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1/\n"
"[dir] dir2 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir2/\n"
"text http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text\n"
+ "text.gz http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text.gz\n"
"text2 http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text2"
);
}
From c2187e0bf0f50589d58a4943cf4c255b297b6ac5 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 29 Feb 2024 22:21:07 +0100
Subject: [PATCH 07/79] OASIS reader creates layers listed in layer map also if
empty
---
.../oasis/db_plugin/dbOASISReader.cc | 6 +++
.../oasis/unit_tests/dbOASISReaderTests.cc | 54 +++++++++++++++----
.../oasis/unit_tests/dbOASISWriterTests.cc | 7 +++
3 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
index 1f7ab2b93..d9fd91ddd 100644
--- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
+++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc
@@ -1080,6 +1080,12 @@ OASISReader::do_read (db::Layout &layout)
LNameJoinOp2 op2;
layer_names ().add (l1, l2 + 1, dt_map, op2);
+ // for singular layers, force a layer entry:
+ // this way we can have empty, but existing layers.
+ if (l1 == l2 && dt1 == dt2) {
+ open_dl (layout, db::LDPair (l1, dt1));
+ }
+
reset_modal_variables ();
// ignore properties attached to this name item
diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
index b87a413c1..1ea0aa44e 100644
--- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
+++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc
@@ -22,6 +22,7 @@
#include "dbOASISReader.h"
+#include "dbOASISWriter.h"
#include "dbTextWriter.h"
#include "dbTestSupport.h"
#include "tlLog.h"
@@ -487,21 +488,18 @@ TEST(99)
// XGEOMTRY tests (#773)
TEST(100)
{
- const char *expected =
+ const char *expected =
"begin_lib 0.0001\n"
"begin_cell {mask}\n"
- "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n"
- "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n"
- "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n"
- "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n"
- "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n"
- "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n"
- "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n"
- "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n"
"boundary 1 0 {17922 6288} {17922 6510} {18150 6510} {18150 6288} {17922 6288}\n"
"boundary 1 0 {18157 647} {18157 676} {21630 676} {21630 647} {18157 647}\n"
"boundary 1 0 {21956 0} {21956 89} {22047 89} {22047 0} {21956 0}\n"
+ "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n"
+ "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n"
"boundary 3 0 {15392 1744} {15392 1774} {15672 1774} {15672 1744} {15392 1744}\n"
+ "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n"
+ "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n"
+ "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n"
"boundary 5 1 {15550 1658} {15550 1673} {15570 1673} {15570 1658} {15550 1658}\n"
"boundary 5 1 {15661 1657} {15641 1659} {15642 1671} {15662 1669} {15661 1657}\n"
"boundary 5 1 {18150 7440} {18150 7460} {18162 7460} {18162 7440} {18150 7440}\n"
@@ -519,6 +517,9 @@ TEST(100)
"boundary 5 1 {25710 1978} {25710 1998} {25722 1998} {25722 1978} {25710 1978}\n"
"boundary 5 1 {25710 2800} {25710 2820} {25722 2820} {25722 2800} {25710 2800}\n"
"boundary 5 2 {18074 6408} {17971 6486} {17983 6502} {18086 6424} {18074 6408}\n"
+ "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n"
+ "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n"
+ "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n"
"end_cell\n"
"end_lib\n"
;
@@ -543,6 +544,41 @@ TEST(100)
EXPECT_EQ (std::string (os.string ()), std::string (expected))
}
+// Empty layers through LAYERMAP
+TEST(101)
+{
+ db::Layout ly;
+ ly.add_cell ("TOP");
+ ly.insert_layer (db::LayerProperties (1, 0, "A"));
+ ly.insert_layer (db::LayerProperties (2, 0, ""));
+ ly.insert_layer (db::LayerProperties (3, 0, "C"));
+
+ std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISReader101.oas");
+
+ {
+ tl::OutputStream stream (tmp_file);
+ db::OASISWriter writer;
+ db::SaveLayoutOptions options;
+ writer.write (ly, stream, options);
+ }
+
+ db::Layout ly_new;
+
+ {
+ tl::InputStream stream (tmp_file);
+ db::Reader reader (stream);
+ reader.read (ly_new);
+ }
+
+ // NOTE: only named layers are written into layer table
+ EXPECT_EQ (ly_new.cell_by_name ("TOP").first, true);
+ EXPECT_EQ (int (ly_new.layers ()), 2);
+ if (int (ly_new.layers ()) == 2) {
+ EXPECT_EQ (ly_new.get_properties (0).to_string (), "A (1/0)");
+ EXPECT_EQ (ly_new.get_properties (1).to_string (), "C (3/0)");
+ }
+}
+
TEST(Bug_121_1)
{
db::Manager m (false);
diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
index 6df3ff111..c85a7f600 100644
--- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
+++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc
@@ -255,6 +255,13 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::Reader reader (stream);
reader.read (layout);
+ // named layers create a mismatch between GDS and OASIS, so we unname them here
+ for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) {
+ db::LayerProperties lp = layout.get_properties ((*l).first);
+ lp.name.clear ();
+ layout.set_properties ((*l).first, lp);
+ }
+
db::SaveLayoutOptions options;
db::OASISWriterOptions oasis_options;
oasis_options.compression_level = compr;
From a431f70ad422e05812cb1b5bd209d6494a1d1d4a Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 29 Feb 2024 22:57:28 +0100
Subject: [PATCH 08/79] Polygon#break, DPolygon#break, SimplPolygon#break,
DSimplePolygon#break
---
src/db/db/gsiDeclDbPolygon.cc | 79 +++++++++++++++++++++++++++-------
testdata/ruby/dbPolygonTest.rb | 48 +++++++++++++++++++++
2 files changed, 111 insertions(+), 16 deletions(-)
diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc
index 417351c16..bc31beb55 100644
--- a/src/db/db/gsiDeclDbPolygon.cc
+++ b/src/db/db/gsiDeclDbPolygon.cc
@@ -31,6 +31,39 @@
namespace gsi
{
+template
+static std::vector split_poly (const C *p)
+{
+ std::vector parts;
+ db::split_polygon (*p, parts);
+ return parts;
+}
+
+template
+static void break_polygon (const C &poly, size_t max_vertex_count, double max_area_ratio, std::vector &result)
+{
+ if ((max_vertex_count > 0 && poly.vertices () > max_vertex_count) ||
+ (max_area_ratio > 0 && poly.area_ratio () > max_area_ratio)) {
+
+ std::vector split_polygons;
+ db::split_polygon (poly, split_polygons);
+ for (auto p = split_polygons.begin (); p != split_polygons.end (); ++p) {
+ break_polygon (*p, max_vertex_count, max_area_ratio, result);
+ }
+
+ } else {
+ result.push_back (poly);
+ }
+}
+
+template
+static std::vector break_poly (const C *p, size_t max_vertex_count, double max_area_ratio)
+{
+ std::vector parts;
+ break_polygon (*p, max_vertex_count, max_area_ratio, parts);
+ return parts;
+}
+
// ---------------------------------------------------------------
// simple polygon binding
@@ -245,13 +278,6 @@ struct simple_polygon_defs
return db::interact (*p, spoly);
}
- static std::vector split_poly (const C *p)
- {
- std::vector parts;
- db::split_polygon (*p, parts);
- return parts;
- }
-
static gsi::Methods methods ()
{
return
@@ -508,7 +534,7 @@ struct simple_polygon_defs
"\n"
"This method was introduced in version 0.25.\n"
) +
- method_ext ("split", &split_poly,
+ method_ext ("split", &split_poly,
"@brief Splits the polygon into two or more parts\n"
"This method will break the polygon into parts. The exact breaking algorithm is unspecified, the "
"result are smaller polygons of roughly equal number of points and 'less concave' nature. "
@@ -521,6 +547,20 @@ struct simple_polygon_defs
"\n"
"This method has been introduced in version 0.25.3."
) +
+ method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"),
+ "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n"
+ "The area ratio is the ratio between the bounding box area and the polygon area. Higher values "
+ "mean more 'skinny' polygons.\n"
+ "\n"
+ "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices "
+ "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the "
+ "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n"
+ "\n"
+ "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the "
+ "parts satisfy the limits.\n"
+ "\n"
+ "This method has been introduced in version 0.29."
+ ) +
method_ext ("area", &area,
"@brief Gets the area of the polygon\n"
"The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise."
@@ -1098,13 +1138,6 @@ struct polygon_defs
return db::interact (*p, spoly);
}
- static std::vector split_spoly (const C *p)
- {
- std::vector parts;
- db::split_polygon (*p, parts);
- return parts;
- }
-
static gsi::Methods methods ()
{
return
@@ -1520,7 +1553,7 @@ struct polygon_defs
"\n"
"This method was introduced in version 0.25.\n"
) +
- method_ext ("split", &split_spoly,
+ method_ext ("split", &split_poly,
"@brief Splits the polygon into two or more parts\n"
"This method will break the polygon into parts. The exact breaking algorithm is unspecified, the "
"result are smaller polygons of roughly equal number of points and 'less concave' nature. "
@@ -1533,6 +1566,20 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.25.3."
) +
+ method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"),
+ "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n"
+ "The area ratio is the ratio between the bounding box area and the polygon area. Higher values "
+ "mean more 'skinny' polygons.\n"
+ "\n"
+ "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices "
+ "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the "
+ "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n"
+ "\n"
+ "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the "
+ "parts satisfy the limits.\n"
+ "\n"
+ "This method has been introduced in version 0.29."
+ ) +
method_ext ("area", &area,
"@brief Gets the area of the polygon\n"
"The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise."
diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb
index bdd290f01..cb1910017 100644
--- a/testdata/ruby/dbPolygonTest.rb
+++ b/testdata/ruby/dbPolygonTest.rb
@@ -821,6 +821,54 @@ class DBPolygon_TestClass < TestBase
end
+ def test_breakPolygon
+
+ pts = []
+ pts << RBA::Point::new(0, 0)
+ pts << RBA::Point::new(0, 1000)
+ pts << RBA::Point::new(100, 1000)
+ pts << RBA::Point::new(100, 100)
+ pts << RBA::Point::new(1000, 100)
+ pts << RBA::Point::new(1000, 0)
+
+ split = RBA::Polygon::new(pts).break(4, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::Polygon::new(pts).break(0, 2.0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::Polygon::new(pts).break(0, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)")
+
+ split = RBA::SimplePolygon::new(pts).break(4, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::SimplePolygon::new(pts).break(0, 2.0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::SimplePolygon::new(pts).break(0, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)")
+
+ pts = []
+ pts << RBA::DPoint::new(0, 0)
+ pts << RBA::DPoint::new(0, 1000)
+ pts << RBA::DPoint::new(100, 1000)
+ pts << RBA::DPoint::new(100, 100)
+ pts << RBA::DPoint::new(1000, 100)
+ pts << RBA::DPoint::new(1000, 0)
+
+ split = RBA::DPolygon::new(pts).break(4, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::DPolygon::new(pts).break(0, 2.0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::DPolygon::new(pts).break(0, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)")
+
+ split = RBA::DSimplePolygon::new(pts).break(4, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::DSimplePolygon::new(pts).break(0, 2.0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)")
+ split = RBA::DSimplePolygon::new(pts).break(0, 0)
+ assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)")
+
+ end
+
def test_voidMethodsReturnSelf
hull = [ RBA::Point::new(0, 0), RBA::Point::new(6000, 0),
From 1e323a0421d86704df16b9990742cf0796dce0c2 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Fri, 1 Mar 2024 23:32:22 +0100
Subject: [PATCH 09/79] WIP
---
src/db/db/dbAsIfFlatEdges.cc | 274 ++++++++++----------
src/db/db/dbAsIfFlatEdges.h | 20 +-
src/db/db/dbDeepEdges.cc | 370 +---------------------------
src/db/db/dbDeepEdges.h | 8 +-
src/db/db/dbEdges.h | 40 +--
src/db/db/dbEdgesDelegate.h | 12 +-
src/db/db/dbEdgesLocalOperations.cc | 352 ++++++++++++++++++++++++++
src/db/db/dbEdgesLocalOperations.h | 74 ++++++
src/db/db/dbEmptyEdges.h | 12 +-
src/db/db/gsiDeclDbEdges.cc | 92 +++++--
10 files changed, 681 insertions(+), 573 deletions(-)
diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc
index e4a482fe2..d48fbb0c3 100644
--- a/src/db/db/dbAsIfFlatEdges.cc
+++ b/src/db/db/dbAsIfFlatEdges.cc
@@ -28,6 +28,7 @@
#include "dbEmptyEdges.h"
#include "dbEdges.h"
#include "dbEdgesUtils.h"
+#include "dbEdgesLocalOperations.h"
#include "dbEdgeBoolean.h"
#include "dbBoxConvert.h"
#include "dbRegion.h"
@@ -39,6 +40,7 @@
#include "dbPolygonGenerators.h"
#include "dbPolygon.h"
#include "dbPath.h"
+#include "dbHierProcessor.h"
#include
@@ -94,104 +96,107 @@ AsIfFlatEdges::to_string (size_t nmax) const
return os.str ();
}
-EdgesDelegate *
-AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const
+namespace {
+
+class OutputPairHolder
{
- // shortcuts
- if (other.empty () || empty ()) {
- return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone ();
- }
+public:
+ OutputPairHolder (int inverse, bool merged_semantics)
+ {
+ m_e1.reset (new FlatEdges (merged_semantics));
+ m_results.push_back (& m_e1->raw_edges ());
- db::box_scanner2 scanner (report_progress (), progress_desc ());
-
- AddressableEdgeDelivery e (begin_merged ());
-
- for ( ; ! e.at_end (); ++e) {
- scanner.insert1 (e.operator-> (), 0);
- }
-
- AddressablePolygonDelivery p = (mode == EdgesInside ? other.addressable_merged_polygons () : other.addressable_polygons ());
-
- for ( ; ! p.at_end (); ++p) {
- scanner.insert2 (p.operator-> (), 1);
- }
-
- std::unique_ptr output (new FlatEdges (true));
-
- if (! inverse) {
-
- edge_to_region_interaction_filter filter (output.get (), mode);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
-
- } else {
-
- std::set result;
- edge_to_region_interaction_filter > filter (&result, mode);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
-
- for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
- if (result.find (*o) == result.end ()) {
- output->insert (*o);
- }
+ if (inverse == 0) {
+ m_e2.reset (new FlatEdges (merged_semantics));
+ m_results.push_back (& m_e2->raw_edges ());
}
-
}
- return output.release ();
+ std::pair region_pair ()
+ {
+ return std::make_pair (m_e1.release (), m_e2.release ());
+ }
+
+ const std::vector &results () { return m_results; }
+
+private:
+ std::unique_ptr m_e1, m_e2;
+ std::vector m_results;
+};
+
}
EdgesDelegate *
-AsIfFlatEdges::selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const
+AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const
{
+ min_count = std::max (size_t (1), min_count);
+
// shortcuts
- if (edges.empty () || empty ()) {
+ if (max_count < min_count || other.empty () || empty ()) {
return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone ();
}
- db::box_scanner scanner (report_progress (), progress_desc ());
+ bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ());
+ OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ());
- AddressableEdgeDelivery e (begin_merged ());
+ db::EdgesIterator edges (begin_merged ());
- for ( ; ! e.at_end (); ++e) {
- scanner.insert (e.operator-> (), 0);
+ db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal);
+
+ db::local_processor proc;
+ proc.set_base_verbosity (base_verbosity ());
+ proc.set_description (progress_desc ());
+ proc.set_report_progress (report_progress ());
+
+ std::vector > others;
+ // NOTE: with counting the other region needs to be merged
+ others.push_back (counting ? other.begin_merged () : other.begin ());
+
+ proc.run_flat (edges, others, std::vector (), &op, oph.results ());
+
+ return oph.region_pair ().first;
+}
+
+EdgesDelegate *
+AsIfFlatEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const
+{
+ min_count = std::max (size_t (1), min_count);
+
+ // @@@
+ // shortcuts
+ if (max_count < min_count || other.empty () || empty ()) {
+ return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone ();
}
- // NOTE: "inside" needs merged edges for the other edges as the algorithm works edge by edge
- AddressableEdgeDelivery ee = (mode == EdgesInside ? edges.addressable_merged_edges () : edges.addressable_edges ());
+ bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ());
+ OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ());
- for ( ; ! ee.at_end (); ++ee) {
- scanner.insert (ee.operator-> (), 1);
- }
+ db::EdgesIterator edges (begin_merged ());
- std::unique_ptr output (new FlatEdges (true));
+ db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal);
- if (! inverse) {
+ db::local_processor proc;
+ proc.set_base_verbosity (base_verbosity ());
+ proc.set_description (progress_desc ());
+ proc.set_report_progress (report_progress ());
- edge_interaction_filter filter (*output, mode);
- scanner.process (filter, 1, db::box_convert ());
+ std::vector > others;
+ // NOTE: with counting the other edge collection needs to be merged
+ others.push_back (counting ? other.begin_merged () : other.begin ());
- } else {
+ proc.run_flat (edges, others, std::vector (), &op, oph.results ());
- std::set result;
- edge_interaction_filter > filter (result, mode);
- scanner.process (filter, 1, db::box_convert ());
-
- for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
- if (result.find (*o) == result.end ()) {
- output->insert (*o);
- }
- }
-
- }
-
- return output.release ();
+ return oph.region_pair ().first;
}
std::pair
-AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const
+AsIfFlatEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const
{
+ min_count = std::max (size_t (1), min_count);
+
+ // @@@
// shortcuts
- if (region.empty () || empty ()) {
+ if (max_count < min_count || other.empty () || empty ()) {
if (mode != EdgesOutside) {
return std::make_pair (new EmptyEdges (), clone ());
} else {
@@ -199,43 +204,35 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInte
}
}
- db::box_scanner2 scanner (report_progress (), progress_desc ());
+ bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ());
+ OutputPairHolder oph (0, merged_semantics () || is_merged ());
- AddressableEdgeDelivery e (begin_merged ());
+ db::EdgesIterator edges (begin_merged ());
- for ( ; ! e.at_end (); ++e) {
- scanner.insert1 (e.operator-> (), 0);
- }
+ db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both);
- AddressablePolygonDelivery p = region.addressable_merged_polygons ();
+ db::local_processor proc;
+ proc.set_base_verbosity (base_verbosity ());
+ proc.set_description (progress_desc ());
+ proc.set_report_progress (report_progress ());
- for ( ; ! p.at_end (); ++p) {
- scanner.insert2 (p.operator-> (), 1);
- }
+ std::vector > others;
+ // NOTE: with counting the other region needs to be merged
+ others.push_back (counting ? other.begin_merged () : other.begin ());
- std::unique_ptr output (new FlatEdges (true));
- std::unique_ptr output2 (new FlatEdges (true));
+ proc.run_flat (edges, others, std::vector (), &op, oph.results ());
- std::set result;
- edge_to_region_interaction_filter > filter (&result, mode);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
-
- for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
- if (result.find (*o) == result.end ()) {
- output2->insert (*o);
- } else {
- output->insert (*o);
- }
- }
-
- return std::make_pair (output.release (), output2.release ());
+ return oph.region_pair ();
}
std::pair
-AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const
+AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const
{
+ min_count = std::max (size_t (1), min_count);
+
+ // @@@
// shortcuts
- if (other.empty () || empty ()) {
+ if (max_count < min_count || other.empty () || empty ()) {
if (mode != EdgesOutside) {
return std::make_pair (new EmptyEdges (), clone ());
} else {
@@ -243,36 +240,25 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeIntera
}
}
- db::box_scanner scanner (report_progress (), progress_desc ());
+ bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ());
+ OutputPairHolder oph (0, merged_semantics () || is_merged ());
- AddressableEdgeDelivery e (begin_merged ());
+ db::EdgesIterator edges (begin_merged ());
- for ( ; ! e.at_end (); ++e) {
- scanner.insert (e.operator-> (), 0);
- }
+ db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both);
- AddressableEdgeDelivery ee = other.addressable_merged_edges ();
+ db::local_processor proc;
+ proc.set_base_verbosity (base_verbosity ());
+ proc.set_description (progress_desc ());
+ proc.set_report_progress (report_progress ());
- for ( ; ! ee.at_end (); ++ee) {
- scanner.insert (ee.operator-> (), 1);
- }
+ std::vector > others;
+ // NOTE: with counting the other edge collection needs to be merged
+ others.push_back (counting ? other.begin_merged () : other.begin ());
- std::unique_ptr output (new FlatEdges (true));
- std::unique_ptr output2 (new FlatEdges (true));
+ proc.run_flat (edges, others, std::vector (), &op, oph.results ());
- std::set results;
- edge_interaction_filter > filter (results, mode);
- scanner.process (filter, 1, db::box_convert ());
-
- for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
- if (results.find (*o) == results.end ()) {
- output2->insert (*o);
- } else {
- output->insert (*o);
- }
- }
-
- return std::make_pair (output.release (), output2.release ());
+ return oph.region_pair ();
}
@@ -343,111 +329,111 @@ AsIfFlatEdges::pull_interacting (const Region &other) const
}
EdgesDelegate *
-AsIfFlatEdges::selected_interacting (const Edges &other) const
+AsIfFlatEdges::selected_interacting (const Edges &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_generic (other, EdgesInteract, false);
+ return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count);
}
EdgesDelegate *
-AsIfFlatEdges::selected_not_interacting (const Edges &other) const
+AsIfFlatEdges::selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_generic (other, EdgesInteract, true);
+ return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count);
}
EdgesDelegate *
-AsIfFlatEdges::selected_interacting (const Region &other) const
+AsIfFlatEdges::selected_interacting (const Region &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_generic (other, EdgesInteract, false);
+ return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count);
}
EdgesDelegate *
-AsIfFlatEdges::selected_not_interacting (const Region &other) const
+AsIfFlatEdges::selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_generic (other, EdgesInteract, true);
+ return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count);
}
std::pair
-AsIfFlatEdges::selected_interacting_pair (const Region &other) const
+AsIfFlatEdges::selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_pair_generic (other, EdgesInteract);
+ return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count);
}
std::pair
-AsIfFlatEdges::selected_interacting_pair (const Edges &other) const
+AsIfFlatEdges::selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const
{
- return selected_interacting_pair_generic (other, EdgesInteract);
+ return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count);
}
EdgesDelegate *
AsIfFlatEdges::selected_outside (const Region &other) const
{
- return selected_interacting_generic (other, EdgesOutside, false);
+ return selected_interacting_generic (other, EdgesOutside, false, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_not_outside (const Region &other) const
{
- return selected_interacting_generic (other, EdgesOutside, true);
+ return selected_interacting_generic (other, EdgesOutside, true, size_t (0), std::numeric_limits::max ());
}
std::pair
AsIfFlatEdges::selected_outside_pair (const Region &other) const
{
- return selected_interacting_pair_generic (other, EdgesOutside);
+ return selected_interacting_pair_generic (other, EdgesOutside, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_inside (const Region &other) const
{
- return selected_interacting_generic (other, EdgesInside, false);
+ return selected_interacting_generic (other, EdgesInside, false, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_not_inside (const Region &other) const
{
- return selected_interacting_generic (other, EdgesInside, true);
+ return selected_interacting_generic (other, EdgesInside, true, size_t (0), std::numeric_limits::max ());
}
std::pair
AsIfFlatEdges::selected_inside_pair (const Region &other) const
{
- return selected_interacting_pair_generic (other, EdgesInside);
+ return selected_interacting_pair_generic (other, EdgesInside, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_outside (const Edges &other) const
{
- return selected_interacting_generic (other, EdgesOutside, false);
+ return selected_interacting_generic (other, EdgesOutside, false, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_not_outside (const Edges &other) const
{
- return selected_interacting_generic (other, EdgesOutside, true);
+ return selected_interacting_generic (other, EdgesOutside, true, size_t (0), std::numeric_limits::max ());
}
std::pair
AsIfFlatEdges::selected_outside_pair (const Edges &other) const
{
- return selected_interacting_pair_generic (other, EdgesOutside);
+ return selected_interacting_pair_generic (other, EdgesOutside, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_inside (const Edges &other) const
{
- return selected_interacting_generic (other, EdgesInside, false);
+ return selected_interacting_generic (other, EdgesInside, false, size_t (0), std::numeric_limits::max ());
}
EdgesDelegate *
AsIfFlatEdges::selected_not_inside (const Edges &other) const
{
- return selected_interacting_generic (other, EdgesInside, true);
+ return selected_interacting_generic (other, EdgesInside, true, size_t (0), std::numeric_limits::max ());
}
std::pair
AsIfFlatEdges::selected_inside_pair (const Edges &other) const
{
- return selected_interacting_pair_generic (other, EdgesInside);
+ return selected_interacting_pair_generic (other, EdgesInside, size_t (0), std::numeric_limits::max ());
}
diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h
index b1f9a07d4..fbdd7064e 100644
--- a/src/db/db/dbAsIfFlatEdges.h
+++ b/src/db/db/dbAsIfFlatEdges.h
@@ -183,12 +183,12 @@ public:
virtual EdgesDelegate *pull_interacting (const Edges &) const;
virtual RegionDelegate *pull_interacting (const Region &) const;
- virtual EdgesDelegate *selected_interacting (const Edges &) const;
- virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
- virtual EdgesDelegate *selected_interacting (const Region &) const;
- virtual EdgesDelegate *selected_not_interacting (const Region &) const;
- virtual std::pair selected_interacting_pair (const Region &other) const;
- virtual std::pair selected_interacting_pair (const Edges &other) const;
+ virtual EdgesDelegate *selected_interacting (const Edges &, size_t min_count, size_t max_count) const;
+ virtual EdgesDelegate *selected_not_interacting (const Edges &, size_t min_count, size_t max_count) const;
+ virtual EdgesDelegate *selected_interacting (const Region &, size_t min_count, size_t max_count) const;
+ virtual EdgesDelegate *selected_not_interacting (const Region &, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const;
virtual EdgesDelegate *selected_outside (const Edges &other) const;
virtual EdgesDelegate *selected_not_outside (const Edges &other) const;
@@ -217,10 +217,10 @@ protected:
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const EdgesCheckOptions &options) const;
virtual EdgesDelegate *pull_generic (const Edges &edges) const;
virtual RegionDelegate *pull_generic (const Region ®ion) const;
- virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const;
- virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const;
- virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const;
- virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const;
+ virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const;
+ virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const;
AsIfFlatEdges &operator= (const AsIfFlatEdges &other);
AsIfFlatEdges (const AsIfFlatEdges &other);
diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc
index 5a2812207..39d406d1b 100644
--- a/src/db/db/dbDeepEdges.cc
+++ b/src/db/db/dbDeepEdges.cc
@@ -1354,363 +1354,10 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t
return res.release ();
}
-namespace
-{
-
-class Edge2EdgeInteractingLocalOperation
- : public local_operation
-{
-public:
- enum output_mode_t { Normal, Inverse, Both };
-
- Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode)
- : m_mode (mode), m_output_mode (output_mode)
- {
- // .. nothing yet ..
- }
-
- virtual db::Coord dist () const
- {
- // touching is sufficient
- return 1;
- }
-
- virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const
- {
- tl_assert (results.size () == (m_output_mode == Both ? 2 : 1));
-
- std::unordered_set &result = results.front ();
-
- std::unordered_set *result2 = 0;
- if (m_output_mode == Both) {
- result2 = &results[1];
- }
-
- db::box_scanner scanner;
-
- std::set others;
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
- others.insert (interactions.intruder_shape (*j).second);
- }
- }
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- const db::Edge &subject = interactions.subject_shape (i->first);
- scanner.insert (&subject, 0);
- }
-
- for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
- scanner.insert (o.operator-> (), 1);
- }
-
- if (m_output_mode == Inverse || m_output_mode == Both) {
-
- std::unordered_set interacting;
- edge_interaction_filter > filter (interacting, m_mode);
- scanner.process (filter, 1, db::box_convert ());
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
-
- const db::Edge &subject = interactions.subject_shape (i->first);
- if (interacting.find (subject) == interacting.end ()) {
- if (m_output_mode != Both) {
- result.insert (subject);
- } else {
- result2->insert (subject);
- }
- } else if (m_output_mode == Both) {
- result.insert (subject);
- }
-
- }
-
- } else {
-
- edge_interaction_filter > filter (result, m_mode);
- scanner.process (filter, 1, db::box_convert ());
-
- }
-
- }
-
- virtual OnEmptyIntruderHint on_empty_intruder_hint () const
- {
- if (m_mode == EdgesOutside) {
- return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy);
- } else {
- return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop);
- }
- }
-
- virtual std::string description () const
- {
- return tl::to_string (tr ("Select interacting edges"));
- }
-
-private:
- EdgeInteractionMode m_mode;
- output_mode_t m_output_mode;
-};
-
-class Edge2EdgePullLocalOperation
- : public local_operation
-{
-public:
- Edge2EdgePullLocalOperation ()
- {
- // .. nothing yet ..
- }
-
- virtual db::Coord dist () const
- {
- // touching is sufficient
- return 1;
- }
-
- virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const
- {
- tl_assert (results.size () == 1);
- std::unordered_set &result = results.front ();
-
- db::box_scanner scanner;
-
- std::set others;
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
- others.insert (interactions.intruder_shape (*j).second);
- }
- }
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- const db::Edge &subject = interactions.subject_shape (i->first);
- scanner.insert (&subject, 1);
- }
-
- for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
- scanner.insert (o.operator-> (), 0);
- }
-
- edge_interaction_filter > filter (result, EdgesInteract);
- scanner.process (filter, 1, db::box_convert ());
-
- }
-
- virtual OnEmptyIntruderHint on_empty_intruder_hint () const
- {
- return Drop;
- }
-
- virtual std::string description () const
- {
- return tl::to_string (tr ("Select interacting edges from other"));
- }
-};
-
-class Edge2PolygonInteractingLocalOperation
- : public local_operation
-{
-public:
- enum output_mode_t { Normal, Inverse, Both };
-
- Edge2PolygonInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode)
- : m_mode (mode), m_output_mode (output_mode)
- {
- // .. nothing yet ..
- }
-
- virtual db::Coord dist () const
- {
- // touching is sufficient
- return 1;
- }
-
- virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const
- {
- tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1));
-
- std::unordered_set &result = results.front ();
-
- std::unordered_set *result2 = 0;
- if (m_output_mode == Both) {
- result2 = &results[1];
- }
-
- db::box_scanner2 scanner;
-
- std::set others;
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
- others.insert (interactions.intruder_shape (*j).second);
- }
- }
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- const db::Edge &subject = interactions.subject_shape (i->first);
- scanner.insert1 (&subject, 0);
- }
-
- std::list heap;
- for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
- heap.push_back (o->obj ().transformed (o->trans ()));
- scanner.insert2 (& heap.back (), 1);
- }
-
- if (m_output_mode == Inverse || m_output_mode == Both) {
-
- std::unordered_set interacting;
- edge_to_region_interaction_filter > filter (&interacting, m_mode);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
-
- const db::Edge &subject = interactions.subject_shape (i->first);
-
- if (interacting.find (subject) == interacting.end ()) {
- if (m_output_mode != Both) {
- result.insert (subject);
- } else {
- result2->insert (subject);
- }
- } else if (m_output_mode == Both) {
- result.insert (subject);
- }
-
- }
-
- } else {
-
- edge_to_region_interaction_filter > filter (&result, m_mode);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
-
- }
- }
-
- virtual OnEmptyIntruderHint on_empty_intruder_hint () const
- {
- if (m_mode == EdgesOutside) {
- return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy);
- } else {
- return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop);
- }
- }
-
- virtual std::string description () const
- {
- if (m_mode == EdgesInteract) {
- if (m_output_mode == Inverse) {
- return tl::to_string (tr ("Select non-interacting edges"));
- } else if (m_output_mode == Normal) {
- return tl::to_string (tr ("Select interacting edges"));
- } else {
- return tl::to_string (tr ("Select interacting and non-interacting edges"));
- }
- } else if (m_mode == EdgesInside) {
- if (m_output_mode == Inverse) {
- return tl::to_string (tr ("Select non-inside edges"));
- } else if (m_output_mode == Normal) {
- return tl::to_string (tr ("Select inside edges"));
- } else {
- return tl::to_string (tr ("Select inside and non-inside edges"));
- }
- } else if (m_mode == EdgesOutside) {
- if (m_output_mode == Inverse) {
- return tl::to_string (tr ("Select non-outside edges"));
- } else if (m_output_mode == Normal) {
- return tl::to_string (tr ("Select outside edges"));
- } else {
- return tl::to_string (tr ("Select outside and non-outside edges"));
- }
- }
- return std::string ();
- }
-
-private:
- EdgeInteractionMode m_mode;
- output_mode_t m_output_mode;
-};
-
-struct ResultInserter
-{
- typedef db::Polygon value_type;
-
- ResultInserter (db::Layout *layout, std::unordered_set &result)
- : mp_layout (layout), mp_result (&result)
- {
- // .. nothing yet ..
- }
-
- void insert (const db::Polygon &p)
- {
- (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ()));
- }
-
-private:
- db::Layout *mp_layout;
- std::unordered_set *mp_result;
-};
-
-class Edge2PolygonPullLocalOperation
- : public local_operation
-{
-public:
- Edge2PolygonPullLocalOperation ()
- {
- // .. nothing yet ..
- }
-
- virtual db::Coord dist () const
- {
- // touching is sufficient
- return 1;
- }
-
- virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const
- {
- tl_assert (results.size () == 1);
- std::unordered_set &result = results.front ();
-
- db::box_scanner2 scanner;
-
- std::set others;
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
- others.insert (interactions.intruder_shape (*j).second);
- }
- }
-
- for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
- const db::Edge &subject = interactions.subject_shape (i->first);
- scanner.insert1 (&subject, 1);
- }
-
- std::list heap;
- for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
- heap.push_back (o->obj ().transformed (o->trans ()));
- scanner.insert2 (& heap.back (), 0);
- }
-
- ResultInserter inserter (layout, result);
- edge_to_region_interaction_filter filter (&inserter, EdgesInteract);
- scanner.process (filter, 1, db::box_convert (), db::box_convert ());
- }
-
- virtual OnEmptyIntruderHint on_empty_intruder_hint () const
- {
- return Drop;
- }
-
- virtual std::string description () const
- {
- return tl::to_string (tr ("Select interacting regions"));
- }
-};
-
-}
-
EdgesDelegate *
-DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const
+DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const
{
+ // @@@
std::unique_ptr dr_holder;
const db::DeepRegion *other_deep = dynamic_cast (other.delegate ());
if (! other_deep) {
@@ -1723,7 +1370,7 @@ DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMod
DeepLayer dl_out (edges.derived ());
- db::Edge2PolygonInteractingLocalOperation op (mode, inverse ? db::Edge2PolygonInteractingLocalOperation::Inverse : db::Edge2PolygonInteractingLocalOperation::Normal);
+ db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal);
db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
proc.set_base_verbosity (base_verbosity ());
@@ -1735,8 +1382,9 @@ DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMod
}
std::pair
-DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode) const
+DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const
{
+ // @@@
std::unique_ptr dr_holder;
const db::DeepRegion *other_deep = dynamic_cast (other.delegate ());
if (! other_deep) {
@@ -1755,7 +1403,7 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti
output_layers.push_back (dl_out.layer ());
output_layers.push_back (dl_out2.layer ());
- db::Edge2PolygonInteractingLocalOperation op (mode, db::Edge2PolygonInteractingLocalOperation::Both);
+ db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both);
db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
proc.set_base_verbosity (base_verbosity ());
@@ -1767,8 +1415,9 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti
}
EdgesDelegate *
-DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse) const
+DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const
{
+ // @@@
std::unique_ptr dr_holder;
const db::DeepEdges *other_deep = dynamic_cast (other.delegate ());
if (! other_deep) {
@@ -1793,8 +1442,9 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode
}
std::pair
-DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const
+DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const
{
+ // @@@
std::unique_ptr dr_holder;
const db::DeepEdges *other_deep = dynamic_cast (other.delegate ());
if (! other_deep) {
diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h
index 0578d704b..7309f93da 100644
--- a/src/db/db/dbDeepEdges.h
+++ b/src/db/db/dbDeepEdges.h
@@ -193,10 +193,10 @@ private:
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const db::EdgesCheckOptions &options) const;
virtual EdgesDelegate *pull_generic (const Edges &edges) const;
virtual RegionDelegate *pull_generic (const Region ®ion) const;
- virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const;
- virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const;
- virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const;
- virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const;
+ virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const;
+ virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const;
+ virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const;
DeepEdges *apply_filter (const EdgeFilterBase &filter) const;
template OutputContainer *processed_impl (const edge_processor &filter) const;
diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h
index e5317f20d..28decbbb6 100644
--- a/src/db/db/dbEdges.h
+++ b/src/db/db/dbEdges.h
@@ -991,9 +991,9 @@ public:
* Merged semantics applies. If merged semantics is chosen, the connected edge parts will be
* selected as a whole.
*/
- Edges &select_interacting (const Region &other)
+ Edges &select_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ())
{
- set_delegate (mp_delegate->selected_interacting (other));
+ set_delegate (mp_delegate->selected_interacting (other, min_count, max_count));
return *this;
}
@@ -1002,9 +1002,9 @@ public:
*
* This method is an out-of-place version of select_interacting.
*/
- Edges selected_interacting (const Region &other) const
+ Edges selected_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- return Edges (mp_delegate->selected_interacting (other));
+ return Edges (mp_delegate->selected_interacting (other, min_count, max_count));
}
/**
@@ -1013,9 +1013,9 @@ public:
* Merged semantics applies. If merged semantics is chosen, the connected edge parts will be
* selected as a whole.
*/
- Edges &select_not_interacting (const Region &other)
+ Edges &select_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ())
{
- set_delegate (mp_delegate->selected_not_interacting (other));
+ set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count));
return *this;
}
@@ -1024,17 +1024,17 @@ public:
*
* This method is an out-of-place version of select_not_interacting.
*/
- Edges selected_not_interacting (const Region &other) const
+ Edges selected_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- return Edges (mp_delegate->selected_not_interacting (other));
+ return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count));
}
/**
* @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region together with the ones that do not
*/
- std::pair selected_interacting_differential (const Region &other) const
+ std::pair selected_interacting_differential (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- std::pair p = mp_delegate->selected_interacting_pair (other);
+ std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count);
return std::pair (Edges (p.first), Edges (p.second));
}
@@ -1280,9 +1280,9 @@ public:
* Merged semantics applies. If merged semantics is chosen, the connected edge parts will be
* selected as a whole.
*/
- Edges &select_interacting (const Edges &other)
+ Edges &select_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ())
{
- set_delegate (mp_delegate->selected_interacting (other));
+ set_delegate (mp_delegate->selected_interacting (other, min_count, max_count));
return *this;
}
@@ -1291,17 +1291,17 @@ public:
*
* This method is an out-of-place version of select_interacting.
*/
- Edges selected_interacting (const Edges &other) const
+ Edges selected_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- return Edges (mp_delegate->selected_interacting (other));
+ return Edges (mp_delegate->selected_interacting (other, min_count, max_count));
}
/**
* @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set together with the ones that do not
*/
- std::pair selected_interacting_differential (const Edges &other) const
+ std::pair selected_interacting_differential (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- std::pair p = mp_delegate->selected_interacting_pair (other);
+ std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count);
return std::pair (Edges (p.first), Edges (p.second));
}
@@ -1311,9 +1311,9 @@ public:
* Merged semantics applies. If merged semantics is chosen, the connected edge parts will be
* selected as a whole.
*/
- Edges &select_not_interacting (const Edges &other)
+ Edges &select_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ())
{
- set_delegate (mp_delegate->selected_not_interacting (other));
+ set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count));
return *this;
}
@@ -1322,9 +1322,9 @@ public:
*
* This method is an out-of-place version of select_not_interacting.
*/
- Edges selected_not_interacting (const Edges &other) const
+ Edges selected_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const
{
- return Edges (mp_delegate->selected_not_interacting (other));
+ return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count));
}
/**
diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h
index 919ba5b8a..e5210c24f 100644
--- a/src/db/db/dbEdgesDelegate.h
+++ b/src/db/db/dbEdgesDelegate.h
@@ -318,12 +318,12 @@ public:
virtual std::pair inside_outside_part_pair (const Region &other) const = 0;
virtual RegionDelegate *pull_interacting (const Region &) const = 0;
virtual EdgesDelegate *pull_interacting (const Edges &) const = 0;
- virtual EdgesDelegate *selected_interacting (const Region &other) const = 0;
- virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0;
- virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0;
- virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0;
- virtual std::pair selected_interacting_pair (const Region &other) const = 0;
- virtual std::pair selected_interacting_pair (const Edges &other) const = 0;
+ virtual EdgesDelegate *selected_interacting (const Region &other, size_t min_count, size_t max_count) const = 0;
+ virtual EdgesDelegate *selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const = 0;
+ virtual EdgesDelegate *selected_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0;
+ virtual EdgesDelegate *selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0;
+ virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const = 0;
+ virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const = 0;
virtual EdgesDelegate *selected_outside (const Region &other) const = 0;
virtual EdgesDelegate *selected_not_outside (const Region &other) const = 0;
diff --git a/src/db/db/dbEdgesLocalOperations.cc b/src/db/db/dbEdgesLocalOperations.cc
index e201df8b5..28bc77979 100644
--- a/src/db/db/dbEdgesLocalOperations.cc
+++ b/src/db/db/dbEdgesLocalOperations.cc
@@ -199,5 +199,357 @@ EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell
}
}
+// ---------------------------------------------------------------------------------------------
+// Edge2EdgeInteractingLocalOperation implementation
+
+Edge2EdgeInteractingLocalOperation::Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode)
+ : m_mode (mode), m_output_mode (output_mode)
+{
+ // .. nothing yet ..
+}
+
+db::Coord Edge2EdgeInteractingLocalOperation::dist () const
+{
+ // touching is sufficient
+ return 1;
+}
+
+void Edge2EdgeInteractingLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const
+{
+ tl_assert (results.size () == (m_output_mode == Both ? 2 : 1));
+
+ std::unordered_set &result = results.front ();
+
+ std::unordered_set *result2 = 0;
+ if (m_output_mode == Both) {
+ result2 = &results[1];
+ }
+
+ db::box_scanner scanner;
+
+ std::set others;
+ for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
+ for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
+ others.insert (interactions.intruder_shape (*j).second);
+ }
+ }
+
+ for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
+ const db::Edge &subject = interactions.subject_shape (i->first);
+ scanner.insert (&subject, 0);
+ }
+
+ for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) {
+ scanner.insert (o.operator-> (), 1);
+ }
+
+ if (m_output_mode == Inverse || m_output_mode == Both) {
+
+ std::unordered_set interacting;
+ edge_interaction_filter > filter (interacting, m_mode);
+ scanner.process (filter, 1, db::box_convert ());
+
+ for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) {
+
+ const db::Edge &subject = interactions.subject_shape (i->first);
+ if (interacting.find (subject) == interacting.end ()) {
+ if (m_output_mode != Both) {
+ result.insert (subject);
+ } else {
+ result2->insert (subject);
+ }
+ } else if (m_output_mode == Both) {
+ result.insert (subject);
+ }
+
+ }
+
+ } else {
+
+ edge_interaction_filter > filter (result, m_mode);
+ scanner.process (filter, 1, db::box_convert