diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 9f64db300..64a94e144 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -28,6 +28,9 @@ #include "dbEdgeBoolean.h" #include "dbCellMapping.h" #include "dbLayoutUtils.h" +#include "dbLocalOperation.h" +#include "dbHierProcessor.h" +#include "dbEmptyEdges.h" namespace db { @@ -506,10 +509,42 @@ EdgesDelegate *DeepEdges::merged () const return res.release (); } +DeepLayer +DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const +{ + DeepLayer dl_out (m_deep_layer.derived ()); + + db::EdgeBoolAndOrNotLocalOperation op (and_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 ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + 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 ()); + + return dl_out; +} + EdgesDelegate *DeepEdges::and_with (const Edges &other) const { - // TODO: implement - return AsIfFlatEdges::and_with (other); + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (! other_deep) { + + return AsIfFlatEdges::and_with (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, true)); + + } } EdgesDelegate *DeepEdges::and_with (const Region &other) const @@ -520,8 +555,27 @@ EdgesDelegate *DeepEdges::and_with (const Region &other) const EdgesDelegate *DeepEdges::not_with (const Edges &other) const { - // TODO: implement - return AsIfFlatEdges::not_with (other); + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, false)); + + } } EdgesDelegate *DeepEdges::not_with (const Region &other) const @@ -532,14 +586,39 @@ EdgesDelegate *DeepEdges::not_with (const Region &other) const EdgesDelegate *DeepEdges::xor_with (const Edges &other) const { - // TODO: implement - return AsIfFlatEdges::xor_with (other); + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return other.delegate ()->clone (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::xor_with (other); + + } else { + + // 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)); + + n1.add_from (n2); + return new DeepEdges (n1); + + } } EdgesDelegate *DeepEdges::or_with (const Edges &other) const { - // TODO: implement - return AsIfFlatEdges::or_with (other); + // NOTE: in the hierarchical case we don't do a merge on "or": just map to add + return add (other); } EdgesDelegate * diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 1b4c448ab..8189f5d20 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -169,6 +169,7 @@ private: void init (); void ensure_merged_edges_valid () const; + DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const; EdgePairs 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; }; diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 7d87c0b85..6ac7341d0 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -29,6 +29,7 @@ #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" #include "dbLocalOperationUtils.h" +#include "dbEdgeBoolean.h" #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" @@ -173,5 +174,72 @@ std::string SelfOverlapMergeLocalOperation::description () const return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); } +// --------------------------------------------------------------------------------------------- +// EdgeBoolAndOrNotLocalOperation implementation + +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +local_operation::on_empty_intruder_mode +EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return m_is_and ? local_operation::Drop : local_operation::Copy; +} + +std::string +EdgeBoolAndOrNotLocalOperation::description () const +{ + return m_is_and ? tl::to_string (tr ("Edge AND operation")) : tl::to_string (tr ("Edge NOT operation")); +} + +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); + + 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)); + } + } + + bool any_subject = false; + + 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) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (subject); + } + } else { + scanner.insert (&subject, 0); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } +} + } diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index fb128a103..294a29f3e 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -144,6 +144,26 @@ private: unsigned int m_wrap_count; }; +/** + * @brief Implements a boolean AND or NOT operation between edges + */ +class DB_PUBLIC EdgeBoolAndOrNotLocalOperation + : public local_operation +{ +public: + EdgeBoolAndOrNotLocalOperation (bool is_and); + + 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; + virtual std::string description () const; + + // edge interaction distance is 1 to force overlap between edges and edge/boxes + virtual db::Coord dist () const { return 1; } + +private: + bool m_is_and; +}; + } #endif diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 79372852d..a3f518232 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -25,6 +25,7 @@ #include "dbReader.h" #include "dbTestSupport.h" #include "dbEdges.h" +#include "dbRegion.h" #include "dbDeepShapeStore.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -106,3 +107,44 @@ TEST(2_MergeEdges) db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au2.gds"); } +TEST(3_Edge2EdgeBooleans) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r2and3 = r2 & r3; + + db::Edges e3 = r3.edges (); + db::Edges e2and3 = r2and3.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + 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); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au3.gds"); +} + diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds new file mode 100644 index 000000000..ae59b5807 Binary files /dev/null and b/testdata/algo/deep_edges_au3.gds differ