From 24759c71748c2d96c5f444baca13436ddf01108d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 18 Nov 2019 19:14:06 +0100 Subject: [PATCH 1/4] WIP: first implementation. Testing needed. --- src/db/db/dbAsIfFlatEdges.h | 6 ++++ src/db/db/dbDeepEdges.cc | 34 ++++++++++++++++++----- src/db/db/dbDeepEdges.h | 5 +++- src/db/db/dbEdgeBoolean.h | 31 +++++++++++++++++++-- src/db/db/dbEdges.h | 10 +++++++ src/db/db/dbEdgesDelegate.h | 6 +--- src/db/db/dbEmptyEdges.h | 1 + src/db/db/dbLocalOperation.cc | 23 ++++++++++----- src/db/db/dbLocalOperation.h | 5 ++-- src/db/db/gsiDeclDbEdges.cc | 7 +++++ src/drc/drc/built-in-macros/_drc_layer.rb | 26 ++++++++++++++++- 11 files changed, 129 insertions(+), 25 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index c47cf8409..6596036b4 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -27,6 +27,7 @@ #include "dbCommon.h" #include "dbBoxScanner.h" #include "dbEdgesDelegate.h" +#include "dbEdgeBoolean.h" #include "dbBoxScanner.h" #include "dbPolygonTools.h" @@ -138,6 +139,11 @@ public: return boolean (&other, EdgeOr); } + virtual EdgesDelegate *intersections (const Edges &other) const + { + return boolean (&other, EdgeIntersections); + } + virtual EdgesDelegate *add_in_place (const Edges &other) { return add (other); diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 220b2c212..e0e11a055 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -751,11 +751,11 @@ EdgesDelegate *DeepEdges::merged () const } DeepLayer -DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const +DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const { DeepLayer dl_out (m_deep_layer.derived ()); - db::EdgeBoolAndOrNotLocalOperation op (and_op); + db::EdgeBoolAndOrNotLocalOperation local_op (op); db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); proc.set_base_verbosity (base_verbosity ()); @@ -763,7 +763,7 @@ DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); - proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + proc.run (&local_op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); return dl_out; } @@ -786,6 +786,26 @@ DeepEdges::edge_region_op (const DeepRegion *other, bool outside, bool include_b return dl_out; } +EdgesDelegate *DeepEdges::intersections (const Edges &other) const +{ + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (! other_deep) { + + return AsIfFlatEdges::intersections (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections)); + + } +} + EdgesDelegate *DeepEdges::and_with (const Edges &other) const { const DeepEdges *other_deep = dynamic_cast (other.delegate ()); @@ -801,7 +821,7 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, true)); + return new DeepEdges (and_or_not_with (other_deep, EdgeAnd)); } } @@ -851,7 +871,7 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, false)); + return new DeepEdges (and_or_not_with (other_deep, EdgeNot)); } } @@ -903,8 +923,8 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const // Implement XOR as (A-B)+(B-A) - only this implementation // is compatible with the local processor scheme - DeepLayer n1 (and_or_not_with (other_deep, false)); - DeepLayer n2 (other_deep->and_or_not_with (this, false)); + DeepLayer n1 (and_or_not_with (other_deep, EdgeNot)); + DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot)); n1.add_from (n2); return new DeepEdges (n1); diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index e60bab2b1..af4f13e4f 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -28,6 +28,7 @@ #include "dbAsIfFlatEdges.h" #include "dbDeepShapeStore.h" +#include "dbEdgeBoolean.h" #include "dbEdgePairs.h" namespace db { @@ -132,6 +133,8 @@ public: virtual EdgesDelegate *add_in_place (const Edges &other); virtual EdgesDelegate *add (const Edges &other) const; + virtual EdgesDelegate *intersections (const Edges &other) const; + virtual EdgesDelegate *inside_part (const Region &other) const; virtual EdgesDelegate *outside_part (const Region &other) const; @@ -168,7 +171,7 @@ private: void init (); void ensure_merged_edges_valid () const; const DeepLayer &merged_deep_layer () const; - DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const; + DeepLayer and_or_not_with(const DeepEdges *other, EdgeBoolOp op) const; DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const; EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index 126960546..db495a873 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -24,7 +24,6 @@ #define HDR_dbEdgeBoolean #include "dbEdge.h" -#include "dbEdgesDelegate.h" #include "dbBoxScanner.h" #include "tlIntervalMap.h" @@ -32,6 +31,11 @@ namespace db { +/** + * @brief A common definition for the boolean operations available on edges + */ +enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd, EdgeIntersections }; + struct OrJoinOp { void operator() (int &v, int n) @@ -216,7 +220,15 @@ struct EdgeBooleanClusterCollector : public db::cluster_collector > { EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op) - : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/) + : db::cluster_collector > (EdgeBooleanCluster (output, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), + mp_intersections (op == EdgeIntersections ? output : 0) + { + // .. nothing yet .. + } + + EdgeBooleanClusterCollector (OutputContainer *output, OutputContainer *intersections, EdgeBoolOp op) + : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/), + mp_intersections (intersections) { // .. nothing yet .. } @@ -227,12 +239,27 @@ struct EdgeBooleanClusterCollector // 1.) not degenerate // 2.) parallel with some tolerance of roughly 1 dbu // 3.) connected + // In intersection-detection mode, identify intersection points otherwise + // and insert into the intersections container as degenerated edges. + if (! o1->is_degenerate () && ! o2->is_degenerate () && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { + db::cluster_collector >::add (o1, p1, o2, p2); + + } else if (mp_intersections && p1 != p2) { + + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + mp_intersections->insert (db::Edge (ip.second, ip.second)); + } + } } + +private: + OutputContainer *mp_intersections; }; } diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index a2c236d54..b4dfa2dc2 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -950,6 +950,16 @@ public: return *this; } + /** + * @brief Intersections with other edges + * Intersections are similar to "AND", but will also report + * non-parallel intersections between crossing edges. + */ + Edges intersections (const Edges &other) const + { + return Edges (mp_delegate->intersections (other)); + } + /** * @brief returns the extended edges * diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 42564daca..6247faebe 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -155,11 +155,6 @@ class DB_PUBLIC EdgeToEdgePairProcessorBase // .. nothing yet .. }; -/** - * @brief A common definition for the boolean operations available on edges - */ -enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd }; - class RecursiveShapeIterator; class EdgeFilterBase; class EdgePairsDelegate; @@ -267,6 +262,7 @@ public: virtual EdgesDelegate *or_with (const Edges &other) const = 0; virtual EdgesDelegate *add_in_place (const Edges &other) = 0; virtual EdgesDelegate *add (const Edges &other) const = 0; + virtual EdgesDelegate *intersections (const Edges &other) const = 0; virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0; diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 243bd56a3..368aabaff 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -81,6 +81,7 @@ public: virtual EdgesDelegate *or_with (const Edges &other) const; virtual EdgesDelegate *add_in_place (const Edges &other); virtual EdgesDelegate *add (const Edges &other) const; + virtual EdgesDelegate *intersections (const Edges &) const { return new EmptyEdges (); } virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const; diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 0789aa2d5..e4eacdc9b 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -177,8 +177,8 @@ std::string SelfOverlapMergeLocalOperation::description () const // --------------------------------------------------------------------------------------------- // EdgeBoolAndOrNotLocalOperation implementation -EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) - : m_is_and (is_and) +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op) + : m_op (op) { // .. nothing yet .. } @@ -186,19 +186,27 @@ EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) local_operation::on_empty_intruder_mode EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const { - return m_is_and ? Drop : Copy; + return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; } std::string EdgeBoolAndOrNotLocalOperation::description () const { - return m_is_and ? tl::to_string (tr ("Edge AND operation")) : tl::to_string (tr ("Edge NOT operation")); + if (m_op == EdgeIntersections) { + return tl::to_string (tr ("Edge INTERSECTION operation")); + } else if (m_op == EdgeAnd) { + return tl::to_string (tr ("Edge AND operation")); + } else if (m_op == EdgeNot) { + return tl::to_string (tr ("Edge NOT operation")); + } else { + return std::string (); + } } void EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const { - EdgeBooleanClusterCollector > cluster_collector (&result, m_is_and ? EdgeAnd : EdgeNot); + EdgeBooleanClusterCollector > cluster_collector (&result, m_op); db::box_scanner scanner; @@ -210,17 +218,18 @@ EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const sh } bool any_subject = false; + bool is_and = (m_op == EdgeAnd || m_op == EdgeIntersections); for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { const db::Edge &subject = interactions.subject_shape (i->first); if (others.find (subject) != others.end ()) { - if (m_is_and) { + if (is_and) { result.insert (subject); } } else if (i->second.empty ()) { // shortcut (not: keep, and: drop) - if (! m_is_and) { + if (! is_and) { result.insert (subject); } } else { diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 499704b29..fa388793a 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -28,6 +28,7 @@ #include "dbCommon.h" #include "dbLayout.h" +#include "dbEdgeBoolean.h" #include #include @@ -151,7 +152,7 @@ class DB_PUBLIC EdgeBoolAndOrNotLocalOperation : public local_operation { public: - EdgeBoolAndOrNotLocalOperation (bool is_and); + EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op); virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; @@ -161,7 +162,7 @@ public: virtual db::Coord dist () const { return 1; } private: - bool m_is_and; + EdgeBoolOp m_op; }; /** diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 2f6d5c35c..d48b1f064 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -943,6 +943,13 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.26.1\n" ) + + method ("intersections", &db::Edges::intersections, gsi::arg ("other"), + "@brief Computes the intersections between this edges and other edges\n" + "This computation is like an AND operation, but also including crossing points between non-coincident edges as " + "degenerated (point-like) edges.\n" + "\n" + "This method has been introduced in version 0.26.2\n" + ) + method ("inside_part", &db::Edges::inside_part, gsi::arg ("other"), "@brief Returns the parts of the edges of this edge collection which are inside the polygons of the region\n" "\n" diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 8d4507afd..7b42e7a02 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1601,6 +1601,16 @@ CODE # This method is available for polygon and edge layers. Edges can be selected # with respect to other edges or polygons. + # %DRC% + # @name intersections + # @brief Returns the intersection points of intersecting edge segments for two edge collections + # @synopsis layer.intersections(edges) + # This operation is similar to the "&" operator, but it does also report intersection points + # between non-colinear, but intersection edges. Such points are reported as point-like, + # degenerated edge objects. + # + # This method is available for edge layers. The argument must be an edge layer. + # %DRC% # @name inside_part # @brief Returns the parts of the edges inside the given region @@ -1731,7 +1741,6 @@ CODE end %w(inside_part outside_part).each do |f| - # In tiled mode, there are no modifying versions. Emulate using the non-modifying one. eval <<"CODE" def #{f}(other) other.requires_region("#{f}") @@ -1746,6 +1755,21 @@ CODE CODE end + %w(intersections).each do |f| + eval <<"CODE" + def #{f}(other) + other.requires_edges("#{f}") + requires_edges("#{f}") + if @engine.is_tiled? + @data = @engine._tcmd(@data, 0, @data.class, :#{f}, other.data) + DRCLayer::new(@engine, @data) + else + DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) + end + end +CODE + end + # %DRC% # @name rectangles # @brief Selects all rectangle polygons from the input From 9af662a512d120c7014cc6bb7ab900abff4f23d1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 18 Nov 2019 23:14:24 +0100 Subject: [PATCH 2/4] WIP: try to avoid duplicate intersection points by eliminating those. Problem persists: intersection points may be duplicates of edges arising from AND --- src/db/db/dbEdgeBoolean.h | 4 +++- src/db/unit_tests/dbEdges.cc | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index db495a873..449d3f8f1 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -24,6 +24,7 @@ #define HDR_dbEdgeBoolean #include "dbEdge.h" +#include "dbHash.h" #include "dbBoxScanner.h" #include "tlIntervalMap.h" @@ -251,7 +252,7 @@ struct EdgeBooleanClusterCollector } else if (mp_intersections && p1 != p2) { std::pair ip = o1->intersect_point (*o2); - if (ip.first) { + if (ip.first && m_seen_intersections.insert (ip.second).second) { mp_intersections->insert (db::Edge (ip.second, ip.second)); } @@ -260,6 +261,7 @@ struct EdgeBooleanClusterCollector private: OutputContainer *mp_intersections; + std::unordered_set m_seen_intersections; }; } diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 86d00817e..ace673825 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -852,6 +852,9 @@ TEST(22) ee.insert (db::Edge (4000,-2000,-2000,-2000)); EXPECT_EQ ((e & ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;400,0);(-2000,0;-2000,0);(1000,0;1000,0);(4000,0;4000,0);(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + // no particular new points with intersections - just endpoints of original edges + EXPECT_EQ (e.intersections (ee).merged ().to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); } TEST(23) From 6c7ceb74dc1f8af29a6d070cb9169c46248f5b71 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Nov 2019 21:19:36 +0100 Subject: [PATCH 3/4] Enhanced intersections algorithm so that the generated points won't overlay with finite edges from the AND part --- src/db/db/dbAsIfFlatEdges.cc | 2 +- src/db/db/dbBoxScanner.h | 78 +++++++++--- src/db/db/dbDeepEdges.cc | 2 +- src/db/db/dbEdgeBoolean.h | 163 +++++++++++++++++++++++++- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/dbHierNetworkProcessor.cc | 10 +- src/db/db/dbOriginalLayerEdges.cc | 2 +- src/db/unit_tests/dbBoxScanner.cc | 40 ++++--- src/db/unit_tests/dbDeepEdgesTests.cc | 3 + src/db/unit_tests/dbEdges.cc | 34 +++++- testdata/algo/deep_edges_au3.gds | Bin 5846 -> 7142 bytes testdata/drc/drcSuiteTests.drc | 16 +++ 12 files changed, 306 insertions(+), 46 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 203a89e9f..209c303ae 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -533,7 +533,7 @@ EdgesDelegate * AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const { std::auto_ptr output (new FlatEdges (true)); - EdgeBooleanClusterCollector cluster_collector (&output->raw_edges (), op); + EdgeBooleanClusterCollectorToShapes cluster_collector (&output->raw_edges (), op); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 868ecc709..eefd7df5a 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -137,6 +137,21 @@ struct box_scanner_receiver * terminate the scan process early if the outcome is known. */ bool stop () const { return false; } + + /** + * @brief Pre-scanning operations + * + * This method is called before the scanning starts. + */ + void initialize () { } + + /** + * @brief Post-scanning operations + * + * This method is called after the scan has finished (without exception). The argument is the + * return value (false if "stop" stopped the process). + */ + void finalize (bool) { } }; /** @@ -265,6 +280,22 @@ public: */ template bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) + { + rec.initialize (); + bool ret = do_process (rec, enl, bc); + rec.finalize (ret); + return ret; + } + +private: + container_type m_pp; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; + + template + bool do_process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) { typedef typename BoxConvert::box_type box_type; typedef typename box_type::coord_type coord_type; @@ -420,13 +451,6 @@ public: return true; } - -private: - container_type m_pp; - double m_fill_factor; - size_t m_scanner_thr; - bool m_report_progress; - std::string m_progress_desc; }; /** @@ -469,6 +493,21 @@ struct box_scanner_receiver2 * terminate the scan process early if the outcome is known. */ bool stop () const { return false; } + + /** + * @brief Pre-scanning operations + * + * This method is called before the scanning starts. + */ + void initialize () { } + + /** + * @brief Post-scanning operations + * + * This method is called after the scan has finished (without exception). The argument is the + * return value (false if "stop" stopped the process). + */ + void finalize (bool) { } }; /** @@ -613,6 +652,23 @@ public: */ template bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + { + rec.initialize (); + bool ret = do_process (rec, enl, bc1, bc2); + rec.finalize (ret); + return ret; + } + +private: + container_type1 m_pp1; + container_type2 m_pp2; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; + + template + bool do_process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) { typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type typedef typename box_type::coord_type coord_type; @@ -845,14 +901,6 @@ public: return true; } - -private: - container_type1 m_pp1; - container_type2 m_pp2; - double m_fill_factor; - size_t m_scanner_thr; - bool m_report_progress; - std::string m_progress_desc; }; /** diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index e0e11a055..e8929c7bf 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -349,7 +349,7 @@ public: // .. and run the merge operation s->second.clear (); - EdgeBooleanClusterCollector cluster_collector (&s->second, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&s->second, EdgeOr); m_scanner.process (cluster_collector, 1, db::box_convert ()); return s->second; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index 449d3f8f1..a85fb6e40 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -26,6 +26,7 @@ #include "dbEdge.h" #include "dbHash.h" #include "dbBoxScanner.h" +#include "dbShapes.h" #include "tlIntervalMap.h" @@ -222,14 +223,14 @@ struct EdgeBooleanClusterCollector { EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op) : db::cluster_collector > (EdgeBooleanCluster (output, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), - mp_intersections (op == EdgeIntersections ? output : 0) + mp_output (output), mp_intersections (op == EdgeIntersections ? output : 0) { // .. nothing yet .. } EdgeBooleanClusterCollector (OutputContainer *output, OutputContainer *intersections, EdgeBoolOp op) : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/), - mp_intersections (intersections) + mp_output (output), mp_intersections (intersections) { // .. nothing yet .. } @@ -252,16 +253,168 @@ struct EdgeBooleanClusterCollector } else if (mp_intersections && p1 != p2) { std::pair ip = o1->intersect_point (*o2); - if (ip.first && m_seen_intersections.insert (ip.second).second) { - mp_intersections->insert (db::Edge (ip.second, ip.second)); + if (ip.first) { + m_intersections.insert (ip.second); } } } + /** + * @brief A receiver for the reducer which removes points that are on the edges + */ + struct RemovePointsOnEdges + : public db::box_scanner_receiver2 + { + public: + RemovePointsOnEdges (std::set &points_to_remove) + : mp_points_to_remove (&points_to_remove) + { } + + void add (const db::Edge *e, const size_t &, const db::Point *pt, const size_t &) + { + if (e->contains (*pt)) { + mp_points_to_remove->insert (*pt); + } + } + + private: + std::set *mp_points_to_remove; + }; + + /** + * @brief An inserter to produce degenerated edges from points + */ + struct PointInserter + : public std::iterator + { + typedef db::Point value_type; + + PointInserter (OutputContainer *output) + : mp_output (output) + { } + + PointInserter &operator= (const db::Point &pt) + { + mp_output->insert (db::Edge (pt, pt)); + return *this; + } + + PointInserter &operator* () { return *this; } + PointInserter &operator++ () { return *this; } + PointInserter &operator++ (int) { return *this; } + + private: + OutputContainer *mp_output; + }; + + /** + * @brief Finalizes the implementation for "EdgeIntersections" + * This method pushes those points which don't interact with the edges to the output container + * as degenerate edges. It needs to be called after the pass has been made. + */ + void finalize (bool) + { + if (m_intersections.empty ()) { + return; + } + + db::box_scanner2 intersections_to_edge_scanner; + for (typename OutputContainer::const_iterator e = mp_output->begin (); e != mp_output->end (); ++e) { + intersections_to_edge_scanner.insert1 (e.operator-> (), 0); + } + for (std::set::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) { + intersections_to_edge_scanner.insert2 (p.operator-> (), 0); + } + + std::set points_to_remove; + RemovePointsOnEdges rpoe (points_to_remove); + intersections_to_edge_scanner.process (rpoe, 1, db::box_convert (), db::box_convert ()); + + std::set_difference (m_intersections.begin (), m_intersections.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (mp_intersections)); + } + private: + OutputContainer *mp_output; OutputContainer *mp_intersections; - std::unordered_set m_seen_intersections; + std::set m_intersections; +}; + +/** + * @brief A helper class to use db::Shapes as container for EdgeBooleanClusterCollector + */ +struct DB_PUBLIC ShapesToOutputContainerAdaptor +{ +public: + struct Iterator + : public db::Shapes::shape_iterator + { + Iterator (const db::Shapes::shape_iterator &iter) + : db::Shapes::shape_iterator (iter) + { } + + Iterator () + : db::Shapes::shape_iterator () + { } + + const db::Edge *operator-> () const + { + return (db::Shapes::shape_iterator::operator* ()).basic_ptr (db::Edge::tag ()); + } + + bool operator!= (const Iterator &other) const + { + // only for testing whether at end: + return at_end () != other.at_end (); + } + + bool operator== (const Iterator &other) const + { + // only for testing whether at end: + return at_end () == other.at_end (); + } + }; + + typedef Iterator const_iterator; + + ShapesToOutputContainerAdaptor (db::Shapes &shapes) + : mp_shapes (&shapes) + { + // .. nothing yet .. + } + + const_iterator begin () + { + return Iterator (mp_shapes->begin (db::ShapeIterator::Edges)); + } + + const_iterator end () + { + return Iterator (); + } + + void insert (const db::Edge &edge) + { + mp_shapes->insert (edge); + } + +private: + db::Shapes *mp_shapes; +}; + +/** + * @brief A specialization of the EdgeBooleanClusterCollector for a Shapes output container + */ +struct DB_PUBLIC EdgeBooleanClusterCollectorToShapes + : EdgeBooleanClusterCollector +{ + EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op) + : EdgeBooleanClusterCollector (&m_adaptor, op), m_adaptor (*output) + { + } + +private: + ShapesToOutputContainerAdaptor m_adaptor; }; } diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 75c977bee..75d80f539 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -112,7 +112,7 @@ FlatEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); db::Shapes tmp (false); - EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (m_edges.size ()); diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 3c85dc9be..1728f32d6 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1228,11 +1228,6 @@ public: add_pair (*c1, *i2, p, t); } - bool stop () const - { - return false; - } - /** * @brief Finally join the clusters in the join set * @@ -1275,6 +1270,11 @@ public: } } + // needs explicit implementation because we have two base classes: + bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } + private: struct InteractionKeyForClustersType : public InstanceToInstanceInteraction diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index ee47f5771..631652f8b 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -253,7 +253,7 @@ OriginalLayerEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); db::Shapes tmp (false); - EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size ()); diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 8d396e3b7..48369cb64 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -36,6 +36,8 @@ struct BoxScannerTestRecorder } bool stop () const { return false; } + void initialize () { str += "[i]"; } + void finalize (bool) { str += "[f]"; } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -54,6 +56,8 @@ struct BoxScannerTestRecorderStopping } bool stop () const { return do_stop; } + void initialize () { str += "[i]"; } + void finalize (bool s) { str += s ? "[f+]" : "[f-]"; } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -70,6 +74,8 @@ struct BoxScannerTestRecorder2 void finish (const db::Box *, size_t) { } bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -91,6 +97,8 @@ struct BoxScannerTestRecorderTwo } bool stop () const { return false; } + void initialize () { str += "[i]"; } + void finalize (bool) { str += "[f]"; } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -113,6 +121,8 @@ struct BoxScannerTestRecorderTwoStopping } bool stop () const { return do_stop; } + void initialize () { str += "[i]"; } + void finalize (bool s) { str += s ? "[f+]" : "[f-]"; } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -130,6 +140,8 @@ struct BoxScannerTestRecorder2Two void finish2 (const db::SimplePolygon *, int) { } bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -159,11 +171,11 @@ TEST(1) db::box_convert bc; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc), true); - EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>"); + EXPECT_EQ (tr.str, "[i](4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>[f]"); BoxScannerTestRecorderStopping trstop; EXPECT_EQ (bs.process (trstop, 1, bc), false); - EXPECT_EQ (trstop.str, "(4-2)"); + EXPECT_EQ (trstop.str, "[i](4-2)[f-]"); } TEST(1a) @@ -182,7 +194,7 @@ TEST(1a) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(1-0)<0><1>"); + EXPECT_EQ (tr.str, "[i](1-0)<0><1>[f]"); } TEST(1b) @@ -204,7 +216,7 @@ TEST(1b) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>[f]"); } TEST(1c) @@ -226,7 +238,7 @@ TEST(1c) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>[f]"); } TEST(1d) @@ -248,7 +260,7 @@ TEST(1d) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "(3-0)<0><3><1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)<0><3><1><4><2>[f]"); } TEST(1e) @@ -269,7 +281,7 @@ TEST(1e) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "(0-3)<0><1><2><3><4>"); + EXPECT_EQ (tr.str, "[i](0-3)<0><1><2><3><4>[f]"); } TEST(1f) @@ -280,7 +292,7 @@ TEST(1f) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, ""); + EXPECT_EQ (tr.str, "[i][f]"); } TEST(1g) @@ -302,7 +314,7 @@ TEST(1g) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "<2><4>(0-3)<0><1><3>"); + EXPECT_EQ (tr.str, "[i]<2><4>(0-3)<0><1><3>[f]"); } void run_test2 (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) @@ -927,7 +939,7 @@ TEST(two_1) db::box_convert bc2; bs.set_scanner_threshold (0); bs.process (tr, 1, bc1, bc2); - EXPECT_EQ (tr.str, "(2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>"); + EXPECT_EQ (tr.str, "[i](2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>[f]"); } TEST(two_1a) @@ -963,7 +975,7 @@ TEST(two_1a) db::box_convert bc2; bs.set_scanner_threshold (0); bs.process (tr, 1, bc1, bc2); - EXPECT_EQ (tr.str, "(2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>"); + EXPECT_EQ (tr.str, "[i](2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>[f]"); } TEST(two_1b) @@ -999,12 +1011,12 @@ TEST(two_1b) db::box_convert bc2; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); - EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); + EXPECT_EQ (tr.str, "[i](1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>[f]"); BoxScannerTestRecorderTwoStopping trstop; EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false); - EXPECT_EQ (trstop.str, "(1-12)"); + EXPECT_EQ (trstop.str, "[i](1-12)[f-]"); } TEST(two_1c) @@ -1035,7 +1047,7 @@ TEST(two_1c) db::box_convert bc2; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); - EXPECT_EQ (tr.str, "<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>"); + EXPECT_EQ (tr.str, "[i]<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>[f]"); } void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 2fa7f90d2..33ff2c563 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -131,6 +131,7 @@ TEST(3_Edge2EdgeBooleans) db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); db::Region r2and3 = r2 & r3; + db::Edges e2 = r2.edges (); db::Edges e3 = r3.edges (); db::Edges e2and3 = r2and3.edges (); @@ -144,6 +145,8 @@ TEST(3_Edge2EdgeBooleans) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 - e2and3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), e3.intersections(e2)); CHECKPOINT(); db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au3.gds"); diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index ace673825..eff26ff66 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -852,9 +852,37 @@ TEST(22) ee.insert (db::Edge (4000,-2000,-2000,-2000)); EXPECT_EQ ((e & ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); - EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;400,0);(-2000,0;-2000,0);(1000,0;1000,0);(4000,0;4000,0);(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); - // no particular new points with intersections - just endpoints of original edges - EXPECT_EQ (e.intersections (ee).merged ().to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + + // Edge/edge intersections + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 150)); + ee.insert (db::Edge (-50, 50, 50, 50)); + ee.insert (db::Edge (-50, 100, 50, 100)); + EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points + EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,100;0,100)"); + + // Edge is intersected by pair with connection point on this line + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 150)); + ee.insert (db::Edge (-50, 50, 0, 50)); + ee.insert (db::Edge (0, 60, 50, 60)); + ee.insert (db::Edge (-50, 100, 0, 100)); + ee.insert (db::Edge (0, 100, 50, 100)); + EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points + EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,60;0,60);(0,100;0,100)"); + + // Coincident edges are crossed by another one + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 250)); + ee.insert (db::Edge (0, 0, 0, 150)); + ee.insert (db::Edge (-50, 100, 50, 100)); + ee.insert (db::Edge (-50, 200, 50, 200)); + EXPECT_EQ ((e & ee).to_string (), "(0,0;0,150)"); + EXPECT_EQ (e.intersections (ee).to_string (), "(0,0;0,150);(0,200;0,200)"); } TEST(23) diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds index ae59b5807c930c016f8938c03934fbf146a57438..62914f8282d8a8fe87cc1196898b06b7179c80ed 100644 GIT binary patch delta 1074 zcmZ`(O=}ZT6n!&HCi9t@%m!de3j-Shj--D0=5eZo@gz6(G+y`41F!biLJn(xJkJKJ;Iq5 z<4RFQ#_JRX&-4~{R0YRcgbO=C*Ukh!QZ@Y4mxxov_aMUCQY`pKMoYYda*Vsy8hTDD z`nH0EY8t2-P0`<>o(nrg&5QBcNbtvO!?4?8*3gUa%Zf>xie-n-w{Q?Rf~QAydu}4) zH&z?F#f0WGux@M$U)543I5x~C=}2j2f}W|sEl0R*a?VxZ^|2G-QJ8yO@pDbNCkIEt zJ)D!KqaekgoMGL|$VUfHgH_TWVc%(>6XbiTdo9wQU{X#WRW<+>`QAxpbJHo?qgFNvW zDb;fysT5&HsbW)0DC(Lux^X z8Jh)PdDmU(+e`!Bz^G)oN6w*dG_qkzR vR+%qb^X3qo6zkXyGJ;Vx?C_WKV*g3PP`X7W^|aDbho|CEt7VzWKf6=)&jo^Mn%1x=H#itNo2w zBq5AAWOVZR>HOK=sluyUUs|UtdsnO@r#=(msyBpmM;QAQ6IbF#N--8LUV99WJ%Oov z5%=5*Q`^RUuS6_-jKjq`7PuDVc+wduiw$WDZ2LJ@BK=PBAe1Vy!cs^vWD=8ZVf@Qs zg-M(n@0OjL*_hgOP?`->?&7DvfaQeZWmrNAsOP(Ml~4pn)#+fCG5o8qG?a>FT<=Z2!?SnK7AxH_igCSxx174ae> pbG3n@o2+pw%h;! diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc index b3164b5cd..be4c43ed6 100644 --- a/testdata/drc/drcSuiteTests.drc +++ b/testdata/drc/drcSuiteTests.drc @@ -589,6 +589,22 @@ def run_testsuite(dm, ic, tiled = false, hier = false) p = c.edges.pull_interacting(b.edges) p.output(lb + 5, dm) + lb += 10 #440 + message "--- edge booleans, intersections #{lb}" + + p = c.edges - b.edges + p.output(lb, dm) + p = b.edges - c.edges + p.output(lb + 1, dm) + p = c.edges & b.edges + p.output(lb + 2, dm) + p = c.edges ^ b.edges + p.output(lb + 3, dm) + p = c.edges.intersections(b.edges) + p.output(lb + 4, dm) + p = b.edges.intersections(c.edges) + p.output(lb + 5, dm) + end if $drc_test_mode == 1 From 772b309d051930838f14aa6e57971cb4c14a3c79 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Nov 2019 21:34:11 +0100 Subject: [PATCH 4/4] Fixed #406: finally added tests for DRC feature. --- testdata/drc/drcSuiteTests_au1.oas | Bin 12342 -> 13479 bytes testdata/drc/drcSuiteTests_au2.oas | Bin 51860 -> 61099 bytes testdata/drc/drcSuiteTests_au3.oas | Bin 1143867 -> 1191582 bytes testdata/drc/drcSuiteTests_au4.oas | Bin 52120 -> 61359 bytes testdata/drc/drcSuiteTests_au5.oas | Bin 12174 -> 13331 bytes testdata/drc/drcSuiteTests_au6.oas | Bin 18672 -> 19829 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/testdata/drc/drcSuiteTests_au1.oas b/testdata/drc/drcSuiteTests_au1.oas index 0774c72d291913c119e57645dbffd0eace8d4791..636910e6b5d6ea37462d40d1ceffe5d64ce98160 100644 GIT binary patch delta 1170 zcmZ`&zi(1e7`-1nA;7Co>hmV(fHpDh&;ZsSfdojKG(r=bSGu4qvt&q%C>pTToCv z)V(b;f?APoQD*U-Y$T^CnIUQTU0^Qlu%cba0Br&*9MXusU+~6 z7vvcBN@XJ^$`3V$JYKGB?62}^ynJL%atvOJTRq~dj@~r;ce#UFe1DT~PNE}iyv?U0 z(<${I%RJF(v>qC7#{%VdB#)^@eD^-sG%Jr##XuFJu6~-gSNPN;r-b$p(g@j4M|-hL z?F^!CL>a$nHg~vvK_ksE zd=q|%+Yu~gU0@yPSoG+cJNZa+DMRW|Ynr1y2I-UHia^Ve+0JkqL%`t`zyqU_u&_@m zzoqqpE)(B_)%)NPi4N#yl6gmsBxuY12+o)5XZfG*W^J$WxN2Dc78dxzr}ZDFsO~Wi zkRGWWFohU)kpxesHwjDf&4#wm=2e1%v;gbrb+#6iZ&>GQ11}c}0^slj%z+Jh)GO5} zWo^l;*SVOd4@93!7%I3bu#^mDDqv20lpx*n8xkN_s5K@&2Uaod+#+*#&Wwb-TD(W> zGUYd)!UzgGB%Kk4nsp`>&z=~{@7uyp&@LRKx(oYkW|s6%Bsbo;mK_)uily-DEK6`* bB_>wtl4}GdslaRg9qgokA3RA%148@-VkAZq delta 20 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 delta 27246 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 ImHGMo7dD-o-~a#s diff --git a/testdata/drc/drcSuiteTests_au4.oas b/testdata/drc/drcSuiteTests_au4.oas index 3bf1fc990f65c915b2f9c220c039194be17562fd..6a45d564829e83044b16b0bebf86972d55198cbe 100644 GIT binary patch delta 9252 zcmeHM-E$OG7N5?D~lWUc`RrunI5Bs2h$8Dlgy{y@E zc9vp-*?3`D>=@bfEDH=FYLgY{fFB(N9Qhj2xaZfoeVIfzY$k*Auv@A6 z<;s*Lkb#M-r`_in6txuM3uwnNX~h*CIyU zW~(EgiX3>m*TU-}hw0UPofth|udRXaqZF(ipYPh2RFquWe=2N@d~<31MP@$5*NO;r zc2n4yOJ~X}N}pB36Ac)@OtV%Cy?0`KxC!Hjmtp+uT^QHy#`pluOJ!l91#cX` z2X9QPc%!G4FdSNe@k94w{PcYoztM*AEg6jOeE{PhK8W#)?HK?4!x%R^F#h&Rnmd+E z$z+V*(A?>nCg`cet|&$_?A(eWzP2x!-Qu*d!V~o1@B9)EZXZ)% zH^2@)t|2>YgdMI=N*}GKR{B`kJETux6t;+S5Q{0|9Icl=;U(C!KH80j$cNzztQQ)k zQ(+jk2(;(U$SIo8Sd;V#&%hReE-sTk*44YDPhp^0`q;1DEq#KWY3UOlOEc-D(K5!f zw!XYaIu&|U>0{@XOP^q{Rr-VnVT%b(u8=;~NB2sf!VTEs!G6~!d>Act@BPv#+yh$# z8qWwPS|R5F=~K7}TLk*pq6fMSI~DC(qaCo-v62Wb(Rvsz2S3Npw%wjz8V1`q+FLwn^|u z>{5L@knaW>BiON~4YVhL)=}(U=To5f4bYvcCGrGQ%@>e(>`g$6^&0x zM)e1Q8VIBYLzMVbeLs-D0}MN3vJ$)E!=|n;In#KPJ31DZ<2#Ig@Wp(Mkg4{5ys&nI zFwP;M_Yu&Y1Y!KsK6f!=FCcRTpv?*?k@F#ZKL2%~=h$O zm5lO{x5`@;(}!|Se>!-avK18@GmTibjQzC8V^%8n46<9se=5pP31vtP8%2?FvQ}Fo zYMWwh*eO1;l}K2cz~+;XMJ%iy30cI#>XDE|EUX?0>GvlVR*!^q{u2wM)9^r3;Rz~3 z<5CWVb3Rp?z$$+D(b>n4w5oOZ(b#x!Q-XJr94O%EaO>)>iBZYcvhi0zT7fCI{zwA$Cq2iWB*m6 zj;B;sRwkqUcdI(Sq_C>|iOA;N2g~JAxirH1m)-}<BKj%JJE|1Ek5u0}( zESE=fXf)?Ur1Cz#5Z+WDPm(wh>0sI3iF&3eB~qDC%A}6=Q;J84-dG}2mMbUHZ?ITB zv7&M!`_Fn}LLrT2R-!3Ubmr(hC>ctt)cH4@TbB&<{I~m>y^Y)g_7!LO|8;07*ZxoH(CHuP(AnQkw6TN|w`(3%6o}A~Dm*JsM`frK zU=k!aOVve+$>=l9(QGoZ>lCZZbXbVZV7WcBc#^{oj z@LF=|hQzY*N4ig^NRk?X(#VfOU9_j5D0r0Da9X zKvUC>Lr3G&ICSkE=w;pzG&1cFDH60&8he&z-~DWvvtRn;*-~b2bzteIl1rF9<5gOF z_pyA6`Z3H%qW9%IN%pZ{!8FdINB4S7WPgVq-K>lLB6@VQKJEZ|bhAG0-_e&7Vcvi( z;v7ehPQ>{deK`^5Ti7B_55puxpueCmCj$Kqwg_|xJvtHSQ}pFTpefiQ&<*tHM4UgN zFDK%>0$aq{iyobb^DcUHw9$n;=;p-Byfi(THoGljs delta 19 bcmZ2~pLxb~<_$AWF)~fw`0fvL0|Ns9UC9WF diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas index 939fa3b9345de492aff52ec0ead0306a2aac259f..c596d626f66f68d2ec28f950bf7a6b9b1d9acebe 100644 GIT binary patch delta 1165 zcmZuwzi(1u6#XtdY2ZM}yDxJ@jf9w_`D47&NFao-dZL%_BZ3GFtms64mYSG}6hS?cE3m&b5{?Eb>l6&D|bv zr}5(*tc15dIVI+Xs281W*-8EyP-`D70Js~qlfArdiSnVrt242kW*TsWmj^_#abnMyun|iwWh&>u!hr!^MD87ve|*9 zhg>@U1iq0=b2ul>A3>g{`dEq0va2=@Tmnq<=am{S?s>WUoDTr92$=JKozja!`TNax zM%|($wHFU>Tb>?13E0aMz^6m4rL40nZ{FeMm6MUP=r+9jcJ_?2sq~nufhiPmy7(Hf zHeBZ@{;r@8b1h#5>OH+(xdC};ge6D%T474hZ3MS{A=eVF;Vf5wUtG^8z~HM{asmz{ z#pkqqOc=qxdE6nNA>RDk^zPOC<$D1L!iiO%7I1<)nbqiLlG(TJg?Pq51J5kmzhMzh aE(qm1*FxAyW%B*48i86h{Z~6W65=0UpGgn^ delta 20 bcmbQ7(HFm=PmhslvZC>a$p*%}ObiSFP|^l8 diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index 2fa6f7253c46a2c30c83e7109a3b8c9c5ed75650..bf3a28492f4ab8495330340596b4fe3802c805d1 100644 GIT binary patch delta 1162 zcmZuwzi$#@7{0fh*BnPZspn^E2b9Frff9l>;gXiLNmETUu-FCDjom-tf{STfBwea5 z5CIr3>=8w^GJ%JFuYKd;Xo9Fa#T9I`%gpg*p(d{(e zqmj*FSFfze#D?XS`Y*WnL)_Je`yB1WnvT_fEVdOIsGeDQZYX6_<-c{!eK|ce)N+ej z^y()6iWElC21Vn>+~b3s*dm%l2dc4D{zRe^Dc<6G&vcWOEZRL)8Hf`AiNG}K>S(O= z=njC0ocEEYH3KHZYua!h&EAo?U{q8$t>yuiM0HD_MB5p(U5|3$Ty0u|BJVU%{q6B~ z8b98}N_gv$S7L66y0PiDo8+%PH4nf7fIBfa+07e{Qhsf5>oc>m24E_`rPNN0yZ1?w zOklBLwKH6Q4w@KILC?`Lm_=nx^GXY}>p5ZnjPN|wCsJ&dUAFPy5@6cDuGM&c&#T>Md;pL|z?}cgFXmxu3f01bT!dV6Q-ckPf+)w9cNmeUF!yPDakW+wdORIWWqW)MKUwrjW!L%GV`l z!*`D4?;83r*Wy*6KG55j8<1B)X-h$@)J5_egC3 delta 14 Wcmex5i}Axm#tpH$n;#iYmIeSfNe00H