diff --git a/.gitignore b/.gitignore index 6a41d55a6..0d4a242a0 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ build-* bin-* mkqtdecl.tmp mkqtdecl5.tmp +mkqtdecl6.tmp testtmp *build.macos* *bin.macos* diff --git a/src/db/db/db.pro b/src/db/db/db.pro index dc634cd78..a65043b30 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -29,6 +29,7 @@ SOURCES = \ dbEdgePairs.cc \ dbEdgeProcessor.cc \ dbEdges.cc \ + dbEdgesLocalOperations.cc \ dbFillTool.cc \ dbFuzzyCellMapping.cc \ dbGenericShapeIterator.cc \ @@ -243,6 +244,7 @@ HEADERS = \ dbEdgePairs.h \ dbEdgeProcessor.h \ dbEdges.h \ + dbEdgesLocalOperations.h \ dbEdgesToContours.h \ dbFillTool.h \ dbFuzzyCellMapping.h \ @@ -260,6 +262,7 @@ HEADERS = \ dbLayoutLayers.h \ dbLayoutQuery.h \ dbLayoutStateModel.h \ + dbLayoutToNetlistEnums.h \ dbLayoutUtils.h \ dbLibrary.h \ dbLibraryManager.h \ @@ -288,6 +291,7 @@ HEADERS = \ dbPolygonTools.h \ dbPolygonGenerators.h \ dbPropertiesRepository.h \ + dbPropertyConstraint.h \ dbReader.h \ dbRecursiveInstanceIterator.h \ dbRecursiveShapeIterator.h \ @@ -372,6 +376,7 @@ HEADERS = \ dbRegionUtils.h \ dbEdgesUtils.h \ dbRegionProcessors.h \ + gsiDeclDbContainerHelpers.h \ gsiDeclDbHelpers.h \ dbNetlistCompare.h \ dbNetlistReader.h \ diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index 0a1315593..0508981fb 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -151,6 +151,7 @@ RegionDelegate * AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const { std::unique_ptr region (new FlatRegion ()); + db::PropertyMapper pm (region->properties_repository (), properties_repository ()); if (filter.result_must_not_be_merged ()) { region->set_merged_semantics (false); @@ -162,7 +163,12 @@ AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase & res_polygons.clear (); filter.process (*e, res_polygons); for (std::vector::const_iterator pr = res_polygons.begin (); pr != res_polygons.end (); ++pr) { - region->insert (*pr); + db::properties_id_type prop_id = pm (e.prop_id ()); + if (prop_id != 0) { + region->insert (db::PolygonWithProperties (*pr, prop_id)); + } else { + region->insert (*pr); + } } } @@ -173,6 +179,7 @@ EdgesDelegate * AsIfFlatEdgePairs::processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const { std::unique_ptr edges (new FlatEdges ()); + db::PropertyMapper pm (edges->properties_repository (), properties_repository ()); if (filter.result_must_not_be_merged ()) { edges->set_merged_semantics (false); @@ -184,7 +191,12 @@ AsIfFlatEdgePairs::processed_to_edges (const EdgePairToEdgeProcessorBase &filter res_edges.clear (); filter.process (*e, res_edges); for (std::vector::const_iterator pr = res_edges.begin (); pr != res_edges.end (); ++pr) { - edges->insert (*pr); + db::properties_id_type prop_id = pm (e.prop_id ()); + if (prop_id != 0) { + edges->insert (db::EdgeWithProperties (*pr, prop_id)); + } else { + edges->insert (*pr); + } } } @@ -195,10 +207,16 @@ EdgePairsDelegate * AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const { std::unique_ptr new_edge_pairs (new FlatEdgePairs ()); + db::PropertyMapper pm (new_edge_pairs->properties_repository (), properties_repository ()); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { if (filter.selected (*p)) { - new_edge_pairs->insert (*p); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id != 0) { + new_edge_pairs->insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + new_edge_pairs->insert (*p); + } } } @@ -209,11 +227,17 @@ RegionDelegate * AsIfFlatEdgePairs::polygons (db::Coord e) const { std::unique_ptr output (new FlatRegion ()); + db::PropertyMapper pm (output->properties_repository (), properties_repository ()); for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { db::Polygon poly = ep->normalized ().to_polygon (e); if (poly.vertices () >= 3) { - output->insert (poly); + db::properties_id_type prop_id = pm (ep.prop_id ()); + if (prop_id != 0) { + output->insert (db::PolygonWithProperties (poly, prop_id)); + } else { + output->insert (poly); + } } } @@ -224,10 +248,17 @@ EdgesDelegate * AsIfFlatEdgePairs::edges () const { std::unique_ptr output (new FlatEdges ()); + db::PropertyMapper pm (output->properties_repository (), properties_repository ()); for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { - output->insert (ep->first ()); - output->insert (ep->second ()); + db::properties_id_type prop_id = pm (ep.prop_id ()); + if (prop_id != 0) { + output->insert (db::EdgeWithProperties (ep->first (), prop_id)); + output->insert (db::EdgeWithProperties (ep->second (), prop_id)); + } else { + output->insert (ep->first ()); + output->insert (ep->second ()); + } } return output.release (); @@ -237,9 +268,15 @@ EdgesDelegate * AsIfFlatEdgePairs::first_edges () const { std::unique_ptr output (new FlatEdges ()); + db::PropertyMapper pm (output->properties_repository (), properties_repository ()); for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { - output->insert (ep->first ()); + db::properties_id_type prop_id = pm (ep.prop_id ()); + if (prop_id != 0) { + output->insert (db::EdgeWithProperties (ep->first (), prop_id)); + } else { + output->insert (ep->first ()); + } } return output.release (); @@ -249,9 +286,15 @@ EdgesDelegate * AsIfFlatEdgePairs::second_edges () const { std::unique_ptr output (new FlatEdges ()); + db::PropertyMapper pm (output->properties_repository (), properties_repository ()); for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { - output->insert (ep->second ()); + db::properties_id_type prop_id = pm (ep.prop_id ()); + if (prop_id != 0) { + output->insert (db::EdgeWithProperties (ep->second (), prop_id)); + } else { + output->insert (ep->second ()); + } } return output.release (); @@ -260,18 +303,25 @@ AsIfFlatEdgePairs::second_edges () const EdgePairsDelegate * AsIfFlatEdgePairs::add (const EdgePairs &other) const { - FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + const FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { std::unique_ptr new_edge_pairs (new FlatEdgePairs (*other_flat)); new_edge_pairs->invalidate_cache (); + db::PropertyMapper pm (new_edge_pairs->properties_repository (), properties_repository ()); + size_t n = new_edge_pairs->raw_edge_pairs ().size () + count (); new_edge_pairs->reserve (n); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - new_edge_pairs->raw_edge_pairs ().insert (*p); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id) { + new_edge_pairs->raw_edge_pairs ().insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } } return new_edge_pairs.release (); @@ -280,15 +330,28 @@ AsIfFlatEdgePairs::add (const EdgePairs &other) const std::unique_ptr new_edge_pairs (new FlatEdgePairs ()); + db::PropertyMapper pm (new_edge_pairs->properties_repository (), properties_repository ()); + db::PropertyMapper pm_other (new_edge_pairs->properties_repository (), &other.properties_repository ()); + size_t n = count () + other.count (); new_edge_pairs->reserve (n); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - new_edge_pairs->raw_edge_pairs ().insert (*p); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id) { + new_edge_pairs->raw_edge_pairs ().insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } } for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { - new_edge_pairs->raw_edge_pairs ().insert (*p); + db::properties_id_type prop_id = pm_other (p.prop_id ()); + if (prop_id) { + new_edge_pairs->raw_edge_pairs ().insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } } return new_edge_pairs.release (); @@ -344,9 +407,16 @@ AsIfFlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, u // improves performance when inserting an original layout into the same layout db::LayoutLocker locker (layout); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { - shapes.insert (*e); + db::properties_id_type prop_id = pm (e.prop_id ()); + if (prop_id) { + shapes.insert (db::EdgePairWithProperties (*e, prop_id)); + } else { + shapes.insert (*e); + } } } @@ -356,9 +426,16 @@ AsIfFlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type // improves performance when inserting an original layout into the same layout db::LayoutLocker locker (layout); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { - shapes.insert (e->normalized ().to_simple_polygon (enl)); + db::properties_id_type prop_id = pm (e.prop_id ()); + if (prop_id) { + shapes.insert (db::SimplePolygonWithProperties (e->normalized ().to_simple_polygon (enl), prop_id)); + } else { + shapes.insert (e->normalized ().to_simple_polygon (enl)); + } } } diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 7d9e8832f..071e0eeda 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -104,7 +104,7 @@ AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractio db::box_scanner2 scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + AddressableEdgeDelivery e (begin_merged ()); for ( ; ! e.at_end (); ++e) { scanner.insert1 (e.operator-> (), 0); @@ -150,7 +150,7 @@ AsIfFlatEdges::selected_interacting_generic (const Edges &edges, EdgeInteraction db::box_scanner scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + AddressableEdgeDelivery e (begin_merged ()); for ( ; ! e.at_end (); ++e) { scanner.insert (e.operator-> (), 0); @@ -201,7 +201,7 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInte db::box_scanner2 scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + AddressableEdgeDelivery e (begin_merged ()); for ( ; ! e.at_end (); ++e) { scanner.insert1 (e.operator-> (), 0); @@ -245,7 +245,7 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeIntera db::box_scanner scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + AddressableEdgeDelivery e (begin_merged ()); for ( ; ! e.at_end (); ++e) { scanner.insert (e.operator-> (), 0); @@ -281,7 +281,7 @@ AsIfFlatEdges::pull_generic (const Edges &edges) const { db::box_scanner scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin (), has_valid_edges ()); + AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { scanner.insert (e.operator-> (), 1); @@ -310,7 +310,7 @@ AsIfFlatEdges::pull_generic (const Region &other) const db::box_scanner2 scanner (report_progress (), progress_desc ()); - AddressableEdgeDelivery e (begin (), true); + AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { scanner.insert1 (e.operator-> (), 0); @@ -480,6 +480,8 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c { if (join) { + // TODO: property support? + std::unique_ptr output (new FlatRegion ()); db::ShapeGenerator sg (output->raw_polygons (), false); JoinEdgesClusterCollector cluster_collector (&sg, ext_b, ext_e, ext_o, ext_i); @@ -487,7 +489,7 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (count ()); - AddressableEdgeDelivery e (begin (), has_valid_edges ()); + AddressableEdgeDelivery e (begin ()); size_t n = 0; for ( ; ! e.at_end (); ++e) { @@ -502,8 +504,15 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c } else { std::unique_ptr output (new FlatRegion ()); + db::PropertyMapper pm (output->properties_repository (), properties_repository ()); + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { - output->insert (extended_edge (*e, ext_b, ext_e, ext_o, ext_i)); + db::properties_id_type prop_id = pm (e.prop_id ()); + if (prop_id != 0) { + output->insert (db::PolygonWithProperties (extended_edge (*e, ext_b, ext_e, ext_o, ext_i), prop_id)); + } else { + output->insert (extended_edge (*e, ext_b, ext_e, ext_o, ext_i)); + } } return output.release (); @@ -733,7 +742,7 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (count () + (other ? other->count () : 0)); - AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + AddressableEdgeDelivery e (begin_merged ()); size_t n = 0; for ( ; ! e.at_end (); ++e) { @@ -774,7 +783,7 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (count () + (other ? other->count () : 0)); - AddressableEdgeDelivery e (begin (), has_valid_edges ()); + AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { if (! e->is_degenerate ()) { @@ -808,7 +817,7 @@ AsIfFlatEdges::boolean_andnot (const Edges *other) const db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (count () + (other ? other->count () : 0)); - AddressableEdgeDelivery e (begin (), has_valid_edges ()); + AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { if (! e->is_degenerate ()) { @@ -876,7 +885,7 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo EdgesDelegate * AsIfFlatEdges::add (const Edges &other) const { - FlatEdges *other_flat = dynamic_cast (other.delegate ()); + const FlatEdges *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { std::unique_ptr new_edges (new FlatEdges (*other_flat)); @@ -960,10 +969,15 @@ AsIfFlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsig { // improves performance when inserting an original layout into the same layout db::LayoutLocker locker (layout); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - shapes.insert (*e); + if (e.prop_id () != 0) { + shapes.insert (db::EdgeWithProperties (*e, pm (e.prop_id ()))); + } else { + shapes.insert (*e); + } } } diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 6cf9d2180..11e73333e 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -40,11 +40,10 @@ #include "dbRegionLocalOperations.h" #include "dbHierProcessor.h" #include "dbCompoundOperation.h" +#include "dbLayoutToNetlist.h" #include -#define USE_LOCAL_PROCESSOR // comment out for original implementation based on a single scan - namespace db { @@ -76,6 +75,23 @@ private: } +static +RegionDelegate *region_from_box (const db::Box &b, const db::PropertiesRepository *pr, db::properties_id_type prop_id) +{ + if (! b.empty () && b.width () > 0 && b.height () > 0) { + FlatRegion *new_region = new FlatRegion (); + if (prop_id != 0) { + db::PropertyMapper pm (const_cast (new_region->properties_repository ()), pr); + new_region->insert (db::BoxWithProperties (b, pm (prop_id))); + } else { + new_region->insert (b); + } + return new_region; + } else { + return new EmptyRegion (); + } +} + // ------------------------------------------------------------------------------------------------------------- // AsIfFlagRegion implementation @@ -130,6 +146,7 @@ EdgesDelegate * AsIfFlatRegion::edges (const EdgeFilterBase *filter) const { std::unique_ptr result (new FlatEdges ()); + db::PropertyMapper pm (result->properties_repository (), properties_repository ()); size_t n = 0; for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { @@ -138,9 +155,14 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const result->reserve (n); for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::properties_id_type prop_id = p.prop_id (); for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { if (! filter || filter->selected (*e)) { - result->insert (*e); + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } } } } @@ -260,6 +282,97 @@ void AsIfFlatRegion::invalidate_bbox () m_bbox_valid = false; } +void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence, unsigned int min_wc, db::PropertiesRepository *target_rp) const +{ + db::PropertyMapper pm (target_rp, properties_repository ()); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + db::properties_id_type prop_id = 0; + bool need_split_props = false; + for (RegionIterator s (begin ()); ! s.at_end (); ++s, ++n) { + if (n == 0) { + prop_id = s.prop_id (); + } else if (! need_split_props && prop_id != s.prop_id ()) { + need_split_props = true; + } + } + + if (need_split_props) { + + db::Shapes result (output.is_editable ()); + + std::vector > polygons_by_prop_id; + polygons_by_prop_id.reserve (n); + + db::AddressablePolygonDelivery addressable_polygons (begin ()); + while (! addressable_polygons.at_end ()) { + polygons_by_prop_id.push_back (std::make_pair (addressable_polygons.prop_id (), addressable_polygons.operator-> ())); + addressable_polygons.inc (); + } + + std::sort (polygons_by_prop_id.begin (), polygons_by_prop_id.end ()); + + for (auto p = polygons_by_prop_id.begin (); p != polygons_by_prop_id.end (); ) { + + auto pp = p; + while (pp != polygons_by_prop_id.end () && pp->first == p->first) { + ++pp; + } + + ep.clear (); + + n = 0; + for (auto i = p; i != pp; ++i) { + n += i->second->vertices (); + } + ep.reserve (n); + + n = 0; + for (auto i = p; i != pp; ++i, ++n) { + ep.insert (*i->second, n); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (result, false /*don't clear*/, pm (p->first)); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + p = pp; + + } + + output.swap (result); + + } else { + + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + output.clear (); + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (output, false /*don't clear*/, pm (prop_id)); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + } +} + RegionDelegate * AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const { @@ -271,6 +384,7 @@ AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const } } + new_region->set_is_merged (true); return new_region.release (); } @@ -289,7 +403,11 @@ AsIfFlatRegion::processed (const PolygonProcessorBase &filter) const poly_res.clear (); filter.process (*p, poly_res); for (std::vector::const_iterator pr = poly_res.begin (); pr != poly_res.end (); ++pr) { - new_region->insert (*pr); + if (p.prop_id () != 0) { + new_region->insert (db::PolygonWithProperties (*pr, p.prop_id ())); + } else { + new_region->insert (*pr); + } } } @@ -312,7 +430,11 @@ AsIfFlatRegion::processed_to_edges (const PolygonToEdgeProcessorBase &filter) co edge_res.clear (); filter.process (*p, edge_res); for (std::vector::const_iterator er = edge_res.begin (); er != edge_res.end (); ++er) { - new_edges->insert (*er); + if (p.prop_id () != 0) { + new_edges->insert (db::EdgeWithProperties (*er, p.prop_id ())); + } else { + new_edges->insert (*er); + } } } @@ -335,7 +457,11 @@ AsIfFlatRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &f edge_pair_res.clear (); filter.process (*p, edge_pair_res); for (std::vector::const_iterator epr = edge_pair_res.begin (); epr != edge_pair_res.end (); ++epr) { - new_edge_pairs->insert (*epr); + if (p.prop_id () != 0) { + new_edge_pairs->insert (db::EdgePairWithProperties (*epr, p.prop_id ())); + } else { + new_edge_pairs->insert (*epr); + } } } @@ -598,8 +724,6 @@ AsIfFlatRegion::pull_generic (const Edges &other) const return new EmptyEdges (); } -#if defined(USE_LOCAL_PROCESSOR) - db::RegionIterator polygons (begin ()); db::pull_with_edge_local_operation op; @@ -619,39 +743,11 @@ AsIfFlatRegion::pull_generic (const Edges &other) const proc.run_flat (polygons, others, std::vector (), &op, results); return output.release (); - -#else - - db::box_scanner2 scanner (report_progress (), progress_desc ()); - scanner.reserve1 (count ()); - scanner.reserve2 (other.count ()); - - std::unique_ptr output (new FlatEdges (false)); - region_to_edge_interaction_filter filter (output->raw_edges (), false); - - AddressablePolygonDelivery p (begin ()); - - for ( ; ! p.at_end (); ++p) { - scanner.insert1 (p.operator-> (), 0); - } - - AddressableEdgeDelivery e (other.addressable_merged_edges ()); - - for ( ; ! e.at_end (); ++e) { - scanner.insert2 (e.operator-> (), 0); - } - - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - return output.release (); -#endif } TextsDelegate * AsIfFlatRegion::pull_generic (const Texts &other) const { -#if defined(USE_LOCAL_PROCESSOR) - db::RegionIterator polygons (begin ()); db::pull_with_text_local_operation op; @@ -671,44 +767,11 @@ AsIfFlatRegion::pull_generic (const Texts &other) const proc.run_flat (polygons, others, std::vector (), &op, results); return output.release (); - -#else - if (other.empty ()) { - return other.delegate ()->clone (); - } else if (empty ()) { - return new EmptyTexts (); - } - - db::box_scanner2 scanner (report_progress (), progress_desc ()); - scanner.reserve1 (count ()); - scanner.reserve2 (other.count ()); - - std::unique_ptr output (new FlatTexts (false)); - region_to_text_interaction_filter filter (output->raw_texts (), false); - - AddressablePolygonDelivery p (begin ()); - - for ( ; ! p.at_end (); ++p) { - scanner.insert1 (p.operator-> (), 0); - } - - AddressableTextDelivery e (other.addressable_texts ()); - - for ( ; ! e.at_end (); ++e) { - scanner.insert2 (e.operator-> (), 0); - } - - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - return output.release (); -#endif } RegionDelegate * AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) const { -#if defined(USE_LOCAL_PROCESSOR) - db::RegionIterator polygons (begin ()); db::pull_local_operation op (mode, touching); @@ -728,63 +791,14 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons proc.run_flat (polygons, others, std::vector (), &op, results); return output.release (); - -#else - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // shortcut - if (empty ()) { - return clone (); - } else if (other.empty ()) { - return new EmptyRegion (); - } - - size_t n = 1; - for (RegionIterator p = other.begin_merged (); ! p.at_end (); ++p, ++n) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, n); - } - } - - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, 0); - } - } - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - std::unique_ptr output (new FlatRegion (false)); - - n = 0; - std::set selected; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - ++n; - selected.insert (i->second); - } - - output->reserve (n); - - n = 1; - for (RegionIterator p = other.begin_merged (); ! p.at_end (); ++p, ++n) { - if (selected.find (n) != selected.end ()) { - output->raw_polygons ().insert (*p); - } - } - - return output.release (); -#endif } template void AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes) { + Trans tr_inv = tr.inverted (); + gx = std::max (db::Coord (1), gx); gy = std::max (db::Coord (1), gy); @@ -803,7 +817,7 @@ AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const T for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { db::Point p = tr * *pt; if ((p.x () % gx) != 0 || (p.y () % gy) != 0) { - shapes.insert (EdgePair (db::Edge (p, p), db::Edge (p, p))); + shapes.insert (EdgePair (db::Edge (p, p), db::Edge (p, p)).transformed (tr_inv)); } } @@ -910,7 +924,7 @@ AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) throw tl::Exception (tl::to_string (tr ("Grid snap requires a positive grid value"))); } - std::unique_ptr new_region (new FlatRegion (merged_semantics ())); + std::unique_ptr new_region (new FlatRegion ()); gx = std::max (db::Coord (1), gx); gy = std::max (db::Coord (1), gy); @@ -980,27 +994,75 @@ void region_cop_impl (AsIfFlatRegion *region, db::Shapes *output_to, db::Compoun proc.run_flat (polygons, others, foreign, &op, results); } +template +static +void region_cop_with_properties_impl (AsIfFlatRegion *region, db::Shapes *output_to, db::PropertiesRepository *target_pr, db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) +{ + db::local_processor > proc; + proc.set_base_verbosity (region->base_verbosity ()); + proc.set_description (region->progress_desc ()); + proc.set_report_progress (region->report_progress ()); + + db::generic_shape_iterator polygons (db::make_wp_iter (region->begin_merged ())); + + std::vector intruder_prs; + const db::PropertiesRepository *subject_pr = region->properties_repository (); + + std::vector > others; + std::vector foreign; + std::vector inputs = node.inputs (); + for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) { + if (*i == subject_regionptr () || *i == foreign_regionptr ()) { + others.push_back (db::make_wp_iter (region->begin_merged ())); + foreign.push_back (*i == foreign_regionptr ()); + intruder_prs.push_back (subject_pr); + } else { + others.push_back (db::make_wp_iter ((*i)->begin ())); + foreign.push_back (false); + intruder_prs.push_back (&(*i)->properties_repository ()); + } + } + + std::vector results; + results.push_back (output_to); + + compound_local_operation_with_properties op (&node, prop_constraint, target_pr, subject_pr, intruder_prs); + proc.run_flat (polygons, others, foreign, &op, results); +} + EdgePairsDelegate * -AsIfFlatRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &node) +AsIfFlatRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { std::unique_ptr output (new FlatEdgePairs ()); - region_cop_impl (this, &output->raw_edge_pairs (), node); + if (pc_skip (prop_constraint)) { + region_cop_impl (this, &output->raw_edge_pairs (), node); + } else { + region_cop_with_properties_impl (this, &output->raw_edge_pairs (), output->properties_repository (), node, prop_constraint); + } return output.release (); } RegionDelegate * -AsIfFlatRegion::cop_to_region (db::CompoundRegionOperationNode &node) +AsIfFlatRegion::cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { std::unique_ptr output (new FlatRegion ()); - region_cop_impl (this, &output->raw_polygons (), node); + if (pc_skip (prop_constraint)) { + region_cop_impl (this, &output->raw_polygons (), node); + } else { + region_cop_with_properties_impl (this, &output->raw_polygons (), output->properties_repository (), node, prop_constraint); + } return output.release (); } EdgesDelegate * -AsIfFlatRegion::cop_to_edges (db::CompoundRegionOperationNode &node) +AsIfFlatRegion::cop_to_edges (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint) { std::unique_ptr output (new FlatEdges ()); - region_cop_impl (this, &output->raw_edges (), node); + if (pc_skip (prop_constraint)) { + region_cop_impl (this, &output->raw_edges (), node); + } else { + region_cop_with_properties_impl (this, &output->raw_edges (), output->properties_repository (), node, prop_constraint); + } return output.release (); } @@ -1067,7 +1129,10 @@ AsIfFlatRegion::inside_check (const Region &other, db::Coord d, const RegionChec EdgePairsDelegate * AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const { -#if defined(USE_LOCAL_PROCESSOR) + // force different polygons in the different properties case to skip intra-polygon checks + if (pc_always_different (options.prop_constraint)) { + different_polygons = true; + } bool needs_merged_primary = different_polygons || options.needs_merged (); @@ -1081,11 +1146,6 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, check.set_min_projection (options.min_projection); check.set_max_projection (options.max_projection); - db::local_processor proc; - proc.set_base_verbosity (base_verbosity ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - std::vector > others; std::vector foreign; bool has_other = false; @@ -1098,6 +1158,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, } else { foreign.push_back (false); if (! other->merged_semantics ()) { + others.push_back (other->begin ()); other_is_merged = true; } else if (options.whole_edges) { // NOTE: whole edges needs both inputs merged @@ -1110,69 +1171,51 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, has_other = true; } - db::check_local_operation op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); - std::unique_ptr output (new FlatEdgePairs ()); + std::vector results; results.push_back (&output->raw_edge_pairs ()); - proc.run_flat (polygons, others, foreign, &op, results); + if (pc_skip (options.prop_constraint)) { - return output.release (); + db::check_local_operation op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); -#else - // not supported in this implementation - tl_assert (! m_options.no_opposite); + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - std::unique_ptr result (new FlatEdgePairs ()); + proc.run_flat (polygons, others, foreign, &op, results); - db::box_scanner scanner (report_progress (), progress_desc ()); - scanner.reserve (count () + (other ? other->count () : 0)); + } else { - AddressablePolygonDelivery p (begin_merged ()); + const db::PropertiesRepository *subject_pr = properties_repository (); + const db::PropertiesRepository *intruder_pr = (other == subject_regionptr () || other == foreign_regionptr ()) ? subject_pr : &other->properties_repository (); - size_t n = 0; - for ( ; ! p.at_end (); ++p) { - scanner.insert (p.operator-> (), n); - n += 2; - } + db::check_local_operation_with_properties op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options, output->properties_repository (), subject_pr, intruder_pr); - AddressablePolygonDelivery po; + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - if (other) { - - po = other->addressable_merged_polygons (); - - n = 1; - for ( ; ! po.at_end (); ++po) { - scanner.insert (po.operator-> (), n); - n += 2; + std::vector > others_wp; + for (auto o = others.begin (); o != others.end (); ++o) { + others_wp.push_back (db::make_wp_iter (std::move (*o))); } + proc.run_flat (db::make_wp_iter (std::move (polygons)), others_wp, foreign, &op, results); + } - EdgeRelationFilter check (rel, d, options.metrics); - check.set_include_zero (false); - check.set_whole_edges (options.whole_edges); - check.set_ignore_angle (options.ignore_angle); - check.set_min_projection (options.min_projection); - check.set_max_projection (options.max_projection); - - edge2edge_check_negative_or_positive edge_check (check, *result, options.negative, different_polygons, other != 0 /*requires different layers*/, options.shielded); - poly2poly_check poly_check (edge_check); - - do { - scanner.process (poly_check, d, db::box_convert ()); - } while (edge_check.prepare_next_pass ()); - - return result.release (); -#endif + return output.release (); } EdgePairsDelegate * AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, const RegionCheckOptions &options) const { std::unique_ptr result (new FlatEdgePairs ()); + db::PropertyMapper pm (result->properties_repository (), properties_repository ()); EdgeRelationFilter check (rel, d, options.metrics); check.set_include_zero (false); @@ -1181,18 +1224,16 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord check.set_min_projection (options.min_projection); check.set_max_projection (options.max_projection); - edge2edge_check_negative_or_positive edge_check (check, *result, options.negative, false /*=same polygons*/, false /*=same layers*/, options.shielded, true /*symmetric edge pairs*/); - poly2poly_check poly_check (edge_check); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - do { + edge2edge_check_negative_or_positive edge_check (check, result->raw_edge_pairs (), options.negative, false /*=same polygons*/, false /*=same layers*/, options.shielded, true /*symmetric edge pairs*/, pc_remove (options.prop_constraint) ? 0 : pm (p.prop_id ())); + poly2poly_check poly_check (edge_check); - size_t n = 0; - for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - poly_check.single (*p, n); - n += 2; - } + do { + poly_check.single (*p, 0); + } while (edge_check.prepare_next_pass ()); - } while (edge_check.prepare_next_pass ()); + } return result.release (); } @@ -1215,47 +1256,14 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const } else { - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - std::unique_ptr new_region (new FlatRegion (true)); - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); + merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, new_region->properties_repository ()); return new_region.release (); } } -RegionDelegate * -AsIfFlatRegion::region_from_box (const db::Box &b) -{ - if (! b.empty () && b.width () > 0 && b.height () > 0) { - FlatRegion *new_region = new FlatRegion (); - new_region->insert (b); - return new_region; - } else { - return new EmptyRegion (); - } -} - RegionDelegate * AsIfFlatRegion::sized (coord_type d, unsigned int mode) const { @@ -1274,24 +1282,36 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const // simplified handling for a box db::Box b = bbox ().enlarged (db::Vector (dx, dy)); - return region_from_box (b); + return region_from_box (b, properties_repository (), begin ()->prop_id ()); } else if (! merged_semantics () || is_merged ()) { // Generic case - std::unique_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + std::unique_ptr new_region (new FlatRegion ()); + db::PropertyMapper pm (new_region->properties_repository (), properties_repository ()); db::ShapeGenerator pc (new_region->raw_polygons (), false); db::PolygonGenerator pg (pc, false, true); db::SizingPolygonFilter sf (pg, dx, dy, mode); for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + pc.set_prop_id (pm (p.prop_id ())); sf.put (*p); } + // in case of negative sizing the output polygons will still be merged (on positive sizing they might + // overlap after size and are not necessarily merged) + if (dx < 0 && dy < 0 && is_merged ()) { + new_region->set_is_merged (true); + } + return new_region.release (); } else { + std::unique_ptr new_region (new FlatRegion ()); + +// old implementation without property support +#if 0 // Generic case - the size operation will merge first db::EdgeProcessor ep (report_progress (), progress_desc ()); ep.set_base_verbosity (base_verbosity ()); @@ -1309,13 +1329,32 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const ep.insert (*p, n); } - std::unique_ptr new_region (new FlatRegion (false /*output isn't merged*/)); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); db::SizingPolygonFilter siz (pg2, dx, dy, mode); db::PolygonGenerator pg (siz, false /*don't resolve holes*/, min_coherence () /*min. coherence*/); db::BooleanOp op (db::BooleanOp::Or); ep.process (pg, op); +#else + + // Generic case + db::PropertyMapper pm (new_region->properties_repository (), properties_repository ()); + + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, true); + db::SizingPolygonFilter sf (pg, dx, dy, mode); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + pc.set_prop_id (pm (p.prop_id ())); + sf.put (*p); + } + +#endif + + // in case of negative sizing the output polygons will still be merged (on positive sizing they might + // overlap after size and are not necessarily merged) + if (dx < 0 && dy < 0 && merged_semantics ()) { + new_region->set_is_merged (true); + } return new_region.release (); @@ -1323,7 +1362,7 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -AsIfFlatRegion::and_with (const Region &other) const +AsIfFlatRegion::and_with (const Region &other, PropertyConstraint property_constraint) const { if (empty () || other.empty ()) { @@ -1332,37 +1371,83 @@ AsIfFlatRegion::and_with (const Region &other) const } else if (is_box () && other.is_box ()) { - // Simplified handling for boxes - db::Box b = bbox (); - b &= other.bbox (); - return region_from_box (b); + if (pc_skip (property_constraint) || pc_match (property_constraint, begin ()->prop_id (), other.begin ().prop_id ())) { + + // Simplified handling for boxes + db::Box b = bbox (); + b &= other.bbox (); + + db::properties_id_type prop_id_out = pc_norm (property_constraint, begin ()->prop_id ()); + + return region_from_box (b, properties_repository (), prop_id_out); + + } else { + return new EmptyRegion (); + } } else if (is_box () && ! other.strict_handling ()) { + db::properties_id_type self_prop_id = pc_skip (property_constraint) ? 0 : begin ()->prop_id (); + // map AND with box to clip .. db::Box b = bbox (); std::unique_ptr new_region (new FlatRegion (false)); + db::PropertyMapper pm (new_region->properties_repository (), properties_repository ()); + + db::properties_id_type prop_id_out = pm (pc_norm (property_constraint, self_prop_id)); std::vector clipped; for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + + db::properties_id_type prop_id = p.prop_id (); + if (pc_match (property_constraint, self_prop_id, prop_id)) { + + clipped.clear (); + clip_poly (*p, b, clipped); + + if (prop_id_out == 0) { + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } else { + for (auto i = clipped.begin (); i != clipped.end (); ++i) { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*i, prop_id_out)); + } + } + + } + } return new_region.release (); } else if (other.is_box () && ! strict_handling ()) { + db::properties_id_type other_prop_id = pc_skip (property_constraint) ? 0 : other.begin ().prop_id (); + // map AND with box to clip .. db::Box b = other.bbox (); std::unique_ptr new_region (new FlatRegion (false)); + db::PropertyMapper pm (new_region->properties_repository (), properties_repository ()); std::vector clipped; for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + + db::properties_id_type prop_id = p.prop_id (); + if (pc_match (property_constraint, prop_id, other_prop_id)) { + + clipped.clear (); + clip_poly (*p, b, clipped); + + db::properties_id_type prop_id_out = pm (pc_norm (property_constraint, prop_id)); + if (prop_id_out == 0) { + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } else { + for (auto i = clipped.begin (); i != clipped.end (); ++i) { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*i, prop_id_out)); + } + } + + } + } return new_region.release (); @@ -1373,44 +1458,12 @@ AsIfFlatRegion::and_with (const Region &other) const return new EmptyRegion (); } else { - - // Generic case - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - std::unique_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::And); - db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - - return new_region.release (); - + return and_or_not_with (true, other, property_constraint); } } RegionDelegate * -AsIfFlatRegion::not_with (const Region &other) const +AsIfFlatRegion::not_with (const Region &other, PropertyConstraint property_constraint) const { if (empty ()) { @@ -1420,14 +1473,22 @@ AsIfFlatRegion::not_with (const Region &other) const } else if (other.empty () && ! strict_handling ()) { // Nothing to do - return clone (); + return clone ()->remove_properties (pc_remove (property_constraint)); } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { // Nothing to do - return clone (); + return clone ()->remove_properties (pc_remove (property_constraint)); } else { + return and_or_not_with (false, other, property_constraint); + } +} + +RegionDelegate * +AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConstraint property_constraint) const +{ + if (pc_skip (property_constraint)) { // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); @@ -1454,19 +1515,40 @@ AsIfFlatRegion::not_with (const Region &other) const } std::unique_ptr new_region (new FlatRegion (true)); - db::BooleanOp op (db::BooleanOp::ANotB); + db::BooleanOp op (is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); ep.process (pg, op); return new_region.release (); + } else { + + db::generic_shape_iterator polygons (db::make_wp_iter (begin ())); + + std::unique_ptr output (new FlatRegion ()); + std::vector results; + results.push_back (&output->raw_polygons ()); + + db::bool_and_or_not_local_operation_with_properties op (is_and, output->properties_repository (), properties_repository (), &other.properties_repository (), property_constraint); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + std::vector > others; + others.push_back (db::make_wp_iter (other.begin ())); + + proc.run_flat (polygons, others, std::vector (), &op, results); + + return output.release (); + } } - std::pair -AsIfFlatRegion::andnot_with (const Region &other) const +AsIfFlatRegion::andnot_with (const Region &other, PropertyConstraint property_constraint) const { if (empty ()) { @@ -1476,14 +1558,14 @@ AsIfFlatRegion::andnot_with (const Region &other) const } else if (other.empty () && ! strict_handling ()) { // Nothing to do - return std::make_pair (new EmptyRegion (), clone ()); + return std::make_pair (new EmptyRegion (), clone ()->remove_properties (pc_remove (property_constraint))); } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { // Nothing to do - return std::make_pair (new EmptyRegion (), clone ()); + return std::make_pair (new EmptyRegion (), clone ()->remove_properties (pc_remove (property_constraint))); - } else { + } else if (pc_skip (property_constraint)) { // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); @@ -1526,11 +1608,35 @@ AsIfFlatRegion::andnot_with (const Region &other) const return std::make_pair (new_region1.release (), new_region2.release ()); + } else { + + db::generic_shape_iterator polygons (db::make_wp_iter (begin ())); + + std::unique_ptr output1 (new FlatRegion ()); + std::unique_ptr output2 (new FlatRegion ()); + std::vector results; + results.push_back (&output1->raw_polygons ()); + results.push_back (&output2->raw_polygons ()); + + db::two_bool_and_not_local_operation_with_properties op (output1->properties_repository (), output2->properties_repository (), properties_repository (), &other.properties_repository (), property_constraint); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + std::vector > others; + others.push_back (db::make_wp_iter (other.begin ())); + + proc.run_flat (polygons, others, std::vector (), &op, results); + + return std::make_pair (output1.release (), output2.release ()); + } } RegionDelegate * -AsIfFlatRegion::xor_with (const Region &other) const +AsIfFlatRegion::xor_with (const Region &other, PropertyConstraint prop_constraint) const { if (empty () && ! other.strict_handling ()) { @@ -1543,10 +1649,12 @@ AsIfFlatRegion::xor_with (const Region &other) const } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { // Simplified handling for disjunct case - return or_with (other); + return or_with (other, prop_constraint); } else { + // TODO: implement property constraint + // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); ep.set_base_verbosity (base_verbosity ()); @@ -1583,7 +1691,7 @@ AsIfFlatRegion::xor_with (const Region &other) const } RegionDelegate * -AsIfFlatRegion::or_with (const Region &other) const +AsIfFlatRegion::or_with (const Region &other, PropertyConstraint /*prop_constraint*/) const { if (empty () && ! other.strict_handling ()) { @@ -1601,6 +1709,8 @@ AsIfFlatRegion::or_with (const Region &other) const } else { + // TODO: implement property constraint + // Generic case db::EdgeProcessor ep (report_progress (), progress_desc ()); ep.set_base_verbosity (base_verbosity ()); @@ -1639,7 +1749,7 @@ AsIfFlatRegion::or_with (const Region &other) const RegionDelegate * AsIfFlatRegion::add (const Region &other) const { - FlatRegion *other_flat = dynamic_cast (other.delegate ()); + const FlatRegion *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { std::unique_ptr new_region (new FlatRegion (*other_flat)); @@ -1676,15 +1786,75 @@ AsIfFlatRegion::add (const Region &other) const } } +static void +deliver_shapes_of_nets_recursive (db::Shapes &out, db::PropertiesRepository *pr, const db::Circuit *circuit, const LayoutToNetlist *l2n, const db::Region *of_layer, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const db::ICplxTrans &tr, const std::set *net_filter) +{ + db::CplxTrans dbu_trans (l2n->internal_layout ()->dbu ()); + auto dbu_trans_inv = dbu_trans.inverted (); + + for (auto n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { + + if (! net_filter || net_filter->find (n.operator-> ()) != net_filter->end ()) { + db::properties_id_type prop_id = db::NetBuilder::make_netname_propid (*pr, prop_mode, net_prop_name, *n); + l2n->shapes_of_net (*n, *of_layer, true, out, prop_id, tr); + } + + // dive into subcircuits + for (auto sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { + const db::Circuit *circuit_ref = sc->circuit_ref (); + db::ICplxTrans tr_ref = tr * (dbu_trans_inv * sc->trans () * dbu_trans); + deliver_shapes_of_nets_recursive (out, pr, circuit_ref, l2n, of_layer, prop_mode, net_prop_name, tr_ref, net_filter); + } + + } +} + +RegionDelegate * +AsIfFlatRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *net_filter) const +{ + if (! l2n->is_netlist_extracted ()) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + std::unique_ptr result (new db::FlatRegion ()); + std::unique_ptr region_for_layer (l2n->layer_by_original (this)); + + if (! region_for_layer) { + throw tl::Exception (tl::to_string (tr ("The given layer is not an original layer used in netlist extraction"))); + } + + if (l2n->netlist ()->top_circuit_count () == 0) { + throw tl::Exception (tl::to_string (tr ("No top circuit found in netlist"))); + } else if (l2n->netlist ()->top_circuit_count () > 1) { + throw tl::Exception (tl::to_string (tr ("More than one top circuit found in netlist"))); + } + const db::Circuit *top_circuit = l2n->netlist ()->begin_top_down ().operator-> (); + + std::set net_filter_set; + if (net_filter) { + net_filter_set.insert (net_filter->begin (), net_filter->end ()); + } + + deliver_shapes_of_nets_recursive (result->raw_polygons (), result->properties_repository (), top_circuit, l2n, region_for_layer.get (), prop_mode, net_prop_name, db::ICplxTrans (), net_filter ? &net_filter_set : 0); + + return result.release (); +} + void AsIfFlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { // improves performance when inserting an original layout into the same layout db::LayoutLocker locker (layout); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - shapes.insert (*p); + db::properties_id_type prop_id = p.prop_id (); + if (prop_id != 0) { + shapes.insert (db::PolygonWithProperties (*p, pm (prop_id))); + } else { + shapes.insert (*p); + } } } diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 998730d2b..0473dceff 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -54,9 +54,9 @@ public: virtual std::string to_string (size_t nmax) const; - virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node); - virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node); - virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node); + virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint); + virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint); + virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint); EdgePairsDelegate *width_check (db::Coord d, const RegionCheckOptions &options) const; EdgePairsDelegate *space_check (db::Coord d, const RegionCheckOptions &options) const; @@ -122,11 +122,11 @@ public: virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; - virtual RegionDelegate *and_with (const Region &other) const; - virtual RegionDelegate *not_with (const Region &other) const; - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - virtual std::pair andnot_with (const Region &) const; + virtual RegionDelegate *and_with (const Region &other, PropertyConstraint property_constraint) const; + virtual RegionDelegate *not_with (const Region &other, PropertyConstraint property_constraint) const; + virtual RegionDelegate *xor_with (const Region &other, PropertyConstraint prop_constraint) const; + virtual RegionDelegate *or_with (const Region &other, PropertyConstraint prop_constraint) const; + virtual std::pair andnot_with (const Region &other, PropertyConstraint property_constraint) const; virtual RegionDelegate *add_in_place (const Region &other) { @@ -278,11 +278,15 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; + virtual RegionDelegate *nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *net_filter) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; protected: void update_bbox (const db::Box &box); void invalidate_bbox (); + void merge_polygons_to (db::Shapes &output, bool min_coherence, unsigned int min_wc, PropertiesRepository *target_rp = 0) const; + RegionDelegate *and_or_not_with (bool is_and, const Region &other, PropertyConstraint property_constraint) const; virtual EdgePairsDelegate *run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const; virtual EdgePairsDelegate *run_single_polygon_check (db::edge_relation_type rel, db::Coord d, const RegionCheckOptions &options) const; @@ -308,7 +312,6 @@ private: mutable db::Box m_bbox; virtual db::Box compute_bbox () const; - static RegionDelegate *region_from_box (const db::Box &b); EdgePairsDelegate *space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const; }; diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index 524c684cb..386090cbe 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -179,7 +179,11 @@ AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) res_polygons.clear (); filter.process (*e, res_polygons); for (std::vector::const_iterator pr = res_polygons.begin (); pr != res_polygons.end (); ++pr) { - region->insert (*pr); + if (e.prop_id () != 0) { + region->insert (db::PolygonWithProperties (*pr, e.prop_id ())); + } else { + region->insert (*pr); + } } } @@ -216,7 +220,7 @@ AsIfFlatTexts::edges () const TextsDelegate * AsIfFlatTexts::add (const Texts &other) const { - FlatTexts *other_flat = dynamic_cast (other.delegate ()); + const FlatTexts *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { std::unique_ptr new_texts (new FlatTexts (*other_flat)); @@ -330,7 +334,7 @@ AsIfFlatTexts::selected_interacting_generic (const Region &other, bool inverse) db::box_scanner2 scanner (report_progress (), progress_desc ()); - AddressableTextDelivery e (begin (), has_valid_texts ()); + AddressableTextDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { scanner.insert1 (e.operator-> (), 0); @@ -376,7 +380,7 @@ AsIfFlatTexts::pull_generic (const Region &other) const db::box_scanner2 scanner (report_progress (), progress_desc ()); - AddressableTextDelivery e (begin (), has_valid_texts ()); + AddressableTextDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { scanner.insert1 (e.operator-> (), 0); diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 1d991db4c..64efb2aaf 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -766,7 +766,7 @@ Cell::copy_shapes (const db::Cell &source_cell, const db::LayerMapping &layer_ma } if (target_layout != source_layout) { - db::PropertyMapper pm (*target_layout, *source_layout); + db::PropertyMapper pm (target_layout, source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { shapes (lm->second).insert_transformed (source_cell.shapes (lm->first), trans, pm); @@ -915,7 +915,7 @@ Cell::move_shapes (db::Cell &source_cell, const db::LayerMapping &layer_mapping) } if (target_layout != source_layout) { - db::PropertyMapper pm (*target_layout, *source_layout); + db::PropertyMapper pm (target_layout, source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { shapes (lm->second).insert_transformed (source_cell.shapes (lm->first), trans, pm); @@ -988,7 +988,7 @@ Cell::move_tree (db::Cell &source_cell) throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); + db::PropertyMapper pm (target_layout, source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::CellMapping cm; @@ -1022,7 +1022,7 @@ Cell::move_tree_shapes (db::Cell &source_cell, const db::CellMapping &cm) throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); + db::PropertyMapper pm (target_layout, source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::LayerMapping lm; @@ -1049,7 +1049,7 @@ Cell::move_tree_shapes (db::Cell &source_cell, const db::CellMapping &cm, const throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); + db::PropertyMapper pm (target_layout, source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); std::vector source_cells; diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 6959d78d3..c4255f762 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -371,7 +371,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & if (! new_cells.empty ()) { - db::PropertyMapper pm (layout_a, layout_b); + db::PropertyMapper pm (&layout_a, &layout_b); // Note: this avoids frequent cell index table rebuilds if source and target layout are identical layout_a.start_changes (); diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc index 5add96e4b..36c515333 100644 --- a/src/db/db/dbClipboardData.cc +++ b/src/db/db/dbClipboardData.cc @@ -29,7 +29,7 @@ namespace db ClipboardData::ClipboardData () : m_layout (), m_incomplete_cells () { - m_prop_id_map.set_target (m_layout); + m_prop_id_map.set_target (&m_layout); m_container_cell_index = m_layout.add_cell (""); } @@ -47,7 +47,7 @@ ClipboardData::add (const db::Layout &layout, unsigned int layer, const db::Shap m_layout.insert_layer (layer, layout.get_properties (layer)); } - m_prop_id_map.set_source (layout); + m_prop_id_map.set_source (&layout); m_layout.cell (m_container_cell_index).shapes (layer).insert (shape, m_prop_id_map); } @@ -60,7 +60,7 @@ ClipboardData::add (const db::Layout &layout, unsigned int layer, const db::Shap m_layout.insert_layer (layer, layout.get_properties (layer)); } - m_prop_id_map.set_source (layout); + m_prop_id_map.set_source (&layout); db::Shape new_shape = m_layout.cell (m_container_cell_index).shapes (layer).insert (shape, m_prop_id_map); m_layout.cell (m_container_cell_index).shapes (layer).transform (new_shape, trans); } @@ -80,7 +80,7 @@ ClipboardData::add (const db::Layout &layout, const db::Instance &inst, unsigned } // Insert the instance mapping the cell to the target cell_index and the property ID using the map - m_prop_id_map.set_source (layout); + m_prop_id_map.set_source (&layout); tl::const_map im (target_cell_index); m_layout.cell (m_container_cell_index).insert (inst, im, m_prop_id_map); } @@ -102,7 +102,7 @@ ClipboardData::add (const db::Layout &layout, const db::Instance &inst, unsigned } // Insert the instance mapping the cell to the target cell_index and the property ID using the map - m_prop_id_map.set_source (layout); + m_prop_id_map.set_source (&layout); tl::const_map im (target_cell_index); db::Instance new_inst = m_layout.cell (m_container_cell_index).insert (inst, im, m_prop_id_map); m_layout.cell (m_container_cell_index).transform (new_inst, trans); @@ -123,7 +123,7 @@ ClipboardData::add (const db::Layout &layout, const db::Cell &cell, unsigned int m_context_info.erase (target_cell_index); } - m_prop_id_map.set_source (layout); + m_prop_id_map.set_source (&layout); // copy the shapes for (unsigned int l = 0; l < layout.layers (); ++l) { @@ -157,7 +157,7 @@ std::vector ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::Cell *cell, std::vector *new_tops, ClipboardDataInsertReceiver *insert_receiver) const { std::vector new_layers; - PropertyMapper prop_id_map (layout, m_layout); + PropertyMapper prop_id_map (&layout, &m_layout); std::map layer_map; for (unsigned int l = 0; l < layout.layers (); ++l) { diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 376457ea3..f63ca361d 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -1548,6 +1548,11 @@ CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegi { set_description ("check"); + // force different polygons in the different properties case to skip intra-polygon checks + if (pc_always_different (m_options.prop_constraint)) { + m_different_polygons = true; + } + m_check.set_include_zero (false); m_check.set_whole_edges (options.whole_edges); m_check.set_ignore_angle (options.ignore_angle); diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index 31d288438..5c3c63c9d 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -1618,6 +1618,69 @@ private: tl::weak_ptr mp_node; }; +/** + * @brief The generic local operation with property support + * + * This local operation executes the operation tree within a local processor. + * When put into a local processor, the operation tree will be executed on each interaction. + */ +template +class DB_PUBLIC compound_local_operation_with_properties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + /** + * @brief Constructor + * + * Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree. + * Ownership of the node is *not* transferred to the local operation. + */ + compound_local_operation_with_properties (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const std::vector &intruder_prs) + : mp_node (node), m_prop_constraint (prop_constraint), m_pms (target_pr, subject_pr) + { + m_pmis.reserve (intruder_prs.size ()); + for (auto i = intruder_prs.begin (); i != intruder_prs.end (); ++i) { + m_pmis.push_back (db::PropertyMapper (target_pr, *i)); + } + } + +protected: + virtual void do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const + { + auto interactions_by_prop_id = separate_interactions_to_interactions_by_properties (interactions, m_prop_constraint, m_pms, m_pmis); + for (auto s2p = interactions_by_prop_id.begin (); s2p != interactions_by_prop_id.end (); ++s2p) { + + std::vector > results_wo_props; + results_wo_props.resize (results.size ()); + + CompoundRegionOperationCache cache; + mp_node->compute_local (&cache, layout, s2p->second, results_wo_props, max_vertex_count, area_ratio); + + for (size_t n = 0; n < results.size (); ++n) { + for (auto i = results_wo_props [n].begin (); i != results_wo_props [n].end (); ++i) { + results [n].insert (db::object_with_properties (*i, pc_norm (m_prop_constraint, s2p->first))); + } + } + + } + } + + virtual db::Coord dist () const { return mp_node->dist (); } + virtual OnEmptyIntruderHint on_empty_intruder_hint () const { return mp_node->on_empty_intruder_hint (); } + virtual bool requests_single_subjects () const { return true; } + virtual std::string description () const { return mp_node->description (); } + + const TransformationReducer *vars () const { return mp_node->vars (); } + bool wants_variants () const { return mp_node->wants_variants (); } + std::vector inputs () const { return mp_node->inputs (); } + +private: + tl::weak_ptr mp_node; + db::PropertyConstraint m_prop_constraint; + mutable db::PropertyMapper m_pms; + mutable std::vector m_pmis; +}; + } #endif diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 1bfd00358..0884a3d41 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -44,7 +44,7 @@ public: typedef db::EdgePair value_type; DeepEdgePairsIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) + : m_iter (iter), m_prop_id (0) { set (); } @@ -72,6 +72,11 @@ public: return &m_edge_pair; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual bool equals (const generic_shape_iterator_delegate_base *other) const { const DeepEdgePairsIterator *o = dynamic_cast (other); @@ -100,12 +105,14 @@ private: db::RecursiveShapeIterator m_iter; mutable value_type m_edge_pair; + mutable db::properties_id_type m_prop_id; void set () const { if (! m_iter.at_end ()) { - m_iter.shape ().edge_pair (m_edge_pair); + m_iter->edge_pair (m_edge_pair); m_edge_pair.transform (m_iter.trans ()); + m_prop_id = m_iter->prop_id (); } } }; @@ -307,6 +314,21 @@ const db::RecursiveShapeIterator *DeepEdgePairs::iter () const return 0; } +void DeepEdgePairs::apply_property_translator (const db::PropertiesTranslator &pt) +{ + DeepShapeCollectionDelegateBase::apply_property_translator (pt); +} + +db::PropertiesRepository *DeepEdgePairs::properties_repository () +{ + return &deep_layer ().layout ().properties_repository (); +} + +const db::PropertiesRepository *DeepEdgePairs::properties_repository () const +{ + return &deep_layer ().layout ().properties_repository (); +} + EdgePairsDelegate * DeepEdgePairs::add_in_place (const EdgePairs &other) { @@ -451,7 +473,11 @@ RegionDelegate *DeepEdgePairs::polygons (db::Coord e) const for (db::Shapes::shape_iterator s = c->shapes (deep_layer ().layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) { db::Polygon poly = s->edge_pair ().normalized ().to_polygon (e); if (poly.vertices () >= 3) { - output.insert (db::PolygonRef (poly, layout.shape_repository ())); + if (s->prop_id () != 0) { + output.insert (db::PolygonRefWithProperties (db::PolygonRef (poly, layout.shape_repository ()), s->prop_id ())); + } else { + output.insert (db::PolygonRef (poly, layout.shape_repository ())); + } } } } @@ -469,10 +495,18 @@ EdgesDelegate *DeepEdgePairs::generic_edges (bool first, bool second) const for (db::Shapes::shape_iterator s = c->shapes (deep_layer ().layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) { db::EdgePair ep = s->edge_pair (); if (first) { - output.insert (ep.first ()); + if (s->prop_id () != 0) { + output.insert (db::EdgeWithProperties (ep.first (), s->prop_id ())); + } else { + output.insert (ep.first ()); + } } if (second) { - output.insert (ep.second ()); + if (s->prop_id () != 0) { + output.insert (db::EdgeWithProperties (ep.second (), s->prop_id ())); + } else { + output.insert (ep.second ()); + } } } } diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index a3847b873..ee1076ae2 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -72,6 +72,9 @@ public: virtual const db::EdgePair *nth (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 720863a31..4c80d42ae 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -35,6 +35,7 @@ #include "dbLocalOperation.h" #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" // for db::ContainedEdgesLocalOperation +#include "dbEdgesLocalOperations.h" #include "dbHierProcessor.h" #include "dbEmptyEdges.h" @@ -54,7 +55,7 @@ public: typedef db::Edge value_type; DeepEdgesIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) + : m_iter (iter), m_prop_id (0) { set (); } @@ -82,6 +83,11 @@ public: return &m_edge; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual bool equals (const generic_shape_iterator_delegate_base *other) const { const DeepEdgesIterator *o = dynamic_cast (other); @@ -110,12 +116,14 @@ private: db::RecursiveShapeIterator m_iter; mutable value_type m_edge; + mutable db::properties_id_type m_prop_id; void set () const { if (! m_iter.at_end ()) { - m_iter.shape ().edge (m_edge); + m_iter->edge (m_edge); m_edge.transform (m_iter.trans ()); + m_prop_id = m_iter->prop_id (); } } }; @@ -212,12 +220,17 @@ void DeepEdges::merged_semantics_changed () // .. nothing yet .. } -void DeepEdges::do_insert (const db::Edge &edge) +void DeepEdges::do_insert (const db::Edge &edge, db::properties_id_type prop_id) { db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - top_cell.shapes (deep_layer ().layer ()).insert (edge); + db::Shapes &shapes = top_cell.shapes (deep_layer ().layer ()); + if (prop_id == 0) { + shapes.insert (edge); + } else { + shapes.insert (db::EdgeWithProperties (edge, prop_id)); + } } invalidate_bbox (); @@ -441,6 +454,24 @@ DeepEdges::iter () const return 0; } +void DeepEdges::apply_property_translator (const db::PropertiesTranslator &pt) +{ + DeepShapeCollectionDelegateBase::apply_property_translator (pt); + + m_merged_edges_valid = false; + m_merged_edges = db::DeepLayer (); +} + +db::PropertiesRepository *DeepEdges::properties_repository () +{ + return &deep_layer ().layout ().properties_repository (); +} + +const db::PropertiesRepository *DeepEdges::properties_repository () const +{ + return &deep_layer ().layout ().properties_repository (); +} + bool DeepEdges::equals (const Edges &other) const { const DeepEdges *other_delegate = dynamic_cast (other.delegate ()); @@ -481,6 +512,60 @@ public: } db::Shapes &merged (size_t cid, db::cell_index_type ci, bool initial = true) + { + return compute_merged (cid, ci, initial); + } + + void erase (size_t cid, db::cell_index_type ci) + { + m_merged_cluster.erase (std::make_pair (cid, ci)); + m_property_id_per_cluster.erase (std::make_pair (cid, ci)); + } + +private: + std::map, db::Shapes> m_merged_cluster; + std::map, db::properties_id_type> m_property_id_per_cluster; + std::set > m_done; + unsigned int m_layer; + const db::hier_clusters *mp_hc; + db::box_scanner m_scanner; + + db::properties_id_type property_id (size_t cid, db::cell_index_type ci, bool initial) + { + std::map, db::properties_id_type>::iterator s = m_property_id_per_cluster.find (std::make_pair (cid, ci)); + + // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again + if (initial) { + tl_assert (s == m_property_id_per_cluster.end ()); + } + + if (s != m_property_id_per_cluster.end ()) { + return s->second; + } + + s = m_property_id_per_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::properties_id_type (0))).first; + + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); + const db::local_cluster &c = cc.cluster_by_id (cid); + + if (c.begin_attr () != c.end_attr ()) { + + s->second = *c.begin_attr (); + + } else { + + const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); + for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end () && s->second == db::properties_id_type (0); ++i) { + s->second = property_id (i->id (), i->inst_cell_index (), false); + } + + } + + return s->second; + + } + + db::Shapes &compute_merged (size_t cid, db::cell_index_type ci, bool initial) { std::map, db::Shapes>::iterator s = m_merged_cluster.find (std::make_pair (cid, ci)); @@ -498,6 +583,8 @@ public: s = m_merged_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::Shapes (false))).first; + db::properties_id_type prop_id = property_id (cid, ci, initial); + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); const db::local_cluster &c = cc.cluster_by_id (cid); @@ -505,7 +592,7 @@ public: const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end (); ++i) { - const db::Shapes &cc_shapes = merged (i->id (), i->inst_cell_index (), false); + const db::Shapes &cc_shapes = compute_merged (i->id (), i->inst_cell_index (), false); merged_child_clusters.push_back (std::make_pair (&cc_shapes, i->inst_trans ())); } @@ -531,18 +618,11 @@ public: // .. and run the merge operation s->second.clear (); - EdgeBooleanClusterCollectorToShapes cluster_collector (&s->second, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&s->second, EdgeOr, prop_id); m_scanner.process (cluster_collector, 1, db::box_convert ()); return s->second; } - -private: - std::map, db::Shapes> m_merged_cluster; - std::set > m_done; - unsigned int m_layer; - const db::hier_clusters *mp_hc; - db::box_scanner m_scanner; }; } @@ -582,7 +662,7 @@ DeepEdges::ensure_merged_edges_valid () const hc.set_base_verbosity (base_verbosity() + 10); hc.build (layout, deep_layer ().initial_cell (), conn); - // collect the clusters and merge them into big polygons + // collect the clusters and merge them into larger edges // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is // hopefully more efficient that collecting everything and will lead to reuse of parts. @@ -596,7 +676,7 @@ DeepEdges::ensure_merged_edges_valid () const if (cc.is_root (*cl)) { db::Shapes &s = cm.merged (*cl, c->cell_index ()); c->shapes (m_merged_edges.layer ()).insert (s); - s.clear (); // not needed anymore + cm.erase (*cl, c->cell_index ()); // not needed anymore } } } @@ -613,6 +693,7 @@ DeepEdges::set_is_merged (bool f) { m_is_merged = f; m_merged_edges_valid = false; + m_merged_edges = db::DeepLayer (); } void @@ -1187,6 +1268,8 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t if (join) { + // TODO: property support + db::hier_clusters hc; db::Connectivity conn (db::Connectivity::EdgesConnectByPoints); conn.connect (edges); @@ -1247,6 +1330,7 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t PolygonRefToShapesGenerator prgen (&layout, out); for (db::Shapes::shape_iterator si = c->shapes (edges.layer ()).begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + prgen.set_prop_id (si->prop_id ()); prgen.put (extended_edge (si->edge ().transformed (v->first), ext_b, ext_e, ext_o, ext_i).transformed (v->first.inverted ())); } diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 7910042c6..78fb04935 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -62,7 +62,7 @@ public: virtual void reserve (size_t n); - virtual void do_insert (const db::Edge &edge); + virtual void do_insert (const db::Edge &edge, properties_id_type prop_id); EdgesDelegate *clone () const; @@ -80,6 +80,9 @@ public: virtual bool has_valid_merged_edges () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const Edges &other) const; virtual bool less (const Edges &other) const; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 74402c33e..0d7d725de 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -41,6 +41,7 @@ #include "dbRegionLocalOperations.h" #include "dbLocalOperationUtils.h" #include "dbCompoundOperation.h" +#include "dbLayoutToNetlist.h" #include "tlTimer.h" namespace db @@ -57,7 +58,7 @@ public: typedef db::Polygon value_type; DeepRegionIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) + : m_iter (iter), m_prop_id (0) { set (); } @@ -85,6 +86,11 @@ public: return &m_polygon; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual bool equals (const generic_shape_iterator_delegate_base *other) const { const DeepRegionIterator *o = dynamic_cast (other); @@ -113,12 +119,14 @@ private: db::RecursiveShapeIterator m_iter; mutable value_type m_polygon; + mutable db::properties_id_type m_prop_id; void set () const { if (! m_iter.at_end ()) { - m_iter.shape ().polygon (m_polygon); + m_iter->polygon (m_polygon); m_polygon.transform (m_iter.trans (), false); + m_prop_id = m_iter->prop_id (); } } }; @@ -220,12 +228,17 @@ void DeepRegion::min_coherence_changed () set_is_merged (false); } -void DeepRegion::do_insert (const db::Polygon &polygon) +void DeepRegion::do_insert (const db::Polygon &polygon, db::properties_id_type prop_id) { db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - top_cell.shapes (deep_layer ().layer ()).insert (db::PolygonRef (polygon, layout.shape_repository ())); + db::Shapes &shapes = top_cell.shapes (deep_layer ().layer ()); + if (prop_id == 0) { + shapes.insert (db::PolygonRef (polygon, layout.shape_repository ())); + } else { + shapes.insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, layout.shape_repository ()), prop_id)); + } } invalidate_bbox (); @@ -339,11 +352,16 @@ flatten_layer (db::DeepLayer &deep_layer) db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { if (iter->is_polygon ()) { db::Polygon poly; iter->polygon (poly); - flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ())); + if (! iter->prop_id ()) { + flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ())); + } else { + flat_shapes.insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ()), iter->prop_id ())); + } } } @@ -439,6 +457,12 @@ DeepRegion::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); } +db::properties_id_type +DeepRegion::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); +} + bool DeepRegion::has_valid_polygons () const { @@ -457,6 +481,24 @@ DeepRegion::iter () const return 0; } +void DeepRegion::apply_property_translator (const db::PropertiesTranslator &pt) +{ + DeepShapeCollectionDelegateBase::apply_property_translator (pt); + + m_merged_polygons_valid = false; + m_merged_polygons = db::DeepLayer (); +} + +db::PropertiesRepository *DeepRegion::properties_repository () +{ + return &deep_layer ().layout ().properties_repository (); +} + +const db::PropertiesRepository *DeepRegion::properties_repository () const +{ + return &deep_layer ().layout ().properties_repository (); +} + bool DeepRegion::equals (const Region &other) const { @@ -504,16 +546,53 @@ public: void erase (size_t cid, db::cell_index_type ci) { m_merged_cluster.erase (std::make_pair (cid, ci)); + m_property_id_per_cluster.erase (std::make_pair (cid, ci)); } private: std::map, db::Shapes> m_merged_cluster; + std::map, db::properties_id_type> m_property_id_per_cluster; unsigned int m_layer; db::Layout *mp_layout; const db::hier_clusters *mp_hc; bool m_min_coherence; db::EdgeProcessor m_ep; + db::properties_id_type property_id (size_t cid, db::cell_index_type ci, bool initial) + { + std::map, db::properties_id_type>::iterator s = m_property_id_per_cluster.find (std::make_pair (cid, ci)); + + // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again + if (initial) { + tl_assert (s == m_property_id_per_cluster.end ()); + } + + if (s != m_property_id_per_cluster.end ()) { + return s->second; + } + + s = m_property_id_per_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::properties_id_type (0))).first; + + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); + const db::local_cluster &c = cc.cluster_by_id (cid); + + if (c.begin_attr () != c.end_attr ()) { + + s->second = *c.begin_attr (); + + } else { + + const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); + for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end () && s->second == db::properties_id_type (0); ++i) { + s->second = property_id (i->id (), i->inst_cell_index (), false); + } + + } + + return s->second; + + } + db::Shapes &compute_merged (size_t cid, db::cell_index_type ci, bool initial, unsigned int min_wc) { std::map, db::Shapes>::iterator s = m_merged_cluster.find (std::make_pair (cid, ci)); @@ -529,6 +608,8 @@ private: s = m_merged_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::Shapes (false))).first; + db::properties_id_type prop_id = property_id (cid, ci, initial); + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); const db::local_cluster &c = cc.cluster_by_id (cid); @@ -580,7 +661,7 @@ private: // and run the merge step db::MergeOp op (min_wc); - db::PolygonRefToShapesGenerator pr (mp_layout, &s->second); + db::PolygonRefToShapesGenerator pr (mp_layout, &s->second, prop_id); db::PolygonGenerator pg (pr, false /*don't resolve holes*/, m_min_coherence); m_ep.process (pg, op); @@ -629,7 +710,7 @@ DeepRegion::ensure_merged_polygons_valid () const db::Connectivity conn; conn.connect (deep_layer ()); hc.set_base_verbosity (base_verbosity () + 10); - hc.build (layout, deep_layer ().initial_cell (), conn); + hc.build (layout, deep_layer ().initial_cell (), conn, 0, 0, true /*separate_attributes*/); // collect the clusters and merge them into big polygons // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is @@ -662,6 +743,7 @@ DeepRegion::set_is_merged (bool f) { m_is_merged = f; m_merged_polygons_valid = false; + m_merged_polygons = db::DeepLayer (); } void @@ -671,131 +753,194 @@ DeepRegion::insert_into (db::Layout *layout, db::cell_index_type into_cell, unsi } RegionDelegate * -DeepRegion::and_with (const Region &other) const +DeepRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *nets) const +{ + db::NetBuilder &net_builder = deep_layer ().store_non_const ()->net_builder_for (l2n); + + if (&l2n->dss () != deep_layer ().store ()) { + throw tl::Exception (tl::to_string (tr ("Extracted netlist is from different scope as this layer - cannot pull net shapes"))); + } + + DeepLayer result = deep_layer ().derived (); + + std::unique_ptr region_for_layer (l2n->layer_by_original (this)); + if (! region_for_layer) { + throw tl::Exception (tl::to_string (tr ("The given layer is not an original layer used in netlist extraction"))); + } + + std::map lmap; + lmap.insert (std::make_pair (result.layer (), region_for_layer.get ())); + + net_builder.build_nets (nets, lmap, prop_mode, net_prop_name); + + return new db::DeepRegion (result); +} + +RegionDelegate * +DeepRegion::and_with (const Region &other, PropertyConstraint property_constraint) const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); if (empty ()) { - return clone (); + return clone ()->remove_properties (pc_remove (property_constraint)); } else if (other.empty ()) { - return other.delegate ()->clone (); + return other.delegate ()->clone ()->remove_properties (pc_remove (property_constraint)); } else if (! other_deep) { - return AsIfFlatRegion::and_with (other); + return AsIfFlatRegion::and_with (other, property_constraint); } else { - return new DeepRegion (and_or_not_with (other_deep, true)); + return new DeepRegion (and_or_not_with (other_deep, true, property_constraint)); } } RegionDelegate * -DeepRegion::not_with (const Region &other) const +DeepRegion::not_with (const Region &other, PropertyConstraint property_constraint) const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); if (empty () || other.empty ()) { - return clone (); + return clone ()->remove_properties (pc_remove (property_constraint)); } else if (! other_deep) { - return AsIfFlatRegion::not_with (other); + return AsIfFlatRegion::not_with (other, property_constraint); } else { - return new DeepRegion (and_or_not_with (other_deep, false)); + return new DeepRegion (and_or_not_with (other_deep, false, property_constraint)); } } RegionDelegate * -DeepRegion::or_with (const Region &other) const +DeepRegion::or_with (const Region &other, db::PropertyConstraint /*property_constraint*/) const { + // TODO: implement property_constraint RegionDelegate *res = add (other); return res->merged_in_place (); } std::pair -DeepRegion::andnot_with (const Region &other) const +DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constraint) const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); if (empty ()) { - return std::make_pair (clone (), clone ()); + return std::make_pair (clone ()->remove_properties (pc_remove (property_constraint)), clone ()->remove_properties (pc_remove (property_constraint))); } else if (other.empty ()) { - return std::make_pair (other.delegate ()->clone (), clone ()); + return std::make_pair (other.delegate ()->clone ()->remove_properties (pc_remove (property_constraint)), clone ()->remove_properties (pc_remove (property_constraint))); } else if (! other_deep) { - return AsIfFlatRegion::andnot_with (other); + return AsIfFlatRegion::andnot_with (other, property_constraint); } else { - std::pair res = and_and_not_with (other_deep); + std::pair res = and_and_not_with (other_deep, property_constraint); return std::make_pair (new DeepRegion (res.first), new DeepRegion (res.second)); } } DeepLayer -DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const +DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyConstraint property_constraint) const { DeepLayer dl_out (deep_layer ().derived ()); - db::BoolAndOrNotLocalOperation op (and_op); + if (pc_skip (property_constraint)) { - db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); - proc.set_base_verbosity (base_verbosity ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_threads (deep_layer ().store ()->threads ()); - proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); - proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + db::BoolAndOrNotLocalOperation op (and_op); - proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ()); + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ()); + + } else { + + db::BoolAndOrNotLocalOperationWithProperties op (and_op, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint); + + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ()); + + } return dl_out; } std::pair -DeepRegion::and_and_not_with (const DeepRegion *other) const +DeepRegion::and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const { DeepLayer dl_out1 (deep_layer ().derived ()); DeepLayer dl_out2 (deep_layer ().derived ()); - db::TwoBoolAndNotLocalOperation op; + if (pc_skip (property_constraint)) { - db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); - proc.set_base_verbosity (base_verbosity ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_threads (deep_layer ().store ()->threads ()); - proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); - proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + db::TwoBoolAndNotLocalOperation op; - std::vector il; - il.push_back (other->deep_layer ().layer ()); + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); - std::vector ol; - ol.push_back (dl_out1.layer ()); - ol.push_back (dl_out2.layer ()); + std::vector il; + il.push_back (other->deep_layer ().layer ()); - proc.run (&op, deep_layer ().layer (), il, ol); + std::vector ol; + ol.push_back (dl_out1.layer ()); + ol.push_back (dl_out2.layer ()); + + proc.run (&op, deep_layer ().layer (), il, ol); + + } else { + + db::PropertiesRepository *pr_out1 = &dl_out1.layout ().properties_repository (); + db::PropertiesRepository *pr_out2 = &dl_out2.layout ().properties_repository (); + const db::PropertiesRepository *pr = &deep_layer ().layout ().properties_repository (); + db::TwoBoolAndNotLocalOperationWithProperties op (pr_out1, pr_out2, pr, pr, property_constraint); + + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + std::vector il; + il.push_back (other->deep_layer ().layer ()); + + std::vector ol; + ol.push_back (dl_out1.layer ()); + ol.push_back (dl_out2.layer ()); + + proc.run (&op, deep_layer ().layer (), il, ol); + + } return std::make_pair (dl_out1, dl_out2); } RegionDelegate * -DeepRegion::xor_with (const Region &other) const +DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_constraint) const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); @@ -811,14 +956,14 @@ DeepRegion::xor_with (const Region &other) const } else if (! other_deep) { - return AsIfFlatRegion::xor_with (other); + return AsIfFlatRegion::xor_with (other, property_constraint); } 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)); + DeepLayer n1 (and_or_not_with (other_deep, false, property_constraint)); + DeepLayer n2 (other_deep->and_or_not_with (this, false, property_constraint)); n1.add_from (n2); return new DeepRegion (n1); @@ -1168,89 +1313,13 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy) return res.release (); } -namespace -{ - -class PolygonToEdgeLocalOperation - : public local_operation -{ -public: - PolygonToEdgeLocalOperation () - : local_operation () - { - // .. nothing yet .. - } - - virtual db::Coord dist () const { return 1; } - virtual bool requests_single_subjects () const { return true; } - virtual std::string description () const { return std::string ("polygon to edges"); } - - virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const - { - db::EdgeProcessor ep; - ep.set_base_verbosity (50); - - for (shape_interactions::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { - ep.insert (s->second); - } - - if (interactions.num_intruders () == 0) { - - db::EdgeToEdgeSetGenerator eg (results.front ()); - db::MergeOp op (0); - ep.process (eg, op); - - } else { - - // With intruders: to compute our local contribution we take the edges without and with intruders - // and deliver what is in both sets - - db::MergeOp op (0); - - std::vector edges1; - db::EdgeContainer ec1 (edges1); - ep.process (ec1, op); - - ep.clear (); - - for (shape_interactions::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { - ep.insert (s->second); - } - for (shape_interactions::intruder_iterator i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) { - ep.insert (i->second.second); - } - - std::vector edges2; - db::EdgeContainer ec2 (edges2); - ep.process (ec2, op); - - // Runs the boolean AND between the result with and without intruders - - db::box_scanner scanner; - scanner.reserve (edges1.size () + edges2.size ()); - - for (std::vector::const_iterator i = edges1.begin (); i != edges1.end (); ++i) { - scanner.insert (i.operator-> (), 0); - } - for (std::vector::const_iterator i = edges2.begin (); i != edges2.end (); ++i) { - scanner.insert (i.operator-> (), 1); - } - - EdgeBooleanClusterCollector > cluster_collector (&results.front (), EdgeAnd); - scanner.process (cluster_collector, 1, db::box_convert ()); - - } - - } -}; - -} - EdgesDelegate * DeepRegion::edges (const EdgeFilterBase *filter) const { + std::unique_ptr res (new db::DeepEdges (deep_layer ().derived ())); + if (empty ()) { - return new db::DeepEdges (deep_layer ().derived ()); + return res.release (); } if (! filter && merged_semantics () && ! merged_polygons_available ()) { @@ -1259,29 +1328,22 @@ DeepRegion::edges (const EdgeFilterBase *filter) const const db::DeepLayer &polygons = deep_layer (); - db::PolygonToEdgeLocalOperation op; + db::PolygonToEdgeLocalOperation op (res->properties_repository (), &polygons.layout ().properties_repository ()); - db::local_processor proc (const_cast (&polygons.layout ()), - const_cast (&polygons.initial_cell ()), - polygons.breakout_cells ()); + db::local_processor proc (&res->deep_layer ().layout (), &res->deep_layer ().initial_cell (), polygons.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); // a boolean core makes somewhat better hierarchy proc.set_boolean_core (true); - std::unique_ptr res (new db::DeepEdges (polygons.derived ())); - proc.run (&op, polygons.layer (), foreign_idlayer (), res->deep_layer ().layer ()); - return res.release (); - } else { const db::DeepLayer &polygons = merged_deep_layer (); + db::PropertyMapper pm (res->properties_repository (), &polygons.layout ().properties_repository ()); std::unique_ptr vars; @@ -1298,7 +1360,6 @@ DeepRegion::edges (const EdgeFilterBase *filter) const db::Layout &layout = const_cast (polygons.layout ()); - std::unique_ptr res (new db::DeepEdges (polygons.derived ())); for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { db::ICplxTrans tr; @@ -1318,7 +1379,7 @@ DeepRegion::edges (const EdgeFilterBase *filter) const for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { if (! filter || filter->selected ((*e).transformed (tr))) { - st.insert (*e); + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); } } @@ -1327,9 +1388,10 @@ DeepRegion::edges (const EdgeFilterBase *filter) const } res->set_is_merged (merged_semantics () || is_merged ()); - return res.release (); } + + return res.release (); } RegionDelegate * @@ -1593,6 +1655,7 @@ DeepRegion::sized (coord_type d, unsigned int mode) const db::SizingPolygonFilter siz (pg2, d_with_mag, d_with_mag, mode); for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + pr.set_prop_id (si->prop_id ()); db::Polygon poly; si->polygon (poly); siz.put (poly); @@ -1655,6 +1718,7 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const db::SizingPolygonFilter siz (pg2, dx_with_mag, dy_with_mag, mode); for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + pr.set_prop_id (si->prop_id ()); db::Polygon poly; si->polygon (poly); siz.put (poly); @@ -1683,17 +1747,16 @@ Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &no } } - db::local_processor proc (const_cast (®ion->deep_layer ().layout ()), - const_cast (®ion->deep_layer ().initial_cell ()), - region->deep_layer ().breakout_cells ()); + const db::DeepLayer &polygons (region->merged_deep_layer ()); + std::unique_ptr res (new Output (polygons.derived ())); + + db::local_processor proc (&res->deep_layer ().layout (), &res->deep_layer ().initial_cell (), region->deep_layer ().breakout_cells ()); proc.set_description (region->progress_desc ()); proc.set_report_progress (region->report_progress ()); proc.set_base_verbosity (region->base_verbosity ()); proc.set_threads (region->deep_layer ().store ()->threads ()); - const db::DeepLayer &polygons (region->merged_deep_layer ()); - std::vector other_layers; for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) { @@ -1714,41 +1777,108 @@ Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &no } - std::unique_ptr res (new Output (polygons.derived ())); compound_local_operation op (&node); proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); return res.release (); } -EdgePairsDelegate * -DeepRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &node) +template +static +Output *region_cop_with_properties_impl (DeepRegion *region, db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { - DeepEdgePairs *output = region_cop_impl (this, node); + // Fall back to flat mode if one of the inputs is flat + std::vector inputs = node.inputs (); + for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) { + if (! is_subject_regionptr (*i) && ! dynamic_cast ((*i)->delegate ())) { + return 0; + } + } + + const db::DeepLayer &polygons (region->merged_deep_layer ()); + std::unique_ptr res (new Output (polygons.derived ())); + + db::local_processor > proc (&res->deep_layer ().layout (), &res->deep_layer ().initial_cell (), region->deep_layer ().breakout_cells ()); + + proc.set_description (region->progress_desc ()); + proc.set_report_progress (region->report_progress ()); + proc.set_base_verbosity (region->base_verbosity ()); + proc.set_threads (region->deep_layer ().store ()->threads ()); + + std::vector other_layers; + std::vector intruder_prs; + const db::PropertiesRepository *subject_pr = &polygons.layout ().properties_repository (); + + for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) { + + if (is_subject_regionptr (*i)) { + if (*i == subject_regionptr ()) { + other_layers.push_back (subject_idlayer ()); + } else { + other_layers.push_back (foreign_idlayer ()); + } + intruder_prs.push_back (subject_pr); + } else { + const db::DeepRegion *other_deep = dynamic_cast ((*i)->delegate ()); + tl_assert (other_deep != 0); + if (&other_deep->deep_layer ().layout () != ®ion->deep_layer ().layout () || &other_deep->deep_layer ().initial_cell () != ®ion->deep_layer ().initial_cell ()) { + throw tl::Exception (tl::to_string (tr ("Complex DeepRegion operations need to use the same layout and top cell for all inputs"))); + } + other_layers.push_back (other_deep->deep_layer ().layer ()); + intruder_prs.push_back (other_deep->properties_repository ()); + } + + } + + compound_local_operation_with_properties op (&node, prop_constraint, res->properties_repository (), subject_pr, intruder_prs); + proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); + + return res.release (); +} + +EdgePairsDelegate * +DeepRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) +{ + DeepEdgePairs *output = 0; + if (pc_skip (prop_constraint)) { + output = region_cop_impl (this, node); + } else { + output = region_cop_with_properties_impl (this, node, prop_constraint); + } if (! output) { - return AsIfFlatRegion::cop_to_edge_pairs (node); + return AsIfFlatRegion::cop_to_edge_pairs (node, prop_constraint); } else { return output; } } RegionDelegate * -DeepRegion::cop_to_region (db::CompoundRegionOperationNode &node) +DeepRegion::cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { - DeepRegion *output = region_cop_impl (this, node); + DeepRegion *output = 0; + if (pc_skip (prop_constraint)) { + output = region_cop_impl (this, node); + } else { + output = region_cop_with_properties_impl (this, node, prop_constraint); + } if (! output) { - return AsIfFlatRegion::cop_to_region (node); + return AsIfFlatRegion::cop_to_region (node, prop_constraint); } else { return output; } } EdgesDelegate * -DeepRegion::cop_to_edges (db::CompoundRegionOperationNode &node) +DeepRegion::cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { - DeepEdges *output = region_cop_impl (this, node); + DeepEdges *output = 0; + if (pc_skip (prop_constraint)) { + output = region_cop_impl (this, node); + } else { + output = region_cop_with_properties_impl (this, node, prop_constraint); + } if (! output) { - return AsIfFlatRegion::cop_to_edges (node); + return AsIfFlatRegion::cop_to_edges (node, prop_constraint); } else { return output; } @@ -1763,6 +1893,11 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons return new db::DeepEdgePairs (deep_layer ().derived ()); } + // force different polygons in the different properties case to skip intra-polygon checks + if (pc_always_different (options.prop_constraint)) { + different_polygons = true; + } + const db::DeepRegion *other_deep = 0; unsigned int other_layer = 0; bool other_is_merged = true; @@ -1782,6 +1917,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); } if (! other->merged_semantics ()) { + other_layer = other_deep->deep_layer ().layer (); other_is_merged = true; } else if (options.whole_edges) { // NOTE: whole edges needs both inputs merged @@ -1804,21 +1940,40 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons std::unique_ptr res (new db::DeepEdgePairs (polygons.derived ())); - db::CheckLocalOperation op (check, different_polygons, primary_is_merged, other_deep != 0, other_is_merged, options); + db::Layout *subject_layout = &res->deep_layer ().layout (); + db::Cell *subject_top = &res->deep_layer ().initial_cell (); + const db::Layout *intruder_layout = other_deep ? &other_deep->deep_layer ().layout () : &polygons.layout (); + const db::Cell *intruder_top = other_deep ? &other_deep->deep_layer ().initial_cell () : &polygons.initial_cell (); + const std::set *subject_breakout_cells = deep_layer ().breakout_cells (); + const std::set *intruder_breakout_cells = other_deep ? other_deep->deep_layer ().breakout_cells () : 0; - db::local_processor proc (const_cast (&polygons.layout ()), - const_cast (&polygons.initial_cell ()), - other_deep ? &other_deep->deep_layer ().layout () : const_cast (&polygons.layout ()), - other_deep ? &other_deep->deep_layer ().initial_cell () : const_cast (&polygons.initial_cell ()), - deep_layer ().breakout_cells (), - other_deep ? other_deep->deep_layer ().breakout_cells () : 0); + if (options.prop_constraint == db::IgnoreProperties) { - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); - proc.set_threads (polygons.store ()->threads ()); + db::CheckLocalOperation op (check, different_polygons, primary_is_merged, other_deep != 0, other_is_merged, options); - proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ()); + db::local_processor proc (subject_layout, subject_top, + intruder_layout, intruder_top, + subject_breakout_cells, intruder_breakout_cells); + + configure_proc (proc); + proc.set_threads (polygons.store ()->threads ()); + + proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ()); + + } else { + + db::check_local_operation_with_properties op (check, different_polygons, primary_is_merged, other_deep != 0, other_is_merged, options, res->properties_repository (), properties_repository (), other_deep ? other_deep->properties_repository () : &polygons.layout ().properties_repository ()); + + db::local_processor proc (subject_layout, subject_top, + intruder_layout, intruder_top, + subject_breakout_cells, intruder_breakout_cells); + + configure_proc (proc); + proc.set_threads (polygons.store ()->threads ()); + + proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ()); + + } return res.release (); } @@ -1849,7 +2004,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::Polygons); ! s.at_end (); ++s) { - edge2edge_check_negative_or_positive edge_check (check, result, options.negative, false /*does not require different polygons*/, false /*does not require different layers*/, options.shielded, true /*symmetric edge pairs*/); + edge2edge_check_negative_or_positive edge_check (check, result, options.negative, false /*does not require different polygons*/, false /*does not require different layers*/, options.shielded, true /*symmetric edge pairs*/, pc_remove (options.prop_constraint) ? 0 : s->prop_id ()); poly2poly_check poly_check (edge_check); db::Polygon poly; @@ -1955,9 +2110,7 @@ DeepRegion::in_and_out_generic (const Region &other, InteractingOutputMode outpu db::ContainedLocalOperation op (output_mode); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); InteractingResultHolder orh (output_mode, merged_semantics (), polygons); @@ -2019,9 +2172,7 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to db::InteractingLocalOperation op (mode, touching, output_mode, min_count, max_count, true); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); if (split_after) { proc.set_area_ratio (polygons.store ()->max_area_ratio ()); @@ -2076,9 +2227,7 @@ DeepRegion::selected_interacting_generic (const Edges &other, InteractingOutputM db::InteractingWithEdgeLocalOperation op (output_mode, min_count, max_count, true); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); if (split_after) { proc.set_area_ratio (polygons.store ()->max_area_ratio ()); @@ -2122,9 +2271,7 @@ DeepRegion::pull_generic (const Region &other, int mode, bool touching) const db::PullLocalOperation op (mode, touching); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); if (split_after) { proc.set_area_ratio (polygons.store ()->max_area_ratio ()); @@ -2162,9 +2309,7 @@ DeepRegion::pull_generic (const Edges &other) const db::PullWithEdgeLocalOperation op; db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_edges.layout (), &other_edges.initial_cell (), polygons.breakout_cells (), other_edges.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); proc.run (&op, polygons.layer (), other_edges.layer (), dl_out.layer ()); @@ -2197,9 +2342,7 @@ DeepRegion::pull_generic (const Texts &other) const db::PullWithTextLocalOperation op; db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_texts.layout (), &other_texts.initial_cell (), polygons.breakout_cells (), other_texts.breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); proc.run (&op, polygons.layer (), other_texts.layer (), dl_out.layer ()); @@ -2246,9 +2389,7 @@ DeepRegion::selected_interacting_generic (const Texts &other, InteractingOutputM db::InteractingWithTextLocalOperation op (output_mode, min_count, max_count); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - proc.set_base_verbosity (base_verbosity ()); + configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); if (split_after) { proc.set_area_ratio (polygons.store ()->max_area_ratio ()); diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 9b08a4bf2..df40fec49 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -53,7 +53,7 @@ public: RegionDelegate *clone () const; - virtual void do_insert (const db::Polygon &polygon); + virtual void do_insert (const db::Polygon &polygon, db::properties_id_type prop_id); virtual void do_transform (const db::Trans &t); virtual void do_transform (const db::ICplxTrans &t); @@ -74,10 +74,14 @@ public: virtual bool is_merged () const; virtual const db::Polygon *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_polygons () const; virtual bool has_valid_merged_polygons () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; @@ -92,15 +96,15 @@ public: virtual std::string to_string (size_t nmax) const; - virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node); - virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node); - virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node); + virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint); + virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint); + virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint); - virtual RegionDelegate *and_with (const Region &other) const; - virtual RegionDelegate *not_with (const Region &other) const; - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; - virtual std::pair andnot_with (const Region &) const; + virtual RegionDelegate *and_with (const Region &other, db::PropertyConstraint property_constraint) const; + virtual RegionDelegate *not_with (const Region &other, db::PropertyConstraint property_constraint) const; + virtual RegionDelegate *xor_with (const Region &other, db::PropertyConstraint property_constraint) const; + virtual RegionDelegate *or_with (const Region &other, db::PropertyConstraint property_constraint) const; + virtual std::pair andnot_with (const Region &, db::PropertyConstraint property_constraint) const; virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add (const Region &other) const; @@ -135,6 +139,8 @@ public: virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + virtual RegionDelegate *nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *nets) const; + virtual DeepShapeCollectionDelegateBase *deep () { return this; @@ -171,11 +177,19 @@ private: void init (); void ensure_merged_polygons_valid () const; - DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const; - std::pair and_and_not_with (const DeepRegion *other) const; + DeepLayer and_or_not_with(const DeepRegion *other, bool and_op, PropertyConstraint property_constraint) const; + std::pair and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const; DeepRegion *apply_filter (const PolygonFilterBase &filter) const; template OutputContainer *processed_impl (const polygon_processor &filter) const; + + template + void configure_proc (Proc &proc) const + { + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + proc.set_base_verbosity (base_verbosity ()); + } }; } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 39211ad6e..93c74ffba 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -33,6 +33,7 @@ #include "dbDeepEdgePairs.h" #include "dbDeepTexts.h" #include "dbShapeCollection.h" +#include "dbLayoutToNetlist.h" #include "tlTimer.h" @@ -50,7 +51,7 @@ DeepLayer::DeepLayer () DeepLayer::DeepLayer (const Region ®ion) : mp_store (), m_layout (0), m_layer (0) { - const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); tl_assert (dr != 0); *this = dr->deep_layer (); } @@ -58,7 +59,7 @@ DeepLayer::DeepLayer (const Region ®ion) DeepLayer::DeepLayer (const Texts &texts) : mp_store (), m_layout (0), m_layer (0) { - const db::DeepTexts *dr = dynamic_cast (texts.delegate ()); + const db::DeepTexts *dr = dynamic_cast (texts.delegate ()); tl_assert (dr != 0); *this = dr->deep_layer (); } @@ -66,7 +67,7 @@ DeepLayer::DeepLayer (const Texts &texts) DeepLayer::DeepLayer (const Edges &edges) : mp_store (), m_layout (0), m_layer (0) { - const db::DeepEdges *dr = dynamic_cast (edges.delegate ()); + const db::DeepEdges *dr = dynamic_cast (edges.delegate ()); tl_assert (dr != 0); *this = dr->deep_layer (); } @@ -74,7 +75,7 @@ DeepLayer::DeepLayer (const Edges &edges) DeepLayer::DeepLayer (const EdgePairs &edge_pairs) : mp_store (), m_layout (0), m_layer (0) { - const db::DeepEdgePairs *dr = dynamic_cast (edge_pairs.delegate ()); + const db::DeepEdgePairs *dr = dynamic_cast (edge_pairs.delegate ()); tl_assert (dr != 0); *this = dr->deep_layer (); } @@ -261,10 +262,31 @@ DeepLayer::check_dss () const } } -// ---------------------------------------------------------------------------------- - struct DeepShapeStore::LayoutHolder { + class L2NStatusChangedListener + : public tl::Object + { + public: + L2NStatusChangedListener (DeepShapeStore::LayoutHolder *lh, db::LayoutToNetlist *l2n) + : mp_lh (lh), mp_l2n (l2n) + { + mp_l2n->status_changed_event ().add (this, &L2NStatusChangedListener::l2n_destroyed); + } + + private: + void l2n_destroyed (gsi::ObjectBase::StatusEventType ev) + { + if (ev == gsi::ObjectBase::ObjectDestroyed) { + // CAUTION: this will eventually delete *this! + mp_lh->remove_l2n (mp_l2n); + } + } + + DeepShapeStore::LayoutHolder *mp_lh; + db::LayoutToNetlist *mp_l2n; + }; + LayoutHolder (const db::ICplxTrans &trans) : refs (0), layout (false), builder (&layout, trans) { @@ -287,16 +309,42 @@ struct DeepShapeStore::LayoutHolder } } + bool has_net_builder_for (db::LayoutToNetlist *l2n) const + { + auto l = net_builders.find (l2n); + return (l != net_builders.end ()); + } + + db::NetBuilder &net_builder_for (db::Cell &top, db::LayoutToNetlist *l2n) + { + auto l = net_builders.find (l2n); + if (l == net_builders.end ()) { + l = net_builders.insert (std::make_pair (l2n, std::make_pair (L2NStatusChangedListener (this, l2n), db::NetBuilder (&layout, l2n->cell_mapping_into (layout, top, false), l2n)))).first; + return l->second.second; + } else { + return l->second.second; + } + } + + void remove_l2n (db::LayoutToNetlist *l2n) + { + auto l = net_builders.find (l2n); + if (l != net_builders.end ()) { + net_builders.erase (l); + } + } + int refs; db::Layout layout; db::HierarchyBuilder builder; + std::map > net_builders; std::map layer_refs; }; // ---------------------------------------------------------------------------------- DeepShapeStoreState::DeepShapeStoreState () - : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_reject_odd_polygons (false), m_text_property_name (), m_text_enlargement (-1) + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_reject_odd_polygons (false), m_text_property_name (), m_text_enlargement (-1), m_subcircuit_hierarchy_for_nets (false) { // .. nothing yet .. } @@ -403,6 +451,19 @@ DeepShapeStoreState::max_vertex_count () const return m_max_vertex_count; } +void +DeepShapeStoreState::set_subcircuit_hierarchy_for_nets (bool f) +{ + m_subcircuit_hierarchy_for_nets = f; +} + +bool +DeepShapeStoreState::subcircuit_hierarchy_for_nets () const +{ + return m_subcircuit_hierarchy_for_nets; +} + + // ---------------------------------------------------------------------------------- static size_t s_instance_count = 0; @@ -475,19 +536,20 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, bool for_n db::Shapes *shapes = &initial_cell ().shapes (layer); db::Box world = db::Box::world (); - // The chain of operators for producing clipped and reduced polygon references - db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout (), text_enlargement (), text_property_name ()); - db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count, m_state.reject_odd_polygons ()); - // try to maintain the texts on top level - go through shape iterator std::pair ii = region.begin_iter (); db::ICplxTrans ttop = trans * ii.second; + + // The chain of operators for producing clipped and reduced polygon references + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout (), ii.first.layout (), text_enlargement (), text_property_name ()); + db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count, m_state.reject_odd_polygons ()); + while (! ii.first.at_end ()) { if (for_netlist && ii.first->is_text () && ii.first.layout () && ii.first.cell () != ii.first.top_cell ()) { // Skip texts on levels below top cell. For the reasoning see the description of this method. } else { - red.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + red.push (*ii.first, ii.first.prop_id (), ttop * ii.first.trans (), world, 0, shapes); } ++ii.first; @@ -515,12 +577,12 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Edges &edges, const db::IC db::Shapes *shapes = &initial_cell ().shapes (layer); db::Box world = db::Box::world (); - db::EdgeBuildingHierarchyBuilderShapeReceiver eb (false); - std::pair ii = edges.begin_iter (); db::ICplxTrans ttop = trans * ii.second; + + db::EdgeBuildingHierarchyBuilderShapeReceiver eb (&layout (), ii.first.layout (), false); while (! ii.first.at_end ()) { - eb.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + eb.push (*ii.first, ii.first.prop_id (), ttop * ii.first.trans (), world, 0, shapes); ++ii.first; } @@ -545,12 +607,13 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Texts &texts, const db::IC db::Shapes *shapes = &initial_cell ().shapes (layer); db::Box world = db::Box::world (); - db::TextBuildingHierarchyBuilderShapeReceiver tb (&layout ()); - std::pair ii = texts.begin_iter (); db::ICplxTrans ttop = trans * ii.second; + + db::TextBuildingHierarchyBuilderShapeReceiver tb (&layout (), ii.first.layout ()); + while (! ii.first.at_end ()) { - tb.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + tb.push (*ii.first, ii.first.prop_id (), ttop * ii.first.trans (), world, 0, shapes); ++ii.first; } @@ -621,6 +684,16 @@ const tl::Variant &DeepShapeStore::text_property_name () const return m_state.text_property_name (); } +void DeepShapeStore::set_subcircuit_hierarchy_for_nets (bool f) +{ + m_state.set_subcircuit_hierarchy_for_nets (f); +} + +bool DeepShapeStore::subcircuit_hierarchy_for_nets () const +{ + return m_state.subcircuit_hierarchy_for_nets (); +} + const std::set * DeepShapeStore::breakout_cells (unsigned int layout_index) const { @@ -651,6 +724,27 @@ DeepShapeStore::add_breakout_cells (unsigned int layout_index, const std::sethas_net_builder_for (l2n); +} + +db::NetBuilder & +DeepShapeStore::net_builder_for (unsigned int layout_index, db::LayoutToNetlist *l2n) +{ + db::NetBuilder &nb = m_layouts [layout_index]->net_builder_for (initial_cell (layout_index), l2n); + + if (subcircuit_hierarchy_for_nets ()) { + nb.set_hier_mode (db::BNH_SubcircuitCells); + nb.set_cell_name_prefix ("X$$"); // TODO: needs to be a configuration option? + } else { + nb.set_hier_mode (db::BNH_Flatten); + } + + return nb; +} + void DeepShapeStore::set_threads (int n) { m_state.set_threads (n); @@ -836,7 +930,7 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator builder.set_target_layer (layer_index); // The chain of operators for producing clipped and reduced polygon references - db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, text_enlargement (), text_property_name ()); + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout, si.layout (), text_enlargement (), text_property_name ()); db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count, m_state.reject_odd_polygons ()); db::ClippingHierarchyBuilderShapeReceiver clip (&red); @@ -905,7 +999,7 @@ DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilder db::Shapes &into = c->shapes (layer_index); const db::Shapes &from = c->shapes (from_layer_index); for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { - pipe->push (*s, trans, region, 0, &into); + pipe->push (*s, s->prop_id (), trans, region, 0, &into); } } @@ -914,13 +1008,19 @@ DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilder DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans) { - db::EdgeBuildingHierarchyBuilderShapeReceiver refs (as_edges); + unsigned int layout_index = layout_for_iter (si, trans); + db::Layout &layout = m_layouts[layout_index]->layout; + + db::EdgeBuildingHierarchyBuilderShapeReceiver refs (&layout, si.layout (), as_edges); return create_custom_layer (si, &refs, trans); } DeepLayer DeepShapeStore::create_edge_pair_layer (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { - db::EdgePairBuildingHierarchyBuilderShapeReceiver refs; + unsigned int layout_index = layout_for_iter (si, trans); + db::Layout &layout = m_layouts[layout_index]->layout; + + db::EdgePairBuildingHierarchyBuilderShapeReceiver refs (&layout, si.layout ()); return create_custom_layer (si, &refs, trans); } @@ -929,7 +1029,7 @@ DeepLayer DeepShapeStore::create_text_layer (const db::RecursiveShapeIterator &s unsigned int layout_index = layout_for_iter (si, trans); db::Layout &layout = m_layouts[layout_index]->layout; - db::TextBuildingHierarchyBuilderShapeReceiver refs (&layout); + db::TextBuildingHierarchyBuilderShapeReceiver refs (&layout, si.layout ()); return create_custom_layer (si, &refs, trans); } @@ -1200,19 +1300,32 @@ DeepShapeStore::insert_as_polygons (const DeepLayer &deep_layer, db::Layout *int if (s->is_edge_pair ()) { - out.insert (s->edge_pair ().normalized ().to_simple_polygon (enl)); + if (s->prop_id () != 0) { + out.insert (db::SimplePolygonWithProperties (s->edge_pair ().normalized ().to_simple_polygon (enl), s->prop_id ())); + } else { + out.insert (s->edge_pair ().normalized ().to_simple_polygon (enl)); + } } else if (s->is_path () || s->is_polygon () || s->is_box ()) { db::Polygon poly; s->polygon (poly); - out.insert (poly); + if (s->prop_id () != 0) { + out.insert (db::PolygonWithProperties (poly, s->prop_id ())); + } else { + out.insert (poly); + } } else if (s->is_text ()) { db::Text t; s->text (t); - out.insert (db::SimplePolygon (t.box ().enlarged (db::Vector (enl, enl)))); + db::SimplePolygon sp (t.box ().enlarged (db::Vector (enl, enl))); + if (s->prop_id () != 0) { + out.insert (db::SimplePolygonWithProperties (sp, s->prop_id ())); + } else { + out.insert (sp); + } } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 40da1fa65..be6e838c0 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -47,6 +47,8 @@ class Edges; class EdgePairs; class Texts; class ShapeCollection; +class NetBuilder; +class LayoutToNetlist; /** * @brief Represents a shape collection from the deep shape store @@ -220,6 +222,16 @@ public: return mp_store.get (); } + /** + * @brief Gets the non-const shape store object + * This feature is intended for internal purposes. + */ + DeepShapeStore *store_non_const () const + { + check_dss (); + return const_cast (mp_store.get ()); + } + private: friend class DeepShapeStore; @@ -262,6 +274,9 @@ public: void add_breakout_cell (unsigned int layout_index, db::cell_index_type ci); void add_breakout_cells (unsigned int layout_index, const std::set &cc); + void set_subcircuit_hierarchy_for_nets (bool f); + bool subcircuit_hierarchy_for_nets () const; + private: int m_threads; double m_max_area_ratio; @@ -270,6 +285,7 @@ private: tl::Variant m_text_property_name; std::vector > m_breakout_cells; int m_text_enlargement; + bool m_subcircuit_hierarchy_for_nets; std::set &ensure_breakout_cells (unsigned int layout_index) { @@ -668,6 +684,37 @@ public: */ bool is_valid_layout_index (unsigned int n) const; + /** + * @brief Gets the net builder object for a given layout index and LayoutToNetlist database + * + * If no net builder is available, one will be created. Use \\has_net_builder to check whether one is + * already created. + */ + db::NetBuilder &net_builder_for (unsigned int layout_index, db::LayoutToNetlist *l2n); + + /** + * @brief Gets the net builder object for a given LayoutToNetlist database (requires the DSS to be singular) + */ + db::NetBuilder &net_builder_for (db::LayoutToNetlist *l2n) + { + require_singular (); + return net_builder_for (0, l2n); + } + + /** + * @brief Gets a value indicating whether a net building is available + */ + bool has_net_builder_for(unsigned int layout_index, db::LayoutToNetlist *l2n); + + /** + * @brief Gets the net builder object for a given LayoutToNetlist database (requires the DSS to be singular) + */ + bool has_net_builder_for (db::LayoutToNetlist *l2n) + { + require_singular (); + return has_net_builder_for (0, l2n); + } + /** * @brief The deep shape store also keeps the number of threads to allocate for the hierarchical processor * @@ -752,6 +799,20 @@ public: */ int text_enlargement () const; + /** + * @brief Sets a value indicating whether to build a subcircuit hierarchy per net + * + * This flag is used to determine the way, net subcircuit hierarchies are built: + * when true, subcells are created for subcircuits on a net. Otherwise the net + * shapes are produced flat inside the cell they appear on. + */ + void set_subcircuit_hierarchy_for_nets (bool f); + + /** + * @brief Gets a value indicating whether to build a subcircuit hierarchy per net + */ + bool subcircuit_hierarchy_for_nets () const; + /** * @brief Gets the breakout cells for a given layout * Returns 0 if there are no breakout cells for this layout. diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index b160f88ba..e5e282f87 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -49,7 +49,7 @@ public: typedef db::Text value_type; DeepTextsIterator (const db::RecursiveShapeIterator &iter) - : m_iter (iter) + : m_iter (iter), m_prop_id (0) { set (); } @@ -77,6 +77,11 @@ public: return &m_text; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual bool equals (const generic_shape_iterator_delegate_base *other) const { const DeepTextsIterator *o = dynamic_cast (other); @@ -105,12 +110,14 @@ private: db::RecursiveShapeIterator m_iter; mutable value_type m_text; + mutable db::properties_id_type m_prop_id; void set () const { if (! m_iter.at_end ()) { - m_iter.shape ().text (m_text); + m_iter->text (m_text); m_text.transform (m_iter.trans ()); + m_prop_id = m_iter->prop_id (); } } }; @@ -331,6 +338,21 @@ const db::RecursiveShapeIterator *DeepTexts::iter () const return 0; } +void DeepTexts::apply_property_translator (const db::PropertiesTranslator &pt) +{ + DeepShapeCollectionDelegateBase::apply_property_translator (pt); +} + +db::PropertiesRepository *DeepTexts::properties_repository () +{ + return &deep_layer ().layout ().properties_repository (); +} + +const db::PropertiesRepository *DeepTexts::properties_repository () const +{ + return &deep_layer ().layout ().properties_repository (); +} + TextsDelegate * DeepTexts::add_in_place (const Texts &other) { diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index cec15d65b..d139147ad 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -73,6 +73,9 @@ public: virtual const db::Text *nth (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual TextsDelegate *filter_in_place (const TextFilterBase &filter); virtual TextsDelegate *filtered (const TextFilterBase &) const; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index c83c22939..6f10973f4 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -409,13 +409,13 @@ public: typedef Iterator const_iterator; ShapesToOutputContainerAdaptor () - : mp_shapes (0) + : mp_shapes (0), m_prop_id (0) { // .. nothing yet .. } - ShapesToOutputContainerAdaptor (db::Shapes &shapes) - : mp_shapes (&shapes) + ShapesToOutputContainerAdaptor (db::Shapes &shapes, db::properties_id_type prop_id = 0) + : mp_shapes (&shapes), m_prop_id (prop_id) { // .. nothing yet .. } @@ -432,11 +432,16 @@ public: void insert (const db::Edge &edge) { - mp_shapes->insert (edge); + if (m_prop_id != 0) { + mp_shapes->insert (db::EdgeWithProperties (edge, m_prop_id)); + } else { + mp_shapes->insert (edge); + } } private: db::Shapes *mp_shapes; + db::properties_id_type m_prop_id; }; /** @@ -445,13 +450,13 @@ private: struct DB_PUBLIC EdgeBooleanClusterCollectorToShapes : EdgeBooleanClusterCollector { - EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op) - : EdgeBooleanClusterCollector (&m_adaptor, op), m_adaptor (*output) + EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op, db::properties_id_type prop_id = 0) + : EdgeBooleanClusterCollector (&m_adaptor, op), m_adaptor (*output, prop_id) { } - EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op, db::Shapes *output2) - : EdgeBooleanClusterCollector (&m_adaptor, op, &m_adaptor2), m_adaptor (*output), m_adaptor2 (*output2) + EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op, db::Shapes *output2, db::properties_id_type prop_id = 0) + : EdgeBooleanClusterCollector (&m_adaptor, op, &m_adaptor2), m_adaptor (*output, prop_id), m_adaptor2 (*output2, prop_id) { } diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 75b1a7b5c..0509afccb 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -149,10 +149,26 @@ const db::RecursiveShapeIterator & EdgePairs::iter () const { static db::RecursiveShapeIterator def_iter; - const db::RecursiveShapeIterator *i = mp_delegate->iter (); + const db::RecursiveShapeIterator *i = mp_delegate ? mp_delegate->iter () : 0; return *(i ? i : &def_iter); } +const db::PropertiesRepository & +EdgePairs::properties_repository () const +{ + static db::PropertiesRepository empty_prop_repo; + const db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + return *(r ? r : &empty_prop_repo); +} + +db::PropertiesRepository & +EdgePairs::properties_repository () +{ + db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + tl_assert (r != 0); + return *r; +} + void EdgePairs::processed (Region &output, const EdgePairToPolygonProcessorBase &filter) const { output = Region (mp_delegate->processed_to_polygons (filter)); diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 31b59addc..7a0e07495 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -44,63 +44,8 @@ class DeepShapeStore; class TransformationReducer; class EdgeFilterBase; -/** - * @brief An edge pair set iterator - * - * The iterator delivers the edge pairs of the edge pair set - */ -class DB_PUBLIC EdgePairsIterator - : public generic_shape_iterator -{ -public: - /** - * @brief Default constructor - */ - EdgePairsIterator () - : generic_shape_iterator () - { - // .. nothing yet .. - } - - /** - * @brief Constructor from a delegate - * The iterator will take ownership over the delegate - */ - EdgePairsIterator (EdgePairsIteratorDelegate *delegate) - : generic_shape_iterator (delegate) - { - // .. nothing yet .. - } - - /** - * @brief Copy constructor and assignment - */ - EdgePairsIterator (const EdgePairsIterator &other) - : generic_shape_iterator (static_cast &> (other)) - { - // .. nothing yet .. - } - - /** - * @brief Assignment - */ - EdgePairsIterator &operator= (const EdgePairsIterator &other) - { - generic_shape_iterator::operator= (other); - return *this; - } - - /** - * @brief Increment - */ - EdgePairsIterator &operator++ () - { - generic_shape_iterator::operator++ (); - return *this; - } -}; - -typedef addressable_shape_delivery_gen AddressableEdgePairDelivery; +typedef generic_shape_iterator EdgePairsIterator; +typedef addressable_shape_delivery AddressableEdgePairDelivery; class EdgePairs; @@ -251,7 +196,15 @@ public: /** * @brief Gets the underlying delegate object */ - EdgePairsDelegate *delegate () const + const EdgePairsDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Gets the underlying delegate object + */ + EdgePairsDelegate *delegate () { return mp_delegate; } @@ -488,7 +441,7 @@ public: */ AddressableEdgePairDelivery addressable_edge_pairs () const { - return AddressableEdgePairDelivery (begin (), has_valid_edge_pairs ()); + return AddressableEdgePairDelivery (begin ()); } /** @@ -498,6 +451,20 @@ public: */ const db::RecursiveShapeIterator &iter () const; + /** + * @brief Gets the property repository + * + * Use this object to decode property IDs. + */ + const db::PropertiesRepository &properties_repository () const; + + /** + * @brief Gets the property repository + * + * Use this object to decode and encode property IDs. + */ + db::PropertiesRepository &properties_repository (); + /** * @brief Equality */ diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index 6f0298734..6cdf92e5c 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -166,6 +166,12 @@ public: virtual EdgePairsDelegate *clone () const = 0; + EdgePairsDelegate *remove_properties (bool remove = true) + { + ShapeCollectionDelegateBase::remove_properties (remove); + return this; + } + void enable_progress (const std::string &progress_desc); void disable_progress (); @@ -205,6 +211,9 @@ public: virtual bool has_valid_edge_pairs () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; + virtual void apply_property_translator (const db::PropertiesTranslator &pt) = 0; + virtual db::PropertiesRepository *properties_repository () = 0; + virtual const db::PropertiesRepository *properties_repository () const = 0; virtual bool equals (const EdgePairs &other) const = 0; virtual bool less (const EdgePairs &other) const = 0; diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 9df2d1824..75a00b6f7 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -108,10 +108,26 @@ const db::RecursiveShapeIterator & Edges::iter () const { static db::RecursiveShapeIterator def_iter; - const db::RecursiveShapeIterator *i = mp_delegate->iter (); + const db::RecursiveShapeIterator *i = mp_delegate ? mp_delegate->iter () : 0; return *(i ? i : &def_iter); } +const db::PropertiesRepository & +Edges::properties_repository () const +{ + static db::PropertiesRepository empty_prop_repo; + const db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + return *(r ? r : &empty_prop_repo); +} + +db::PropertiesRepository & +Edges::properties_repository () +{ + db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + tl_assert (r != 0); + return *r; +} + void Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 613e7af05..d5bf8d116 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -39,63 +39,8 @@ class MutableEdges; class EmptyEdges; class DeepShapeStore; -/** - * @brief An edge set iterator - * - * The iterator delivers the edges of the edge set - */ -class DB_PUBLIC EdgesIterator - : public generic_shape_iterator -{ -public: - /** - * @brief Default constructor - */ - EdgesIterator () - : generic_shape_iterator () - { - // .. nothing yet .. - } - - /** - * @brief Constructor from a delegate - * The iterator will take ownership over the delegate - */ - EdgesIterator (EdgesIteratorDelegate *delegate) - : generic_shape_iterator (delegate) - { - // .. nothing yet .. - } - - /** - * @brief Copy constructor and assignment - */ - EdgesIterator (const EdgesIterator &other) - : generic_shape_iterator (static_cast &> (other)) - { - // .. nothing yet .. - } - - /** - * @brief Assignment - */ - EdgesIterator &operator= (const EdgesIterator &other) - { - generic_shape_iterator::operator= (other); - return *this; - } - - /** - * @brief Increment - */ - EdgesIterator &operator++ () - { - generic_shape_iterator::operator++ (); - return *this; - } -}; - -typedef addressable_shape_delivery_gen AddressableEdgeDelivery; +typedef generic_shape_iterator EdgesIterator; +typedef addressable_shape_delivery AddressableEdgeDelivery; class Edges; @@ -269,7 +214,15 @@ public: /** * @brief Gets the underlying delegate object */ - EdgesDelegate *delegate () const + const EdgesDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Gets the underlying delegate object + */ + EdgesDelegate *delegate () { return mp_delegate; } @@ -1441,7 +1394,7 @@ public: */ AddressableEdgeDelivery addressable_edges () const { - return AddressableEdgeDelivery (begin (), has_valid_edges ()); + return AddressableEdgeDelivery (begin ()); } /** @@ -1460,7 +1413,7 @@ public: */ AddressableEdgeDelivery addressable_merged_edges () const { - return AddressableEdgeDelivery (begin_merged (), has_valid_merged_edges ()); + return AddressableEdgeDelivery (begin_merged ()); } /** @@ -1470,6 +1423,20 @@ public: */ const db::RecursiveShapeIterator &iter () const; + /** + * @brief Gets the property repository + * + * Use this object to decode property IDs. + */ + const db::PropertiesRepository &properties_repository () const; + + /** + * @brief Gets the property repository + * + * Use this object to decode and encode property IDs. + */ + db::PropertiesRepository &properties_repository (); + /** * @brief Equality */ diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 243a4fb8c..182700bae 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -239,6 +239,12 @@ public: virtual EdgesDelegate *clone () const = 0; + EdgesDelegate *remove_properties (bool remove = true) + { + ShapeCollectionDelegateBase::remove_properties (remove); + return this; + } + void set_base_verbosity (int vb); int base_verbosity () const { @@ -340,6 +346,9 @@ public: virtual bool has_valid_merged_edges () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; + virtual void apply_property_translator (const db::PropertiesTranslator &pt) = 0; + virtual db::PropertiesRepository *properties_repository () = 0; + virtual const db::PropertiesRepository *properties_repository () const = 0; virtual bool equals (const Edges &other) const = 0; virtual bool less (const Edges &other) const = 0; diff --git a/src/db/db/dbEdgesLocalOperations.cc b/src/db/db/dbEdgesLocalOperations.cc new file mode 100644 index 000000000..cd69d034f --- /dev/null +++ b/src/db/db/dbEdgesLocalOperations.cc @@ -0,0 +1,203 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbEdgesLocalOperations.h" +#include "dbHierProcessor.h" +#include "dbLocalOperationUtils.h" + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// EdgeBoolAndOrNotLocalOperation implementation + +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op) + : m_op (op) +{ + // .. nothing yet .. +} + +OnEmptyIntruderHint +EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; +} + +std::string +EdgeBoolAndOrNotLocalOperation::description () const +{ + 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::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == size_t (m_op == EdgeAndNot ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (results.size () > 1) { + result2 = &results[1]; + } + + EdgeBooleanClusterCollector > cluster_collector (&result, m_op, result2); + + 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); + } + } + + bool any_subject = false; + bool is_and = (m_op == EdgeAnd || m_op == EdgeAndNot || 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 (is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! 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 ()); + + } +} + +// --------------------------------------------------------------------------------------------- +// EdgeToPolygonLocalOperation implementation + +EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders) + : m_op (op), m_include_borders (include_borders) +{ + // .. nothing yet .. +} + +OnEmptyIntruderHint +EdgeToPolygonLocalOperation::on_empty_intruder_hint () const +{ + return m_op == EdgePolygonOp::Inside ? Drop : (m_op == EdgePolygonOp::Outside ? Copy : CopyToSecond); +} + +std::string +EdgeToPolygonLocalOperation::description () const +{ + if (m_op == EdgePolygonOp::Inside) { + return tl::to_string (tr ("Edge to polygon AND/INSIDE")); + } else if (m_op == EdgePolygonOp::Outside) { + return tl::to_string (tr ("Edge to polygon NOT/OUTSIDE")); + } else { + return tl::to_string (tr ("Edge to polygon ANDNOT/INOUTSIDE")); + } +} + +void +EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == size_t (m_op == EdgePolygonOp::Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (results.size () > 1) { + result2 = &results[1]; + } + + db::EdgeProcessor ep; + + 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); + } + } + + 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 (i->second.empty ()) { + // shortcut (outside: keep, otherwise: drop) + if (m_op == db::EdgePolygonOp::Outside) { + result.insert (subject); + } else if (m_op == db::EdgePolygonOp::Both) { + result2->insert (subject); + } + } else { + ep.insert (subject, 1); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, 0); + } + } + + std::unique_ptr cc_second; + if (result2) { + cc_second.reset (new db::EdgeToEdgeSetGenerator (*result2, 2 /*second tag*/)); + } + + db::EdgeToEdgeSetGenerator cc (result, 1 /*first tag*/, cc_second.get ()); + db::EdgePolygonOp op (m_op, m_include_borders); + ep.process (cc, op); + + } +} + +} + diff --git a/src/db/db/dbEdgesLocalOperations.h b/src/db/db/dbEdgesLocalOperations.h new file mode 100644 index 000000000..163bf5a59 --- /dev/null +++ b/src/db/db/dbEdgesLocalOperations.h @@ -0,0 +1,84 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_dbEdgesLocalOperation +#define HDR_dbEdgesLocalOperation + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbEdgeBoolean.h" +#include "dbEdgeProcessor.h" +#include "dbLocalOperation.h" + +namespace db +{ + +/** + * @brief Implements a boolean AND or NOT operation between edges + */ +class DB_PUBLIC EdgeBoolAndOrNotLocalOperation + : public local_operation +{ +public: + EdgeBoolAndOrNotLocalOperation (db::EdgeBoolOp op); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint 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: + db::EdgeBoolOp m_op; +}; + +/** + * @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders) + * + * "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true". + * With "include_borders == false" the operations are "INSIDE" and "OUTSIDE". + */ +class DB_PUBLIC EdgeToPolygonLocalOperation + : public local_operation +{ +public: + EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint 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 m_include_borders ? 1 : 0; } + +private: + db::EdgePolygonOp::mode_t m_op; + bool m_include_borders; +}; + +} + +#endif diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index 699c17d4d..dee797a24 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -73,6 +73,9 @@ public: virtual bool has_valid_edge_pairs () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } + virtual void apply_property_translator (const db::PropertiesTranslator &) { } + virtual db::PropertiesRepository *properties_repository () { return 0; } + virtual const db::PropertiesRepository *properties_repository () const { return 0; } virtual bool equals (const EdgePairs &other) const; virtual bool less (const EdgePairs &other) const; diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index ce3361ef7..3e1574fe7 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -122,6 +122,9 @@ public: virtual bool has_valid_merged_edges () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } + virtual void apply_property_translator (const db::PropertiesTranslator &) { } + virtual db::PropertiesRepository *properties_repository () { return 0; } + virtual const db::PropertiesRepository *properties_repository () const { return 0; } virtual bool equals (const Edges &other) const; virtual bool less (const Edges &other) const; diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc index b1baa137e..b647d57d5 100644 --- a/src/db/db/dbEmptyRegion.cc +++ b/src/db/db/dbEmptyRegion.cc @@ -67,19 +67,20 @@ EmptyRegion::add (const Region &other) const } RegionDelegate * -EmptyRegion::xor_with (const Region &other) const +EmptyRegion::xor_with (const Region &other, db::PropertyConstraint prop_constraint) const { - return or_with (other); + return or_with (other, prop_constraint); } RegionDelegate * -EmptyRegion::or_with (const Region &other) const +EmptyRegion::or_with (const Region &other, db::PropertyConstraint /*prop_constraint*/) const { if (other.empty ()) { return new EmptyRegion (); } else if (! other.strict_handling ()) { return other.delegate ()->clone (); } else { + // TODO: implement prop_constraint return other.delegate ()->merged (); } } @@ -97,19 +98,19 @@ EmptyRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &) co } EdgePairsDelegate * -EmptyRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &) +EmptyRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &, db::PropertyConstraint) { return new EmptyEdgePairs (); } RegionDelegate * -EmptyRegion::cop_to_region (db::CompoundRegionOperationNode &) +EmptyRegion::cop_to_region (db::CompoundRegionOperationNode &, db::PropertyConstraint) { return new EmptyRegion (); } EdgesDelegate * -EmptyRegion::cop_to_edges (db::CompoundRegionOperationNode &) +EmptyRegion::cop_to_edges (db::CompoundRegionOperationNode &, db::PropertyConstraint) { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 44270bdbb..c1f04e0f2 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -62,9 +62,9 @@ public: virtual Box bbox () const { return Box (); } - virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node); - virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node); - virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node); + virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node, PropertyConstraint); + virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node, PropertyConstraint); + virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node, PropertyConstraint); virtual EdgePairsDelegate *width_check (db::Coord, const RegionCheckOptions &) const; virtual EdgePairsDelegate *space_check (db::Coord, const RegionCheckOptions &) const; @@ -98,11 +98,11 @@ public: virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } - virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } - virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } - virtual std::pair andnot_with (const Region &) const { return std::make_pair (new EmptyRegion (), new EmptyRegion ()); } - virtual RegionDelegate *xor_with (const Region &other) const; - virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *and_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); } + virtual RegionDelegate *not_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); } + virtual std::pair andnot_with (const Region &, db::PropertyConstraint) const { return std::make_pair (new EmptyRegion (), new EmptyRegion ()); } + virtual RegionDelegate *xor_with (const Region &other, db::PropertyConstraint prop_constraint) const; + virtual RegionDelegate *or_with (const Region &other, db::PropertyConstraint prop_constraint) const; virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add (const Region &other) const; @@ -138,14 +138,20 @@ public: virtual bool has_valid_polygons () const { return true; } virtual bool has_valid_merged_polygons () const { return true; } virtual const db::Polygon *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual const db::RecursiveShapeIterator *iter () const { return 0; } + virtual void apply_property_translator (const db::PropertiesTranslator &) { } + virtual db::PropertiesRepository *properties_repository () { return 0; } + virtual const db::PropertiesRepository *properties_repository () const { return 0; } virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { } + virtual RegionDelegate *nets (LayoutToNetlist *, NetPropertyMode, const tl::Variant &, const std::vector *) const { return new EmptyRegion (); } + private: EmptyRegion &operator= (const EmptyRegion &other); }; diff --git a/src/db/db/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index 386260a0c..903d58972 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -71,6 +71,9 @@ public: virtual bool has_valid_texts () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } + virtual void apply_property_translator (const db::PropertiesTranslator &) { } + virtual db::PropertiesRepository *properties_repository () { return 0; } + virtual const db::PropertiesRepository *properties_repository () const { return 0; } virtual bool equals (const Texts &other) const; virtual bool less (const Texts &other) const; diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index f2ed1f700..a376f1193 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -32,7 +32,7 @@ namespace db // FlatEdgePairs implementation FlatEdgePairs::FlatEdgePairs () - : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (false)) + : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { // .. nothing yet .. } @@ -43,13 +43,13 @@ FlatEdgePairs::~FlatEdgePairs () } FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other) - : MutableEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs) + : MutableEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs), mp_properties_repository (other.mp_properties_repository) { // .. nothing yet .. } FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs) - : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs)) + : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs)), mp_properties_repository (new db::PropertiesRepository ()) { // .. nothing yet .. } @@ -97,6 +97,8 @@ Box FlatEdgePairs::compute_bbox () const EdgePairsDelegate * FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter) { + // TODO: implement property support + db::Shapes &ep = *mp_edge_pairs; edge_pair_iterator_type pw = ep.get_layer ().begin (); @@ -121,22 +123,22 @@ EdgePairsDelegate *FlatEdgePairs::add (const EdgePairs &other) const std::unique_ptr new_edge_pairs (new FlatEdgePairs (*this)); new_edge_pairs->invalidate_cache (); - FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + db::PropertyMapper pm (new_edge_pairs->properties_repository (), &other.properties_repository ()); + + const FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { - new_edge_pairs->raw_edge_pairs ().insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + new_edge_pairs->raw_edge_pairs ().insert (other_flat->raw_edge_pairs (), pm); } else { - size_t n = new_edge_pairs->raw_edge_pairs ().size (); for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - new_edge_pairs->raw_edge_pairs ().reserve (db::EdgePair::tag (), n); - - for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { - new_edge_pairs->raw_edge_pairs ().insert (*p); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id != 0) { + new_edge_pairs->raw_edge_pairs ().insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } } } @@ -150,22 +152,22 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other) db::Shapes &ep = *mp_edge_pairs; - FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + db::PropertyMapper pm (properties_repository (), &other.properties_repository ()); + + const FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { - ep.insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + ep.insert (other_flat->raw_edge_pairs (), pm); } else { - size_t n = ep.size (); for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - ep.reserve (db::EdgePair::tag (), n); - - for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { - ep.insert (*p); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id != 0) { + ep.insert (db::EdgePairWithProperties (*p, prop_id)); + } else { + ep.insert (*p); + } } } @@ -188,19 +190,50 @@ const db::RecursiveShapeIterator *FlatEdgePairs::iter () const return 0; } +void FlatEdgePairs::apply_property_translator (const db::PropertiesTranslator &pt) +{ + if ((mp_edge_pairs->type_mask () & db::ShapeIterator::Properties) != 0) { + + db::Shapes new_edge_pairs (mp_edge_pairs->is_editable ()); + new_edge_pairs.assign (*mp_edge_pairs, pt); + mp_edge_pairs->swap (new_edge_pairs); + + invalidate_cache (); + + } +} + +db::PropertiesRepository *FlatEdgePairs::properties_repository () +{ + return mp_properties_repository.get_non_const (); +} + +const db::PropertiesRepository *FlatEdgePairs::properties_repository () const +{ + return mp_properties_repository.get_const (); +} + void FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const { db::Shapes &out = layout->cell (into_cell).shapes (into_layer); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - out.insert (p->normalized ().to_simple_polygon (enl)); + db::properties_id_type prop_id = pm (p.prop_id ()); + if (prop_id != 0) { + out.insert (db::SimplePolygonWithProperties (p->normalized ().to_simple_polygon (enl), prop_id)); + } else { + out.insert (p->normalized ().to_simple_polygon (enl)); + } } } void FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { - layout->cell (into_cell).shapes (into_layer).insert (*mp_edge_pairs); + db::PropertyMapper pm (&layout->properties_repository (), properties_repository ()); + layout->cell (into_cell).shapes (into_layer).insert (*mp_edge_pairs, pm); } void diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index 3a90c5a00..f6a9c4d3c 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -78,6 +78,9 @@ public: virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; @@ -119,6 +122,7 @@ private: FlatEdgePairs &operator= (const FlatEdgePairs &other); mutable tl::copy_on_write_ptr mp_edge_pairs; + mutable tl::copy_on_write_ptr mp_properties_repository; template void transform_generic (const Trans &trans) diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 7cbe40d9b..5e386c431 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -33,7 +33,7 @@ namespace db // FlatEdges implementation FlatEdges::FlatEdges () - : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); } @@ -44,7 +44,7 @@ FlatEdges::~FlatEdges () } FlatEdges::FlatEdges (const FlatEdges &other) - : MutableEdges (other), mp_edges (other.mp_edges), mp_merged_edges (other.mp_merged_edges) + : MutableEdges (other), mp_edges (other.mp_edges), mp_merged_edges (other.mp_merged_edges), mp_properties_repository (other.mp_properties_repository) { init (); @@ -53,7 +53,7 @@ FlatEdges::FlatEdges (const FlatEdges &other) } FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) - : MutableEdges (), mp_edges (new db::Shapes (edges)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (edges)), mp_merged_edges (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); @@ -61,7 +61,7 @@ FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) } FlatEdges::FlatEdges (bool is_merged) - : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); @@ -88,7 +88,8 @@ void FlatEdges::init () void FlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { - layout->cell (into_cell).shapes (into_layer).insert (*mp_edges); + db::PropertyMapper pm (&layout->properties_repository (), mp_properties_repository.get_const ()); + layout->cell (into_cell).shapes (into_layer).insert (*mp_edges, pm); } void FlatEdges::merged_semantics_changed () @@ -109,19 +110,62 @@ FlatEdges::ensure_merged_edges_valid () const mp_merged_edges->clear (); - db::Shapes tmp (false); - EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); - db::box_scanner scanner (report_progress (), progress_desc ()); - scanner.reserve (mp_edges->size ()); - for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); + // count edges and reserve memory + size_t n = 0; + db::properties_id_type prop_id = 0; + bool need_split_props = false; + for (EdgesIterator s (begin ()); ! s.at_end (); ++s, ++n) { + if (n == 0) { + prop_id = s.prop_id (); + } else if (! need_split_props && prop_id != s.prop_id ()) { + need_split_props = true; } } - scanner.process (cluster_collector, 1, db::box_convert ()); + db::Shapes tmp (false); + + if (! need_split_props) { + + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); + + scanner.reserve (mp_edges->size ()); + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } else { + + std::map > edges_by_props; + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + edges_by_props [e.prop_id ()].push_back (e.operator-> ()); + } + } + + for (auto s2p = edges_by_props.begin (); s2p != edges_by_props.end (); ++s2p) { + + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr, s2p->first); + + scanner.clear (); + scanner.reserve (s2p->second.size ()); + + for (auto s = s2p->second.begin (); s != s2p->second.end (); ++s) { + scanner.insert (*s, 0); + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } + + } mp_merged_edges->swap (tmp); m_merged_edges_valid = true; @@ -192,23 +236,36 @@ FlatEdges::processed_in_place (const EdgeProcessorBase &filter) db::Shapes &e = *mp_edges; edge_iterator_type pw = e.get_layer ().begin (); + edge_iterator_wp_type pw_wp = e.get_layer ().begin (); + for (EdgesIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { edge_res.clear (); filter.process (*p, edge_res); for (std::vector::const_iterator pr = edge_res.begin (); pr != edge_res.end (); ++pr) { - if (pw == e.get_layer ().end ()) { - e.get_layer ().insert (*pr); - pw = e.get_layer ().end (); + if (p.prop_id () != 0) { + if (pw_wp == e.get_layer ().end ()) { + e.get_layer ().insert (db::EdgeWithProperties (*pr, p.prop_id ())); + pw_wp = e.get_layer ().end (); + } else { + e.get_layer ().replace (pw_wp++, db::EdgeWithProperties (*pr, p.prop_id ())); + } } else { - e.get_layer ().replace (pw++, *pr); + if (pw == e.get_layer ().end ()) { + e.get_layer ().insert (*pr); + pw = e.get_layer ().end (); + } else { + e.get_layer ().replace (pw++, *pr); + } } } } e.get_layer ().erase (pw, e.get_layer ().end ()); + e.get_layer ().erase (pw_wp, e.get_layer ().end ()); + mp_merged_edges->clear (); m_is_merged = filter.result_is_merged () && merged_semantics (); @@ -221,18 +278,31 @@ FlatEdges::filter_in_place (const EdgeFilterBase &filter) db::Shapes &e = *mp_edges; edge_iterator_type pw = e.get_layer ().begin (); + edge_iterator_wp_type pw_wp = e.get_layer ().begin (); + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { if (filter.selected (*p)) { - if (pw == e.get_layer ().end ()) { - e.get_layer ().insert (*p); - pw = e.get_layer ().end (); + if (p.prop_id () != 0) { + if (pw_wp == e.get_layer ().end ()) { + e.get_layer ().insert (db::EdgeWithProperties (*p, p.prop_id ())); + pw_wp = e.get_layer ().end (); + } else { + e.get_layer ().replace (pw_wp++, db::EdgeWithProperties (*p, p.prop_id ())); + } } else { - e.get_layer ().replace (pw++, *p); + if (pw == e.get_layer ().end ()) { + e.get_layer ().insert (*p); + pw = e.get_layer ().end (); + } else { + e.get_layer ().replace (pw++, *p); + } } } } e.get_layer ().erase (pw, e.get_layer ().end ()); + e.get_layer ().erase (pw_wp, e.get_layer ().end ()); + mp_merged_edges->clear (); m_is_merged = merged_semantics (); @@ -245,7 +315,7 @@ EdgesDelegate *FlatEdges::add (const Edges &other) const new_region->invalidate_cache (); new_region->set_is_merged (false); - FlatEdges *other_flat = dynamic_cast (other.delegate ()); + const FlatEdges *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { new_region->raw_edges ().insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); @@ -275,7 +345,7 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other) db::Shapes &e = *mp_edges; - FlatEdges *other_flat = dynamic_cast (other.delegate ()); + const FlatEdges *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { e.insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); @@ -318,12 +388,40 @@ const db::RecursiveShapeIterator *FlatEdges::iter () const return 0; } +void FlatEdges::apply_property_translator (const db::PropertiesTranslator &pt) +{ + if ((mp_edges->type_mask () & db::ShapeIterator::Properties) != 0) { + + db::Shapes new_edges (mp_edges->is_editable ()); + new_edges.assign (*mp_edges, pt); + mp_edges->swap (new_edges); + + invalidate_cache (); + + } +} + +db::PropertiesRepository *FlatEdges::properties_repository () +{ + return mp_properties_repository.get_non_const (); +} + +const db::PropertiesRepository *FlatEdges::properties_repository () const +{ + return mp_properties_repository.get_const (); +} + void -FlatEdges::do_insert (const db::Edge &edge) +FlatEdges::do_insert (const db::Edge &edge, db::properties_id_type prop_id) { m_is_merged = empty (); - mp_edges->insert (edge); + if (prop_id == 0) { + mp_edges->insert (edge); + } else { + mp_edges->insert (db::EdgeWithProperties (edge, prop_id)); + } + invalidate_cache (); } diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h index 6bf4d4338..826b4a83b 100644 --- a/src/db/db/dbFlatEdges.h +++ b/src/db/db/dbFlatEdges.h @@ -50,6 +50,8 @@ public: typedef db::layer edge_layer_type; typedef edge_layer_type::iterator edge_iterator_type; + typedef db::layer edge_layer_wp_type; + typedef edge_layer_wp_type::iterator edge_iterator_wp_type; FlatEdges (); FlatEdges (const db::Shapes &edges, bool is_merged); @@ -91,8 +93,11 @@ public: virtual bool has_valid_merged_edges () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; - void do_insert (const db::Edge &edge); + void do_insert (const db::Edge &edge, properties_id_type prop_id); void do_transform (const db::Trans &t) { @@ -132,6 +137,7 @@ private: mutable tl::copy_on_write_ptr mp_edges; mutable tl::copy_on_write_ptr mp_merged_edges; mutable bool m_merged_edges_valid; + mutable tl::copy_on_write_ptr mp_properties_repository; void init (); void ensure_merged_edges_valid () const; @@ -144,6 +150,9 @@ private: for (edge_iterator_type p = e.template get_layer ().begin (); p != e.get_layer ().end (); ++p) { e.get_layer ().replace (p, p->transformed (trans)); } + for (edge_iterator_wp_type p = e.template get_layer ().begin (); p != e.get_layer ().end (); ++p) { + e.get_layer ().replace (p, p->transformed (trans)); + } invalidate_cache (); } } diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 72e884f02..307984b9d 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -25,6 +25,7 @@ #include "dbEmptyRegion.h" #include "dbRegion.h" #include "dbShapeProcessor.h" +#include "tlSList.h" namespace db { @@ -33,18 +34,13 @@ namespace db // FlatRegion implementation FlatRegion::FlatRegion () - : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); } -FlatRegion::~FlatRegion () -{ - // .. nothing yet .. -} - FlatRegion::FlatRegion (const FlatRegion &other) - : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons) + : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons), mp_properties_repository (other.mp_properties_repository) { init (); @@ -53,7 +49,7 @@ FlatRegion::FlatRegion (const FlatRegion &other) } FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) - : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); @@ -61,13 +57,18 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) } FlatRegion::FlatRegion (bool is_merged) - : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); m_is_merged = is_merged; } +FlatRegion::~FlatRegion () +{ + // .. nothing yet .. +} + void FlatRegion::set_is_merged (bool m) { m_is_merged = m; @@ -108,33 +109,8 @@ void FlatRegion::ensure_merged_polygons_valid () const { if (! m_merged_polygons_valid) { - - mp_merged_polygons->clear (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (*mp_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); - + merge_polygons_to (*mp_merged_polygons, min_coherence (), 0); m_merged_polygons_valid = true; - } } @@ -196,20 +172,33 @@ Box FlatRegion::compute_bbox () const RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) { db::layer &poly_layer = mp_polygons->get_layer (); + db::layer &poly_layer_wp = mp_polygons->get_layer (); polygon_iterator_type pw = poly_layer.begin (); + polygon_iterator_wp_type pw_wp = poly_layer_wp.begin (); + for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { if (filter.selected (*p)) { - if (pw == poly_layer.end ()) { - poly_layer.insert (*p); - poly_layer.end (); + if (p.prop_id () != 0) { + if (pw_wp == poly_layer_wp.end ()) { + poly_layer_wp.insert (db::PolygonWithProperties (*p, p.prop_id ())); + pw_wp = poly_layer_wp.end (); + } else { + poly_layer_wp.replace (pw_wp++, db::PolygonWithProperties (*p, p.prop_id ())); + } } else { - poly_layer.replace (pw++, *p); + if (pw == poly_layer.end ()) { + poly_layer.insert (*p); + pw = poly_layer.end (); + } else { + poly_layer.replace (pw++, *p); + } } } } poly_layer.erase (pw, poly_layer.end ()); + poly_layer_wp.erase (pw_wp, poly_layer_wp.end ()); mp_merged_polygons->clear (); m_is_merged = filter.requires_raw_input () ? false : merged_semantics (); @@ -221,15 +210,24 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter { db::layer &poly_layer = mp_polygons->get_layer (); db::layer out; + db::layer &poly_layer_wp = mp_polygons->get_layer (); + db::layer out_wp; std::vector poly_res; for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { poly_res.clear (); filter.process (*p, poly_res); - out.insert (poly_res.begin (), poly_res.end ()); + if (p.prop_id () != 0) { + for (auto r = poly_res.begin (); r != poly_res.end (); ++r) { + out_wp.insert (db::PolygonWithProperties (*r, p.prop_id ())); + } + } else { + out.insert (poly_res.begin (), poly_res.end ()); + } } poly_layer.swap (out); + poly_layer_wp.swap (out_wp); mp_merged_polygons->clear (); m_is_merged = filter.result_is_merged () && merged_semantics (); @@ -279,28 +277,7 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi } else { invalidate_cache (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (*mp_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); + merge_polygons_to (*mp_polygons, min_coherence, min_wc); m_is_merged = true; @@ -330,7 +307,7 @@ RegionDelegate *FlatRegion::add (const Region &other) const new_region->invalidate_cache (); new_region->set_is_merged (false); - FlatRegion *other_flat = dynamic_cast (other.delegate ()); + const FlatRegion *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); @@ -360,7 +337,7 @@ RegionDelegate *FlatRegion::add_in_place (const Region &other) db::Shapes &polygons = *mp_polygons; - FlatRegion *other_flat = dynamic_cast (other.delegate ()); + const FlatRegion *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); @@ -385,7 +362,46 @@ RegionDelegate *FlatRegion::add_in_place (const Region &other) const db::Polygon *FlatRegion::nth (size_t n) const { - return n < mp_polygons->size () ? &mp_polygons->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_polygons->size ()) { + return 0; + } + + const db::layer &l = mp_polygons->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_polygons->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatRegion::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_polygons->size ()) { + return 0; + } + + const db::layer &l = mp_polygons->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_polygons->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatRegion::has_valid_polygons () const @@ -403,19 +419,47 @@ const db::RecursiveShapeIterator *FlatRegion::iter () const return 0; } +void FlatRegion::apply_property_translator (const db::PropertiesTranslator &pt) +{ + if ((mp_polygons->type_mask () & db::ShapeIterator::Properties) != 0) { + + db::Shapes new_polygons (mp_polygons->is_editable ()); + new_polygons.assign (*mp_polygons, pt); + mp_polygons->swap (new_polygons); + + invalidate_cache (); + + } +} + +db::PropertiesRepository *FlatRegion::properties_repository () +{ + return mp_properties_repository.get_non_const (); +} + +const db::PropertiesRepository *FlatRegion::properties_repository () const +{ + return mp_properties_repository.get_const (); +} + void FlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { - layout->cell (into_cell).shapes (into_layer).insert (*mp_polygons); + db::PropertyMapper pm (&layout->properties_repository (), mp_properties_repository.get_const ()); + layout->cell (into_cell).shapes (into_layer).insert (*mp_polygons, pm); } void -FlatRegion::do_insert (const db::Polygon &polygon) +FlatRegion::do_insert (const db::Polygon &polygon, properties_id_type prop_id) { if (polygon.holes () > 0 || polygon.vertices () > 0) { bool is_box = (empty () && polygon.is_box ()); - mp_polygons->insert (polygon); + if (prop_id != 0) { + mp_polygons->insert (db::PolygonWithProperties (polygon, prop_id)); + } else { + mp_polygons->insert (polygon); + } set_is_merged (is_box); invalidate_cache (); diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 7ec9b9fbb..f11f20a53 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -48,6 +48,8 @@ public: typedef db::Polygon value_type; typedef db::layer polygon_layer_type; typedef polygon_layer_type::iterator polygon_iterator_type; + typedef db::layer polygon_layer_wp_type; + typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (); FlatRegion (const db::Shapes &polygons, bool is_merged); @@ -92,12 +94,16 @@ public: virtual RegionDelegate *add (const Region &other) const; virtual const db::Polygon *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t) const; virtual bool has_valid_polygons () const; virtual bool has_valid_merged_polygons () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; - void do_insert (const db::Polygon &polygon); + void do_insert (const db::Polygon &polygon, db::properties_id_type prop_id); void do_transform (const db::Trans &t) { @@ -141,6 +147,7 @@ private: mutable tl::copy_on_write_ptr mp_polygons; mutable tl::copy_on_write_ptr mp_merged_polygons; mutable bool m_merged_polygons_valid; + mutable tl::copy_on_write_ptr mp_properties_repository; void init (); void ensure_merged_polygons_valid () const; @@ -153,6 +160,9 @@ private: for (polygon_iterator_type p = polygons.get_layer ().begin (); p != polygons.get_layer ().end (); ++p) { polygons.get_layer ().replace (p, p->transformed (trans)); } + for (polygon_iterator_wp_type p = polygons.get_layer ().begin (); p != polygons.get_layer ().end (); ++p) { + polygons.get_layer ().replace (p, p->transformed (trans)); + } invalidate_cache (); } } diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index 16631389f..26a718116 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -32,7 +32,7 @@ namespace db // FlatTexts implementation FlatTexts::FlatTexts () - : MutableTexts (), mp_texts (new db::Shapes (false)) + : MutableTexts (), mp_texts (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { // .. nothing yet .. } @@ -43,13 +43,13 @@ FlatTexts::~FlatTexts () } FlatTexts::FlatTexts (const FlatTexts &other) - : MutableTexts (other), mp_texts (other.mp_texts) + : MutableTexts (other), mp_texts (other.mp_texts), mp_properties_repository (other.mp_properties_repository) { // .. nothing yet .. } FlatTexts::FlatTexts (const db::Shapes &texts) - : MutableTexts (), mp_texts (new db::Shapes (texts)) + : MutableTexts (), mp_texts (new db::Shapes (texts)), mp_properties_repository (new db::PropertiesRepository ()) { // .. nothing yet .. } @@ -121,7 +121,7 @@ TextsDelegate *FlatTexts::add (const Texts &other) const std::unique_ptr new_texts (new FlatTexts (*this)); new_texts->invalidate_cache (); - FlatTexts *other_flat = dynamic_cast (other.delegate ()); + const FlatTexts *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { new_texts->raw_texts ().insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); @@ -150,7 +150,7 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other) db::Shapes &texts = *mp_texts; - FlatTexts *other_flat = dynamic_cast (other.delegate ()); + const FlatTexts *other_flat = dynamic_cast (other.delegate ()); if (other_flat) { texts.insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); @@ -188,6 +188,29 @@ const db::RecursiveShapeIterator *FlatTexts::iter () const return 0; } +void FlatTexts::apply_property_translator (const db::PropertiesTranslator &pt) +{ + if ((mp_texts->type_mask () & db::ShapeIterator::Properties) != 0) { + + db::Shapes new_texts (mp_texts->is_editable ()); + new_texts.assign (*mp_texts, pt); + mp_texts->swap (new_texts); + + invalidate_cache (); + + } +} + +db::PropertiesRepository *FlatTexts::properties_repository () +{ + return mp_properties_repository.get_non_const (); +} + +const db::PropertiesRepository *FlatTexts::properties_repository () const +{ + return mp_properties_repository.get_const (); +} + void FlatTexts::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const { diff --git a/src/db/db/dbFlatTexts.h b/src/db/db/dbFlatTexts.h index daa7b059e..dde68500a 100644 --- a/src/db/db/dbFlatTexts.h +++ b/src/db/db/dbFlatTexts.h @@ -79,6 +79,9 @@ public: virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; @@ -120,6 +123,7 @@ private: FlatTexts &operator= (const FlatTexts &other); mutable tl::copy_on_write_ptr mp_texts; + mutable tl::copy_on_write_ptr mp_properties_repository; template void transform_generic (const Trans &trans) diff --git a/src/db/db/dbGenericShapeIterator.h b/src/db/db/dbGenericShapeIterator.h index 20f280ce1..d396b80fd 100644 --- a/src/db/db/dbGenericShapeIterator.h +++ b/src/db/db/dbGenericShapeIterator.h @@ -46,6 +46,7 @@ public: virtual bool at_end () const = 0; virtual void increment () = 0; virtual const T *get () const = 0; + virtual db::properties_id_type prop_id () const = 0; virtual generic_shape_iterator_delegate_base *clone () const = 0; virtual bool equals (const generic_shape_iterator_delegate_base *other) const = 0; }; @@ -86,6 +87,11 @@ public: return m_iter.operator-> (); } + virtual db::properties_id_type prop_id () const + { + return 0; + } + generic_shape_iterator_delegate_base *clone () const { return new generic_shape_iterator_delegate2 (*this); @@ -141,6 +147,11 @@ public: return m_iter.operator-> (); } + virtual db::properties_id_type prop_id () const + { + return 0; + } + generic_shape_iterator_delegate_base *clone () const { return new generic_shape_iterator_delegate1 (*this); @@ -170,7 +181,7 @@ public: const_cast (mp_shapes)->update (); } m_iter = mp_shapes->begin (shape_flags ()); - m_is_addressable = shape_flags () == shape_flags_pure () || mp_shapes->begin (shape_flags () - shape_flags_pure ()).at_end (); + m_is_addressable = ! shape_flags_with_props () && (shape_flags () == shape_flags_pure () || mp_shapes->begin (shape_flags () - shape_flags_pure ()).at_end ()); set (); } @@ -217,6 +228,11 @@ public: } } + virtual db::properties_id_type prop_id () const + { + return m_iter->prop_id (); + } + generic_shape_iterator_delegate_base *clone () const { return new generic_shapes_iterator_delegate (*this); @@ -289,9 +305,23 @@ public: : mp_delegate (other.mp_delegate ? other.mp_delegate->clone () : 0) { } + generic_shape_iterator (generic_shape_iterator &&other) + : mp_delegate (0) + { + std::swap (mp_delegate, other.mp_delegate); + } + ~generic_shape_iterator () { delete mp_delegate; + mp_delegate = 0; + } + + generic_shape_iterator &set_delegate (generic_shape_iterator_delegate_base *delegate) + { + delete mp_delegate; + mp_delegate = delegate; + return *this; } generic_shape_iterator &operator= (const generic_shape_iterator &other) @@ -319,6 +349,11 @@ public: return ! mp_delegate || mp_delegate->is_addressable (); } + db::properties_id_type prop_id () const + { + return mp_delegate ? mp_delegate->prop_id () : 0; + } + reference operator* () const { return *mp_delegate->get (); @@ -372,12 +407,108 @@ public: }; /** - * @brief A helper class allowing delivery of addressable edges + * @brief Wraps a generic shape iterator to provide a property-enabled one + */ + +template +class DB_PUBLIC generic_shape_iterator_with_properties_delegate + : public generic_shape_iterator_delegate_base > +{ +public: + generic_shape_iterator_with_properties_delegate (generic_shape_iterator &&basic) + : m_basic (basic) + { + set (); + } + + generic_shape_iterator_with_properties_delegate (generic_shape_iterator_delegate_base *delegate) + : m_basic (delegate) + { + set (); + } + + generic_shape_iterator_with_properties_delegate (const generic_shape_iterator &basic) + : m_basic (basic) + { + set (); + } + + generic_shape_iterator_with_properties_delegate *clone () const + { + return new generic_shape_iterator_with_properties_delegate (m_basic); + } + + virtual void do_reset (const db::Box ®ion, bool overlapping) + { + m_basic.reset (region, overlapping); + } + + virtual db::Box bbox () const + { + return m_basic.bbox (); + } + + virtual bool is_addressable () const + { + return false; + } + + virtual bool at_end () const + { + return m_basic.at_end (); + } + + virtual void increment () + { + ++m_basic; + set (); + } + + virtual const db::object_with_properties *get () const + { + return &m_object; + } + + virtual db::properties_id_type prop_id () const + { + return m_object.properties_id (); + } + + virtual bool equals (const generic_shape_iterator_delegate_base > *other) const + { + const generic_shape_iterator_with_properties_delegate *other_cast = dynamic_cast *> (other); + return other_cast && m_basic == other_cast->m_basic; + } + +private: + db::generic_shape_iterator m_basic; + db::object_with_properties m_object; + + void set () + { + m_object = db::object_with_properties (*m_basic, m_basic.prop_id ()); + } +}; + +template +generic_shape_iterator > make_wp_iter (generic_shape_iterator &&basic) +{ + return generic_shape_iterator > ().set_delegate (new generic_shape_iterator_with_properties_delegate (basic)); +} + +template +generic_shape_iterator > make_wp_iter (db::generic_shape_iterator_delegate_base *delegate) +{ + return generic_shape_iterator > ().set_delegate (new generic_shape_iterator_with_properties_delegate (delegate)); +} + +/** + * @brief A helper class allowing delivery of addressable objects * * In some applications (i.e. box scanner), shapes need to be taken * by address. An iterator cannot always deliver addressable objects. - * This class help providing this ability by keeping a temporary copy - * if required. + * The addressable_shape_delivery class help providing this ability by keeping temporary copies + * if required on a heap. */ template @@ -422,32 +553,22 @@ public: } } + const value_type &operator* () const + { + return *operator-> (); + } + + db::properties_id_type prop_id () const + { + return m_iter.prop_id (); + } + private: Iter m_iter; bool m_iterator_is_addressable; std::list m_heap; }; -template -class DB_PUBLIC addressable_shape_delivery_gen - : public addressable_shape_delivery_impl -{ -public: - addressable_shape_delivery_gen () - : addressable_shape_delivery_impl () - { } - - explicit addressable_shape_delivery_gen (const Iter &iter, bool iterator_is_addressable) - : addressable_shape_delivery_impl (iter, iterator_is_addressable) - { } - - addressable_shape_delivery_gen &operator++ () - { - addressable_shape_delivery_impl::inc (); - return *this; - } -}; - template class DB_PUBLIC addressable_shape_delivery : public addressable_shape_delivery_impl > @@ -470,6 +591,29 @@ public: } }; +template +class DB_PUBLIC unaddressable_shape_delivery + : public addressable_shape_delivery_impl > +{ +public: + typedef db::generic_shape_iterator iter_type; + + unaddressable_shape_delivery () + : addressable_shape_delivery_impl () + { } + + explicit unaddressable_shape_delivery (const iter_type &iter) + : addressable_shape_delivery_impl (iter, true) + { } + + unaddressable_shape_delivery &operator++ () + { + addressable_shape_delivery_impl::inc (); + return *this; + } +}; + + } #endif diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8ed5314e4..7817fd527 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -872,8 +872,8 @@ struct cluster_building_receiver typedef std::set global_nets; typedef std::pair cluster_value; - cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) - : mp_conn (&conn), mp_attr_equivalence (attr_equivalence) + cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool separate_attributes) + : mp_conn (&conn), mp_attr_equivalence (attr_equivalence), m_separate_attributes (separate_attributes) { if (mp_attr_equivalence) { // cache the global nets per attribute equivalence cluster @@ -930,6 +930,9 @@ struct cluster_building_receiver void add (const T *s1, std::pair p1, const T *s2, std::pair p2) { + if (m_separate_attributes && p1.second != p2.second) { + return; + } if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first)) { return; } @@ -1021,6 +1024,7 @@ private: std::list m_clusters; std::map > m_global_nets_by_attribute_cluster; const tl::equivalence_clusters *mp_attr_equivalence; + bool m_separate_attributes; void join (typename std::list::iterator ic1, typename std::list::iterator ic2) { @@ -1038,36 +1042,6 @@ private: } }; -template -struct addressable_object_from_shape -{ - const T *operator () (const db::Shape &shape) - { - typename T::tag object_tag; - return shape.basic_ptr (object_tag); - } -}; - -template <> -struct addressable_object_from_shape -{ - const NetShape *operator () (const db::Shape &shape) - { - if (shape.type () == db::Shape::TextRef) { - m_heap.push_back (db::NetShape (shape.text_ref ())); - return &m_heap.back (); - } else if (shape.type () == db::Shape::PolygonRef) { - m_heap.push_back (db::NetShape (shape.polygon_ref ())); - return &m_heap.back (); - } else { - tl_assert (false); - } - } - -private: - std::list m_heap; -}; - template struct attr_accessor { @@ -1126,7 +1100,7 @@ struct get_shape_flags template void -local_clusters::build_clusters (const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool report_progress) +local_clusters::build_clusters (const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool report_progress, bool separate_attributes) { static std::string desc = tl::to_string (tr ("Building local clusters")); @@ -1143,7 +1117,7 @@ local_clusters::build_clusters (const db::Cell &cell, const db::Connectivity } } - cluster_building_receiver rec (conn, attr_equivalence); + cluster_building_receiver rec (conn, attr_equivalence, separate_attributes); bs.process (rec, 1 /*==touching*/, bc); rec.generate_clusters (*this); @@ -1391,11 +1365,11 @@ hier_clusters::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpos template void -hier_clusters::build (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells) +hier_clusters::build (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells, bool separate_attributes) { clear (); cell_clusters_box_converter cbc (layout, *this); - do_build (cbc, layout, cell, conn, attr_equivalence, breakout_cells); + do_build (cbc, layout, cell, conn, attr_equivalence, breakout_cells, separate_attributes); } namespace @@ -1437,9 +1411,18 @@ public: /** * @brief Constructor */ - hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn, const std::set *breakout_cells, typename hier_clusters::instance_interaction_cache_type *instance_interaction_cache) - : mp_layout (&layout), mp_cell (&cell), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn), mp_breakout_cells (breakout_cells), m_cluster_cache_misses (0), m_cluster_cache_hits (0), mp_instance_interaction_cache (instance_interaction_cache) + hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn, const std::set *breakout_cells, typename hier_clusters::instance_interaction_cache_type *instance_interaction_cache, bool separate_attributes) { + mp_layout = &layout; + mp_cell = &cell; + mp_tree = &tree; + mp_cbc = &cbc; + mp_conn = &conn; + mp_breakout_cells = breakout_cells; + m_cluster_cache_misses = 0; + m_cluster_cache_hits = 0; + mp_instance_interaction_cache = instance_interaction_cache; + m_separate_attributes = separate_attributes; mp_cell_clusters = &cell_clusters; } @@ -1568,6 +1551,7 @@ private: instance_interaction_cache, std::list > > m_interaction_cache_for_clusters; size_t m_cluster_cache_misses, m_cluster_cache_hits; typename hier_clusters::instance_interaction_cache_type *mp_instance_interaction_cache; + bool m_separate_attributes; /** * @brief Investigate a pair of instances @@ -1838,7 +1822,7 @@ private: box_type bc2 = (common2 & i->bbox ().transformed (t12)); for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc2); ! j.at_end (); ++j) { - if (i->interacts (*j, t21, *mp_conn)) { + if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn)) { new_interactions.push_back (std::make_pair (i->id (), j->id ())); } @@ -2010,7 +1994,7 @@ private: for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) { - if (c1.interacts (*j, t2, *mp_conn)) { + if ((! m_separate_attributes || c1.same_attrs (*j)) && c1.interacts (*j, t2, *mp_conn)) { interactions.push_back (std::make_pair (c1.id (), j->id ())); } @@ -2249,7 +2233,7 @@ hier_clusters::propagate_cluster_inst (const db::Layout &layout, const db::Ce template void -hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells) +hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells, bool separate_attributes) { tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Computing shape clusters"))); @@ -2284,7 +2268,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou } } - build_local_cluster (layout, layout.cell (*c), conn, ec); + build_local_cluster (layout, layout.cell (*c), conn, ec, separate_attributes); ++progress; @@ -2315,7 +2299,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou todo.push_back (*c); } else { tl_assert (! todo.empty ()); - build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache); + build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache, separate_attributes); done.insert (todo.begin (), todo.end ()); todo.clear (); todo.push_back (*c); @@ -2325,7 +2309,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou } - build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache); + build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache, separate_attributes); } if (tl::verbosity () >= m_base_verbosity + 20) { @@ -2335,7 +2319,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou template void -hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) +hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool separate_attributes) { std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= m_base_verbosity + 20) { @@ -2344,15 +2328,15 @@ hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg); connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; - local.build_clusters (cell, conn, attr_equivalence, true); + local.build_clusters (cell, conn, attr_equivalence, true, separate_attributes); } template void -hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, const std::set *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache) +hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, const std::set *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes) { for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { - build_hier_connections (cbc, layout, layout.cell (*c), conn, breakout_cells, instance_interaction_cache); + build_hier_connections (cbc, layout, layout.cell (*c), conn, breakout_cells, instance_interaction_cache, separate_attributes); ++progress; } } @@ -2436,7 +2420,7 @@ private: template void -hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set *breakout_cells, instance_interaction_cache_type &instance_interaction_cache) +hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set *breakout_cells, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes) { std::string msg = tl::to_string (tr ("Computing hierarchical clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= m_base_verbosity + 20) { @@ -2448,7 +2432,7 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // NOTE: this is a receiver for both the child-to-child and // local to child interactions. - std::unique_ptr > rec (new hc_receiver (layout, cell, local, *this, cbc, conn, breakout_cells, &instance_interaction_cache)); + std::unique_ptr > rec (new hc_receiver (layout, cell, local, *this, cbc, conn, breakout_cells, &instance_interaction_cache, separate_attributes)); cell_inst_clusters_box_converter cibc (cbc); // The box scanner needs pointers so we have to first store the instances diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index a440439b2..f528967c6 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -337,6 +337,14 @@ public: */ void add_attr (attr_id a); + /** + * @brief Gets a value indicating whether the attributes of the clusters are identical + */ + bool same_attrs (const local_cluster &other) const + { + return m_attrs == other.m_attrs; + } + /** * @brief Gets the attribute iterator (begin) */ @@ -537,7 +545,7 @@ public: * cluster joining may happen in this case, because multi-attribute * assignment might create connections too. */ - void build_clusters (const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0, bool report_progress = false); + void build_clusters (const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0, bool report_progress = false, bool separate_attributes = false); /** * @brief Creates and inserts a new clusters @@ -1212,7 +1220,7 @@ public: /** * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity */ - void build (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence = 0, const std::set *breakout_cells = 0); + void build (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence = 0, const std::set *breakout_cells = 0, bool separate_attributes = false); /** * @brief Gets the connected clusters for a given cell @@ -1255,10 +1263,10 @@ public: void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const; private: - void build_local_cluster (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence); - void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set *breakout_cells, instance_interaction_cache_type &instance_interaction_cache); - void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, const std::set *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache); - void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells); + void build_local_cluster (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool separate_attributes); + void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set *breakout_cells, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes); + void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, const std::set *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes); + void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells, bool separate_attributes); std::map > m_per_cell_clusters; int m_base_verbosity; diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 20543592e..e69057b84 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -26,6 +26,7 @@ #include "dbRecursiveShapeIterator.h" #include "dbBoxConvert.h" #include "dbEdgeProcessor.h" +#include "dbEdgeBoolean.h" #include "dbPolygonGenerators.h" #include "dbLocalOperationUtils.h" #include "dbShapeFlags.h" @@ -182,6 +183,31 @@ public: shape_reference_translator (db::Layout * /*target_layout*/) { } }; +template +class shape_reference_translator > + : public shape_reference_translator +{ +public: + typedef db::object_with_properties shape_type; + + shape_reference_translator (db::Layout *target_layout) + : shape_reference_translator (target_layout) + { + // .. nothing yet .. + } + + shape_type operator() (const shape_type &s) const + { + return shape_type (shape_reference_translator::operator () (s), s.properties_id ()); + } + + template + shape_type operator() (const shape_type &s, const Trans &tr) const + { + return shape_type (shape_reference_translator::operator () (s, tr), s.properties_id ()); + } +}; + template class shape_reference_translator_with_trans_from_shape_ref { @@ -273,6 +299,26 @@ private: Trans m_trans; }; +template +class shape_reference_translator_with_trans, Trans> + : public shape_reference_translator_with_trans +{ +public: + typedef db::object_with_properties shape_type; + + shape_reference_translator_with_trans (db::Layout *target_layout) + : shape_reference_translator_with_trans (target_layout) + { + // .. nothing yet .. + } + + shape_type operator() (const shape_type &s) const + { + // CAUTION: no property ID translation happens here (reasoning: the main use case is fake ID for net tagging) + return shape_type (shape_reference_translator_with_trans::operator () (s), s.properties_id ()); + } +}; + // --------------------------------------------------------------------------------------------- /** @@ -309,6 +355,8 @@ db::Box safe_box_enlarged (const db::Box &box, db::Coord dx, db::Coord dy) { if (box.empty ()) { return box; + } else if (box == db::Box::world ()) { + return box; } else { db::Coord w2 = db::Coord (box.width () / 2); db::Coord h2 = db::Coord (box.height () / 2); @@ -392,20 +440,6 @@ local_processor_cell_context::propagate (unsigned int output_layer, } } -// explicit instantiations -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; - // --------------------------------------------------------------------------------------------- // LocalProcessorCellContexts implementation @@ -493,6 +527,62 @@ subtract (std::unordered_set &res, const std::unordered_set +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + size_t max_vertex_count = proc->max_vertex_count (); + double area_ratio = proc->area_ratio (); + + std::unordered_set first; + first.swap (res); + + std::map, std::vector > > by_prop_id; + for (auto i = first.begin (); i != first.end (); ++i) { + by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); + } + for (auto i = other.begin (); i != other.end (); ++i) { + by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); + } + + db::EdgeProcessor ep; + ep.set_base_verbosity (proc->base_verbosity () + 30); + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + db::properties_id_type prop_id = s2p->first; + size_t p1 = 0, p2 = 1; + + ep.clear (); + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + ep.insert (**i, p1); + p1 += 2; + } + + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + ep.insert (**i, p2); + p2 += 2; + } + + db::BooleanOp op (db::BooleanOp::ANotB); + db::polygon_ref_generator_with_properties pr (layout, res, prop_id); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.process (pg, op); + + } +} + template static void subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) @@ -523,6 +613,59 @@ subtract (std::unordered_set &res, const std::unordered_set res.swap (result); } +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + std::unordered_set first; + first.swap (res); + + std::map, std::vector > > by_prop_id; + for (auto i = first.begin (); i != first.end (); ++i) { + by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); + } + for (auto i = other.begin (); i != other.end (); ++i) { + by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); + } + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + if (s2p->second.second.empty ()) { + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + res.insert (**i); + } + + } else { + + db::box_scanner scanner; + scanner.reserve (s2p->second.first.size () + s2p->second.second.size ()); + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + scanner.insert (*i, 0); + } + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + scanner.insert (*i, 1); + } + + db::property_injector > prop_inject (&res, s2p->first); + EdgeBooleanClusterCollector > > cluster_collector (&prop_inject, EdgeNot); + scanner.process (cluster_collector, 1, db::box_convert ()); + + } + + } +} + template static void subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor * /*proc*/) @@ -705,20 +848,6 @@ local_processor_cell_contexts::compute_results (const local_processo } } -// explicit instantiations -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; - // --------------------------------------------------------------------------------------------- template @@ -810,20 +939,6 @@ shape_interactions::intruder_shape (unsigned int id) const } } -// explicit instantiations -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; -template class DB_PUBLIC shape_interactions; - // --------------------------------------------------------------------------------------------- // Helper classes for the LocalProcessor @@ -1014,6 +1129,7 @@ private: void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int /*inst_id*/, const db::Box ®ion) { db::shape_reference_translator rt (mp_subject_layout); + db::shape_to_object s2o; // Look up all shapes from the intruder instance which interact with the subject shape // (given through region) @@ -1024,7 +1140,7 @@ private: // NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the // subject, not from the intruder. - TI ref2 = rt (*si.shape ().basic_ptr (typename TI::tag ()), tn * si.trans ()); + TI ref2 = rt (s2o (si.shape ()), tn * si.trans ()); // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on // the intruder side. @@ -1279,28 +1395,6 @@ local_processor_context_computation_task::perform () mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist); } -// explicit instantiations -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; -template class DB_PUBLIC local_processor_context_computation_task; - // --------------------------------------------------------------------------------------------- // LocalProcessorResultComputationTask implementation @@ -1341,20 +1435,6 @@ local_processor_result_computation_task::perform () } } -// explicit instantiations -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; -template class DB_PUBLIC local_processor_result_computation_task; - // --------------------------------------------------------------------------------------------- // LocalProcessor implementation @@ -1660,6 +1740,7 @@ void local_processor::compute_contexts (local_processor_contexts scanner; + db::addressable_object_from_shape heap; interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { @@ -1676,7 +1757,7 @@ void local_processor::compute_contexts (local_processor_contexts::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) { for (db::Shapes::shape_iterator i = im->second->begin (shape_flags ()); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (typename TI::tag ()), im->first); + scanner.insert2 (heap (*i), im->first); } } @@ -1828,11 +1909,12 @@ struct scan_shape2shape_same_layer operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set &intruders, unsigned int intruder_layer_index, shape_interactions &interactions, db::Coord dist) const { db::box_scanner2 scanner; + db::addressable_object_from_shape heap; interaction_registration_shape1 rec (&interactions, intruder_layer_index); unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { - const TS *ref = i->basic_ptr (typename TS::tag ()); + const TS *ref = heap (*i); scanner.insert1 (ref, id++); } @@ -1852,12 +1934,12 @@ struct scan_shape2shape_same_layer operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set &intruders, unsigned int intruder_layer, shape_interactions &interactions, db::Coord dist) const { db::box_scanner scanner; + db::addressable_object_from_shape heap; interaction_registration_shape1 rec (&interactions, intruder_layer); unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { - const T *ref = i->basic_ptr (typename T::tag ()); - scanner.insert (ref, id++); + scanner.insert (heap (*i), id++); } // TODO: can we confine this search to the subject's (sized) bounding box? @@ -1876,12 +1958,13 @@ struct scan_shape2shape_different_layers operator () (db::Layout *layout, const db::Shapes *subject_shapes, const db::Shapes *intruder_shapes, unsigned int subject_id0, const std::set *intruders, unsigned int intruder_layer_index, shape_interactions &interactions, db::Coord dist) const { db::box_scanner2 scanner; + db::addressable_object_from_shape sheap; + db::addressable_object_from_shape iheap; interaction_registration_shape2shape rec (layout, &interactions, intruder_layer_index); unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i, ++id) { - const TS *ref = i->basic_ptr (typename TS::tag ()); - scanner.insert1 (ref, id); + scanner.insert1 (sheap (*i), id); } // TODO: can we confine this search to the subject's (sized) bounding box? @@ -1900,7 +1983,7 @@ struct scan_shape2shape_different_layers unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags ()); !i.at_end (); ++i, ++id) { unsigned int iid = interactions.next_id (); - scanner.insert2 (i->basic_ptr (typename TI::tag ()), iid); + scanner.insert2 (iheap (*i), iid); rec.same (id, iid); } @@ -1908,7 +1991,7 @@ struct scan_shape2shape_different_layers // TODO: can we confine this search to the subject's (sized) bounding box? for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags ()); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (typename TI::tag ()), interactions.next_id ()); + scanner.insert2 (iheap (*i), interactions.next_id ()); } } @@ -1924,6 +2007,7 @@ void local_processor::compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::vector > &result) const { const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); + db::shape_to_object s2o; shape_interactions interactions; @@ -1938,8 +2022,7 @@ local_processor::compute_local_cell (const db::local_processor_conte } if (op->on_empty_intruder_hint () != OnEmptyIntruderHint::Drop) { - const TS *ref = i->basic_ptr (typename TS::tag ()); - interactions.add_subject (id, *ref); + interactions.add_subject (id, s2o (*i)); } } @@ -1983,11 +2066,12 @@ local_processor::compute_local_cell (const db::local_processor_conte if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) { db::box_scanner2 scanner; + db::addressable_object_from_shape heap; interaction_registration_shape2inst rec (mp_subject_layout, mp_intruder_layout, ail, il_index, op->dist (), &interactions); unsigned int id = subject_id0; for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (typename TS::tag ()), id++); + scanner.insert1 (heap (*i), id++); } unsigned int inst_id = 0; @@ -2167,21 +2251,16 @@ local_processor::run_flat (const generic_shape_iterator &subject db::Coord dist = op->dist (); - db::Box subjects_box = subjects.bbox (); - if (subjects_box != db::Box::world ()) { - subjects_box.enlarge (db::Vector (dist, dist)); - } + db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist); db::Box intruders_box; for (typename std::vector >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) { intruders_box += il->bbox (); } - if (intruders_box != db::Box::world ()) { - intruders_box.enlarge (db::Vector (dist, dist)); - } + intruders_box = safe_box_enlarged (intruders_box, dist, dist); db::Box common_box = intruders_box & subjects_box; - if (common_box.empty ()) { + if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) { if (needs_isolated_subjects) { for (generic_shape_iterator is = subjects; ! is.at_end (); ++is) { @@ -2192,13 +2271,14 @@ local_processor::run_flat (const generic_shape_iterator &subject } else { + common_box = safe_box_enlarged (common_box, -1, -1); + if (needs_isolated_subjects) { - addressable_shape_delivery is (subjects); + unaddressable_shape_delivery is (subjects); for ( ; !is.at_end (); ++is) { - const TS *shape = is.operator-> (); unsigned int id = interactions.next_id (); - interactions.add_subject (id, *shape); + interactions.add_subject (id, *is); } unsigned int il_index = 0; @@ -2240,7 +2320,7 @@ local_processor::run_flat (const generic_shape_iterator &subject } else { - addressable_shape_delivery ii ((*il).confined (common_box, true)); + addressable_shape_delivery ii ((*il).confined (common_box, false)); for (; !ii.at_end (); ++ii) { scanner.insert2 (ii.operator-> (), interactions.next_id ()); } @@ -2276,7 +2356,7 @@ local_processor::run_flat (const generic_shape_iterator &subject interaction_registration_shape1_scanner_combo scanner (&interactions, il_index, m_report_progress, scan_description); - addressable_shape_delivery is (subjects.confined (common_box, true)); + addressable_shape_delivery is (subjects.confined (common_box, false)); unsigned int id = id_first; for ( ; ! is.at_end (); ++is, ++id) { @@ -2295,7 +2375,7 @@ local_processor::run_flat (const generic_shape_iterator &subject // this is the case of intra-layer interactions ("foreign"): we pretend we have two layers and // reject shape self-interactions by registering them as "same" - addressable_shape_delivery is (subjects.confined (common_box, true)); + addressable_shape_delivery is (subjects.confined (common_box, false)); unsigned int id = id_first; for ( ; ! is.at_end (); ++is, ++id) { @@ -2309,8 +2389,8 @@ local_processor::run_flat (const generic_shape_iterator &subject } else { - addressable_shape_delivery is (subjects.confined (common_box, true)); - addressable_shape_delivery ii ((*il).confined (common_box, true)); + addressable_shape_delivery is (subjects.confined (common_box, false)); + addressable_shape_delivery ii ((*il).confined (common_box, false)); unsigned int id = id_first; for ( ; ! is.at_end (); ++is, ++id) { @@ -2356,12 +2436,136 @@ local_processor::run_flat (const generic_shape_iterator &subject } } +// --------------------------------------------------------------------------------------------- + +// NOTE: don't forget to update the explicit instantiations in dbLocalOperation.cc + +// explicit instantiations +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; + +// explicit instantiations +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; + +// explicit instantiations +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; + +// explicit instantiations +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; + +// explicit instantiations +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; + // explicit instantiations template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 959b34276..c8a77cbb6 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -407,11 +407,11 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } void -HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &apply_always, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) +HierarchyBuilder::shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &apply_always, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) { for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { db::Shapes &shapes = (*c)->shapes (m_target_layer); - mp_pipe->push (shape, m_trans * apply_always, region, complex_region, &shapes); + mp_pipe->push (shape, iter->prop_id (), m_trans * apply_always, region, complex_region, &shapes); } } @@ -424,54 +424,54 @@ ClippingHierarchyBuilderShapeReceiver::ClippingHierarchyBuilderShapeReceiver (Hi } void -ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { static db::Box world = db::Box::world (); if (region == world || is_inside (shape.bbox (), region, complex_region)) { - mp_pipe->push (shape, trans, world, 0, target); + mp_pipe->push (shape, prop_id, trans, world, 0, target); } else if (! is_outside (shape.bbox (), region, complex_region)) { // clip the shape if required if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { - mp_pipe->push (shape, trans, world, 0, target); + mp_pipe->push (shape, prop_id, trans, world, 0, target); } else if (shape.is_box ()) { - insert_clipped (shape.box (), trans, region, complex_region, target); + insert_clipped (shape.box (), prop_id, trans, region, complex_region, target); } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { db::Polygon poly; shape.polygon (poly); - insert_clipped (poly, trans, region, complex_region, target); + insert_clipped (poly, prop_id, trans, region, complex_region, target); } } } void -ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { static db::Box world = db::Box::world (); if (! complex_region) { db::Box r = shape & region; if (! r.empty()) { - mp_pipe->push (r, trans, world, 0, target); + mp_pipe->push (r, prop_id, trans, world, 0, target); } } else { - insert_clipped (shape, trans, region, complex_region, target); + insert_clipped (shape, prop_id, trans, region, complex_region, target); } } void -ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { static db::Box world = db::Box::world (); if (region == world || (shape.box ().inside (region) && ! complex_region)) { - mp_pipe->push (shape, trans, world, 0, target); + mp_pipe->push (shape, prop_id, trans, world, 0, target); } else { - insert_clipped (shape, trans, region, complex_region, target); + insert_clipped (shape, prop_id, trans, region, complex_region, target); } } @@ -529,7 +529,7 @@ ClippingHierarchyBuilderShapeReceiver::is_outside (const db::Box &box, const db: } void -ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { db::Box bb = box & region; static db::Box world = db::Box::world (); @@ -538,16 +538,16 @@ ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert ()); ! cr.at_end (); ++cr) { db::Box bc = *cr & bb; if (! bc.empty ()) { - mp_pipe->push (bc, trans, world, 0, target); + mp_pipe->push (bc, prop_id, trans, world, 0, target); } } } else if (! bb.empty ()) { - mp_pipe->push (bb, trans, world, 0, target); + mp_pipe->push (bb, prop_id, trans, world, 0, target); } } void -ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { std::vector clipped_poly; static db::Box world = db::Box::world (); @@ -562,7 +562,7 @@ ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, } for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { - mp_pipe->push (*p, trans, world, 0, target); + mp_pipe->push (*p, prop_id, trans, world, 0, target); } } @@ -575,33 +575,33 @@ ReducingHierarchyBuilderShapeReceiver::ReducingHierarchyBuilderShapeReceiver (Hi } void -ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { - mp_pipe->push (shape, trans, region, complex_region, target); + mp_pipe->push (shape, prop_id, trans, region, complex_region, target); } else if (shape.is_box ()) { - mp_pipe->push (shape.box (), trans, region, complex_region, target); + mp_pipe->push (shape.box (), prop_id, trans, region, complex_region, target); } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { db::Polygon poly; shape.polygon (poly); - reduce (poly, trans, region, complex_region, target); + reduce (poly, prop_id, trans, region, complex_region, target); } } void -ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { - mp_pipe->push (shape, trans, region, complex_region, target); + mp_pipe->push (shape, prop_id, trans, region, complex_region, target); } void -ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { - reduce (shape, trans, region, complex_region, target); + reduce (shape, prop_id, trans, region, complex_region, target); } void -ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target, bool check) +ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target, bool check) { if (check && m_reject_odd_polygons && is_non_orientable_polygon (poly)) { // non-orientable polygons generate an error @@ -620,26 +620,41 @@ ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db db::split_polygon (poly, split_polygons); for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { - reduce (*sp, trans, region, complex_region, target, false); + reduce (*sp, prop_id, trans, region, complex_region, target, false); } } else { - mp_pipe->push (poly, trans, region, complex_region, target); + mp_pipe->push (poly, prop_id, trans, region, complex_region, target); } } // --------------------------------------------------------------------------------------------- -PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name) +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, const db::Layout *source_layout, int text_enlargement, const tl::Variant &text_prop_name) : mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0) { if (! text_prop_name.is_nil ()) { m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name); m_make_text_prop = true; } + + if (source_layout && source_layout != layout) { + m_pm.set_source (source_layout); + m_pm.set_target (layout); + } } -void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +void PolygonReferenceHierarchyBuilderShapeReceiver::make_pref (db::Shapes *target, const db::Polygon &poly, db::properties_id_type prop_id) +{ + prop_id = m_pm (prop_id); + if (prop_id != 0) { + target->insert (db::PolygonRefWithProperties (db::PolygonRef (poly, mp_layout->shape_repository ()), prop_id)); + } else { + target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + } +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { @@ -652,25 +667,35 @@ void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape // NOTE: as this is a specialized receiver for the purpose of building region // representations we don't need empty polygons here if (poly.area2 () > 0) { - target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + make_pref (target, poly, prop_id); } } else if (shape.is_text () && m_text_enlargement >= 0) { + // Texts generate small marker shapes with text_enlargement defining the box + db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement)); if (! trans.is_unity ()) { poly.transform (trans); } db::PolygonRef pref (poly, mp_layout->shape_repository ()); + db::properties_id_type pid; + if (m_make_text_prop) { + // NOTE: text properties override the prop_id passed down from the hierarchy builder when generating the + // text marker shape db::PropertiesRepository::properties_set ps; ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ()))); - db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps); + pid = mp_layout->properties_repository ().properties_id (ps); + } else { + pid = m_pm (prop_id); + } + + if (pid != 0) { target->insert (db::PolygonRefWithProperties (pref, pid)); - } else { target->insert (pref); } @@ -678,89 +703,126 @@ void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape } } -void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (shape.area () > 0) { - target->insert (db::PolygonRef (db::Polygon (shape.transformed (trans)), mp_layout->shape_repository ())); + make_pref (target, db::Polygon (shape).transformed (trans), prop_id); } } -void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (shape.area2 () > 0) { - target->insert (db::PolygonRef (shape.transformed (trans), mp_layout->shape_repository ())); + make_pref (target, shape.transformed (trans), prop_id); } } // --------------------------------------------------------------------------------------------- -EdgeBuildingHierarchyBuilderShapeReceiver::EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges) +EdgeBuildingHierarchyBuilderShapeReceiver::EdgeBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const db::Layout *source_layout, bool as_edges) : m_as_edges (as_edges) { - // .. nothing yet .. + if (source_layout && source_layout != layout) { + m_pm.set_source (source_layout); + m_pm.set_target (layout); + } } -void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { if (m_as_edges && (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ())) { db::Polygon poly; shape.polygon (poly); - push (poly, trans, region, complex_region, target); + push (poly, prop_id, trans, region, complex_region, target); } else if (m_as_edges && shape.is_box ()) { - push (shape.box (), trans, region, complex_region, target); + push (shape.box (), prop_id, trans, region, complex_region, target); } else if (shape.is_edge ()) { - target->insert (shape.edge ()); + prop_id = m_pm (prop_id); + if (prop_id != 0) { + target->insert (db::EdgeWithProperties (shape.edge (), shape.prop_id ())); + } else { + target->insert (shape.edge ()); + } } } -void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Box &box, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Box &box, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (m_as_edges && ! box.empty ()) { - target->insert (db::Edge (box.p1 (), box.upper_left ()).transformed (trans)); - target->insert (db::Edge (box.upper_left (), box.p2 ()).transformed (trans)); - target->insert (db::Edge (box.p2 (), box.lower_right ()).transformed (trans)); - target->insert (db::Edge (box.lower_right (), box.p1 ()).transformed (trans)); + prop_id = m_pm (prop_id); + if (prop_id != 0) { + target->insert (db::EdgeWithProperties (db::Edge (box.p1 (), box.upper_left ()).transformed (trans), prop_id)); + target->insert (db::EdgeWithProperties (db::Edge (box.upper_left (), box.p2 ()).transformed (trans), prop_id)); + target->insert (db::EdgeWithProperties (db::Edge (box.p2 (), box.lower_right ()).transformed (trans), prop_id)); + target->insert (db::EdgeWithProperties (db::Edge (box.lower_right (), box.p1 ()).transformed (trans), prop_id)); + } else { + target->insert (db::Edge (box.p1 (), box.upper_left ()).transformed (trans)); + target->insert (db::Edge (box.upper_left (), box.p2 ()).transformed (trans)); + target->insert (db::Edge (box.p2 (), box.lower_right ()).transformed (trans)); + target->insert (db::Edge (box.lower_right (), box.p1 ()).transformed (trans)); + } } } -void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Polygon &poly, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (m_as_edges) { + prop_id = m_pm (prop_id); for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - target->insert ((*e).transformed (trans)); + if (prop_id != 0) { + target->insert (db::EdgeWithProperties ((*e).transformed (trans), prop_id)); + } else { + target->insert ((*e).transformed (trans)); + } } } } // --------------------------------------------------------------------------------------------- -EdgePairBuildingHierarchyBuilderShapeReceiver::EdgePairBuildingHierarchyBuilderShapeReceiver () +EdgePairBuildingHierarchyBuilderShapeReceiver::EdgePairBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const db::Layout *source_layout) { - // .. nothing yet .. + if (source_layout && source_layout != layout) { + m_pm.set_source (source_layout); + m_pm.set_target (layout); + } } -void EdgePairBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target) +void EdgePairBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target) { if (shape.is_edge_pair ()) { - target->insert (shape.edge_pair ().transformed (trans)); + prop_id = m_pm (prop_id); + if (prop_id != 0) { + target->insert (db::EdgePairWithProperties (shape.edge_pair ().transformed (trans), prop_id)); + } else { + target->insert (shape.edge_pair ().transformed (trans)); + } } } // --------------------------------------------------------------------------------------------- -TextBuildingHierarchyBuilderShapeReceiver::TextBuildingHierarchyBuilderShapeReceiver (db::Layout *layout) +TextBuildingHierarchyBuilderShapeReceiver::TextBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const db::Layout *source_layout) : mp_layout (layout) { - // .. nothing yet .. + if (source_layout && source_layout != layout) { + m_pm.set_source (source_layout); + m_pm.set_target (layout); + } } -void TextBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target) +void TextBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target) { if (shape.is_text ()) { // NOTE: we intentionally skip all the text attributes (font etc.) here because in the context // of a text collections we're only interested in the locations. db::Text t (shape.text_string (), shape.text_trans ()); - target->insert (db::TextRef (t.transformed (trans), mp_layout->shape_repository ())); + prop_id = m_pm (prop_id); + if (prop_id != 0) { + target->insert (db::TextRefWithProperties (db::TextRef (t.transformed (trans), mp_layout->shape_repository ()), prop_id)); + } else { + target->insert (db::TextRef (t.transformed (trans), mp_layout->shape_repository ())); + } } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index a248e4cd4..4cd47efce 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -27,6 +27,7 @@ #include "dbRecursiveShapeIterator.h" #include "dbLayout.h" +#include "dbLayoutUtils.h" #include #include @@ -55,9 +56,9 @@ public: HierarchyBuilderShapeReceiver () { } virtual ~HierarchyBuilderShapeReceiver () { } - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; }; /** @@ -69,28 +70,45 @@ class DB_PUBLIC HierarchyBuilderShapeInserter public: HierarchyBuilderShapeInserter () { } - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + virtual void push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { - tl::ident_map pm; + tl::const_map pm (prop_id); target->insert (shape, trans, pm); } - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (trans.is_ortho ()) { - target->insert (shape.transformed (trans)); + if (prop_id != 0) { + target->insert (db::BoxWithProperties (shape.transformed (trans), prop_id)); + } else { + target->insert (shape.transformed (trans)); + } } else { - db::Polygon poly (shape); - target->insert (poly.transformed (trans)); + if (prop_id != 0) { + db::PolygonWithProperties poly (db::Polygon (shape), prop_id); + target->insert (poly.transformed (trans)); + } else { + db::Polygon poly (shape); + target->insert (poly.transformed (trans)); + } } } - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (trans.is_unity ()) { - target->insert (shape); + if (prop_id != 0) { + target->insert (db::PolygonWithProperties (shape, prop_id)); + } else { + target->insert (shape); + } } else { - target->insert (shape.transformed (trans)); + if (prop_id != 0) { + target->insert (db::PolygonWithProperties (shape.transformed (trans), prop_id)); + } else { + target->insert (shape.transformed (trans)); + } } } }; @@ -104,13 +122,13 @@ class DB_PUBLIC ClippingHierarchyBuilderShapeReceiver public: ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); private: - void insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); - void insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + void insert_clipped (const db::Box &box, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + void insert_clipped (const db::Polygon &poly, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); static bool is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); static bool is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); @@ -126,12 +144,12 @@ class DB_PUBLIC ReducingHierarchyBuilderShapeReceiver public: ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0, double area_ratio = 3.0, size_t max_vertex_count = 16, bool reject_odd_polygons = false); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); private: - void reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target, bool check = true); + void reduce (const db::Polygon &poly, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target, bool check = true); HierarchyBuilderShapeReceiver *mp_pipe; double m_area_ratio; @@ -146,17 +164,20 @@ class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ()); + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, const Layout *source_layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ()); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Shape &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); private: + void make_pref (db::Shapes *target, const db::Polygon &polygon, db::properties_id_type prop_id); + db::Layout *mp_layout; int m_text_enlargement; bool m_make_text_prop; db::property_names_id_type m_text_prop_id; + db::PropertyMapper m_pm; }; /** @@ -166,14 +187,15 @@ class DB_PUBLIC EdgeBuildingHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges); + EdgeBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const Layout *source_layout, bool as_edges); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Shape &shape, properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, db::properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); private: bool m_as_edges; + db::PropertyMapper m_pm; }; /** @@ -183,11 +205,14 @@ class DB_PUBLIC EdgePairBuildingHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - EdgePairBuildingHierarchyBuilderShapeReceiver (); + EdgePairBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const Layout *source_layout); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } - virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Shape &shape, properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + +private: + db::PropertyMapper m_pm; }; /** @@ -197,14 +222,15 @@ class DB_PUBLIC TextBuildingHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - TextBuildingHierarchyBuilderShapeReceiver (db::Layout *layout); + TextBuildingHierarchyBuilderShapeReceiver (db::Layout *layout, const Layout *source_layout); - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); - virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } - virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Shape &shape, properties_id_type prop_id, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } private: db::Layout *mp_layout; + db::PropertyMapper m_pm; }; /** diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index 3b35358a4..d52ec900e 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -670,11 +670,11 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout na.properties_repository () = a.properties_repository (); nb.properties_repository () = b.properties_repository (); - db::PropertyMapper prop_normalize_a (n, a); - db::PropertyMapper prop_normalize_b (n, b); + db::PropertyMapper prop_normalize_a (&n, &a); + db::PropertyMapper prop_normalize_b (&n, &b); - db::PropertyMapper prop_remap_to_a (na, n); - db::PropertyMapper prop_remap_to_b (nb, n); + db::PropertyMapper prop_remap_to_a (&na, &n); + db::PropertyMapper prop_remap_to_b (&nb, &n); // compare layers diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index fce467f85..83395e612 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -38,7 +38,10 @@ namespace db { -// the iterator provides the hierarchical selection (enabling/disabling cells etc.) +// ----------------------------------------------------------------------------------------------- +// LayoutToNetlist implementation + +// Note: the iterator provides the hierarchical selection (enabling/disabling cells etc.) LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) @@ -155,9 +158,7 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n) si.shape_flags (db::ShapeIterator::Nothing); std::unique_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -168,9 +169,7 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st si.shape_flags (db::ShapeIterator::All); std::unique_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -181,9 +180,7 @@ db::Texts *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std si.shape_flags (db::ShapeIterator::Texts); std::unique_ptr texts (new db::Texts (si, dss ())); - if (! n.empty ()) { - register_layer (*texts, n); - } + register_layer (*texts, n); return texts.release (); } @@ -194,9 +191,7 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); std::unique_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -264,7 +259,7 @@ void LayoutToNetlist::connect (const db::Region &l) reset_extracted (); if (! is_persisted (l)) { - register_layer (l, make_new_name ()); + register_layer (l); } // we need to keep a reference, so we can safely delete the region @@ -279,10 +274,10 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap reset_extracted (); if (! is_persisted (a)) { - register_layer (a, make_new_name ()); + register_layer (a); } if (! is_persisted (b)) { - register_layer (b, make_new_name ()); + register_layer (b); } // we need to keep a reference, so we can safely delete the region @@ -299,7 +294,7 @@ size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const reset_extracted (); if (! is_persisted (l)) { - register_layer (l, make_new_name ()); + register_layer (l); } // we need to keep a reference, so we can safely delete the region @@ -430,6 +425,8 @@ void LayoutToNetlist::mem_stat (MemStatistics *stat, MemStatistics::purpose_t pu db::mem_stat (stat, purpose, cat, m_dlrefs, true, (void *) this); db::mem_stat (stat, purpose, cat, m_named_regions, true, (void *) this); db::mem_stat (stat, purpose, cat, m_name_of_layer, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_region_by_original, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_region_of_layer, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_net_names, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_net_names_per_cell, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_nets, true, (void *) this); @@ -534,11 +531,29 @@ db::Region *LayoutToNetlist::layer_by_name (const std::string &name) db::Region *LayoutToNetlist::layer_by_index (unsigned int index) { - std::map::const_iterator n = m_name_of_layer.find (index); - if (n == m_name_of_layer.end ()) { + auto n = m_region_of_layer.find (index); + if (n == m_region_of_layer.end ()) { return 0; } else { - return layer_by_name (n->second); + return new db::Region (new db::DeepRegion (n->second)); + } +} + +db::Region *LayoutToNetlist::layer_by_original (const ShapeCollectionDelegateBase *original_delegate) +{ + auto n = m_region_by_original.find (tl::id_of (original_delegate)); + if (n == m_region_by_original.end ()) { + + DeepShapeCollectionDelegateBase *dl = const_cast (original_delegate)->deep (); + if (dl && dl->deep_layer ().store () == mp_dss.get ()) { + // implicitly original because the collection is inside our DSS + return new db::Region (new db::DeepRegion (dl->deep_layer ())); + } else { + return 0; + } + + } else { + return new db::Region (new db::DeepRegion (n->second)); } } @@ -565,12 +580,19 @@ std::string LayoutToNetlist::name (const ShapeCollection &coll) const } } -void LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n) +void LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in) { - if (m_named_regions.find (n) != m_named_regions.end ()) { - throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n); + if (m_region_by_original.find (tl::id_of (collection.get_delegate ())) != m_region_by_original.end ()) { + throw tl::Exception (tl::to_string (tr ("The layer is already registered"))); } + if (! n_in.empty () && m_named_regions.find (n_in) != m_named_regions.end ()) { + throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n_in); + } + + // Caution: this make create names which clash with future explicit names. Hopefully, the generated names are unique enough. + std::string n = n_in.empty () ? make_new_name () : n_in; + db::DeepLayer dl; if (m_is_flat) { @@ -586,17 +608,14 @@ void LayoutToNetlist::register_layer (const ShapeCollection &collection, const s } else { - if (is_persisted (collection)) { - std::string prev_name = name (collection); - m_named_regions.erase (prev_name); - } - dl = delegate->deep_layer (); } } + m_region_by_original [tl::id_of (collection.get_delegate ())] = dl; + m_region_of_layer [dl.layer ()] = dl; m_named_regions [n] = dl; m_name_of_layer [dl.layer ()] = n; } @@ -618,6 +637,17 @@ db::DeepLayer LayoutToNetlist::deep_layer_of (const db::ShapeCollection &coll) c } } +bool LayoutToNetlist::is_persisted_impl (const db::ShapeCollection &coll) const +{ + if (coll.get_delegate ()->deep () && coll.get_delegate ()->deep ()->deep_layer ().store () == mp_dss.get ()) { + // implicitly persisted because the collection is inside our DSS + return true; + } else { + // explicitly persisted through "register" + return m_region_by_original.find (tl::id_of (coll.get_delegate ())) != m_region_by_original.end (); + } +} + db::CellMapping LayoutToNetlist::make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector *nets, bool with_device_cells) { std::set device_cells; @@ -742,28 +772,28 @@ static bool deliver_shape (const db::NetShape &s, db::Shapes &shapes, const Tr & db::PolygonRef pr = s.polygon_ref (); - if (pr.obj ().is_box ()) { + db::Layout *layout = shapes.layout (); + if (layout) { + // NOTE: by maintaining the PolygonRefs we can directly use the output of "build_nets" as input + // for a hierarchical processor. + db::PolygonRef polygon_ref (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ()); + if (propid) { + shapes.insert (db::PolygonRefWithProperties (polygon_ref, propid)); + } else { + shapes.insert (polygon_ref); + } + } else if (pr.obj ().is_box ()) { if (propid) { shapes.insert (db::BoxWithProperties (pr.obj ().box ().transformed (pr.trans ()).transformed (tr), propid)); } else { shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); } } else { - db::Layout *layout = shapes.layout (); - if (layout) { - db::PolygonRef polygon_ref (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ()); - if (propid) { - shapes.insert (db::PolygonRefWithProperties (polygon_ref, propid)); - } else { - shapes.insert (polygon_ref); - } + db::Polygon polygon (pr.obj ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + shapes.insert (db::PolygonWithProperties (polygon, propid)); } else { - db::Polygon polygon (pr.obj ().transformed (pr.trans ()).transformed (tr)); - if (propid) { - shapes.insert (db::PolygonWithProperties (polygon, propid)); - } else { - shapes.insert (polygon); - } + shapes.insert (polygon); } } @@ -850,7 +880,7 @@ static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const return true; } -void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid) const +void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const { unsigned int lid = layer_of (of_layer); const db::Circuit *circuit = net.circuit (); @@ -859,10 +889,10 @@ void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_la std::map lmap; lmap [lid] = &to; - deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, db::ICplxTrans (), propid); + deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, trans, propid); } -db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const +db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, const db::ICplxTrans &trans) const { unsigned int lid = layer_of (of_layer); const db::Circuit *circuit = net.circuit (); @@ -872,268 +902,44 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region std::map lmap; lmap [lid] = res.get (); - deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, db::ICplxTrans (), 0); + deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, trans, 0); return res.release (); } void -LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const { - const db::Circuit *circuit = net.circuit (); - tl_assert (circuit != 0); + NetBuilder builder (&target, this); + builder.set_hier_mode (hier_mode); + builder.set_cell_name_prefix (cell_name_prefix); + builder.set_device_cell_name_prefix (device_cell_name_prefix); - build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, tr); + builder.build_net (target_cell, net, lmap, net_prop_mode, netname_prop); } void -LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map &lmap, const db::Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { - db::Cell *target_cell = &tc; + NetBuilder builder (&target, cmap, this); + builder.set_hier_mode (hier_mode); + builder.set_net_cell_name_prefix (net_cell_name_prefix); + builder.set_cell_name_prefix (circuit_cell_name_prefix); + builder.set_device_cell_name_prefix (device_cell_name_prefix); - if (net_cell_name_prefix) { - - const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (ci); - - bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty (); - if (! any_connections) { - - StopOnFirst sof; - std::map sof_lmap; - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - sof_lmap.insert (std::make_pair (layer_of (*l->second), &sof)); - } - } - - bool consider_cell = ! deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, sof_lmap, tr, 0); - if (! consider_cell) { - // shortcut if cell is empty -> no net cell will be produced - return; - } - - } - - // make a specific cell for the net if requested - - target_cell = &target.cell (target.add_cell ((std::string (net_cell_name_prefix) + net->expanded_name ()).c_str ())); - tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ())); - - } - - std::map target_lmap; - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - target_lmap.insert (std::make_pair (layer_of (*l->second), &target_cell->shapes (l->first))); - } - } - - deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, target_lmap, tr, netname_propid); - - if (hier_mode != BNH_SubcircuitCells && ! device_cell_name_prefix) { - return; - } - - // NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance - // (we want to avoid magnified instances) - db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ()); - db::ICplxTrans tr_mag (tr.mag ()); - - const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (ci); - typedef db::connected_clusters::connections_type connections_type; - const connections_type &connections = clusters.connections_for_cluster (cid); - for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { - - db::cell_index_type subci = c->inst_cell_index (); - size_t subcid = c->id (); - - CellReuseTableKey cmap_key (subci, netname_propid, subcid); - - cell_reuse_table_type::const_iterator cm = reuse_table.find (cmap_key); - if (cm == reuse_table.end ()) { - - const char *name_prefix = 0; - if (mp_netlist->device_abstract_by_cell_index (subci)) { - name_prefix = device_cell_name_prefix; - } else { - name_prefix = circuit_cell_name_prefix; - } - - if (name_prefix) { - - std::string cell_name = internal_layout ()->cell_name (subci); - - db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); - cm = reuse_table.insert (std::make_pair (cmap_key, target_ci)).first; - - build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, reuse_table, tr_mag); - - } else { - cm = reuse_table.insert (std::make_pair (cmap_key, std::numeric_limits::max ())).first; - } - - } - - if (cm->second != std::numeric_limits::max ()) { - db::CellInstArray ci (db::CellInst (cm->second), tr_wo_mag * c->inst_trans ()); - ci.transform_into (tr_mag); - target_cell->insert (ci); - } - - } -} - -db::properties_id_type -LayoutToNetlist::make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const -{ - if (! netname_prop.is_nil () || net.begin_properties () != net.end_properties ()) { - - db::PropertiesRepository::properties_set propset; - - // add the user properties too (TODO: make this configurable?) - for (db::Net::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { - db::property_names_id_type key_propnameid = ly.properties_repository ().prop_name_id (p->first); - propset.insert (std::make_pair (key_propnameid, p->second)); - } - - if (! netname_prop.is_nil ()) { - db::property_names_id_type name_propnameid = ly.properties_repository ().prop_name_id (netname_prop); - propset.insert (std::make_pair (name_propnameid, tl::Variant (net.expanded_name ()))); - } - - return ly.properties_repository ().properties_id (propset); - - } else { - return 0; - } + builder.build_nets (0, lmap, net_prop_mode, netname_prop); } void -LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { - if (! m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); - } + NetBuilder builder (&target, cmap, this); + builder.set_hier_mode (hier_mode); + builder.set_net_cell_name_prefix (net_cell_name_prefix); + builder.set_cell_name_prefix (circuit_cell_name_prefix); + builder.set_device_cell_name_prefix (device_cell_name_prefix); - cell_reuse_table_type cell_reuse_table; - - double mag = internal_layout ()->dbu () / target.dbu (); - - db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, net); - build_net_rec (net, target, target_cell, lmap, 0, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans (mag)); -} - -void -LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const -{ - build_nets (0, cmap, target, lmap, net_cell_name_prefix, netname_prop, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix); -} - -void -LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::cell_index_type circuit_cell, const db::CellMapping &cmap, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const -{ - if (! cmap.has_mapping (circuit_cell)) { - - const db::Cell &cc = internal_layout ()->cell (circuit_cell); - - for (db::Cell::parent_inst_iterator p = cc.begin_parent_insts (); ! p.at_end (); ++p) { - - db::CellInstArray ci = p->child_inst ().cell_inst (); - for (db::CellInstArray::iterator ia = ci.begin (); ! ia.at_end(); ++ia) { - - db::ICplxTrans tr_parent = ci.complex_trans (*ia) * tr; - build_net_rec (net, target, p->parent_cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, tr_parent); - - } - - } - - } else { - - double mag = internal_layout ()->dbu () / target.dbu (); - - db::cell_index_type target_ci = cmap.cell_mapping (circuit_cell); - build_net_rec (net, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, db::ICplxTrans (mag) * tr); - - } -} - -void -LayoutToNetlist::build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const -{ - if (! m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); - } - - std::set net_set; - if (nets) { - net_set.insert (nets->begin (), nets->end ()); - } - - cell_reuse_table_type cell_reuse_table; - - const db::Netlist *netlist = mp_netlist.get (); - for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { - - bool is_top_circuit = c->begin_parents () == c->end_parents (); - - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - - // exclude local nets in recursive mode except if they are explicitly selected - if (! nets && hier_mode != BNH_Disconnected && ! is_top_circuit && n->pin_count () > 0) { - continue; - } - - if (! nets || net_set.find (n.operator-> ()) != net_set.end ()) { - db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n); - build_net_rec (*n, target, c->cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans ()); - } - - } - - if (hier_mode != BNH_Disconnected && ! nets) { - - // With recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will - // get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from - // subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because - // this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net" - // - // In explicit selection mode we don't care about this as nets are explicitly taken or not. - - const db::Circuit &circuit = *c; - for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) { - - const db::SubCircuit &subcircuit = *sc; - for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { - - if (! subcircuit.net_for_pin (p->id ())) { - - const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ()); - if (n) { - - double dbu = target.dbu (); - db::ICplxTrans tr = db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu); - - db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n); - - if (net_cell_name_prefix) { - std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":"; - build_net_rec (*n, target, c->cell_index (), cmap, lmap, ncn.c_str (), netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr); - } else { - build_net_rec (*n, target, c->cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr); - } - - } - - } - - } - - } - - } - - } + builder.build_nets (nets, lmap, net_prop_mode, netname_prop); } db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point, std::vector *sc_path_out, db::Circuit *initial_circuit) @@ -1692,4 +1498,400 @@ void LayoutToNetlist::set_generator (const std::string &g) m_generator = g; } +// ----------------------------------------------------------------------------------------------- +// NetBuilder implementation + +NetBuilder::NetBuilder () + : m_hier_mode (BNH_Flatten), m_has_net_cell_name_prefix (false), m_has_cell_name_prefix (false), m_has_device_cell_name_prefix (false) +{ + // .. nothing yet .. +} + +NetBuilder::NetBuilder (db::Layout *target, const db::CellMapping &cmap, const db::LayoutToNetlist *source) + : mp_target (target), m_cmap (cmap), mp_source (const_cast (source)), + m_hier_mode (BNH_Flatten), m_has_net_cell_name_prefix (false), m_has_cell_name_prefix (false), m_has_device_cell_name_prefix (false) +{ + // .. nothing yet .. +} + +NetBuilder::NetBuilder (db::Layout *target, const db::LayoutToNetlist *source) + : mp_target (target), mp_source (const_cast (source)), + m_hier_mode (BNH_Flatten), m_has_net_cell_name_prefix (false), m_has_cell_name_prefix (false), m_has_device_cell_name_prefix (false) +{ + // .. nothing yet .. +} + +NetBuilder::NetBuilder (const db::NetBuilder &other) +{ + operator=(other); +} + +NetBuilder::NetBuilder (db::NetBuilder &&other) +{ + operator=(other); +} + +NetBuilder & +NetBuilder::operator= (const db::NetBuilder &other) +{ + if (this != &other) { + mp_target = other.mp_target; + mp_source = other.mp_source; + m_cmap = other.m_cmap; + m_reuse_table = other.m_reuse_table; + m_hier_mode = other.m_hier_mode; + m_has_net_cell_name_prefix = other.m_has_net_cell_name_prefix; + m_net_cell_name_prefix = other.m_net_cell_name_prefix; + m_has_cell_name_prefix = other.m_has_cell_name_prefix; + m_cell_name_prefix = other.m_cell_name_prefix; + m_has_device_cell_name_prefix = other.m_has_device_cell_name_prefix; + m_device_cell_name_prefix = other.m_device_cell_name_prefix; + } + + return *this; +} + +NetBuilder & +NetBuilder::operator= (db::NetBuilder &&other) +{ + if (this != &other) { + mp_target = other.mp_target; + other.mp_target.reset (0); + mp_source = other.mp_source; + other.mp_source.reset (0); + m_cmap.swap (other.m_cmap); + m_reuse_table.swap (other.m_reuse_table); + std::swap (m_hier_mode, other.m_hier_mode); + std::swap (m_has_net_cell_name_prefix, other.m_has_net_cell_name_prefix); + m_net_cell_name_prefix.swap (other.m_net_cell_name_prefix); + std::swap (m_has_cell_name_prefix, other.m_has_cell_name_prefix); + m_cell_name_prefix.swap (other.m_cell_name_prefix); + std::swap (m_has_device_cell_name_prefix, other.m_has_device_cell_name_prefix); + m_device_cell_name_prefix.swap (other.m_device_cell_name_prefix); + } + + return *this; +} + +void +NetBuilder::set_net_cell_name_prefix (const char *s) +{ + m_has_net_cell_name_prefix = (s != 0); + m_net_cell_name_prefix = std::string (s ? s : ""); +} + +void +NetBuilder::set_cell_name_prefix (const char *s) +{ + bool has_cell_name_prefix = (s != 0); + std::string cell_name_prefix (s ? s : ""); + + if (has_cell_name_prefix != m_has_cell_name_prefix || cell_name_prefix != m_cell_name_prefix) { + m_reuse_table.clear (); + m_has_cell_name_prefix = has_cell_name_prefix; + m_cell_name_prefix = cell_name_prefix; + } +} + +void +NetBuilder::set_device_cell_name_prefix (const char *s) +{ + bool has_device_cell_name_prefix = (s != 0); + std::string device_cell_name_prefix (s ? s : ""); + + if (has_device_cell_name_prefix != m_has_device_cell_name_prefix || device_cell_name_prefix != m_device_cell_name_prefix) { + m_reuse_table.clear (); + m_has_device_cell_name_prefix = has_device_cell_name_prefix; + m_device_cell_name_prefix = device_cell_name_prefix; + } +} + +void +NetBuilder::prepare_build_nets () const +{ + tl_assert (mp_target.get ()); + tl_assert (mp_source.get ()); + + if (! mp_source->is_netlist_extracted ()) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + // Resets the "initialized" flag so existing cells are reused but freshly filled + for (auto i = m_reuse_table.begin (); i != m_reuse_table.end (); ++i) { + i->second.second = false; + } +} + +void +NetBuilder::build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const +{ + prepare_build_nets (); + + double mag = mp_source->internal_layout ()->dbu () / mp_target->dbu (); + + db::properties_id_type netname_propid = make_netname_propid (target ().properties_repository (), net_prop_mode, netname_prop, net); + build_net_rec (net, target_cell, lmap, std::string (), netname_propid, db::ICplxTrans (mag)); +} + +void +NetBuilder::build_all_nets (const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const +{ + build_nets (0, lmap, net_prop_mode, netname_prop); +} + +void +NetBuilder::build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const +{ + prepare_build_nets (); + + std::set net_set; + if (nets) { + net_set.insert (nets->begin (), nets->end ()); + } + + const db::Netlist *netlist = mp_source->netlist (); + for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + + bool is_top_circuit = c->begin_parents () == c->end_parents (); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // exclude local nets in recursive mode except if they are explicitly selected + if (! nets && m_hier_mode != BNH_Disconnected && ! is_top_circuit && n->pin_count () > 0) { + continue; + } + + if (! nets || net_set.find (n.operator-> ()) != net_set.end ()) { + db::properties_id_type netname_propid = make_netname_propid (target ().properties_repository (), prop_mode, netname_prop, *n); + build_net_rec (*n, c->cell_index (), lmap, std::string (), netname_propid, db::ICplxTrans ()); + } + + } + + if (m_hier_mode != BNH_Disconnected && ! nets) { + + // With recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will + // get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from + // subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because + // this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net" + // + // In explicit selection mode we don't care about this as nets are explicitly taken or not. + + const db::Circuit &circuit = *c; + for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) { + + const db::SubCircuit &subcircuit = *sc; + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + + if (! subcircuit.net_for_pin (p->id ())) { + + const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ()); + if (n) { + + double dbu = mp_target->dbu (); + db::ICplxTrans tr = db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu); + + std::string net_name_prefix = subcircuit.expanded_name () + ":"; + db::properties_id_type netname_propid = make_netname_propid (target ().properties_repository (), prop_mode, netname_prop, *n, net_name_prefix); + + build_net_rec (*n, c->cell_index (), lmap, net_name_prefix, netname_propid, tr); + + } + + } + + } + + } + + } + + } +} + +void +NetBuilder::build_net_rec (const db::Net &net, db::Cell &target_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const +{ + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + build_net_rec (circuit->cell_index (), net.cluster_id (), target_cell, lmap, &net, add_net_cell_name_prefix, netname_propid, tr); +} + +void +NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, const std::map &lmap, const db::Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const +{ + db::Cell *target_cell = &tc; + + if (net && m_has_net_cell_name_prefix) { + + const db::connected_clusters &ccl = mp_source->net_clusters ().clusters_per_cell (ci); + + bool any_connections = m_has_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty (); + if (! any_connections) { + + StopOnFirst sof; + std::map sof_lmap; + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + if (l->second) { + sof_lmap.insert (std::make_pair (mp_source->layer_of (*l->second), &sof)); + } + } + + bool consider_cell = ! deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, sof_lmap, tr, 0); + if (! consider_cell) { + // shortcut if cell is empty -> no net cell will be produced + return; + } + + } + + // make a specific cell for the net if requested + + target_cell = &target ().cell (target ().add_cell ((m_net_cell_name_prefix + add_net_cell_name_prefix + net->expanded_name ()).c_str ())); + tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ())); + + } + + std::map target_lmap; + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + if (l->second) { + target_lmap.insert (std::make_pair (mp_source->layer_of (*l->second), &target_cell->shapes (l->first))); + } + } + + deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, target_lmap, tr, netname_propid); + + if (m_hier_mode != BNH_SubcircuitCells && ! m_has_device_cell_name_prefix) { + return; + } + + // NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance + // (we want to avoid magnified instances) + db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ()); + db::ICplxTrans tr_mag (tr.mag ()); + + const db::connected_clusters &clusters = mp_source->net_clusters ().clusters_per_cell (ci); + typedef db::connected_clusters::connections_type connections_type; + const connections_type &connections = clusters.connections_for_cluster (cid); + for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { + + db::cell_index_type subci = c->inst_cell_index (); + size_t subcid = c->id (); + + CellReuseTableKey cmap_key (subci, netname_propid, subcid); + + cell_reuse_table_type::iterator cm = m_reuse_table.find (cmap_key); + if (cm == m_reuse_table.end ()) { + + bool has_name_prefix = false; + std::string name_prefix; + if (mp_source->netlist ()->device_abstract_by_cell_index (subci)) { + name_prefix = m_device_cell_name_prefix; + has_name_prefix = m_has_device_cell_name_prefix; + } else { + name_prefix = m_cell_name_prefix; + has_name_prefix = m_has_cell_name_prefix; + } + + if (has_name_prefix) { + + std::string cell_name = mp_source->internal_layout ()->cell_name (subci); + + db::cell_index_type target_ci = target ().add_cell ((name_prefix + cell_name).c_str ()); + cm = m_reuse_table.insert (std::make_pair (cmap_key, std::make_pair (target_ci, true))).first; + + build_net_rec (subci, subcid, target ().cell (target_ci), lmap, 0, std::string (), netname_propid, tr_mag); + + } else { + cm = m_reuse_table.insert (std::make_pair (cmap_key, std::make_pair (std::numeric_limits::max (), false))).first; + } + + } else if (!cm->second.second && cm->second.first != std::numeric_limits::max ()) { + + // initialize cell (after reuse of the net builder) + build_net_rec (subci, subcid, target ().cell (cm->second.first), lmap, 0, std::string (), netname_propid, tr_mag); + cm->second.second = true; + + } + + if (cm->second.first != std::numeric_limits::max ()) { + db::CellInstArray ci (db::CellInst (cm->second.first), tr_wo_mag * c->inst_trans ()); + ci.transform_into (tr_mag); + target_cell->insert (ci); + } + + } +} + +void +NetBuilder::build_net_rec (const db::Net &net, db::cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const +{ + if (! m_cmap.has_mapping (circuit_cell)) { + + const db::Cell &cc = mp_source->internal_layout ()->cell (circuit_cell); + + for (db::Cell::parent_inst_iterator p = cc.begin_parent_insts (); ! p.at_end (); ++p) { + + db::CellInstArray ci = p->child_inst ().cell_inst (); + for (db::CellInstArray::iterator ia = ci.begin (); ! ia.at_end(); ++ia) { + + db::ICplxTrans tr_parent = ci.complex_trans (*ia) * tr; + build_net_rec (net, p->parent_cell_index (), lmap, add_net_cell_name_prefix, netname_propid, tr_parent); + + } + + } + + } else { + + double mag = mp_source->internal_layout ()->dbu () / mp_target->dbu (); + + db::cell_index_type target_ci = m_cmap.cell_mapping (circuit_cell); + build_net_rec (net, target ().cell (target_ci), lmap, add_net_cell_name_prefix, netname_propid, db::ICplxTrans (mag) * tr); + + } +} + +db::properties_id_type +NetBuilder::make_netname_propid (db::PropertiesRepository &pr, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, const db::Net &net, const std::string &net_name_prefix) +{ + if (net_prop_mode == NPM_NoProperties) { + + return 0; + + } else if (! netname_prop.is_nil () || (net_prop_mode == NPM_AllProperties && net.begin_properties () != net.end_properties ())) { + + db::PropertiesRepository::properties_set propset; + + // add the user properties too (TODO: make this configurable?) + for (db::Net::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { + db::property_names_id_type key_propnameid = pr.prop_name_id (p->first); + propset.insert (std::make_pair (key_propnameid, p->second)); + } + + if (! netname_prop.is_nil ()) { + db::property_names_id_type name_propnameid = pr.prop_name_id (netname_prop); + if (net_prop_mode == NPM_NetQualifiedNameOnly) { + std::vector l; + l.reserve (2); + l.push_back (tl::Variant (net_name_prefix + net.expanded_name ())); + l.push_back (tl::Variant (net.circuit ()->name ())); + propset.insert (std::make_pair (name_propnameid, tl::Variant (l))); + } else if (net_prop_mode == NPM_NetIDOnly) { + propset.insert (std::make_pair (name_propnameid, tl::Variant (reinterpret_cast (&net)))); + } else { + propset.insert (std::make_pair (name_propnameid, tl::Variant (net_name_prefix + net.expanded_name ()))); + } + } + + return pr.properties_id (propset); + + } else { + + return 0; + + } +} + } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index a032b38d5..e6bb022af 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -27,11 +27,14 @@ #include "dbCellMapping.h" #include "dbNetlistExtractor.h" #include "dbNetlistDeviceExtractor.h" +#include "dbLayoutToNetlistEnums.h" #include "tlGlobPattern.h" namespace db { +class NetlistBuilder; + /** * @brief A generic framework for extracting netlists from layouts * @@ -234,14 +237,22 @@ public: double device_scaling () const; /** - * @brief Register a layer under the given name - * This is a formal name for the layer. Using a name or layer properties - * (see below) enhances readability of backannotated information + * @brief Register a layer, optionally under the given name + * Using a name or layer properties (see below) enhances readability of backannotated information * if layers are involved. Use this method to attach a name to a region * derived by boolean operations for example. - * Named regions are persisted inside the LayoutToNetlist object. + * + * Registered regions are persisted inside the LayoutToNetlist object + * if they are flat or original layer regions. + * This allows passing flat or original layer collections. + * + * If no name is given, the region will not be registered under a name. + * Still the collection will be persisted if required. + * + * In addition to regions, text collections can be registered too. + * Including texts in "connect" makes net names begin assigned from the text strings. */ - void register_layer (const ShapeCollection &collection, const std::string &name); + void register_layer (const ShapeCollection &collection, const std::string &name = std::string ()); /** * @brief Gets the name of the given collection @@ -276,7 +287,7 @@ public: template bool is_persisted (const Collection &coll) const { - return m_name_of_layer.find (layer_of (coll)) != m_name_of_layer.end (); + return is_persisted_impl (coll); } /** @@ -297,6 +308,20 @@ public: */ db::Region *layer_by_index (unsigned int index); + /** + * @brief Gets the internal layer from the original layer + */ + db::Region *layer_by_original (const ShapeCollection &original_layer) + { + return layer_by_original (original_layer.get_delegate ()); + } + + /** + * @brief Gets the layer from the original layer's delegate + * Returns 0 if the original layer was not registered as an input_layer. + */ + db::Region *layer_by_original (const ShapeCollectionDelegateBase *original_delegate); + /** * @brief Iterates over the layer indexes and names managed by this object (begin) */ @@ -688,7 +713,7 @@ public: * This methods returns a new'd Region. It's the responsibility of the caller * to delete this object. */ - db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, const db::ICplxTrans &trans = db::ICplxTrans ()) const; /** * @brief Delivers all shapes of a specific net and layer to the given Shapes container. @@ -701,30 +726,7 @@ public: * * propid is an optional properties ID which is attached to the shapes if not 0. */ - void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0) const; - - /** - * @brief An enum describing the way the net hierarchy is mapped - */ - enum BuildNetHierarchyMode - { - /** - * @brief Flatten the net - * Collects all shapes of a net and puts that into the net cell or circuit cell - */ - BNH_Flatten = 0, - /** - * @brief Build a net hierarchy adding cells for each subcircuit on the net - * Uses the circuit_cell_prefix to build the subcircuit cell names - */ - BNH_SubcircuitCells = 1, - /** - * @brief No hierarchy - * Just output the shapes of the net belonging to the circuit cell. - * Connections are not indicated! - */ - BNH_Disconnected = 2 - }; + void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0, const db::ICplxTrans &trans = db::ICplxTrans ()) const; /** * @brief Builds a net representation in the given layout and cell @@ -747,15 +749,17 @@ public: * using a name like device_cell_name_prefix + device name. Otherwise the device shapes are * treated as part of the net. * + * @param net The net to build * @param target The target layout * @param target_cell The target cell * @param lmap Target layer indexes (keys) and net regions (values) * @param hier_mode See description of this method + * @param net_prop_mode How to attach properties to shapes * @param netname_prop An (optional) property name to which to attach the net name * @param cell_name_prefix Chooses recursive mode if non-null * @param device_cell_name_prefix See above */ - void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const; + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Builds a full hierarchical representation of the nets @@ -788,18 +792,19 @@ public: * @param cmap The mapping of internal layout to target layout for the circuit mapping * @param target The target layout * @param lmap Target layer indexes (keys) and net regions (values) - * @param hier_mode See description of this method - * @param netname_prop An (optional) property name to which to attach the net name - * @param circuit_cell_name_prefix See method description * @param net_cell_name_prefix See method description + * @param net_prop_mode How to attach properties to shapes + * @param netname_prop The property key to use for the net name or "nil" for no netname properties + * @param hier_mode See description of this method + * @param circuit_cell_name_prefix See method description * @param device_cell_name_prefix See above */ - void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Like build_all_nets, but with the ability to select some nets */ - void build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + void build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Finds the net by probing a specific location on the given layer @@ -935,6 +940,8 @@ private: std::set m_dlrefs; std::map m_named_regions; std::map m_name_of_layer; + std::map m_region_by_original; + std::map m_region_of_layer; bool m_netlist_extracted; bool m_is_flat; double m_device_scaling; @@ -946,6 +953,127 @@ private: std::list > m_joined_nets; std::list > > m_joined_nets_per_cell; + void init (); + void ensure_netlist (); + size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); + db::DeepLayer deep_layer_of (const ShapeCollection &coll) const; + void ensure_layout () const; + std::string make_new_name (const std::string &stem = std::string ()); + db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector *nets, bool with_device_cells); + void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); + size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn); + bool is_persisted_impl (const db::ShapeCollection &coll) const; + + // implementation of NetlistManipulationCallbacks + virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); + virtual void link_nets (const db::Net *net, const db::Net *with); +}; + +/** + * @brief An object building nets (net-to-layout) + * + * This object can be used to persist netlist builder information - e.g. reusing net cells when building individual + * layers from nets. In this case, build nets with a layer selection and call the build_net function many times. + */ + +class DB_PUBLIC NetBuilder +{ +public: + /** + * @brief Default constructor + */ + NetBuilder (); + + /** + * @brief Constructs a net builder with a target layout, a cell mapping table and a LayoutToNetlist source + * + * @param target The target layout + * @param cmap The cell mapping from the internal layout (inside LayoutToNetlist) to the target - use LayoutInfo::cell_mapping_into to generate this map + * @param source The LayoutToNetlist source. + * + * A cell map needs to be supplied only if intending to build many nets in hierarchical mode from multiple circuits. + */ + NetBuilder (db::Layout *target, const db::CellMapping &cmap, const db::LayoutToNetlist *source); + + /** + * @brief Constructs a net builder with a source only + * + * @param source The LayoutToNetlist source. + * + * This net builder can be used to build single nets into dedicated target cells. + */ + NetBuilder (db::Layout *target, const db::LayoutToNetlist *source); + + /** + * @brief Copy constructor + */ + NetBuilder (const db::NetBuilder &other); + + /** + * @brief Move constructor + */ + NetBuilder (db::NetBuilder &&other); + + /** + * @brief Assignment + */ + NetBuilder &operator= (const db::NetBuilder &other); + + /** + * @brief Move + */ + NetBuilder &operator= (db::NetBuilder &&other); + + /** + * @brief Sets the net-to-hierarchy generation mode + */ + void set_hier_mode (BuildNetHierarchyMode hm) + { + m_hier_mode = hm; + } + + /** + * @brief Sets or resets the net cell name prefix + * + * Pass 0 to this string value to reset it. + */ + void set_net_cell_name_prefix (const char *s); + + /** + * @brief Sets or resets the circuit cell name prefix + * + * Pass 0 to this string value to reset it. + */ + void set_cell_name_prefix (const char *s); + + /** + * @brief Sets or resets the device cell name prefix + * + * Pass 0 to this string value to reset it. + */ + void set_device_cell_name_prefix (const char *s); + + /** + * @brief See \LayoutToNetlist for details of this function + */ + void build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + + /** + * @brief See \LayoutToNetlist for details of this function + */ + void build_all_nets (const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + + /** + * @brief See \LayoutToNetlist for details of this function + */ + void build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + + /** + * @brief A helper function to create a property ID for a given net, net property name and net property mode + */ + static db::properties_id_type make_netname_propid (db::PropertiesRepository &pr, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, const db::Net &net, const std::string &net_name_prefix = std::string ()); + +private: struct CellReuseTableKey { CellReuseTableKey (db::cell_index_type _cell_index, db::properties_id_type _netname_propid, size_t _cluster_id) @@ -973,25 +1101,29 @@ private: size_t cluster_id; }; - typedef std::map cell_reuse_table_type; + typedef std::map > cell_reuse_table_type; - void init (); - void ensure_netlist (); - size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); - void build_net_rec (const db::Net &net, db::Layout &target, cell_index_type circuit_cell, const db::CellMapping &cmap, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const; - void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const; - void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const; - db::DeepLayer deep_layer_of (const ShapeCollection &coll) const; - void ensure_layout () const; - std::string make_new_name (const std::string &stem = std::string ()); - db::properties_id_type make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const; - db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector *nets, bool with_device_cells); - void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); - size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn); + tl::weak_ptr mp_target; + db::CellMapping m_cmap; + tl::weak_ptr mp_source; + mutable cell_reuse_table_type m_reuse_table; + BuildNetHierarchyMode m_hier_mode; + bool m_has_net_cell_name_prefix; + std::string m_net_cell_name_prefix; + bool m_has_cell_name_prefix; + std::string m_cell_name_prefix; + bool m_has_device_cell_name_prefix; + std::string m_device_cell_name_prefix; - // implementation of NetlistManipulationCallbacks - virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); - virtual void link_nets (const db::Net *net, const db::Net *with); + void build_net_rec (const db::Net &net, cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void build_net_rec (const db::Net &net, db::Cell &target_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &target_cell, const std::map &lmap, const Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void prepare_build_nets () const; + + db::Layout &target () const + { + return const_cast (*mp_target); + } }; /** diff --git a/src/db/db/dbLayoutToNetlistEnums.h b/src/db/db/dbLayoutToNetlistEnums.h new file mode 100644 index 000000000..708426294 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistEnums.h @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbLayoutToNetlistEnums +#define _HDR_dbLayoutToNetlistEnums + +namespace db +{ + +/** + * @brief An enum describing the way how net information is attached to shapes as properties in "build_nets" + */ +enum NetPropertyMode +{ + /** + * @brief Do no generate properties + */ + NPM_NoProperties, + + /** + * @brief Attach all net properties plus the net name (if a "netname_prop" is specified to "build_nets") + */ + NPM_AllProperties, + + /** + * @brief Attach net name only (if a "netname_prop" is specified to "build_nets") + */ + NPM_NetNameOnly, + + /** + * @brief Like NetNameOnly, but use a unique net ID (db::Net address actually) instead of name + */ + NPM_NetIDOnly, + + /** + * @brief Like NetNameOnly, but use a tuple of net and circuit name + */ + NPM_NetQualifiedNameOnly, +}; + +/** + * @brief An enum describing the way the net hierarchy is mapped + */ +enum BuildNetHierarchyMode +{ + /** + * @brief Flatten the net + * Collects all shapes of a net and puts that into the net cell or circuit cell + */ + BNH_Flatten = 0, + /** + * @brief Build a net hierarchy adding cells for each subcircuit on the net + * Uses the circuit_cell_prefix to build the subcircuit cell names + */ + BNH_SubcircuitCells = 1, + /** + * @brief No hierarchy + * Just output the shapes of the net belonging to the circuit cell. + * Connections are not indicated! + */ + BNH_Disconnected = 2 +}; + +} + +#endif diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index cb7ff35e7..b22464d51 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -26,6 +26,7 @@ #include "dbPolygonTools.h" #include "tlProgress.h" #include "tlTimer.h" +#include "tlThreads.h" namespace db { @@ -60,8 +61,14 @@ DirectLayerMapping::map_layer (const LayerProperties &lprops) // ------------------------------------------------------------------------------------ // PropertyMapper implementation -PropertyMapper::PropertyMapper (db::Layout &target, const db::Layout &source) - : mp_target (&target), mp_source (&source) +PropertyMapper::PropertyMapper (db::Layout *target, const db::Layout *source) + : mp_target (target ? &target->properties_repository () : 0), mp_source (source ? &source->properties_repository () : 0) +{ + // .. nothing yet .. +} + +PropertyMapper::PropertyMapper (db::PropertiesRepository *target, const db::PropertiesRepository *source) + : mp_target (target), mp_source (source) { // .. nothing yet .. } @@ -82,11 +89,24 @@ PropertyMapper::PropertyMapper () * @brief Specify the source layout */ void -PropertyMapper::set_source (const db::Layout &source) +PropertyMapper::set_source (const db::Layout *source) { - if (&source != mp_source) { + const db::PropertiesRepository *pr = source ? &source->properties_repository () : 0; + if (pr != mp_source) { m_prop_id_map.clear (); - mp_source = &source; + mp_source = pr; + } +} + +/** + * @brief Specify the source property repository + */ +void +PropertyMapper::set_source (const db::PropertiesRepository *source) +{ + if (source != mp_source) { + m_prop_id_map.clear (); + mp_source = source; } } @@ -94,11 +114,24 @@ PropertyMapper::set_source (const db::Layout &source) * @brief Specify the target layout */ void -PropertyMapper::set_target (db::Layout &target) +PropertyMapper::set_target (db::Layout *target) { - if (&target != mp_target) { + db::PropertiesRepository *pr = target ? &target->properties_repository () : 0; + if (pr != mp_target) { m_prop_id_map.clear (); - mp_target = ⌖ + mp_target = pr; + } +} + +/** + * @brief Specify the target property repository + */ +void +PropertyMapper::set_target (db::PropertiesRepository *target) +{ + if (target != mp_target) { + m_prop_id_map.clear (); + mp_target = target; } } @@ -108,17 +141,20 @@ PropertyMapper::set_target (db::Layout &target) db::Layout::properties_id_type PropertyMapper::operator() (db::Layout::properties_id_type source_id) { - if (source_id == 0 || mp_source == mp_target) { + if (source_id == 0 || mp_source == mp_target || ! mp_source || ! mp_target) { return source_id; } tl_assert (mp_source != 0); tl_assert (mp_target != 0); + static tl::Mutex s_mutex; + tl::MutexLocker locker (&s_mutex); + std::map ::const_iterator p = m_prop_id_map.find (source_id); if (p == m_prop_id_map.end ()) { - db::Layout::properties_id_type new_id = mp_target->properties_repository ().translate (mp_source->properties_repository (), source_id); + db::Layout::properties_id_type new_id = mp_target->translate (*mp_source, source_id); m_prop_id_map.insert (std::make_pair (source_id, new_id)); return new_id; } else { @@ -196,7 +232,7 @@ merge_layouts (db::Layout &target, } // provide the property mapper - db::PropertyMapper pm (target, source); + db::PropertyMapper pm (&target, &source); tl::RelativeProgress progress (tl::to_string (tr ("Merge cells")), all_cells_to_copy.size (), 1); @@ -308,7 +344,7 @@ copy_or_move_shapes (db::Layout &target, collect_cells_to_copy (source, source_cells, cell_mapping, all_top_level_cells, all_cells_to_copy); // provide the property mapper - db::PropertyMapper pm (target, source); + db::PropertyMapper pm (&target, &source); tl::RelativeProgress progress (tl::to_string (tr ("Merge cells")), all_cells_to_copy.size () * layer_mapping.size (), 1); diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index 3a980fde3..fed5457c3 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -80,8 +80,16 @@ public: * @param source The source layout * @param target The target layout */ - PropertyMapper (db::Layout &target, const db::Layout &source); + PropertyMapper (db::Layout *target, const db::Layout *source); + /** + * @brief Instantiate a property mapper for mapping of property ids from the source to the target property repository + * + * @param source The source property repository + * @param target The target property repository + */ + PropertyMapper (db::PropertiesRepository *target, const db::PropertiesRepository *source); + /** * @brief Instantiate a property mapper for mapping of property ids from the source to the target layout * @@ -93,21 +101,31 @@ public: /** * @brief Specify the source layout */ - void set_source (const db::Layout &source); + void set_source (const db::Layout *source); + /** + * @brief Specify the source property repository + */ + void set_source (const db::PropertiesRepository *source); + /** * @brief Specify the target layout */ - void set_target (db::Layout &target); + void set_target (db::Layout *target); + /** + * @brief Specify the target property repository + */ + void set_target (db::PropertiesRepository *target); + /** * @brief The actual mapping function */ db::Layout::properties_id_type operator() (db::Layout::properties_id_type source_id); private: - db::Layout *mp_target; - const db::Layout *mp_source; + db::PropertiesRepository *mp_target; + const db::PropertiesRepository *mp_source; std::map m_prop_id_map; }; diff --git a/src/db/db/dbLibraryProxy.cc b/src/db/db/dbLibraryProxy.cc index 950bfd703..7afa8ce24 100644 --- a/src/db/db/dbLibraryProxy.cc +++ b/src/db/db/dbLibraryProxy.cc @@ -224,7 +224,7 @@ LibraryProxy::update (db::ImportLayerMapping *layer_mapping) clear_shapes (); clear_insts (); - PropertyMapper prop_id_map (*layout (), lib->layout ()); + PropertyMapper prop_id_map (layout (), &lib->layout ()); for (unsigned int l = 0; l < lib->layout ().layers (); ++l) { if (layer_indices [l] >= 0) { diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index e9bdb84f8..94eb19e29 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -22,17 +22,6 @@ #include "dbLocalOperation.h" #include "dbHierProcessor.h" -#include "dbBoxScanner.h" -#include "dbRecursiveShapeIterator.h" -#include "dbBoxConvert.h" -#include "dbPolygonGenerators.h" -#include "dbPolygonTools.h" -#include "dbLocalOperationUtils.h" -#include "dbEdgeBoolean.h" -#include "tlLog.h" -#include "tlTimer.h" -#include "tlInternational.h" -#include "tlProgress.h" namespace db { @@ -92,6 +81,14 @@ template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; @@ -109,401 +106,5 @@ template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; -// --------------------------------------------------------------------------------------------- -// BoolAndOrNotLocalOperation implementation - -BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) - : m_is_and (is_and) -{ - // .. nothing yet .. -} - -OnEmptyIntruderHint -BoolAndOrNotLocalOperation::on_empty_intruder_hint () const -{ - return m_is_and ? Drop : Copy; -} - -std::string -BoolAndOrNotLocalOperation::description () const -{ - return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); -} - -void -BoolAndOrNotLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - - 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::PolygonRef &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 { - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () || p1 > 0) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::PolygonRefGenerator pr (layout, result); - db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); - db::PolygonGenerator pg (splitter, true, true); - ep.set_base_verbosity (50); - ep.process (pg, op); - - } -} - -// --------------------------------------------------------------------------------------------- -// TwoBoolAndNotLocalOperation implementation - -TwoBoolAndNotLocalOperation::TwoBoolAndNotLocalOperation () - : db::local_operation () -{ - // .. nothing yet .. -} - -void -TwoBoolAndNotLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 2); - - db::EdgeProcessor ep; - - std::unordered_set &result0 = results [0]; - std::unordered_set &result1 = results [1]; - - size_t p1 = 0, p2 = 1; - - std::set others; - for (db::shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (db::shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (db::shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::PolygonRef &subject = interactions.subject_shape (i->first); - if (others.find (subject) != others.end ()) { - result0.insert (subject); - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - result1.insert (subject); - } else { - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () || p1 > 0) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op0 (db::BooleanOp::And); - db::PolygonRefGenerator pr0 (layout, result0); - db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); - db::PolygonGenerator pg0 (splitter0, true, true); - - db::BooleanOp op1 (db::BooleanOp::ANotB); - db::PolygonRefGenerator pr1 (layout, result1); - db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); - db::PolygonGenerator pg1 (splitter1, true, true); - - ep.set_base_verbosity (50); - - std::vector > procs; - procs.push_back (std::make_pair (&pg0, &op0)); - procs.push_back (std::make_pair (&pg1, &op1)); - ep.process (procs); - - } - -} - -std::string TwoBoolAndNotLocalOperation::description () const -{ - return tl::to_string (tr ("ANDNOT operation")); -} - -// --------------------------------------------------------------------------------------------- - -SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) - : m_wrap_count (wrap_count) -{ - // .. nothing yet .. -} - -void -SelfOverlapMergeLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - if (m_wrap_count == 0) { - return; - } - - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - std::set seen; - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - if (seen.find (i->first) == seen.end ()) { - seen.insert (i->first); - const db::PolygonRef &subject = interactions.subject_shape (i->first); - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - for (db::shape_interactions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { - // don't take the same (really the same, not an identical one) shape twice - the interaction - // set does not take care to list just one copy of the same item on the intruder side. - if (seen.find (*o) == seen.end ()) { - seen.insert (*o); - const db::PolygonRef &intruder = interactions.intruder_shape (*o).second; - for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - } - - } - - db::MergeOp op (m_wrap_count - 1); - db::PolygonRefGenerator pr (layout, result); - db::PolygonGenerator pg (pr, true, true); - ep.set_base_verbosity (50); - ep.process (pg, op); -} - -OnEmptyIntruderHint SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const -{ - return m_wrap_count > 1 ? Drop : Copy; -} - -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 (EdgeBoolOp op) - : m_op (op) -{ - // .. nothing yet .. -} - -OnEmptyIntruderHint -EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const -{ - return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; -} - -std::string -EdgeBoolAndOrNotLocalOperation::description () const -{ - 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::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == size_t (m_op == EdgeAndNot ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (results.size () > 1) { - result2 = &results[1]; - } - - EdgeBooleanClusterCollector > cluster_collector (&result, m_op, result2); - - 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); - } - } - - bool any_subject = false; - bool is_and = (m_op == EdgeAnd || m_op == EdgeAndNot || 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 (is_and) { - result.insert (subject); - } - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - if (! 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 ()); - - } -} - -// --------------------------------------------------------------------------------------------- -// EdgeToPolygonLocalOperation implementation - -EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders) - : m_op (op), m_include_borders (include_borders) -{ - // .. nothing yet .. -} - -OnEmptyIntruderHint -EdgeToPolygonLocalOperation::on_empty_intruder_hint () const -{ - return m_op == EdgePolygonOp::Inside ? Drop : (m_op == EdgePolygonOp::Outside ? Copy : CopyToSecond); -} - -std::string -EdgeToPolygonLocalOperation::description () const -{ - if (m_op == EdgePolygonOp::Inside) { - return tl::to_string (tr ("Edge to polygon AND/INSIDE")); - } else if (m_op == EdgePolygonOp::Outside) { - return tl::to_string (tr ("Edge to polygon NOT/OUTSIDE")); - } else { - return tl::to_string (tr ("Edge to polygon ANDNOT/INOUTSIDE")); - } -} - -void -EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == size_t (m_op == EdgePolygonOp::Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (results.size () > 1) { - result2 = &results[1]; - } - - db::EdgeProcessor ep; - - 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); - } - } - - 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 (i->second.empty ()) { - // shortcut (outside: keep, otherwise: drop) - if (m_op == db::EdgePolygonOp::Outside) { - result.insert (subject); - } else if (m_op == db::EdgePolygonOp::Both) { - result2->insert (subject); - } - } else { - ep.insert (subject, 1); - any_subject = true; - } - - } - - if (! others.empty () || any_subject) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, 0); - } - } - - std::unique_ptr cc_second; - if (result2) { - cc_second.reset (new db::EdgeToEdgeSetGenerator (*result2, 2 /*second tag*/)); - } - - db::EdgeToEdgeSetGenerator cc (result, 1 /*first tag*/, cc_second.get ()); - db::EdgePolygonOp op (m_op, m_include_borders); - ep.process (cc, op); - - } -} - } diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 3907fbfc3..87161fd20 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -26,10 +26,7 @@ #define HDR_dbLocalOperation #include "dbCommon.h" - #include "dbLayout.h" -#include "dbEdgeBoolean.h" -#include "dbEdgeProcessor.h" #include #include @@ -129,102 +126,6 @@ protected: virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const = 0; }; -/** - * @brief Implements a boolean AND or NOT operation - */ -class DB_PUBLIC BoolAndOrNotLocalOperation - : public local_operation -{ -public: - BoolAndOrNotLocalOperation (bool is_and); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - bool m_is_and; -}; - -/** - * @brief Implements a boolean AND plus NOT operation - * - * This processor delivers two outputs: the first one having the AND result, the second - * one having the NOT result. - */ -class DB_PUBLIC TwoBoolAndNotLocalOperation - : public local_operation -{ -public: - TwoBoolAndNotLocalOperation (); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual std::string description () const; -}; - -/** - * @brief Implements a merge operation with an overlap count - * With a given wrap_count, the result will only contains shapes where - * the original shapes overlap at least "wrap_count" times. - */ -class DB_PUBLIC SelfOverlapMergeLocalOperation - : public local_operation -{ -public: - SelfOverlapMergeLocalOperation (unsigned int wrap_count); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - -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 (db::EdgeBoolOp op); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint 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: - db::EdgeBoolOp m_op; -}; - -/** - * @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders) - * - * "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true". - * With "include_borders == false" the operations are "INSIDE" and "OUTSIDE". - */ -class DB_PUBLIC EdgeToPolygonLocalOperation - : public local_operation -{ -public: - EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint 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 m_include_borders ? 1 : 0; } - -private: - db::EdgePolygonOp::mode_t m_op; - bool m_include_borders; -}; - } #endif diff --git a/src/db/db/dbLocalOperationUtils.cc b/src/db/db/dbLocalOperationUtils.cc index 3712f67e1..e280eabee 100644 --- a/src/db/db/dbLocalOperationUtils.cc +++ b/src/db/db/dbLocalOperationUtils.cc @@ -27,38 +27,11 @@ namespace db { -// ----------------------------------------------------------------------------------------------- -// class EdgeToEdgeSetGenerator - -EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set &edges, int tag, EdgeToEdgeSetGenerator *chained) - : mp_edges (&edges), m_tag (tag), mp_chained (chained) -{ - // .. nothing yet .. -} - -void EdgeToEdgeSetGenerator::put (const db::Edge &edge) -{ - mp_edges->insert (edge); - if (mp_chained) { - mp_chained->put (edge); - } -} - -void EdgeToEdgeSetGenerator::put (const db::Edge &edge, int tag) -{ - if (m_tag == 0 || m_tag == tag) { - mp_edges->insert (edge); - } - if (mp_chained) { - mp_chained->put (edge, tag); - } -} - // ----------------------------------------------------------------------------------------------- // class PolygonRefGenerator -PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes) - : PolygonSink (), mp_layout (layout), mp_shapes (shapes) +PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes, db::properties_id_type prop_id) + : PolygonSink (), mp_layout (layout), mp_shapes (shapes), m_prop_id (prop_id) { // .. nothing yet .. } @@ -66,7 +39,11 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db void PolygonRefToShapesGenerator::put (const db::Polygon &polygon) { tl::MutexLocker locker (&mp_layout->lock ()); - mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + if (m_prop_id != 0) { + mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id)); + } else { + mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + } } // ----------------------------------------------------------------------------------------------- diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h index f1f89cf5c..f93e8c67b 100644 --- a/src/db/db/dbLocalOperationUtils.h +++ b/src/db/db/dbLocalOperationUtils.h @@ -28,7 +28,9 @@ #include "dbCommon.h" #include "dbLayout.h" +#include "dbPropertyConstraint.h" #include "dbPolygonGenerators.h" +#include "dbLocalOperation.h" #include "dbHash.h" #include "tlThreads.h" @@ -37,6 +39,8 @@ namespace db { +class PropertyMapper; + template class polygon_transformation_filter : public PolygonSink @@ -123,31 +127,117 @@ private: typedef polygon_ref_generator PolygonRefGenerator; -class DB_PUBLIC EdgeToEdgeSetGenerator +template +class DB_PUBLIC polygon_ref_generator_with_properties; + +template <> +class DB_PUBLIC polygon_ref_generator_with_properties + : public PolygonSink +{ +public: + /** + * @brief Constructor + */ + polygon_ref_generator_with_properties (db::Layout *layout, std::unordered_set &polyrefs, db::properties_id_type prop_id) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs), m_prop_id (prop_id) + { + // .. nothing yet .. + } + + /** + * @brief Implementation of the PolygonSink interface + */ + void put (const db::Polygon &polygon) + { + tl::MutexLocker locker (&mp_layout->lock ()); + mp_polyrefs->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id)); + } + +private: + db::Layout *mp_layout; + std::unordered_set *mp_polyrefs; + db::properties_id_type m_prop_id; +}; + +template <> +class DB_PUBLIC polygon_ref_generator_with_properties + : public PolygonSink +{ +public: + /** + * @brief Constructor + */ + polygon_ref_generator_with_properties (db::Layout *, std::unordered_set &polygons, db::properties_id_type prop_id) + : mp_polygons (&polygons), m_prop_id (prop_id) + { + // .. nothing yet .. + } + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon) + { + mp_polygons->insert (db::PolygonWithProperties (polygon, m_prop_id)); + } + +private: + std::unordered_set *mp_polygons; + db::properties_id_type m_prop_id; +}; + +typedef polygon_ref_generator PolygonRefGenerator; + +template +class DB_PUBLIC edge_to_edge_set_generator : public EdgeSink { public: /** * @brief Constructor */ - EdgeToEdgeSetGenerator (std::unordered_set &edges, int tag = 0, EdgeToEdgeSetGenerator *chained = 0); + edge_to_edge_set_generator (Container &edges, int tag = 0, EdgeSink *chained = 0) + : mp_edges (&edges), m_tag (tag), mp_chained (chained) + { + // .. nothing yet .. + } /** * @brief Implementation of the PolygonSink interface */ - virtual void put (const db::Edge &edge); + virtual void put (const db::Edge &edge) + { + if (mp_edges) { + mp_edges->insert (edge); + } + if (mp_chained) { + mp_chained->put (edge); + } + } /** * @brief Implementation of the PolygonSink interface */ - virtual void put (const db::Edge &edge, int tag); + virtual void put (const db::Edge &edge, int tag) + { + if (m_tag == 0 || m_tag == tag) { + if (mp_edges) { + mp_edges->insert (edge); + } + } + if (mp_chained) { + mp_chained->put (edge, tag); + } + } private: - std::unordered_set *mp_edges; + Container *mp_edges; int m_tag; - EdgeToEdgeSetGenerator *mp_chained; + EdgeSink *mp_chained; }; +typedef edge_to_edge_set_generator > EdgeToEdgeSetGenerator; + class DB_PUBLIC PolygonRefToShapesGenerator : public PolygonSink { @@ -155,7 +245,15 @@ public: /** * @brief Constructor specifying an external vector for storing the polygons */ - PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes); + PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes, db::properties_id_type prop_id = 0); + + /** + * @brief Sets the property ID to be used for the next polygon + */ + void set_prop_id (db::properties_id_type prop_id) + { + m_prop_id = prop_id; + } /** * @brief Implementation of the PolygonSink interface @@ -165,6 +263,7 @@ public: private: db::Layout *mp_layout; db::Shapes *mp_shapes; + db::properties_id_type m_prop_id; }; class DB_PUBLIC PolygonSplitter @@ -184,6 +283,129 @@ private: size_t m_max_vertex_count; }; +template +class DB_PUBLIC property_injector +{ +public: + typedef typename Container::const_iterator const_iterator; + + property_injector (Container *container, db::properties_id_type prop_id) + : mp_container (container), m_prop_id (prop_id) + { + // .. nothing yet .. + } + + const_iterator begin () const + { + return mp_container->begin (); + } + + const_iterator end () const + { + return mp_container->end (); + } + + void insert (const T &t) + { + mp_container->insert (db::object_with_properties (t, m_prop_id)); + } + +private: + Container *mp_container; + db::properties_id_type m_prop_id; +}; + +/** + * @brief Separates the interacting shapes by property relation + * + * Returns a map of property ID, subject shapes and intruder shapes belonging to the subject shapes. + * Depending on the property constraint the intruders will either be ones with and properties (NoPropertyConstraint), + * the same properties than the subject (SamePropertiesConstraint) or different properties (DifferentPropertiesConstraint). + */ +template +DB_PUBLIC_TEMPLATE +std::map, std::set > > +separate_interactions_by_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyConstraint property_constraint, db::PropertyMapper &pms, db::PropertyMapper &pmi) +{ + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + db::properties_id_type prop_id = pms (subject.properties_id ()); + + std::pair, std::set > &s2p = by_prop_id [prop_id]; + s2p.first.push_back (&subject); + + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + + const std::pair > &intruder = interactions.intruder_shape (*ii); + + if (pc_match (property_constraint, prop_id, pmi (intruder.second.properties_id ()))) { + s2p.second.insert (&intruder.second); + } + + } + + } + + return by_prop_id; +} + +/** + * @brief Separates the interacting shapes by property relation + * + * Returns a map of property ID, subject shapes and intruder shapes belonging to the subject shapes. + * Depending on the property constraint the intruders will either be ones with and properties (NoPropertyConstraint), + * the same properties than the subject (SamePropertiesConstraint) or different properties (DifferentPropertiesConstraint). + */ +template +DB_PUBLIC_TEMPLATE +std::map > +separate_interactions_to_interactions_by_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyConstraint property_constraint, db::PropertyMapper &pms, std::vector &pmis) +{ + std::map > by_prop_id; + std::map > intruder_ids_by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + db::properties_id_type prop_id = pms (subject.properties_id ()); + + db::shape_interactions &s2p = by_prop_id [prop_id]; + std::set &intruder_ids = intruder_ids_by_prop_id [prop_id]; + s2p.add_subject (i->first, subject); + + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + + const std::pair > &intruder = interactions.intruder_shape (*ii); + tl_assert (intruder.first < (unsigned int) pmis.size ()); + + if (pc_match (property_constraint, prop_id, pmis[intruder.first] (intruder.second.properties_id ()))) { + s2p.add_interaction (i->first, *ii); + intruder_ids.insert (*ii); + } + + } + + } + + for (auto i = intruder_ids_by_prop_id.begin (); i != intruder_ids_by_prop_id.end (); ++i) { + + db::shape_interactions &s2p = by_prop_id [i->first]; + const std::set &intruder_ids = intruder_ids_by_prop_id [i->first]; + + for (auto ii = intruder_ids.begin (); ii != intruder_ids.end (); ++ii) { + auto is = interactions.intruder_shape (*ii); + s2p.add_intruder_shape (*ii, is.first, is.second); + } + + } + + return by_prop_id; +} + } #endif diff --git a/src/db/db/dbMutableEdges.cc b/src/db/db/dbMutableEdges.cc index 4c4e6166c..2ce7efc41 100644 --- a/src/db/db/dbMutableEdges.cc +++ b/src/db/db/dbMutableEdges.cc @@ -50,10 +50,21 @@ void MutableEdges::insert (const db::Box &box) { if (! box.empty () && box.width () > 0 && box.height () > 0) { - do_insert (db::Edge (box.lower_left (), box.upper_left ())); - do_insert (db::Edge (box.upper_left (), box.upper_right ())); - do_insert (db::Edge (box.upper_right (), box.lower_right ())); - do_insert (db::Edge (box.lower_right (), box.lower_left ())); + do_insert (db::Edge (box.lower_left (), box.upper_left ()), 0); + do_insert (db::Edge (box.upper_left (), box.upper_right ()), 0); + do_insert (db::Edge (box.upper_right (), box.lower_right ()), 0); + do_insert (db::Edge (box.lower_right (), box.lower_left ()), 0); + } +} + +void +MutableEdges::insert (const db::BoxWithProperties &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + do_insert (db::Edge (box.lower_left (), box.upper_left ()), box.properties_id ()); + do_insert (db::Edge (box.upper_left (), box.upper_right ()), box.properties_id ()); + do_insert (db::Edge (box.upper_right (), box.lower_right ()), box.properties_id ()); + do_insert (db::Edge (box.lower_right (), box.lower_left ()), box.properties_id ()); } } @@ -65,12 +76,30 @@ MutableEdges::insert (const db::Path &path) } } +void +MutableEdges::insert (const db::PathWithProperties &path) +{ + if (path.points () > 0) { + insert (db::PolygonWithProperties (path.polygon (), path.properties_id ())); + } +} + void MutableEdges::insert (const db::Polygon &polygon) { if (polygon.holes () > 0 || polygon.vertices () > 0) { for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - do_insert (*e); + do_insert (*e, 0); + } + } +} + +void +MutableEdges::insert (const db::PolygonWithProperties &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + do_insert (*e, polygon.properties_id ()); } } } @@ -80,7 +109,17 @@ MutableEdges::insert (const db::SimplePolygon &polygon) { if (polygon.vertices () > 0) { for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - do_insert (*e); + do_insert (*e, 0); + } + } +} + +void +MutableEdges::insert (const db::SimplePolygonWithProperties &polygon) +{ + if (polygon.vertices () > 0) { + for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + do_insert (*e, polygon.properties_id ()); } } } @@ -88,17 +127,21 @@ MutableEdges::insert (const db::SimplePolygon &polygon) void MutableEdges::insert (const db::Shape &shape) { + db::properties_id_type prop_id = shape.prop_id (); + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { db::Polygon poly; shape.polygon (poly); - insert (poly); + for (auto e = poly.begin_edge (); ! e.at_end (); ++e) { + do_insert (*e, prop_id); + } } else if (shape.is_edge ()) { db::Edge edge; shape.edge (edge); - do_insert (edge); + do_insert (edge, prop_id); } } diff --git a/src/db/db/dbMutableEdges.h b/src/db/db/dbMutableEdges.h index 206161e36..40b097999 100644 --- a/src/db/db/dbMutableEdges.h +++ b/src/db/db/dbMutableEdges.h @@ -54,7 +54,7 @@ public: virtual void reserve (size_t n) = 0; - virtual void do_insert (const db::Edge &edge) = 0; + virtual void do_insert (const db::Edge &edge, db::properties_id_type prop_id) = 0; void transform (const db::UnitTrans &) { } void transform (const db::Disp &t) { do_transform (db::Trans (t)); } @@ -63,29 +63,37 @@ public: void transform (const db::IMatrix2d &t) { do_transform (t); } void transform (const db::IMatrix3d &t) { do_transform (t); } - void insert (const db::Edge &edge) { do_insert (edge); } + void insert (const db::Edge &edge) { do_insert (edge, 0); } + void insert (const db::EdgeWithProperties &edge) { do_insert (edge, edge.properties_id ()); } void insert (const db::Box &box); + void insert (const db::BoxWithProperties &box); void insert (const db::Path &path); + void insert (const db::PathWithProperties &path); void insert (const db::SimplePolygon &polygon); + void insert (const db::SimplePolygonWithProperties &polygon); void insert (const db::Polygon &polygon); + void insert (const db::PolygonWithProperties &polygon); void insert (const db::Shape &shape); template void insert (const db::Shape &shape, const T &trans) { + db::properties_id_type prop_id = shape.prop_id (); + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { db::Polygon poly; shape.polygon (poly); - poly.transform (trans); - insert (poly); + for (auto e = poly.begin_edge (); ! e.at_end (); ++e) { + do_insert ((*e).transformed (trans), prop_id); + } } else if (shape.is_edge ()) { db::Edge edge; shape.edge (edge); edge.transform (trans); - insert (edge); + do_insert (edge, prop_id); } } diff --git a/src/db/db/dbMutableRegion.cc b/src/db/db/dbMutableRegion.cc index 9180b6f14..2b4042d02 100644 --- a/src/db/db/dbMutableRegion.cc +++ b/src/db/db/dbMutableRegion.cc @@ -50,7 +50,15 @@ void MutableRegion::insert (const db::Box &box) { if (! box.empty () && box.width () > 0 && box.height () > 0) { - do_insert (db::Polygon (box)); + do_insert (db::Polygon (box), 0); + } +} + +void +MutableRegion::insert (const db::BoxWithProperties &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + do_insert (db::Polygon (box), box.properties_id ()); } } @@ -58,7 +66,15 @@ void MutableRegion::insert (const db::Path &path) { if (path.points () > 0) { - do_insert (path.polygon ()); + do_insert (path.polygon (), 0); + } +} + +void +MutableRegion::insert (const db::PathWithProperties &path) +{ + if (path.points () > 0) { + do_insert (path.polygon (), path.properties_id ()); } } @@ -68,7 +84,17 @@ MutableRegion::insert (const db::SimplePolygon &polygon) if (polygon.vertices () > 0) { db::Polygon poly; poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); - do_insert (poly); + do_insert (poly, 0); + } +} + +void +MutableRegion::insert (const db::SimplePolygonWithProperties &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + do_insert (poly, polygon.properties_id ()); } } @@ -78,13 +104,8 @@ MutableRegion::insert (const db::Shape &shape) if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { db::Polygon poly; shape.polygon (poly); - insert (poly); - } else if (shape.is_path ()) { - insert (shape.path ()); - } else if (shape.is_box ()) { - insert (shape.box ()); + do_insert (poly, shape.prop_id ()); } } } - diff --git a/src/db/db/dbMutableRegion.h b/src/db/db/dbMutableRegion.h index 94e8fd614..c6d8d0ab3 100644 --- a/src/db/db/dbMutableRegion.h +++ b/src/db/db/dbMutableRegion.h @@ -45,7 +45,7 @@ public: MutableRegion (const MutableRegion &other); virtual ~MutableRegion (); - virtual void do_insert (const db::Polygon &polygon) = 0; + virtual void do_insert (const db::Polygon &polygon, db::properties_id_type prop_id) = 0; void transform (const db::UnitTrans &) { } void transform (const db::Disp &t) { do_transform (db::Trans (t)); } @@ -63,10 +63,14 @@ public: virtual void reserve (size_t n) = 0; - void insert (const db::Polygon &polygon) { do_insert (polygon); } + void insert (const db::Polygon &polygon) { do_insert (polygon, 0); } + void insert (const db::PolygonWithProperties &polygon) { do_insert (polygon, polygon.properties_id ()); } void insert (const db::Box &box); + void insert (const db::BoxWithProperties &box); void insert (const db::Path &path); + void insert (const db::PathWithProperties &path); void insert (const db::SimplePolygon &polygon); + void insert (const db::SimplePolygonWithProperties &polygon); void insert (const db::Shape &shape); @@ -77,7 +81,7 @@ public: db::Polygon poly; shape.polygon (poly); poly.transform (trans); - insert (poly); + do_insert (poly, shape.prop_id ()); } } diff --git a/src/db/db/dbNetShape.h b/src/db/db/dbNetShape.h index 05f98c8da..652578c87 100644 --- a/src/db/db/dbNetShape.h +++ b/src/db/db/dbNetShape.h @@ -27,6 +27,9 @@ #include "dbText.h" #include "dbShapeRepository.h" #include "dbBoxConvert.h" +#include "dbShape.h" +#include "dbShapeFlags.h" // for addressable_object_from_shape +#include "tlSList.h" namespace db { @@ -172,6 +175,28 @@ struct box_convert } }; +template <> +struct addressable_object_from_shape +{ + typedef db::NetShape value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::TextRef) { + m_heap.push_back (db::NetShape (shape.text_ref ())); + return &m_heap.back (); + } else if (shape.type () == db::Shape::PolygonRef) { + m_heap.push_back (db::NetShape (shape.polygon_ref ())); + return &m_heap.back (); + } else { + tl_assert (false); + } + } + +private: + tl::slist m_heap; +}; + } #endif diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 0eba50e93..2f02bc160 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -285,7 +285,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { const db::local_cluster &lc = clusters.cluster_by_id (*c); - if (clusters.connections_for_cluster (*c).empty () && lc.empty ()) { + const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c); + if (cc.empty () && lc.empty ()) { // this is an entirely empty cluster so we skip it. // Such clusters are left over when joining clusters. continue; @@ -313,6 +314,23 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo } } +#if 0 + // This code will pull net names from subcircuits into their parents if those nets are dummy connections + // made to satisfy the subcircuit's pin, but not to make a physical connection. + // Don't know whether this is a good idea, so this code is disabled for now. + + if (net_names.empty () && clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) { + // in the case of a dummy connection (partially connected subcircuits) create a + // new name indicating the subcircuit and the subcircuit net name - this makes subcircuit + // net names available (the net is pseudo-root inside in the subcircuit) + const db::NetSubcircuitPinRef &sc_pin = *net->begin_subcircuit_pins (); + const db::Net *sc_net = sc_pin.subcircuit ()->circuit_ref ()->net_for_pin (sc_pin.pin_id ()); + if (sc_net && ! sc_net->name ().empty ()) { + net_names.insert (sc_pin.subcircuit ()->expanded_name () + ":" + sc_net->name ()); + } + } +#endif + assign_net_names (net, net_names); if (! clusters.is_root (*c)) { diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index ad88a26c7..7b1d46c05 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -176,6 +176,15 @@ public: m_id = id; } + /** + * @brief Returns the transformed object + */ + template + object_with_properties transformed (const Trans &tr) const + { + return object_with_properties (Obj::transformed (tr), m_id); + } + private: properties_id_type m_id; }; diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index 4904431ff..b1c9ec1b0 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -41,7 +41,7 @@ namespace typedef db::EdgePair value_type; OriginalLayerEdgePairsIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans) + : m_rec_iter (iter), m_iter_trans (trans), m_prop_id (0) { set (); } @@ -67,6 +67,11 @@ namespace return &m_shape; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual EdgePairsIteratorDelegate *clone () const { return new OriginalLayerEdgePairsIterator (*this); @@ -100,15 +105,17 @@ namespace db::RecursiveShapeIterator m_rec_iter; db::ICplxTrans m_iter_trans; value_type m_shape; + db::properties_id_type m_prop_id; void set () { - while (! m_rec_iter.at_end () && !m_rec_iter.shape ().is_edge_pair ()) { + while (! m_rec_iter.at_end () && !m_rec_iter->is_edge_pair ()) { ++m_rec_iter; } if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().edge_pair (m_shape); + m_rec_iter->edge_pair (m_shape); m_shape.transform (m_iter_trans * m_rec_iter.trans ()); + m_prop_id = m_rec_iter.prop_id (); } } @@ -195,6 +202,24 @@ OriginalLayerEdgePairs::iter () const return &m_iter; } +void +OriginalLayerEdgePairs::apply_property_translator (const db::PropertiesTranslator &pt) +{ + m_iter.apply_property_translator (pt); +} + +db::PropertiesRepository * +OriginalLayerEdgePairs::properties_repository () +{ + return m_iter.layout () ? &const_cast(m_iter.layout ())->properties_repository () : 0; +} + +const db::PropertiesRepository * +OriginalLayerEdgePairs::properties_repository () const +{ + return m_iter.layout () ? &m_iter.layout ()->properties_repository () : 0; +} + bool OriginalLayerEdgePairs::equals (const EdgePairs &other) const { diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h index 4cfa45135..9800c7485 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.h +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -56,6 +56,9 @@ public: virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const EdgePairs &other) const; virtual bool less (const EdgePairs &other) const; diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index 791ac0841..b088d299c 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -42,7 +42,7 @@ namespace typedef db::Edge value_type; OriginalLayerEdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans) + : m_rec_iter (iter), m_iter_trans (trans), m_prop_id (0) { set (); } @@ -68,6 +68,11 @@ namespace return &m_shape; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual EdgesIteratorDelegate *clone () const { return new OriginalLayerEdgesIterator (*this); @@ -101,15 +106,17 @@ namespace db::RecursiveShapeIterator m_rec_iter; db::ICplxTrans m_iter_trans; value_type m_shape; + db::properties_id_type m_prop_id; void set () { - while (! m_rec_iter.at_end () && !m_rec_iter.shape ().is_edge ()) { + while (! m_rec_iter.at_end () && !m_rec_iter->is_edge ()) { ++m_rec_iter; } if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().edge (m_shape); + m_rec_iter->edge (m_shape); m_shape.transform (m_iter_trans * m_rec_iter.trans ()); + m_prop_id = m_rec_iter.prop_id (); } } @@ -120,7 +127,6 @@ namespace } } }; - } OriginalLayerEdges::OriginalLayerEdges () @@ -245,6 +251,27 @@ OriginalLayerEdges::iter () const return &m_iter; } +void +OriginalLayerEdges::apply_property_translator (const db::PropertiesTranslator &pt) +{ + m_iter.apply_property_translator (pt); + + m_merged_edges_valid = false; + m_merged_edges.clear (); +} + +db::PropertiesRepository * +OriginalLayerEdges::properties_repository () +{ + return m_iter.layout () ? &const_cast(m_iter.layout ())->properties_repository () : 0; +} + +const db::PropertiesRepository * +OriginalLayerEdges::properties_repository () const +{ + return m_iter.layout () ? &m_iter.layout ()->properties_repository () : 0; +} + bool OriginalLayerEdges::equals (const Edges &other) const { @@ -287,7 +314,7 @@ OriginalLayerEdges::ensure_merged_edges_valid () const db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (count ()); - AddressableEdgeDelivery e (begin (), has_valid_edges ()); + AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { if (! e->is_degenerate ()) { diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h index 0bb9d8546..2136042b2 100644 --- a/src/db/db/dbOriginalLayerEdges.h +++ b/src/db/db/dbOriginalLayerEdges.h @@ -62,6 +62,9 @@ public: virtual bool has_valid_merged_edges () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const Edges &other) const; virtual bool less (const Edges &other) const; diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index ea870226e..e4773219e 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -25,7 +25,6 @@ #include "dbFlatRegion.h" #include "dbFlatEdges.h" #include "dbRegion.h" -#include "dbShapeProcessor.h" #include "dbDeepEdges.h" #include "dbDeepRegion.h" #include "dbDeepShapeStore.h" @@ -46,7 +45,7 @@ namespace { public: OriginalLayerRegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans) + : m_rec_iter (iter), m_iter_trans (trans), m_prop_id (0) { set (); } @@ -72,6 +71,11 @@ namespace return &m_polygon; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual RegionIteratorDelegate *clone () const { return new OriginalLayerRegionIterator (*this); @@ -105,15 +109,17 @@ namespace db::RecursiveShapeIterator m_rec_iter; db::ICplxTrans m_iter_trans; db::Polygon m_polygon; + db::properties_id_type m_prop_id; void set () { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + while (! m_rec_iter.at_end () && ! (m_rec_iter->is_polygon () || m_rec_iter->is_path () || m_rec_iter->is_box ())) { ++m_rec_iter; } if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); + m_rec_iter->polygon (m_polygon); m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + m_prop_id = m_rec_iter.prop_id (); } } @@ -234,10 +240,10 @@ OriginalLayerRegion::count () const size_t nn = 0; if (iter.multiple_layers ()) { for (std::vector::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) { - nn += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions); + nn += layout.cell (*c).shapes (*l).size (iter.shape_flags () & (db::ShapeIterator::Regions | db::ShapeIterator::Properties)); } } else if (iter.layer () < layout.layers ()) { - nn += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions); + nn += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & (db::ShapeIterator::Regions | db::ShapeIterator::Properties)); } n += cc.weight (*c) * nn; } @@ -339,6 +345,12 @@ OriginalLayerRegion::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); } +db::properties_id_type +OriginalLayerRegion::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); +} + bool OriginalLayerRegion::has_valid_polygons () const { @@ -357,6 +369,27 @@ OriginalLayerRegion::iter () const return &m_iter; } +void +OriginalLayerRegion::apply_property_translator (const db::PropertiesTranslator &pt) +{ + m_iter.apply_property_translator (pt); + + m_merged_polygons_valid = false; + m_merged_polygons.clear (); +} + +db::PropertiesRepository * +OriginalLayerRegion::properties_repository () +{ + return m_iter.layout () ? &const_cast(m_iter.layout ())->properties_repository () : 0; +} + +const db::PropertiesRepository * +OriginalLayerRegion::properties_repository () const +{ + return m_iter.layout () ? &m_iter.layout ()->properties_repository () : 0; +} + bool OriginalLayerRegion::equals (const Region &other) const { @@ -386,6 +419,17 @@ OriginalLayerRegion::init () m_merged_polygons_valid = false; } +namespace { + +struct AssignProp +{ + AssignProp () : prop_id (0) { } + db::properties_id_type operator() (db::properties_id_type) { return prop_id; } + db::properties_id_type prop_id; +}; + +} + void OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const { @@ -397,12 +441,19 @@ OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, db::Shapes &sh = layout->cell (into_cell).shapes (into_layer); + db::PropertyMapper pm; + if (m_iter.layout ()) { + pm = db::PropertyMapper (layout, m_iter.layout ()); + } + // NOTE: if the source (r) is from the same layout than the shapes live in, we better // lock the layout against updates while inserting db::LayoutLocker locker (layout); + AssignProp ap; for (db::RecursiveShapeIterator i = m_iter; !i.at_end (); ++i) { - tl::ident_map pm; - sh.insert (*i, i.trans (), pm); + db::properties_id_type prop_id = i.prop_id (); + ap.prop_id = (prop_id != 0 ? pm (prop_id) : 0); + sh.insert (*i, i.trans (), ap); } } @@ -412,28 +463,7 @@ OriginalLayerRegion::ensure_merged_polygons_valid () const if (! m_merged_polygons_valid) { m_merged_polygons.clear (); - - db::EdgeProcessor ep (report_progress (), progress_desc ()); - ep.set_base_verbosity (base_verbosity ()); - - // count edges and reserve memory - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); - ep.process (pg, op); + merge_polygons_to (m_merged_polygons, min_coherence (), 0); m_merged_polygons_valid = true; diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index 702a36aca..8019cc7c8 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -62,10 +62,14 @@ public: virtual size_t hier_count () const; virtual const db::Polygon *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t) const; virtual bool has_valid_polygons () const; virtual bool has_valid_merged_polygons () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; diff --git a/src/db/db/dbOriginalLayerTexts.cc b/src/db/db/dbOriginalLayerTexts.cc index e80a994b4..dbc0e068d 100644 --- a/src/db/db/dbOriginalLayerTexts.cc +++ b/src/db/db/dbOriginalLayerTexts.cc @@ -41,7 +41,7 @@ namespace typedef db::Text value_type; OriginalLayerTextsIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans) + : m_rec_iter (iter), m_iter_trans (trans), m_prop_id (0) { set (); } @@ -67,6 +67,11 @@ namespace return &m_shape; } + virtual db::properties_id_type prop_id () const + { + return m_prop_id; + } + virtual OriginalLayerTextsIterator *clone () const { return new OriginalLayerTextsIterator (*this); @@ -100,6 +105,7 @@ namespace db::RecursiveShapeIterator m_rec_iter; db::ICplxTrans m_iter_trans; value_type m_shape; + db::properties_id_type m_prop_id; void set () { @@ -107,8 +113,9 @@ namespace ++m_rec_iter; } if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().text (m_shape); + m_rec_iter->text (m_shape); m_shape.transform (m_iter_trans * m_rec_iter.trans ()); + m_prop_id = m_rec_iter.prop_id (); } } @@ -195,6 +202,24 @@ OriginalLayerTexts::iter () const return &m_iter; } +void +OriginalLayerTexts::apply_property_translator (const db::PropertiesTranslator &pt) +{ + m_iter.apply_property_translator (pt); +} + +db::PropertiesRepository * +OriginalLayerTexts::properties_repository () +{ + return m_iter.layout () ? &const_cast(m_iter.layout ())->properties_repository () : 0; +} + +const db::PropertiesRepository * +OriginalLayerTexts::properties_repository () const +{ + return m_iter.layout () ? &m_iter.layout ()->properties_repository () : 0; +} + bool OriginalLayerTexts::equals (const Texts &other) const { diff --git a/src/db/db/dbOriginalLayerTexts.h b/src/db/db/dbOriginalLayerTexts.h index 6b216db22..d823e6103 100644 --- a/src/db/db/dbOriginalLayerTexts.h +++ b/src/db/db/dbOriginalLayerTexts.h @@ -56,6 +56,9 @@ public: virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; + virtual void apply_property_translator (const db::PropertiesTranslator &pt); + virtual db::PropertiesRepository *properties_repository (); + virtual const db::PropertiesRepository *properties_repository () const; virtual bool equals (const Texts &other) const; virtual bool less (const Texts &other) const; diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index 3359c7203..99d688d60 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -42,6 +42,12 @@ PropertiesRepository::PropertiesRepository (db::LayoutStateModel *state_model) tl_assert (id == 0); } +PropertiesRepository::PropertiesRepository (const PropertiesRepository &d) + : mp_state_model (0) +{ + operator= (d); +} + PropertiesRepository & PropertiesRepository::operator= (const PropertiesRepository &d) { @@ -207,5 +213,135 @@ PropertiesRepository::translate (const PropertiesRepository &rep, properties_id_ return properties_id (new_pset); } + +// ---------------------------------------------------------------------------------- +// PropertiesRepository implementation + +PropertiesTranslator::PropertiesTranslator () + : m_pass (true), m_null (true) +{ + // .. nothing yet .. +} + +PropertiesTranslator::PropertiesTranslator (bool pass) + : m_pass (pass), m_null (false) +{ + // .. nothing yet .. +} + +PropertiesTranslator::PropertiesTranslator (const std::map &map) + : m_map (map), m_pass (false), m_null (false) +{ + // .. nothing yet .. +} + +PropertiesTranslator +PropertiesTranslator::operator* (const PropertiesTranslator &other) const +{ + if (other.m_pass) { + + // NOTE: by handling this first, "pass_all * null" will give "pass_all" which is desired + // for RecursiveShapeIterator::apply_property_translator. + return *this; + + } else if (m_pass) { + + return other; + + } else { + + std::map new_map; + + for (auto i = other.m_map.begin (); i != other.m_map.end (); ++i) { + auto ii = m_map.find (i->second); + if (ii != m_map.end ()) { + new_map.insert (std::make_pair (i->first, ii->second)); + } + } + + return PropertiesTranslator (new_map); + + } +} + +db::properties_id_type +PropertiesTranslator::operator() (db::properties_id_type id) const +{ + if (m_pass || id == 0) { + return id; + } else { + auto i = m_map.find (id); + return i != m_map.end () ? i->second : 0; + } +} + +PropertiesTranslator +PropertiesTranslator::make_remove_all () +{ + return PropertiesTranslator (false); +} + +PropertiesTranslator +PropertiesTranslator::make_pass_all () +{ + return PropertiesTranslator (true); +} + +PropertiesTranslator +PropertiesTranslator::make_filter (db::PropertiesRepository &repo, const std::set &keys) +{ + std::map map; + std::set names_selected; + + for (auto k = keys.begin (); k != keys.end (); ++k) { + names_selected.insert (repo.prop_name_id (*k)); + } + + db::PropertiesRepository org_repo = repo; + + for (auto p = org_repo.begin (); p != org_repo.end (); ++p) { + db::PropertiesRepository::properties_set new_set; + for (auto i = p->second.begin (); i != p->second.end (); ++i) { + if (names_selected.find (i->first) != names_selected.end ()) { + new_set.insert (*i); + } + } + if (! new_set.empty ()) { + map.insert (std::make_pair (p->first, repo.properties_id (new_set))); + } + } + + return PropertiesTranslator (map); +} + +PropertiesTranslator +PropertiesTranslator::make_key_mapper (db::PropertiesRepository &repo, const std::map &keys) +{ + std::map map; + std::map name_map; + + for (auto k = keys.begin (); k != keys.end (); ++k) { + name_map.insert (std::make_pair (repo.prop_name_id (k->first), repo.prop_name_id (k->second))); + } + + db::PropertiesRepository org_repo = repo; + + for (auto p = org_repo.begin (); p != org_repo.end (); ++p) { + db::PropertiesRepository::properties_set new_set; + for (auto i = p->second.begin (); i != p->second.end (); ++i) { + auto nm = name_map.find (i->first); + if (nm != name_map.end ()) { + new_set.insert (std::make_pair (nm->second, i->second)); + } + } + if (! new_set.empty ()) { + map.insert (std::make_pair (p->first, repo.properties_id (new_set))); + } + } + + return PropertiesTranslator (map); +} + + } // namespace db diff --git a/src/db/db/dbPropertiesRepository.h b/src/db/db/dbPropertiesRepository.h index 3ddbfa92f..1a3a3d11e 100644 --- a/src/db/db/dbPropertiesRepository.h +++ b/src/db/db/dbPropertiesRepository.h @@ -63,6 +63,11 @@ public: */ PropertiesRepository (db::LayoutStateModel *state_model = 0); + /** + * @brief Copy constructor + */ + PropertiesRepository (const PropertiesRepository &d); + /** * @brief Assignment */ @@ -232,8 +237,117 @@ private: std::map m_properties_component_table; db::LayoutStateModel *mp_state_model; +}; - PropertiesRepository (const PropertiesRepository &d); +/** + * @brief A map for selecting/translating properties + * + * The following rules apply: + * - All non-mapped properties are mapped to 0 (removed) + * - 0 is always mapped to 0 + * - Do not include key or value 0 in the map passed to the constructor + * + * A "pass translator" will pass all IDs unchanged. + * + * Note that a property translator - specifically the filters and + * mappers created by "make_filter" and "make_key_mapper" - are snapshots. + * As creating new filters will generate new property IDs for the mapping + * targets, property translators generated previously may become invalid. + * In general it is safe to concatenate new translators after old ones. + * The old ones will not map the property IDs understood by the new ones, + * but as such IDs cannot become input to the old translator, this should + * not matter. + */ + +class DB_PUBLIC PropertiesTranslator +{ +public: + /** + * @brief Default constructor - this creates a null translator + */ + PropertiesTranslator (); + + /** + * @brief Creates a "pass all" (pass = true) or "remove all" (pass = false) translator + */ + PropertiesTranslator (bool pass); + + /** + * @brief Creates a property ID mapper from a table + */ + PropertiesTranslator (const std::map &map); + + /** + * @brief Gets a value indicating whether the translator is "pass" + */ + bool is_pass () const + { + return m_pass; + } + + /** + * @brief Gets a value indicating whether the translator is "empty" (remove all) + */ + bool is_empty () const + { + return ! m_pass && m_map.empty (); + } + + /** + * @brief Gets a value indicating whether the translator is "null" (default-constructed) + */ + bool is_null () const + { + return m_null; + } + + /** + * @brief Concatenates two translators (the right one first) + */ + PropertiesTranslator operator* (const PropertiesTranslator &other) const; + + /** + * @brief Concatenates two translators (the right one first) - in place version + */ + PropertiesTranslator &operator*= (const PropertiesTranslator &other) + { + *this = this->operator* (other); + return *this; + } + + /** + * @brief Translation of the property ID + */ + db::properties_id_type operator() (db::properties_id_type id) const; + + /** + * @brief Factory: create a "remove all" translator + */ + static PropertiesTranslator make_remove_all (); + + /** + * @brief Factory: create a "pass all" translator + */ + static PropertiesTranslator make_pass_all (); + + /** + * @brief Factory: create a filter translator + * + * The translator delivered by this function will leave only the given keys in the properties. + */ + static PropertiesTranslator make_filter (db::PropertiesRepository &repo, const std::set &keys); + + /** + * @brief Factory: create a key mapper translator + * + * The translator delivered by this function will translate the given keys to new ones + * and remove non-listed keys. + */ + static PropertiesTranslator make_key_mapper (db::PropertiesRepository &repo, const std::map &keys); + +private: + std::map m_map; + bool m_pass, m_null; }; /** diff --git a/src/db/db/dbPropertyConstraint.h b/src/db/db/dbPropertyConstraint.h new file mode 100644 index 000000000..b59a4f7a8 --- /dev/null +++ b/src/db/db/dbPropertyConstraint.h @@ -0,0 +1,131 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_dbPropertyConstraint +#define HDR_dbPropertyConstraint + +#include "dbCommon.h" +#include "dbTypes.h" + +namespace db +{ + +/** + * @brief Specifies a property constraint for some operations + */ +enum PropertyConstraint +{ + /** + * @brief Ignore properties + * + * In this mode, properties are not considered and erased. + */ + IgnoreProperties = 0, + + /** + * @brief No constraint, shapes are processed regardless of their properties + * + * Properties are attached to the outputs where applicable. + */ + NoPropertyConstraint = 1, + + /** + * @brief Shapes are processed if their properties are the same + * + * Properties are attached to the outputs where applicable. + */ + SamePropertiesConstraint = 2, + + /** + * @brief Shapes are processed if their properties are the same + * + * No properties are attached to the output. + */ + SamePropertiesConstraintDrop = 3, + + /** + * @brief Shapes are processed if their properties are different + * + * Properties are attached to the outputs where applicable. + */ + DifferentPropertiesConstraint = 4, + + /** + * @brief Shapes are processed if their properties are different + * + * No properties are attached to the output. + */ + DifferentPropertiesConstraintDrop = 5 +}; + +/** + * @brief Returns a predicate indicating whether properties need to be considered + */ +bool inline pc_skip (PropertyConstraint pc) +{ + return pc == IgnoreProperties; +} + +/** + * @brief Returns a predicate indicating whether properties are always different + */ +bool inline pc_always_different (PropertyConstraint pc) +{ + return pc == DifferentPropertiesConstraint || pc == DifferentPropertiesConstraintDrop; +} + +/** + * @brief Returns a value indicating whether two properties satisfy the condition + */ +bool inline pc_match (PropertyConstraint pc, db::properties_id_type a, db::properties_id_type b) +{ + if (pc == SamePropertiesConstraint || pc == SamePropertiesConstraintDrop) { + return a == b; + } else if (pc == DifferentPropertiesConstraint || pc == DifferentPropertiesConstraintDrop) { + return a != b; + } else { + return true; + } +} + +/** + * @brief Returns a value indicating whether the property can be removed on output + */ +bool inline pc_remove (PropertyConstraint pc) +{ + return pc == IgnoreProperties || pc == SamePropertiesConstraintDrop || pc == DifferentPropertiesConstraintDrop; +} + +/** + * @brief Returns a normalized property for output + */ +db::properties_id_type inline pc_norm (PropertyConstraint pc, db::properties_id_type prop_id) +{ + return pc_remove (pc) ? 0 : prop_id; +} + +} + +#endif + diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index aa1ef77eb..af551b6e2 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -73,6 +73,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_shape = d.m_shape; m_trans = d.m_trans; m_global_trans = d.m_global_trans; + m_property_translator = d.m_property_translator; m_trans_stack = d.m_trans_stack; m_inst_iterators = d.m_inst_iterators; m_inst_array_iterators = d.m_inst_array_iterators; @@ -284,6 +285,7 @@ RecursiveShapeIterator::init () mp_cell = 0; m_current_layer = 0; m_global_trans = cplx_trans_type (); + m_property_translator = db::PropertiesTranslator (); } void diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index c830c7052..99ffeb1fc 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -320,6 +320,41 @@ public: return mp_top_cell; } + /** + * @brief Gets the installed property translator + * + * The property translator is not automatically applied, but available to consumers + * of shapes to perform property translation. + */ + const db::PropertiesTranslator &property_translator () const + { + return m_property_translator; + } + + /** + * @brief Applies a PropertyTranslator + * + * The property translator is available for receivers of the recursive shape + * iterator items. This method will apply an additional property translator + * atop of existing ones. + */ + void apply_property_translator (const db::PropertiesTranslator &pt) + { + m_property_translator = pt * m_property_translator; + } + + /** + * @brief Sets a PropertyTranslator + * + * The property translator is available for receivers of the recursive shape + * iterator items. This method will apply an additional property translator + * atop of existing ones. + */ + void set_property_translator (const db::PropertiesTranslator &pt) + { + m_property_translator = pt; + } + /** * @brief Gets the basic region the iterator is using (will be world if none is set) * In addition to the basic region, a complex region may be defined that is further confining the @@ -648,6 +683,21 @@ public: */ bool at_end () const; + /** + * @brief Gets the translated property ID + * + * This version employs the property translator to deliver the real property ID. + */ + db::properties_id_type prop_id () const + { + if (m_property_translator.is_null ()) { + return 0; + } else { + validate (0); + return m_property_translator (m_shape->prop_id ()); + } + } + /** * @brief Gets the current cell's index */ @@ -765,6 +815,7 @@ private: bool m_overlapping; std::set m_start, m_stop; cplx_trans_type m_global_trans; + db::PropertiesTranslator m_property_translator; tl::weak_ptr mp_layout; const cell_type *mp_top_cell; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 6e2cfc065..943e5e137 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -105,7 +105,7 @@ const db::RecursiveShapeIterator & Region::iter () const { static db::RecursiveShapeIterator def_iter; - const db::RecursiveShapeIterator *i = mp_delegate->iter (); + const db::RecursiveShapeIterator *i = mp_delegate ? mp_delegate->iter () : 0; return *(i ? i : &def_iter); } @@ -155,9 +155,13 @@ void Region::insert (const Sh &shape) } template DB_PUBLIC void Region::insert (const db::Box &); +template DB_PUBLIC void Region::insert (const db::BoxWithProperties &); template DB_PUBLIC void Region::insert (const db::SimplePolygon &); +template DB_PUBLIC void Region::insert (const db::SimplePolygonWithProperties &); template DB_PUBLIC void Region::insert (const db::Polygon &); +template DB_PUBLIC void Region::insert (const db::PolygonWithProperties &); template DB_PUBLIC void Region::insert (const db::Path &); +template DB_PUBLIC void Region::insert (const db::PathWithProperties &); void Region::insert (const db::Shape &shape) { @@ -197,35 +201,35 @@ Region::mutable_region () } EdgePairs -Region::cop_to_edge_pairs (db::CompoundRegionOperationNode &node) +Region::cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { tl_assert (node.result_type () == db::CompoundRegionOperationNode::EdgePairs); - return EdgePairs (mp_delegate->cop_to_edge_pairs (node)); + return EdgePairs (mp_delegate->cop_to_edge_pairs (node, prop_constraint)); } Region -Region::cop_to_region (db::CompoundRegionOperationNode &node) +Region::cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { tl_assert (node.result_type () == db::CompoundRegionOperationNode::Region); - return Region (mp_delegate->cop_to_region (node)); + return Region (mp_delegate->cop_to_region (node, prop_constraint)); } Edges -Region::cop_to_edges (db::CompoundRegionOperationNode &node) +Region::cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { tl_assert (node.result_type () == db::CompoundRegionOperationNode::Edges); - return Edges (mp_delegate->cop_to_edges (node)); + return Edges (mp_delegate->cop_to_edges (node, prop_constraint)); } tl::Variant -Region::cop (db::CompoundRegionOperationNode &node) +Region::cop (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) { if (node.result_type () == db::CompoundRegionOperationNode::EdgePairs) { - return tl::Variant::make_variant (new EdgePairs (mp_delegate->cop_to_edge_pairs (node))); + return tl::Variant::make_variant (new EdgePairs (mp_delegate->cop_to_edge_pairs (node, prop_constraint))); } else if (node.result_type () == db::CompoundRegionOperationNode::Edges) { - return tl::Variant::make_variant (new Edges (mp_delegate->cop_to_edges (node))); + return tl::Variant::make_variant (new Edges (mp_delegate->cop_to_edges (node, prop_constraint))); } else if (node.result_type () == db::CompoundRegionOperationNode::Region) { - return tl::Variant::make_variant (new Region (mp_delegate->cop_to_region (node))); + return tl::Variant::make_variant (new Region (mp_delegate->cop_to_region (node, prop_constraint))); } else { return tl::Variant (); } @@ -376,6 +380,8 @@ static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const db::Layout *layout = 0; if (org_deep) { + // NOTE: deep regions can store texts in a special way - as small boxes with a special property attached. + // The property will give the text string. This function can restore these pseudo-texts as Text objects. layout = &org_deep->deep_layer ().layout (); const db::DeepShapeStore *store = org_deep->deep_layer ().store (); if (! store->text_property_name ().is_nil ()) { @@ -451,7 +457,7 @@ public: } } - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) + virtual void push (const db::Shape &shape, db::properties_id_type, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) { bool is_text = false; std::string text_string; @@ -494,8 +500,8 @@ public: } } - virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } - virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Box &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, db::properties_id_type, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } private: Delivery m_delivery; diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index ec1c0fc00..47e687d16 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -43,62 +43,7 @@ class DeepShapeStore; class TransformationReducer; class CompoundRegionOperationNode; -/** - * @brief A region iterator - * - * The iterator delivers the polygons of the region - */ -class DB_PUBLIC RegionIterator - : public generic_shape_iterator -{ -public: - /** - * @brief Default constructor - */ - RegionIterator () - : generic_shape_iterator () - { - // .. nothing yet .. - } - - /** - * @brief Constructor from a delegate - * The iterator will take ownership over the delegate - */ - RegionIterator (RegionIteratorDelegate *delegate) - : generic_shape_iterator (delegate) - { - // .. nothing yet .. - } - - /** - * @brief Copy constructor and assignment - */ - RegionIterator (const RegionIterator &other) - : generic_shape_iterator (static_cast &> (other)) - { - // .. nothing yet .. - } - - /** - * @brief Assignment - */ - RegionIterator &operator= (const RegionIterator &other) - { - generic_shape_iterator::operator= (other); - return *this; - } - - /** - * @brief Increment - */ - RegionIterator &operator++ () - { - generic_shape_iterator::operator++ (); - return *this; - } -}; - +typedef generic_shape_iterator RegionIterator; typedef addressable_shape_delivery AddressablePolygonDelivery; /** @@ -168,6 +113,15 @@ public: insert (s); } + /** + * @brief Constructor from a box with properties + */ + explicit Region (const db::BoxWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a polygon */ @@ -177,6 +131,15 @@ public: insert (s); } + /** + * @brief Constructor from a polygon with properties + */ + explicit Region (const db::PolygonWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a simple polygon */ @@ -186,6 +149,15 @@ public: insert (s); } + /** + * @brief Constructor from a simple polygon with properties + */ + explicit Region (const db::SimplePolygonWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a path */ @@ -195,6 +167,15 @@ public: insert (s); } + /** + * @brief Constructor from a path with properties + */ + explicit Region (const db::PathWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Sequence constructor * @@ -262,7 +243,15 @@ public: /** * @brief Gets the underlying delegate object */ - RegionDelegate *delegate () const + const RegionDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Gets the underlying delegate object + */ + RegionDelegate *delegate () { return mp_delegate; } @@ -624,7 +613,7 @@ public: * The compound operation needs to feature edge pair output, e.g. * node.result_type() needs to be EdgePairs. */ - EdgePairs cop_to_edge_pairs (db::CompoundRegionOperationNode &node); + EdgePairs cop_to_edge_pairs (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint = db::IgnoreProperties); /** * @brief Performs a compound operation rendering a region @@ -632,7 +621,7 @@ public: * The compound operation needs to feature region output, e.g. * node.result_type() needs to be Region. */ - Region cop_to_region (db::CompoundRegionOperationNode &node); + Region cop_to_region (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint = db::IgnoreProperties); /** * @brief Performs a compound operation rendering edges @@ -640,7 +629,7 @@ public: * The compound operation needs to feature region output, e.g. * node.result_type() needs to be Edges. */ - Edges cop_to_edges (db::CompoundRegionOperationNode &node); + Edges cop_to_edges (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint = db::IgnoreProperties); /** * @brief A universal form of the compound operation @@ -648,7 +637,7 @@ public: * The returned variant will be of the type requested by the * compound operation node. */ - tl::Variant cop (db::CompoundRegionOperationNode &node); + tl::Variant cop (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint = db::IgnoreProperties); /** * @brief Applies a width check and returns EdgePairs which correspond to violation markers @@ -992,7 +981,15 @@ public: */ Region operator& (const Region &other) const { - return Region (mp_delegate->and_with (other)); + return Region (mp_delegate->and_with (other, db::IgnoreProperties)); + } + + /** + * @brief Boolean AND operator with options + */ + Region bool_and (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) const + { + return Region (mp_delegate->and_with (other, prop_constraint)); } /** @@ -1003,7 +1000,19 @@ public: */ Region &operator&= (const Region &other) { - set_delegate (mp_delegate->and_with (other)); + set_delegate (mp_delegate->and_with (other, db::IgnoreProperties)); + return *this; + } + + /** + * @brief In-place boolean AND operator with options + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &bool_and_with (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) + { + set_delegate (mp_delegate->and_with (other, prop_constraint)); return *this; } @@ -1012,7 +1021,15 @@ public: */ Region operator- (const Region &other) const { - return Region (mp_delegate->not_with (other)); + return Region (mp_delegate->not_with (other, db::IgnoreProperties)); + } + + /** + * @brief Boolean NOT operator with options + */ + Region bool_not (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) const + { + return Region (mp_delegate->not_with (other, prop_constraint)); } /** @@ -1023,7 +1040,19 @@ public: */ Region &operator-= (const Region &other) { - set_delegate (mp_delegate->not_with (other)); + set_delegate (mp_delegate->not_with (other, db::IgnoreProperties)); + return *this; + } + + /** + * @brief In-place boolean NOT operator with options + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region bool_not_with (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) + { + set_delegate (mp_delegate->not_with (other, prop_constraint)); return *this; } @@ -1032,7 +1061,17 @@ public: */ Region operator^ (const Region &other) const { - return Region (mp_delegate->xor_with (other)); + return Region (mp_delegate->xor_with (other, db::IgnoreProperties)); + } + + /** + * @brief Boolean XOR operator with options + * + * TODO: property constraints are not implemented properly yet. + */ + Region bool_xor (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) const + { + return Region (mp_delegate->xor_with (other, prop_constraint)); } /** @@ -1043,7 +1082,21 @@ public: */ Region &operator^= (const Region &other) { - set_delegate (mp_delegate->xor_with (other)); + set_delegate (mp_delegate->xor_with (other, db::IgnoreProperties)); + return *this; + } + + /** + * @brief In-place boolean XOR operator with options + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + * + * TODO: property constraints are not implemented properly yet. + */ + Region &bool_xor_with (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) + { + set_delegate (mp_delegate->xor_with (other, prop_constraint)); return *this; } @@ -1054,7 +1107,19 @@ public: */ Region operator| (const Region &other) const { - return Region (mp_delegate->or_with (other)); + return Region (mp_delegate->or_with (other, db::IgnoreProperties)); + } + + /** + * @brief Boolean OR operator with options + * + * This method merges the polygons of both regions. + * + * TODO: property constraints are not implemented properly yet. + */ + Region bool_or (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) const + { + return Region (mp_delegate->or_with (other, prop_constraint)); } /** @@ -1062,7 +1127,18 @@ public: */ Region &operator|= (const Region &other) { - set_delegate (mp_delegate->or_with (other)); + set_delegate (mp_delegate->or_with (other, db::IgnoreProperties)); + return *this; + } + + /** + * @brief In-place boolean OR operator with options + * + * TODO: property constraints are not implemented properly yet. + */ + Region &bool_or_with (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) + { + set_delegate (mp_delegate->or_with (other, prop_constraint)); return *this; } @@ -1090,9 +1166,9 @@ public: * * The first region delivered will be the AND result, the second one the NOT result. */ - std::pair andnot (const Region &other) const + std::pair andnot (const Region &other, PropertyConstraint prop_constraint = db::IgnoreProperties) const { - std::pair res = mp_delegate->andnot_with (other); + std::pair res = mp_delegate->andnot_with (other, prop_constraint); return std::make_pair (Region (res.first), Region (res.second)); } @@ -1652,6 +1728,17 @@ public: return mp_delegate->nth (n); } + /** + * @brief Returns the nth polygon's property ID + * + * This operation is available only for flat regions - i.e. such for which + * "has_valid_polygons" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the region * @@ -1743,6 +1830,20 @@ public: return mp_delegate->insert_into (layout, into_cell, into_layer); } + /** + * @brief Pulls the net shapes from a LayoutToNetlist database + * + * This will pull the net shapes from the LayoutToNetlist database, provided that this + * layer was an input to the netlist extraction. + * + * Netlist names will be attached as properties according to prop_mode and net_prop_name. + * A net filter can be provided so that only certain nets are produced. + */ + Region nets (LayoutToNetlist &l2n, NetPropertyMode prop_mode = db::NPM_NoProperties, const tl::Variant &net_prop_name = tl::Variant (0), const std::vector *nets = 0) const + { + return Region (mp_delegate->nets (&l2n, prop_mode, net_prop_name, nets)); + } + /** * @brief Delivers texts as dots (degenerated edges) * diff --git a/src/db/db/dbRegionCheckUtils.h b/src/db/db/dbRegionCheckUtils.h index ded2c375f..b2a55fd84 100644 --- a/src/db/db/dbRegionCheckUtils.h +++ b/src/db/db/dbRegionCheckUtils.h @@ -156,14 +156,14 @@ class DB_PUBLIC_TEMPLATE edge2edge_check : public Edge2EdgeCheckBase { public: - edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0) + edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges, db::properties_id_type prop_id = 0) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0), m_prop_id (prop_id) { // .. nothing yet .. } - edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra) + edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges, db::properties_id_type prop_id = 0) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra), m_prop_id (prop_id) { // .. nothing yet .. } @@ -172,15 +172,24 @@ protected: void put (const db::EdgePair &edge, bool inter_polygon) const { if (! inter_polygon || ! mp_output_intra) { - mp_output_inter->insert (edge); + if (m_prop_id != 0) { + mp_output_inter->insert (db::EdgePairWithProperties(edge, m_prop_id)); + } else { + mp_output_inter->insert (edge); + } } else { - mp_output_intra->insert (edge); + if (m_prop_id != 0) { + mp_output_intra->insert (db::EdgePairWithProperties(edge, m_prop_id)); + } else { + mp_output_intra->insert (edge); + } } } private: Output *mp_output_inter; Output *mp_output_intra; + db::properties_id_type m_prop_id; }; /** @@ -194,16 +203,16 @@ class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output : public edge2edge_check { public: - edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges), + edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges, db::properties_id_type prop_id = 0) + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges, prop_id), mp_l1_negative_output (&l1_negative_output), mp_l2_negative_output (&l2_negative_output) { edge2edge_check::set_has_negative_edge_output (true); } - edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges), + edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges, db::properties_id_type prop_id = 0) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges, prop_id), mp_l1_negative_output (&l1_negative_output), mp_l2_negative_output (&l2_negative_output) { @@ -272,15 +281,15 @@ class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive : public edge2edge_check { public: - edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) - : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric) + edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric, db::properties_id_type prop_id = 0) + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric, prop_id) { edge2edge_check::set_has_negative_edge_output (negative_output); edge2edge_check::set_has_edge_pair_output (! negative_output); } - edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) - : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric) + edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric, db::properties_id_type prop_id = 0) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric, prop_id) { edge2edge_check::set_has_negative_edge_output (negative_output); edge2edge_check::set_has_edge_pair_output (! negative_output); diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index ed0d812ff..1b701f26d 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -35,8 +35,10 @@ #include "dbGenericShapeIterator.h" #include "dbRegionLocalOperations.h" #include "dbHash.h" +#include "dbLayoutToNetlistEnums.h" #include +#include #include namespace db { @@ -46,6 +48,8 @@ class EdgeFilterBase; class EdgesDelegate; class EdgePairsDelegate; class CompoundRegionOperationNode; +class LayoutToNetlist; +class Net; /** * @brief A base class for polygon filters @@ -192,6 +196,12 @@ public: virtual RegionDelegate *clone () const = 0; + RegionDelegate *remove_properties (bool remove = true) + { + ShapeCollectionDelegateBase::remove_properties (remove); + return this; + } + void set_base_verbosity (int vb); int base_verbosity () const { @@ -237,9 +247,9 @@ public: virtual perimeter_type perimeter (const db::Box &box) const = 0; virtual Box bbox () const = 0; - virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node) = 0; - virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node) = 0; - virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node) = 0; + virtual EdgePairsDelegate *cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) = 0; + virtual RegionDelegate *cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) = 0; + virtual EdgesDelegate *cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint) = 0; virtual EdgePairsDelegate *width_check (db::Coord d, const RegionCheckOptions &options) const = 0; virtual EdgePairsDelegate *space_check (db::Coord d, const RegionCheckOptions &options) const = 0; @@ -273,13 +283,13 @@ public: virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; - virtual RegionDelegate *and_with (const Region &other) const = 0; - virtual RegionDelegate *not_with (const Region &other) const = 0; - virtual RegionDelegate *xor_with (const Region &other) const = 0; - virtual RegionDelegate *or_with (const Region &other) const = 0; + virtual RegionDelegate *and_with (const Region &other, PropertyConstraint prop_constraint) const = 0; + virtual RegionDelegate *not_with (const Region &other, PropertyConstraint prop_constraint) const = 0; + virtual RegionDelegate *xor_with (const Region &other, PropertyConstraint prop_constraint) const = 0; + virtual RegionDelegate *or_with (const Region &other, PropertyConstraint prop_constraint) const = 0; virtual RegionDelegate *add_in_place (const Region &other) = 0; virtual RegionDelegate *add (const Region &other) const = 0; - virtual std::pair andnot_with (const Region &other) const = 0; + virtual std::pair andnot_with (const Region &other, PropertyConstraint prop_constraint) const = 0; virtual RegionDelegate *selected_outside (const Region &other) const = 0; virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; @@ -311,16 +321,23 @@ public: virtual std::pair in_and_out (const Region &other) const = 0; virtual const db::Polygon *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_polygons () const = 0; virtual bool has_valid_merged_polygons () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; + virtual void apply_property_translator (const db::PropertiesTranslator &pt) = 0; + virtual db::PropertiesRepository *properties_repository () = 0; + virtual const db::PropertiesRepository *properties_repository () const = 0; + virtual bool equals (const Region &other) const = 0; virtual bool less (const Region &other) const = 0; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0; + virtual RegionDelegate *nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *net_filter) const = 0; + const std::string &progress_desc () const { return m_progress_desc; diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 90d0399ec..aaaa73cf3 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -25,6 +25,7 @@ #include "dbRegionUtils.h" #include "dbLocalOperationUtils.h" #include "dbHierProcessor.h" +#include "dbEdgeBoolean.h" namespace db { @@ -95,6 +96,9 @@ private: // --------------------------------------------------------------------------------------------------------------- +namespace +{ + static inline bool shields_interaction (const db::EdgePair &ep, const db::Edge &q) { db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); @@ -121,16 +125,6 @@ static bool shields_interaction (const db::EdgePair &ep, const P &poly) return false; } -template -check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) - : m_check (check), m_different_polygons (different_polygons), m_is_merged (is_merged), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) -{ - // .. nothing yet .. -} - -namespace -{ - template void insert_into_hash (std::unordered_set &, const S &) { @@ -143,8 +137,6 @@ void insert_into_hash (std::unordered_set &hash, const T &shape) hash.insert (shape); } -} - template static uint32_t compute_error_pattern (const TS &subject, std::unordered_set &result, std::map &edges_with_errors) @@ -195,13 +187,20 @@ static bool rect_filter_can_be_waived (uint32_t error_pattern, uint32_t rect_fil return can_be_waived; } +} + + +template +check_local_operation_base::check_local_operation_base (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) + : m_check (check), m_different_polygons (different_polygons), m_is_merged (is_merged), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) +{ + // .. nothing yet .. +} + template void -check_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +check_local_operation_base::compute_results (db::Layout *layout, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result) const { - tl_assert (results.size () == 1); - std::unordered_set result, intra_polygon_result; - // NOTE: the rectangle and opposite filters are unsymmetric bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter; @@ -214,14 +213,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::EdgeProcessor ep; ep.set_base_verbosity (50); - std::set ids; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - ids.insert (*j); - } - } - - bool take_all = edge_check.has_negative_edge_output () || interactions.num_intruders () == 0; + bool take_all = edge_check.has_negative_edge_output () || intruders.empty (); db::Box common_box; if (! take_all) { @@ -229,14 +221,14 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::Vector e (edge_check.distance (), edge_check.distance ()); db::Box subject_box; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - subject_box += db::box_convert () (interactions.subject_shape (i->first)); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + subject_box += db::box_convert () (**i); } if (edge_check.requires_different_layers ()) { db::Box intruder_box; - for (auto id = ids.begin (); id != ids.end (); ++id) { - intruder_box += db::box_convert () (interactions.intruder_shape (*id).second); + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + intruder_box += db::box_convert () (**i); } common_box = subject_box.enlarged (e) & intruder_box.enlarged (e); } else { @@ -249,14 +241,13 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; - if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) { - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &subject = interactions.subject_shape (i->first); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { if (! take_all) { - poly_check.enter (subject, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (subject, n); + poly_check.enter (**i, n); } n += 2; } @@ -268,9 +259,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &is = interactions.subject_shape (i->first); - for (typename TS::polygon_edge_iterator e = is.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -296,11 +286,11 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // merge the intruders to remove inner edges - if (ids.empty ()) { + if (intruders.empty ()) { // empty intruders - } else if (! m_other_is_merged && (ids.size () > 1 || ! interactions.intruder_shape (*ids.begin ()).second.is_box ())) { + } else if (! m_other_is_merged && (intruders.size () > 1 || ! (*intruders.begin ())->is_box ())) { // NOTE: this local merge is not necessarily giving the same results than a global merge before running // the processor. Reason: the search range is limited, hence not all necessary components may have been @@ -309,9 +299,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &is = interactions.intruder_shape (*id).second; - for (auto e = is.begin_edge (); ! e.at_end (); ++e) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -338,11 +327,11 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { n = 1; - for (auto id = ids.begin (); id != ids.end (); ++id) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { if (! take_all) { - poly_check.enter (interactions.intruder_shape (*id).second, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (interactions.intruder_shape (*id).second, n); + poly_check.enter (**i, n); } n += 2; } @@ -351,41 +340,39 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { - if (m_is_merged || (interactions.size () == 1 && ids.empty () && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) { // no merge required // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield size_t n = 0; - std::unordered_set subjects; + std::unordered_set subjects_hash; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { // we can't directly insert because TS may be != TI - const TS &ts = interactions.subject_shape (i->first); - insert_into_hash (subjects, ts); + insert_into_hash (subjects_hash, **i); if (! take_all) { - poly_check.enter (ts, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (ts, n); + poly_check.enter (**i, n); } n += 2; } n = 1; - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - if (subjects.find (ti) == subjects.end ()) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + if (subjects_hash.find (**i) == subjects_hash.end ()) { if (! take_all) { - poly_check.enter (ti, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (ti, n); + poly_check.enter (**i, n); } } } - } else if (ids.empty ()) { + } else if (intruders.empty ()) { // merge needed for the subject shapes - no intruders present so this is the simple case @@ -394,9 +381,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -427,17 +413,15 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; } - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - for (auto e = ti.begin_edge (); ! e.at_end (); ++e) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -453,16 +437,14 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape std::vector subject_edges; size_t sz = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - sz += ts.vertices (); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + sz += (*i)->vertices (); } subject_edges.reserve (sz); - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { subject_edges.push_back (*e); } } @@ -507,201 +489,245 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape do { poly_check.process (); } while (edge_check.prepare_next_pass ()); +} - // detect and remove parts of the result which have or do not have results "opposite" - // ("opposite" is defined by the projection of edges "through" the subject shape) - if (m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { +template +void +check_local_operation_base::apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const +{ + db::EdgeRelationFilter opp (db::WidthRelation, std::numeric_limits::max (), db::Projection); - db::EdgeRelationFilter opp (db::WidthRelation, std::numeric_limits::max (), db::Projection); + std::unordered_set cleaned_result; - std::unordered_set cleaned_result; + if (m_has_other) { - if (m_has_other) { + tl_assert (intra_polygon_result.empty ()); - tl_assert (intra_polygon_result.empty ()); + // filter out opposite edges: this is the case of two-layer checks where we can maintain the edge pairs but + // strip them of the filtered-out part. - // filter out opposite edges: this is the case of two-layer checks where we can maintain the edge pairs but - // strip them of the filtered-out part. + std::vector projections; + for (std::unordered_set::const_iterator ep1 = result.begin (); ep1 != result.end (); ++ep1) { - std::vector projections; - for (std::unordered_set::const_iterator ep1 = result.begin (); ep1 != result.end (); ++ep1) { + projections.clear (); - projections.clear (); + for (std::unordered_set::const_iterator ep2 = result.begin (); ep2 != result.end (); ++ep2) { - for (std::unordered_set::const_iterator ep2 = result.begin (); ep2 != result.end (); ++ep2) { + if (ep1 == ep2) { + continue; + } - if (ep1 == ep2) { - continue; + db::EdgePair ep_opp; + if (opp.check (ep1->first (), ep2->first (), &ep_opp)) { + + bool shielded = false; + for (auto i = subjects.begin (); i != subjects.end () && ! shielded; ++i) { + shielded = shields_interaction (ep_opp, **i); } - db::EdgePair ep_opp; - if (opp.check (ep1->first (), ep2->first (), &ep_opp)) { - - bool shielded = false; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end () && ! shielded; ++i) { - shielded = shields_interaction (ep_opp, interactions.subject_shape (i->first)); - } - - if (! shielded) { - projections.push_back (ep_opp.first ()); - } - + if (! shielded) { + projections.push_back (ep_opp.first ()); } } - if (! projections.empty ()) { - db::Edges ce; - if (m_options.opposite_filter == db::OnlyOpposite) { - ce = db::Edges (ep1->first ()) & db::Edges (projections.begin (), projections.end ()); - } else if (m_options.opposite_filter == db::NotOpposite) { - ce = db::Edges (ep1->first ()) - db::Edges (projections.begin (), projections.end ()); - } - for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { - cleaned_result.insert (db::EdgePair (*re, ep1->second ())); - } + } + + if (! projections.empty ()) { + db::Edges ce; + if (m_options.opposite_filter == db::OnlyOpposite) { + ce = db::Edges (ep1->first ()) & db::Edges (projections.begin (), projections.end ()); } else if (m_options.opposite_filter == db::NotOpposite) { - cleaned_result.insert (*ep1); + ce = db::Edges (ep1->first ()) - db::Edges (projections.begin (), projections.end ()); } - - } - - } else { - - // this is the single-layer case where we cannot maintain the edge pairs as we don't know how the - // other side will be filtered. For the filtering we only need the first edges and both edges of the - // intra-polygon checks - - std::unordered_set edges; - - for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { - edges.insert (ep->first ()); - } - - for (std::unordered_set::const_iterator ep = intra_polygon_result.begin (); ep != intra_polygon_result.end (); ++ep) { - edges.insert (ep->first ()); - edges.insert (ep->second ()); - } - - // filter out opposite edges - std::vector projections; - for (std::unordered_set::const_iterator e1 = edges.begin (); e1 != edges.end (); ++e1) { - - projections.clear (); - - for (std::unordered_set::const_iterator e2 = edges.begin (); e2 != edges.end (); ++e2) { - - if (e1 == e2) { - continue; - } - - db::EdgePair ep_opp; - if (opp.check (*e1, *e2, &ep_opp)) { - - bool shielded = false; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end () && ! shielded; ++i) { - shielded = shields_interaction (ep_opp, interactions.subject_shape (i->first)); - } - - if (! shielded) { - projections.push_back (ep_opp.first ()); - } - - } - + for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { + cleaned_result.insert (db::EdgePair (*re, ep1->second ())); } - - if (! projections.empty ()) { - db::Edges ce; - if (m_options.opposite_filter == db::OnlyOpposite) { - ce = db::Edges (*e1) & db::Edges (projections.begin (), projections.end ()); - } else if (m_options.opposite_filter == db::NotOpposite) { - ce = db::Edges (*e1) - db::Edges (projections.begin (), projections.end ()); - } - for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { - cleaned_result.insert (db::EdgePair (*re, re->swapped_points ())); - } - } else if (m_options.opposite_filter == db::NotOpposite) { - cleaned_result.insert (db::EdgePair (*e1, e1->swapped_points ())); - } - + } else if (m_options.opposite_filter == db::NotOpposite) { + cleaned_result.insert (*ep1); } } - result.swap (cleaned_result); - } else { - result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + // this is the single-layer case where we cannot maintain the edge pairs as we don't know how the + // other side will be filtered. For the filtering we only need the first edges and both edges of the + // intra-polygon checks - } + std::unordered_set edges; - // implements error filtering on rectangles - if (m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { + edges.insert (ep->first ()); + } - std::unordered_set waived; + for (std::unordered_set::const_iterator ep = intra_polygon_result.begin (); ep != intra_polygon_result.end (); ++ep) { + edges.insert (ep->first ()); + edges.insert (ep->second ()); + } - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + // filter out opposite edges + std::vector projections; + for (std::unordered_set::const_iterator e1 = edges.begin (); e1 != edges.end (); ++e1) { - const TS &subject = interactions.subject_shape (i->first); - if (! subject.is_box ()) { - continue; - } + projections.clear (); - std::map edges_with_errors; - unsigned int error_pattern = compute_error_pattern (subject, result, edges_with_errors); + for (std::unordered_set::const_iterator e2 = edges.begin (); e2 != edges.end (); ++e2) { - if (rect_filter_can_be_waived (error_pattern, (uint32_t) m_options.rect_filter)) { + if (e1 == e2) { + continue; + } - for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { - if (edges_with_errors.find (ep->first ()) != edges_with_errors.end ()) { - waived.insert (*ep); + db::EdgePair ep_opp; + if (opp.check (*e1, *e2, &ep_opp)) { + + bool shielded = false; + for (auto i = subjects.begin (); i != subjects.end () && ! shielded; ++i) { + shielded = shields_interaction (ep_opp, **i); } + + if (! shielded) { + projections.push_back (ep_opp.first ()); + } + } } - } - - if (! waived.empty ()) { - for (std::unordered_set::const_iterator i = waived.begin (); i != waived.end (); ++i) { - result.erase (*i); + if (! projections.empty ()) { + db::Edges ce; + if (m_options.opposite_filter == db::OnlyOpposite) { + ce = db::Edges (*e1) & db::Edges (projections.begin (), projections.end ()); + } else if (m_options.opposite_filter == db::NotOpposite) { + ce = db::Edges (*e1) - db::Edges (projections.begin (), projections.end ()); + } + for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { + cleaned_result.insert (db::EdgePair (*re, re->swapped_points ())); + } + } else if (m_options.opposite_filter == db::NotOpposite) { + cleaned_result.insert (db::EdgePair (*e1, e1->swapped_points ())); } + } - if (! m_has_other) { + } - // this is the case of single-layer interaction. We need to separate the results - // from edge pairs into single edges (basically returning the first edge only) - // Reasoning: we cannot say what's going to happen on the other side of the - // error - it may not be waived and we cannot waive half of an edge pair. + result.swap (cleaned_result); +} - for (std::unordered_set::const_iterator i = result.begin (); i != result.end (); ++i) { - results.front ().insert (db::EdgePair (i->first (), i->first ().swapped_points ())); +template +void +check_local_operation_base::apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const +{ + std::unordered_set waived; + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + + const TS &subject = **i; + if (! subject.is_box ()) { + continue; + } + + std::map edges_with_errors; + unsigned int error_pattern = compute_error_pattern (subject, result, edges_with_errors); + + if (rect_filter_can_be_waived (error_pattern, (uint32_t) m_options.rect_filter)) { + + for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { + if (edges_with_errors.find (ep->first ()) != edges_with_errors.end ()) { + waived.insert (*ep); + } } - result.clear (); } } + if (! waived.empty ()) { + for (std::unordered_set::const_iterator i = waived.begin (); i != waived.end (); ++i) { + result.erase (*i); + } + } + + if (! m_has_other) { + + // this is the case of single-layer interaction. We need to separate the results + // from edge pairs into single edges (basically returning the first edge only) + // Reasoning: we cannot say what's going to happen on the other side of the + // error - it may not be waived and we cannot waive half of an edge pair. + + std::unordered_set filtered; + for (std::unordered_set::const_iterator i = result.begin (); i != result.end (); ++i) { + filtered.insert (db::EdgePair (i->first (), i->first ().swapped_points ())); + } + + result.swap (filtered); + + } +} + +// --------------------------------------------------------------------------------------------------------------- + +template +check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) + : check_local_operation_base (check, different_polygons, is_merged, has_other, other_is_merged, options) +{ + // .. nothing yet .. +} + +template +void +check_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + std::vector subjects; + subjects.reserve (interactions.size ()); + + std::set intruders; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + subjects.push_back (&interactions.subject_shape (i->first)); + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + intruders.insert (&interactions.intruder_shape (*ii).second); + } + } + + tl_assert (results.size () == 1); + + std::unordered_set result, intra_polygon_result; + + // perform the basic check + check_local_operation_base::compute_results (layout, subjects, intruders, result, intra_polygon_result); + + // detect and remove parts of the result which have or do not have results "opposite" + // ("opposite" is defined by the projection of edges "through" the subject shape) + if (check_local_operation_base::m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { + check_local_operation_base::apply_opposite_filter (subjects, result, intra_polygon_result); + } else { + result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + } + + // implements error filtering on rectangles + if (check_local_operation_base::m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + check_local_operation_base::apply_rectangle_filter (subjects, result); + } + results.front ().insert (result.begin (), result.end ()); } + template db::Coord check_local_operation::dist () const { // TODO: will the distance be sufficient? Or should we take somewhat more? - return m_check.distance (); + return check_local_operation_base::m_check.distance (); } template OnEmptyIntruderHint check_local_operation::on_empty_intruder_hint () const { - return m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; + return check_local_operation_base::m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; } template @@ -717,6 +743,79 @@ template class DB_PUBLIC check_local_operation; // --------------------------------------------------------------------------------------------------------------- +template +check_local_operation_with_properties::check_local_operation_with_properties (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr) + : check_local_operation_base (check, different_polygons, is_merged, has_other, other_is_merged, options), m_pms (target_pr, subject_pr), m_pmi (target_pr, intruder_pr) +{ + // .. nothing yet .. +} + +template +void +check_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == 1); + + auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base::m_options.prop_constraint, m_pms, m_pmi); + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + std::unordered_set result, intra_polygon_result; + + const std::vector &subjects = s2p->second.first; + const std::set &intruders = s2p->second.second; + + // perform the basic check + check_local_operation_base::compute_results (layout, subjects, intruders, result, intra_polygon_result); + + // detect and remove parts of the result which have or do not have results "opposite" + // ("opposite" is defined by the projection of edges "through" the subject shape) + if (check_local_operation_base::m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { + check_local_operation_base::apply_opposite_filter (subjects, result, intra_polygon_result); + } else { + result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + } + + // implements error filtering on rectangles + if (check_local_operation_base::m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + check_local_operation_base::apply_rectangle_filter (subjects, result); + } + + for (auto r = result.begin (); r != result.end (); ++r) { + results.front ().insert (db::EdgePairWithProperties (*r, pc_norm (check_local_operation_base::m_options.prop_constraint, s2p->first))); + } + + } +} + +template +db::Coord +check_local_operation_with_properties::dist () const +{ + // TODO: will the distance be sufficient? Or should we take somewhat more? + return check_local_operation_base::m_check.distance (); +} + +template +OnEmptyIntruderHint +check_local_operation_with_properties::on_empty_intruder_hint () const +{ + return check_local_operation_base::m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; +} + +template +std::string +check_local_operation_with_properties::description () const +{ + return tl::to_string (tr ("Generic DRC check")); +} + +// explicit instantiations +template class DB_PUBLIC check_local_operation_with_properties; +template class DB_PUBLIC check_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------------------------- + namespace { class PolygonToEdgeProcessor @@ -1430,4 +1529,563 @@ std::string interacting_with_text_local_operation::description () co template class DB_PUBLIC interacting_with_text_local_operation; template class DB_PUBLIC interacting_with_text_local_operation; +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +template +bool_and_or_not_local_operation::bool_and_or_not_local_operation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +template +OnEmptyIntruderHint +bool_and_or_not_local_operation::on_empty_intruder_hint () const +{ + return m_is_and ? Drop : Copy; +} + +template +std::string +bool_and_or_not_local_operation::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +template +void +bool_and_or_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const TR &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 { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::polygon_ref_generator pr (layout, result); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + } +} + +template class DB_PUBLIC bool_and_or_not_local_operation; +template class DB_PUBLIC bool_and_or_not_local_operation; + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperationWithProperties implementation + +template +bool_and_or_not_local_operation_with_properties::bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) + : m_is_and (is_and), m_property_constraint (property_constraint), m_pms (target_pr, subject_pr), m_pmi (target_pr, intruder_pr) +{ + // .. nothing yet .. +} + +template +OnEmptyIntruderHint +bool_and_or_not_local_operation_with_properties::on_empty_intruder_hint () const +{ + return m_is_and ? Drop : Copy; +} + +template +std::string +bool_and_or_not_local_operation_with_properties::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +template +void +bool_and_or_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 1); + std::unordered_set > &result = results.front (); + + db::EdgeProcessor ep; + + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + if (i->second.empty ()) { + + if (! m_is_and) { + result.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); + } + + } else { + + db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); + + auto &shapes_by_prop = by_prop_id [prop_id_s]; + shapes_by_prop.first.push_front (subject); + + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; + if (pc_match (m_property_constraint, prop_id_s, m_pmi (intruder.properties_id ()))) { + shapes_by_prop.second.insert (intruder); + } + } + + } + + } + + for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { + + ep.clear (); + size_t p1 = 0, p2 = 1; + + const std::set &others = p2s->second.second; + db::properties_id_type prop_id = pc_norm (m_property_constraint, p2s->first); + + for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { + + const TS &subject = *s; + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (db::object_with_properties (subject, prop_id)); + } + } else if (others.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (db::object_with_properties (subject, prop_id)); + } + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::polygon_ref_generator_with_properties > pr (layout, result, prop_id); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + } + + } +} + +template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; +template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------- +// TwoBoolAndNotLocalOperation implementation + +template +two_bool_and_not_local_operation::two_bool_and_not_local_operation () + : db::local_operation () +{ + // .. nothing yet .. +} + +template +void +two_bool_and_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 2); + + db::EdgeProcessor ep; + + std::unordered_set &result0 = results [0]; + std::unordered_set &result1 = results [1]; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const TS &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + result0.insert (subject); + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + result1.insert (subject); + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op0 (db::BooleanOp::And); + db::polygon_ref_generator pr0 (layout, result0); + db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); + db::PolygonGenerator pg0 (splitter0, true, true); + + db::BooleanOp op1 (db::BooleanOp::ANotB); + db::polygon_ref_generator pr1 (layout, result1); + db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); + db::PolygonGenerator pg1 (splitter1, true, true); + + ep.set_base_verbosity (50); + + std::vector > procs; + procs.push_back (std::make_pair (&pg0, &op0)); + procs.push_back (std::make_pair (&pg1, &op1)); + ep.process (procs); + + } + +} + +template +std::string two_bool_and_not_local_operation::description () const +{ + return tl::to_string (tr ("ANDNOT operation")); +} + +template class DB_PUBLIC two_bool_and_not_local_operation; +template class DB_PUBLIC two_bool_and_not_local_operation; + +// --------------------------------------------------------------------------------------------- +// TwoBoolAndNotLocalOperationWithProperties implementation + +template +two_bool_and_not_local_operation_with_properties::two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) + : db::local_operation, db::object_with_properties, db::object_with_properties > (), + m_property_constraint (property_constraint), m_pms (target1_pr, subject_pr), m_pmi (target1_pr, intruder_pr), m_pm12 (target2_pr, target1_pr) +{ + // .. nothing yet .. +} + +template +void +two_bool_and_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 2); + std::unordered_set > &result0 = results [0]; + std::unordered_set > &result1 = results [1]; + + db::EdgeProcessor ep; + + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + if (i->second.empty ()) { + + result1.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); + + } else { + + db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); + + auto &shapes_by_prop = by_prop_id [prop_id_s]; + shapes_by_prop.first.push_front (subject); + + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; + if (pc_match (m_property_constraint, prop_id_s, m_pmi (intruder.properties_id ()))) { + shapes_by_prop.second.insert (intruder); + } + } + + } + + } + + for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { + + ep.clear (); + size_t p1 = 0, p2 = 1; + + const std::set &others = p2s->second.second; + db::properties_id_type prop_id = pc_norm (m_property_constraint, p2s->first); + + for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { + + const TR &subject = *s; + if (others.find (subject) != others.end ()) { + result0.insert (db::object_with_properties (subject, prop_id)); + } else if (others.empty ()) { + // shortcut (not: keep, and: drop) + result1.insert (db::object_with_properties (subject, m_pm12 (prop_id))); + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + std::unordered_set result0_wo_props; + std::unordered_set result1_wo_props; + + db::BooleanOp op0 (db::BooleanOp::And); + db::polygon_ref_generator pr0 (layout, result0_wo_props); + db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); + db::PolygonGenerator pg0 (splitter0, true, true); + + db::BooleanOp op1 (db::BooleanOp::ANotB); + db::polygon_ref_generator pr1 (layout, result1_wo_props); + db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); + db::PolygonGenerator pg1 (splitter1, true, true); + + ep.set_base_verbosity (50); + + std::vector > procs; + procs.push_back (std::make_pair (&pg0, &op0)); + procs.push_back (std::make_pair (&pg1, &op1)); + ep.process (procs); + + for (auto r = result0_wo_props.begin (); r != result0_wo_props.end (); ++r) { + result0.insert (db::object_with_properties (*r, prop_id)); + } + for (auto r = result1_wo_props.begin (); r != result1_wo_props.end (); ++r) { + result1.insert (db::object_with_properties (*r, m_pm12 (prop_id))); + } + + } + + } +} + +template +std::string two_bool_and_not_local_operation_with_properties::description () const +{ + return tl::to_string (tr ("ANDNOT operation")); +} + +template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; +template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------- + +SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) + : m_wrap_count (wrap_count) +{ + // .. nothing yet .. +} + +void +SelfOverlapMergeLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + if (m_wrap_count == 0) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + std::set seen; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + if (seen.find (i->first) == seen.end ()) { + seen.insert (i->first); + const db::PolygonRef &subject = interactions.subject_shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (db::shape_interactions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { + // don't take the same (really the same, not an identical one) shape twice - the interaction + // set does not take care to list just one copy of the same item on the intruder side. + if (seen.find (*o) == seen.end ()) { + seen.insert (*o); + const db::PolygonRef &intruder = interactions.intruder_shape (*o).second; + for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + } + + } + + db::MergeOp op (m_wrap_count - 1); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); +} + +OnEmptyIntruderHint SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const +{ + return m_wrap_count > 1 ? Drop : Copy; +} + +std::string SelfOverlapMergeLocalOperation::description () const +{ + return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); +} + +// --------------------------------------------------------------------------------------------- + +PolygonToEdgeLocalOperation::PolygonToEdgeLocalOperation (db::PropertiesRepository *target_pr, const db::PropertiesRepository *source_pr) + : local_operation (), m_pm (target_pr, source_pr) +{ + // .. nothing yet .. +} + +std::string +PolygonToEdgeLocalOperation::description () const +{ + return std::string ("polygon to edges"); +} + +void +PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + db::EdgeProcessor ep; + ep.set_base_verbosity (50); + + auto by_prop_id = separate_interactions_by_properties (interactions, db::SamePropertiesConstraint, m_pm, m_pm); + for (auto shapes_by_prop_id = by_prop_id.begin (); shapes_by_prop_id != by_prop_id.end (); ++shapes_by_prop_id) { + + db::properties_id_type prop_id = shapes_by_prop_id->first; + + for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) { + ep.insert (**s); + } + + db::property_injector > results_with_properties (&results.front (), prop_id); + + if (shapes_by_prop_id->second.second.empty ()) { + + db::edge_to_edge_set_generator > > eg (results_with_properties, prop_id); + db::MergeOp op (0); + ep.process (eg, op); + + } else { + + // With intruders: to compute our local contribution we take the edges without and with intruders + // and deliver what is in both sets + + db::MergeOp op (0); + + std::vector edges1; + db::EdgeContainer ec1 (edges1); + ep.process (ec1, op); + + ep.clear (); + + for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { + ep.insert (s->second); + } + for (auto i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) { + ep.insert (i->second.second); + } + + std::vector edges2; + db::EdgeContainer ec2 (edges2); + ep.process (ec2, op); + + // Runs the boolean AND between the result with and without intruders + + db::box_scanner scanner; + scanner.reserve (edges1.size () + edges2.size ()); + + for (std::vector::const_iterator i = edges1.begin (); i != edges1.end (); ++i) { + scanner.insert (i.operator-> (), 0); + } + for (std::vector::const_iterator i = edges2.begin (); i != edges2.end (); ++i) { + scanner.insert (i.operator-> (), 1); + } + + EdgeBooleanClusterCollector > > cluster_collector (&results_with_properties, EdgeAnd); + scanner.process (cluster_collector, 1, db::box_convert ()); + + } + + } + +} + } diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index e2b9a1f59..f5e4a238a 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -27,8 +27,11 @@ #include "dbCommon.h" #include "dbEdgePairRelations.h" #include "dbLocalOperation.h" +#include "dbLocalOperationUtils.h" #include "dbEdgeProcessor.h" #include "dbRegionCheckUtils.h" +#include "dbPropertyConstraint.h" +#include "dbLayoutUtils.h" #include #include @@ -124,7 +127,8 @@ struct DB_PUBLIC RegionCheckOptions bool _shielded = true, OppositeFilter _opposite_filter = NoOppositeFilter, RectFilter _rect_filter = NoRectFilter, - bool _negative = false) + bool _negative = false, + PropertyConstraint _prop_constraint = IgnoreProperties) : whole_edges (_whole_edges), metrics (_metrics), ignore_angle (_ignore_angle), @@ -133,7 +137,8 @@ struct DB_PUBLIC RegionCheckOptions shielded (_shielded), opposite_filter (_opposite_filter), rect_filter (_rect_filter), - negative (_negative) + negative (_negative), + prop_constraint (_prop_constraint) { } /** @@ -198,6 +203,11 @@ struct DB_PUBLIC RegionCheckOptions */ bool negative; + /** + * @brief Specifies a property constraint - e.g. checking only shapes with the same properties + */ + PropertyConstraint prop_constraint; + /** * @brief Gets a value indicating whether merged primary input is required */ @@ -212,9 +222,28 @@ struct DB_PUBLIC RegionCheckOptions } }; +template +class check_local_operation_base +{ +public: + check_local_operation_base (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); + +protected: + EdgeRelationFilter m_check; + bool m_different_polygons; + bool m_is_merged; + bool m_has_other; + bool m_other_is_merged; + db::RegionCheckOptions m_options; + + void compute_results (db::Layout *layout, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; + void apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; + void apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const; +}; + template class check_local_operation - : public local_operation + : public local_operation, public check_local_operation_base { public: check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); @@ -225,14 +254,24 @@ public: virtual std::string description () const; virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; +}; + +template +class check_local_operation_with_properties + : public local_operation, db::object_with_properties, db::EdgePairWithProperties>, public check_local_operation_base +{ +public: + check_local_operation_with_properties (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr); + + virtual db::Coord dist () const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual bool requests_single_subjects () const { return true; } + virtual std::string description () const; + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; private: - EdgeRelationFilter m_check; - bool m_different_polygons; - bool m_is_merged; - bool m_has_other; - bool m_other_is_merged; - db::RegionCheckOptions m_options; + mutable db::PropertyMapper m_pms, m_pmi; }; typedef check_local_operation CheckLocalOperation; @@ -371,6 +410,131 @@ typedef contained_local_operation ContainedEdgesLocalOperation; +/** + * @brief Implements a boolean AND or NOT operation + */ + +template +class DB_PUBLIC bool_and_or_not_local_operation + : public local_operation +{ +public: + bool_and_or_not_local_operation (bool is_and); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; +}; + +typedef bool_and_or_not_local_operation BoolAndOrNotLocalOperation; + +/** + * @brief Implements a boolean AND or NOT operation with property handling + */ +template +class DB_PUBLIC bool_and_or_not_local_operation_with_properties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; + db::PropertyConstraint m_property_constraint; + mutable db::PropertyMapper m_pms; + mutable db::PropertyMapper m_pmi; +}; + +typedef bool_and_or_not_local_operation_with_properties BoolAndOrNotLocalOperationWithProperties; + +/** + * @brief Implements a boolean AND plus NOT operation + * + * This processor delivers two outputs: the first one having the AND result, the second + * one having the NOT result. + */ + +template +class DB_PUBLIC two_bool_and_not_local_operation + : public local_operation +{ +public: + two_bool_and_not_local_operation (); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual std::string description () const; +}; + +typedef two_bool_and_not_local_operation TwoBoolAndNotLocalOperation; + +/** + * @brief Implements a boolean AND plus NOT operation + * + * This processor delivers two outputs: the first one having the AND result, the second + * one having the NOT result. + */ +template +class DB_PUBLIC two_bool_and_not_local_operation_with_properties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; + virtual std::string description () const; + +private: + db::PropertyConstraint m_property_constraint; + mutable db::PropertyMapper m_pms, m_pmi, m_pm12; +}; + +typedef two_bool_and_not_local_operation_with_properties TwoBoolAndNotLocalOperationWithProperties; + +/** + * @brief Implements a merge operation with an overlap count + * With a given wrap_count, the result will only contains shapes where + * the original shapes overlap at least "wrap_count" times. + */ +class DB_PUBLIC SelfOverlapMergeLocalOperation + : public local_operation +{ +public: + SelfOverlapMergeLocalOperation (unsigned int wrap_count); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + unsigned int m_wrap_count; +}; + +/** + * @brief Converts polygons to edges + */ +class DB_PUBLIC PolygonToEdgeLocalOperation + : public local_operation +{ +public: + PolygonToEdgeLocalOperation (db::PropertiesRepository *target_pr, const db::PropertiesRepository *source_pr); + + virtual db::Coord dist () const { return 1; } + virtual bool requests_single_subjects () const { return true; } + virtual std::string description () const; + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; + +private: + mutable db::PropertyMapper m_pm; +}; + } // namespace db #endif diff --git a/src/db/db/dbShapeCollection.cc b/src/db/db/dbShapeCollection.cc index e69de29bb..ba1eb224b 100644 --- a/src/db/db/dbShapeCollection.cc +++ b/src/db/db/dbShapeCollection.cc @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbShapeCollection.h" +#include "dbPropertiesRepository.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +DeepShapeCollectionDelegateBase::DeepShapeCollectionDelegateBase () +{ + // .. nothing yet .. +} + +DeepShapeCollectionDelegateBase::DeepShapeCollectionDelegateBase (const DeepShapeCollectionDelegateBase &other) +{ + m_deep_layer = other.m_deep_layer.copy (); +} + +DeepShapeCollectionDelegateBase & +DeepShapeCollectionDelegateBase::operator= (const DeepShapeCollectionDelegateBase &other) +{ + if (this != &other) { + m_deep_layer = other.m_deep_layer.copy (); + } + return *this; +} + +void +DeepShapeCollectionDelegateBase::apply_property_translator (const db::PropertiesTranslator &pt) +{ + db::Layout &layout = m_deep_layer.layout (); + for (auto c = layout.begin (); c != layout.end (); ++c) { + + db::Shapes &shapes = c->shapes (m_deep_layer.layer ()); + if ((shapes.type_mask () & ShapeIterator::Properties) != 0) { + + // properties are present - need to translate them + + db::Shapes new_shapes (shapes.is_editable ()); + shapes.swap (new_shapes); + + shapes.assign (new_shapes, pt); + + } + + } +} + +// ------------------------------------------------------------------------------------------------------------- +// ShapeCollection implementation + +const db::PropertiesRepository & +ShapeCollection::properties_repository () const +{ + static db::PropertiesRepository empty_prop_repo; + const db::PropertiesRepository *r = get_delegate () ? get_delegate ()->properties_repository () : 0; + return *(r ? r : &empty_prop_repo); +} + +db::PropertiesRepository & +ShapeCollection::properties_repository () +{ + db::PropertiesRepository *r = get_delegate () ? get_delegate ()->properties_repository () : 0; + tl_assert (r != 0); + return *r; +} + +bool +ShapeCollection::has_properties_repository () const +{ + return get_delegate () && get_delegate ()->properties_repository (); +} + +void +ShapeCollection::apply_property_translator (const db::PropertiesTranslator &pt) +{ + if (get_delegate ()) { + get_delegate ()->apply_property_translator (pt); + } +} + +} diff --git a/src/db/db/dbShapeCollection.h b/src/db/db/dbShapeCollection.h index c689db0c9..a036e0887 100644 --- a/src/db/db/dbShapeCollection.h +++ b/src/db/db/dbShapeCollection.h @@ -26,31 +26,25 @@ #include "dbCommon.h" #include "dbDeepShapeStore.h" #include "tlUniqueId.h" +#include "tlVariant.h" #include "gsiObject.h" namespace db { +class PropertiesTranslator; +class PropertiesRepository; + /** * @brief A base class for the deep collection delegates */ class DB_PUBLIC DeepShapeCollectionDelegateBase { public: - DeepShapeCollectionDelegateBase () { } + DeepShapeCollectionDelegateBase (); + DeepShapeCollectionDelegateBase (const DeepShapeCollectionDelegateBase &other); - DeepShapeCollectionDelegateBase (const DeepShapeCollectionDelegateBase &other) - { - m_deep_layer = other.m_deep_layer.copy (); - } - - DeepShapeCollectionDelegateBase &operator= (const DeepShapeCollectionDelegateBase &other) - { - if (this != &other) { - m_deep_layer = other.m_deep_layer.copy (); - } - return *this; - } + DeepShapeCollectionDelegateBase &operator= (const DeepShapeCollectionDelegateBase &other); const db::DeepLayer &deep_layer () const { @@ -62,6 +56,8 @@ public: return m_deep_layer; } + void apply_property_translator (const db::PropertiesTranslator &pt); + protected: virtual void set_deep_layer (const db::DeepLayer &dl) { @@ -83,6 +79,17 @@ public: virtual ~ShapeCollectionDelegateBase () { } virtual DeepShapeCollectionDelegateBase *deep () { return 0; } + + virtual void apply_property_translator (const db::PropertiesTranslator & /*pt*/) = 0; + virtual db::PropertiesRepository *properties_repository () = 0; + virtual const db::PropertiesRepository *properties_repository () const = 0; + + void remove_properties (bool remove = true) + { + if (remove) { + apply_property_translator (db::PropertiesTranslator::make_remove_all ()); + } + } }; /** @@ -96,6 +103,35 @@ public: virtual ~ShapeCollection () { } virtual ShapeCollectionDelegateBase *get_delegate () const = 0; + + /** + * @brief Applies a PropertyTranslator + * + * This method will translate the property IDs according to the given property translator. + * + * Note that the property translator needs to be built from the PropertiesRepository + * delivered by "properties_repository". + */ + void apply_property_translator (const db::PropertiesTranslator &pt); + + /** + * @brief Gets the property repository + * + * Use this object to decode and encode property IDs. + */ + db::PropertiesRepository &properties_repository (); + + /** + * @brief Gets the property repository (const version) + * + * Use this object to decode property IDs. + */ + const db::PropertiesRepository &properties_repository () const; + + /** + * @brief Gets a value indicating whether a properties repository is available + */ + bool has_properties_repository () const; }; } diff --git a/src/db/db/dbShapeCollectionUtils.h b/src/db/db/dbShapeCollectionUtils.h index 897464809..817fd8d3f 100644 --- a/src/db/db/dbShapeCollectionUtils.h +++ b/src/db/db/dbShapeCollectionUtils.h @@ -39,9 +39,9 @@ namespace db { /** - * @brief A template base class for edge processors + * @brief A template base class for a shape processors * - * A polygon processor can turn a edge into something else. + * A shape processor can turn a shape into something else. */ template class DB_PUBLIC_TEMPLATE shape_collection_processor @@ -209,6 +209,7 @@ shape_collection_processed_impl (const db::DeepLayer &input, const shape_collect } shape_collection_processor_delivery delivery (&layout, st); + shape_collection_processor_delivery > delivery_wp (&layout, st); const db::ICplxTrans &tr = v->first; db::ICplxTrans trinv = tr.inverted (); @@ -220,7 +221,11 @@ shape_collection_processed_impl (const db::DeepLayer &input, const shape_collect heap.clear (); filter.process (s, heap); for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { - delivery.put (i->transformed (trinv)); + if (si->prop_id ()) { + delivery_wp.put (db::object_with_properties (i->transformed (trinv), si->prop_id ())); + } else { + delivery.put (i->transformed (trinv)); + } } } @@ -230,6 +235,7 @@ shape_collection_processed_impl (const db::DeepLayer &input, const shape_collect db::Shapes &st = c->shapes (res->deep_layer ().layer ()); shape_collection_processor_delivery delivery (&layout, &st); + shape_collection_processor_delivery > delivery_wp (&layout, &st); for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { Shape s; @@ -237,7 +243,11 @@ shape_collection_processed_impl (const db::DeepLayer &input, const shape_collect heap.clear (); filter.process (s, heap); for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { - delivery.put (*i); + if (si->prop_id ()) { + delivery_wp.put (db::object_with_properties (*i, si->prop_id ())); + } else { + delivery.put (*i); + } } } diff --git a/src/db/db/dbShapeFlags.h b/src/db/db/dbShapeFlags.h index b812e5df4..700a104e5 100644 --- a/src/db/db/dbShapeFlags.h +++ b/src/db/db/dbShapeFlags.h @@ -26,48 +26,145 @@ #include "dbCommon.h" #include "dbShapes.h" +#include "tlSList.h" namespace db { -template unsigned int shape_flags (); -template unsigned int shape_flags_pure (); - -template <> inline unsigned int shape_flags () { return 1 << db::ShapeIterator::PolygonRef; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::PolygonRef; } - -template <> inline unsigned int shape_flags () { return 1 << db::ShapeIterator::TextRef; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::TextRef; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::Boxes; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::Box; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::Paths; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::Path; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::Polygons; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::Polygon; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::Edges; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::Edge; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::EdgePairs; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::EdgePair; } - -template <> inline unsigned int shape_flags () { return db::ShapeIterator::Texts; } -template <> inline unsigned int shape_flags_pure () { return 1 << db::ShapeIterator::Text; } - - template -struct DB_PUBLIC shape_to_object +struct shape_flags_traits { - void set (const db::Shape &) { } - const T *get (const db::Shape &s) const { return s.basic_ptr (typename T::tag ()); } + static unsigned int generic () { return 0; } + static unsigned int pure () { return 0; } + static bool with_props () { return false; } }; +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return 1 << db::ShapeIterator::PolygonRef; } + static unsigned int pure () { return 1 << db::ShapeIterator::PolygonRef; } +}; template <> -struct DB_PUBLIC shape_to_object +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return 1 << db::ShapeIterator::TextRef; } + static unsigned int pure () { return 1 << db::ShapeIterator::TextRef; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Boxes; } + static unsigned int pure () { return 1 << db::ShapeIterator::Box; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Paths; } + static unsigned int pure () { return 1 << db::ShapeIterator::Path; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Regions; } + static unsigned int pure () { return 1 << db::ShapeIterator::Polygon; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Regions; } + static unsigned int pure () { return 1 << db::ShapeIterator::SimplePolygon; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Edges; } + static unsigned int pure () { return 1 << db::ShapeIterator::Edge; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::EdgePairs; } + static unsigned int pure () { return 1 << db::ShapeIterator::EdgePair; } +}; + +template <> +struct shape_flags_traits + : public shape_flags_traits +{ + static unsigned int generic () { return db::ShapeIterator::Texts; } + static unsigned int pure () { return 1 << db::ShapeIterator::Text; } +}; + +template +struct shape_flags_traits > + : public shape_flags_traits +{ + static bool with_props () { return true; } +}; + +template unsigned int shape_flags () { return shape_flags_traits::generic (); } +template unsigned int shape_flags_pure () { return shape_flags_traits::pure (); } +template bool shape_flags_with_props () { return shape_flags_traits::with_props (); } + +/** + * @brief Converter helpers for changing a shape to an object of a specific type + * + * These converters a volatile. The pointer delivered is not valid after the next object has + * been retrieved. + */ + +template +struct DB_PUBLIC shape_to_object_impl +{ + typedef T value_type; + + void set (const db::Shape &) { } + const value_type *get (const db::Shape &s) const { return s.basic_ptr (typename T::tag ()); } +}; + +template +struct DB_PUBLIC shape_to_object_impl > +{ + typedef db::object_with_properties value_type; + + void set (const db::Shape &s) + { + if (! s.has_prop_id ()) { + m_shape = value_type (*s.basic_ptr (typename T::tag ()), 0); + } + } + + const value_type *get (const db::Shape &s) const + { + if (! s.has_prop_id ()) { + return &m_shape; + } else { + return s.basic_ptr (typename value_type::tag ()); + } + } + +private: + value_type m_shape; +}; + +template <> +struct DB_PUBLIC shape_to_object_impl { typedef db::Polygon value_type; @@ -79,7 +176,27 @@ private: }; template <> -struct DB_PUBLIC shape_to_object +struct DB_PUBLIC shape_to_object_impl +{ + typedef db::PolygonWithProperties value_type; + + void set (const db::Shape &s) + { + s.polygon (m_shape); + m_shape.properties_id (s.prop_id ()); + } + + const value_type *get (const db::Shape &) const + { + return &m_shape; + } + +private: + value_type m_shape; +}; + +template <> +struct DB_PUBLIC shape_to_object_impl { typedef db::SimplePolygon value_type; @@ -91,7 +208,27 @@ private: }; template <> -struct DB_PUBLIC shape_to_object +struct DB_PUBLIC shape_to_object_impl +{ + typedef db::SimplePolygonWithProperties value_type; + + void set (const db::Shape &s) + { + s.simple_polygon (m_shape); + m_shape.properties_id (s.prop_id ()); + } + + const value_type *get (const db::Shape &) const + { + return &m_shape; + } + +private: + value_type m_shape; +}; + +template <> +struct DB_PUBLIC shape_to_object_impl { typedef db::Path value_type; @@ -103,7 +240,27 @@ private: }; template <> -struct DB_PUBLIC shape_to_object +struct DB_PUBLIC shape_to_object_impl +{ + typedef db::PathWithProperties value_type; + + void set (const db::Shape &s) + { + s.path (m_shape); + m_shape.properties_id (s.prop_id ()); + } + + const value_type *get (const db::Shape &) const + { + return &m_shape; + } + +private: + value_type m_shape; +}; + +template <> +struct DB_PUBLIC shape_to_object_impl { typedef db::Text value_type; @@ -115,17 +272,395 @@ private: }; template <> -struct DB_PUBLIC shape_to_object +struct DB_PUBLIC shape_to_object_impl +{ + typedef db::TextWithProperties value_type; + + void set (const db::Shape &s) + { + s.text (m_shape); + m_shape.properties_id (s.prop_id ()); + } + + const value_type *get (const db::Shape &) const + { + return &m_shape; + } + +private: + value_type m_shape; +}; + +template <> +struct DB_PUBLIC shape_to_object_impl { typedef db::Box value_type; - void set (const db::Shape *s) { s->box (m_shape); } + void set (const db::Shape &s) { s.box (m_shape); } const value_type *get (const db::Shape *) const { return &m_shape; } private: value_type m_shape; }; +template <> +struct DB_PUBLIC shape_to_object_impl +{ + typedef db::BoxWithProperties value_type; + + void set (const db::Shape &s) + { + s.box (m_shape); + m_shape.properties_id (s.prop_id ()); + } + + const value_type *get (const db::Shape &) const + { + return &m_shape; + } + +private: + value_type m_shape; +}; + +template +struct DB_PUBLIC shape_to_object + : public shape_to_object_impl +{ + const typename shape_to_object_impl::value_type &operator() (const db::Shape &s) + { + shape_to_object_impl::set (s); + return *shape_to_object_impl::get (s); + } +}; + +/** + * @brief Implements an addressable object heap + * + * This object can deliver addressable objects from shapes. It will keep temporary objects + * internally if required. + */ + +template +struct addressable_object_from_shape +{ + typedef T value_type; + + const T *operator () (const db::Shape &shape) + { + typename T::tag object_tag; + return shape.basic_ptr (object_tag); + } +}; + +template +struct addressable_object_from_shape > +{ + typedef db::object_with_properties value_type; + + const db::object_with_properties *operator () (const db::Shape &shape) + { + if (shape.has_prop_id ()) { + typename db::object_with_properties::tag object_tag; + return shape.basic_ptr (object_tag); + } else { + typename T::tag object_tag; + m_heap.push_back (db::object_with_properties (*shape.basic_ptr (object_tag), 0)); + return &m_heap.back (); + } + } + +private: + tl::slist > m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::Box value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::Box) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.box (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::BoxWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::Box) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.box (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::Polygon value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::Polygon) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.polygon (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::PolygonWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::Polygon) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.polygon (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::SimplePolygon value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::SimplePolygon) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.simple_polygon (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::SimplePolygonWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::SimplePolygon) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.simple_polygon (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::Path value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::Path) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.path (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::PathWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::Path) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.path (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::Edge value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::Edge) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.edge (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::EdgeWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::Edge) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.edge (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::EdgePair value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::EdgePair) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.edge_pair (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::EdgePairWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::EdgePair) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.edge_pair (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::Text value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.type () == db::Shape::Text) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.text (m_heap.front ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + +template <> +struct addressable_object_from_shape +{ + typedef db::TextWithProperties value_type; + + const value_type *operator () (const db::Shape &shape) + { + if (shape.has_prop_id () && shape.type () == db::Shape::Text) { + return shape.basic_ptr (value_type::tag ()); + } else { + m_heap.push_front (value_type ()); + shape.text (m_heap.front ()); + m_heap.front ().properties_id (shape.prop_id ()); + return &m_heap.front (); + } + } + +private: + tl::slist m_heap; +}; + } #endif diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index a532a69ec..95f939ab0 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -56,18 +56,32 @@ public: * the start method is called and when the shape container is cleared if "clear_shapes" * is set. * + * @param shapes Where to store the shapes * @param clear_shapes If true, the shapes container is cleared on the start event. + * @param prop_id The properties ID to assign to all the output shapes (or 0 if no property shall be assigned) */ - ShapeGenerator (db::Shapes &shapes, bool clear_shapes = false) - : PolygonSink (), mp_shapes (&shapes), m_clear_shapes (clear_shapes) + ShapeGenerator (db::Shapes &shapes, bool clear_shapes = false, db::properties_id_type prop_id = 0) + : PolygonSink (), mp_shapes (&shapes), m_clear_shapes (clear_shapes), m_prop_id (prop_id) { } + /** + * @brief Sets the properties ID to be used for the next polygon + */ + void set_prop_id (db::properties_id_type prop_id) + { + m_prop_id = prop_id; + } + /** * @brief Implementation of the PolygonSink interface */ virtual void put (const db::Polygon &polygon) { - mp_shapes->insert (polygon); + if (m_prop_id) { + mp_shapes->insert (db::PolygonWithProperties (polygon, m_prop_id)); + } else { + mp_shapes->insert (polygon); + } } /** @@ -85,6 +99,7 @@ public: private: db::Shapes *mp_shapes; bool m_clear_shapes; + db::properties_id_type m_prop_id; }; /** diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index c738c5aaf..d8b439404 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -469,6 +469,8 @@ template Shapes::shape_type Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_delegate_base &pm) { + db::properties_id_type new_pid = shape.has_prop_id () ? pm (shape.prop_id ()) : 0; + switch (shape.m_type) { case shape_type::Null: default: @@ -478,10 +480,10 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del shape_type::polygon_type p (shape.polygon ()); // Hint: we don't compress so we don't loose information p.transform (t, false); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::PolygonRef: @@ -492,10 +494,10 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del // Hint: we don't compress so we don't loose information p.transform (t, false); // TODO: could create a reference again, but this is what a transform would to as well. - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::SimplePolygon: @@ -503,10 +505,10 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del shape_type::simple_polygon_type p (shape.simple_polygon ()); // Hint: we don't compress so we don't loose information p.transform (t, false); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::SimplePolygonRef: @@ -517,50 +519,50 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del // Hint: we don't compress so we don't loose information p.transform (t, false); // TODO: could create a reference again, but this is what a transform would to as well. - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::Edge: { shape_type::edge_type p (shape.edge ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::Point: { shape_type::point_type p (shape.point ()); p = t.trans (p); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::EdgePair: { shape_type::edge_pair_type p (shape.edge_pair ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::Path: { shape_type::path_type p (shape.path ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::PathRef: @@ -570,10 +572,10 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del shape.path (p); p.transform (t); // TODO: could create a reference again, but this is what a transform would to as well. - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::Box: @@ -584,19 +586,19 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del if (t.is_ortho ()) { shape_type::box_type p (shape.box ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } else { // A box cannot stay a box in this case ... shape_type::simple_polygon_type p (shape.box ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } } @@ -604,10 +606,10 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del { shape_type::text_type p (shape.text ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::TextRef: @@ -617,20 +619,20 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del shape.text (p); p.transform (t); // TODO: could create a reference again, but this is what a transform would to as well. - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::UserObject: { shape_type::user_object_type p (shape.user_object ()); p.transform (t); - if (! shape.has_prop_id ()) { + if (new_pid == 0) { return insert (p); } else { - return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + return insert (db::object_with_properties (p, new_pid)); } } case shape_type::PolygonPtrArray: @@ -709,7 +711,7 @@ Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type p throw tl::Exception (tl::to_string (tr ("Function 'replace_prop_id' is permitted only in editable mode"))); } - if (ref.with_props ()) { + if (ref.has_prop_id ()) { // this assumes we can simply patch the properties ID .. switch (ref.m_type) { @@ -1241,7 +1243,7 @@ Shapes::reinsert_member_with_props (typename db::object_tag, const shape_ty } // the shape types are not equal - resolve into erase and insert (of new) - if (! ref.with_props ()) { + if (! ref.has_prop_id ()) { erase_shape (ref); return insert (sh); } else { @@ -1260,7 +1262,7 @@ Shapes::replace_member_with_props (typename db::object_tag, const shape_typ } // the shape types are not equal - resolve into erase and insert (of new) - if (! ref.with_props ()) { + if (! ref.has_prop_id ()) { erase_shape (ref); return insert (sh); } else { @@ -1310,7 +1312,7 @@ Shapes::replace_member_with_props (typename db::object_tag tag, const shape_ throw tl::Exception (tl::to_string (tr ("Function 'replace' is permitted only in editable mode"))); } - if (! ref.with_props ()) { + if (! ref.has_prop_id ()) { if (manager () && manager ()->transacting ()) { check_is_editable_for_undo_redo (); diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index 197150b5a..b8114e360 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -787,7 +787,7 @@ public: } else { // translate and transform into this for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - (*l)->transform_into (this, shape_repository (), array_repository (), pm_delegate); + (*l)->translate_into (this, shape_repository (), array_repository (), pm_delegate); } } @@ -1266,7 +1266,8 @@ public: { size_t n = 0; for (tl::vector::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - if ((flags & (*l)->type_mask ()) != 0) { + unsigned int tm = (*l)->type_mask (); + if (((flags & db::ShapeIterator::Properties) == 0 || (tm & db::ShapeIterator::Properties) != 0) && (flags & tm) != 0) { n += (*l)->size (); } } @@ -1465,6 +1466,25 @@ public: return (db::Cell *) (size_t (mp_cell) & ~3); } + /** + * @brief Gets a flag indicating whether an update is needed + * + * This flag means that the shape collection has been modified and the bounding box + * and the quad trees will be recomputed (internally). + */ + bool is_dirty () const + { + return (size_t (mp_cell) & 1) != 0; + } + + /** + * @brief Gets a value indicating that the shape collection is constructed with editable scope + */ + bool is_editable () const + { + return (size_t (mp_cell) & 2) != 0; + } + /** * @brief Gets the pointer to layout that the shapes container belongs to * @@ -1472,7 +1492,7 @@ public: */ db::Layout *layout () const; - /** + /** * @brief Implementation of the redo method */ void redo (db::Op *op); @@ -1504,18 +1524,6 @@ private: return m_layers; } - // extract dirty flag from mp_cell - bool is_dirty () const - { - return (size_t (mp_cell) & 1) != 0; - } - - // extract editable flag from mp_cell - bool is_editable () const - { - return (size_t (mp_cell) & 2) != 0; - } - // get the shape repository associated with this container db::GenericRepository &shape_repository () const; diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index 59cfe2260..39534f646 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -487,7 +487,7 @@ struct translate_into_shapes mp_shapes->insert (new_shape); } - template + template void operator() (const db::object_with_properties &sh) { Sh new_shape; @@ -495,7 +495,7 @@ struct translate_into_shapes mp_shapes->insert (db::object_with_properties (new_shape, sh.properties_id ())); } - template + template void operator() (const db::object_with_properties &sh, PropIdMap &pm) { Sh new_shape; @@ -755,7 +755,7 @@ inline unsigned int iterator_type_mask (ShapeIterator::user_object_type::tag) template inline unsigned int iterator_type_mask (db::object_tag< db::object_with_properties >) { - return iterator_type_mask (typename Sh::tag ()); + return iterator_type_mask (typename Sh::tag ()) | ShapeIterator::Properties; } template diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 4a43546a2..125b4702e 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -419,4 +419,24 @@ bool compare (const db::Box &box, const std::string &string) return box.to_string () == string; } +/** + * @brief Converts a property ID into a property key/value string representation + */ +std::string prop2string (const db::PropertiesRepository &pr, db::properties_id_type prop_id) +{ + const db::PropertiesRepository::properties_set &ps = pr.properties (prop_id); + + std::string res; + for (auto i = ps.begin (); i != ps.end (); ++i) { + if (i != ps.begin ()) { + res += "\n"; + } + res += pr.prop_name (i->first).to_string (); + res += "="; + res += i->second.to_string (); + } + + return res; +} + } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 015a1188d..a87615279 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -37,6 +37,7 @@ namespace tl namespace db { +class PropertiesRepository; class Layout; class Cell; class LayerMap; @@ -117,6 +118,11 @@ bool DB_PUBLIC compare (const db::Texts &texts, const std::string &string); */ bool DB_PUBLIC compare (const db::Box &box, const std::string &string); +/** + * @brief Converts a property ID into a property key/value string representation + */ +std::string DB_PUBLIC prop2string (const db::PropertiesRepository &pr, db::properties_id_type prop_id); + } #endif diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 393c25382..2a8d8ee19 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -146,10 +146,26 @@ const db::RecursiveShapeIterator & Texts::iter () const { static db::RecursiveShapeIterator def_iter; - const db::RecursiveShapeIterator *i = mp_delegate->iter (); + const db::RecursiveShapeIterator *i = mp_delegate ? mp_delegate->iter () : 0; return *(i ? i : &def_iter); } +const db::PropertiesRepository & +Texts::properties_repository () const +{ + static db::PropertiesRepository empty_prop_repo; + const db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + return *(r ? r : &empty_prop_repo); +} + +db::PropertiesRepository & +Texts::properties_repository () +{ + db::PropertiesRepository *r = delegate () ? delegate ()->properties_repository () : 0; + tl_assert (r != 0); + return *r; +} + void Texts::polygons (Region &output, db::Coord e) const { output.set_delegate (mp_delegate->polygons (e)); diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 8af002c91..bc1882708 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -43,64 +43,8 @@ class Region; class DeepShapeStore; class TransformationReducer; -/** - * @brief An text set iterator - * - * The iterator delivers the texts of the text set - */ - -class DB_PUBLIC TextsIterator - : public generic_shape_iterator -{ -public: - /** - * @brief Default constructor - */ - TextsIterator () - : generic_shape_iterator () - { - // .. nothing yet .. - } - - /** - * @brief Constructor from a delegate - * The iterator will take ownership over the delegate - */ - TextsIterator (TextsIteratorDelegate *delegate) - : generic_shape_iterator (delegate) - { - // .. nothing yet .. - } - - /** - * @brief Copy constructor and assignment - */ - TextsIterator (const TextsIterator &other) - : generic_shape_iterator (static_cast &> (other)) - { - // .. nothing yet .. - } - - /** - * @brief Assignment - */ - TextsIterator &operator= (const TextsIterator &other) - { - generic_shape_iterator::operator= (other); - return *this; - } - - /** - * @brief Increment - */ - TextsIterator &operator++ () - { - generic_shape_iterator::operator++ (); - return *this; - } -}; - -typedef addressable_shape_delivery_gen AddressableTextDelivery; +typedef generic_shape_iterator TextsIterator; +typedef addressable_shape_delivery AddressableTextDelivery; class Texts; @@ -248,7 +192,15 @@ public: /** * @brief Gets the underlying delegate object */ - TextsDelegate *delegate () const + const TextsDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Gets the underlying delegate object + */ + TextsDelegate *delegate () { return mp_delegate; } @@ -514,7 +466,7 @@ public: */ AddressableTextDelivery addressable_texts () const { - return AddressableTextDelivery (begin (), has_valid_texts ()); + return AddressableTextDelivery (begin ()); } /** @@ -524,6 +476,20 @@ public: */ const db::RecursiveShapeIterator &iter () const; + /** + * @brief Gets the property repository + * + * Use this object to decode property IDs. + */ + const db::PropertiesRepository &properties_repository () const; + + /** + * @brief Gets the property repository + * + * Use this object to decode and encode property IDs. + */ + db::PropertiesRepository &properties_repository (); + /** * @brief Equality */ diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index a778f9e2e..726714dad 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -66,6 +66,12 @@ public: virtual TextsDelegate *clone () const = 0; + TextsDelegate *remove_properties (bool remove = true) + { + ShapeCollectionDelegateBase::remove_properties (remove); + return this; + } + void enable_progress (const std::string &progress_desc); void disable_progress (); @@ -102,6 +108,9 @@ public: virtual bool has_valid_texts () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; + virtual void apply_property_translator (const db::PropertiesTranslator &pt) = 0; + virtual db::PropertiesRepository *properties_repository () = 0; + virtual const db::PropertiesRepository *properties_repository () const = 0; virtual bool equals (const Texts &other) const = 0; virtual bool less (const Texts &other) const = 0; diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index f3ce30aca..00db50666 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1192,7 +1192,7 @@ static void move_or_copy_from_other_cell (db::Cell *cell, db::Cell *src_cell, un } else if (cell->layout () != src_cell->layout ()) { - db::PropertyMapper pm (*cell->layout (), *src_cell->layout ()); + db::PropertyMapper pm (cell->layout (), src_cell->layout ()); db::ICplxTrans tr (src_cell->layout ()->dbu () / cell->layout ()->dbu ()); cell->shapes (dest_layer).insert_transformed (src_cell->shapes (src_layer), tr, pm); diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 728a0f98e..1abfd4515 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -280,7 +280,10 @@ static db::CompoundRegionOperationNode *new_polygon_breaker (db::CompoundRegionO static db::CompoundRegionOperationNode *new_sized (db::CompoundRegionOperationNode *input, db::Coord dx, db::Coord dy, unsigned int mode) { check_non_null (input, "input"); - return new db::CompoundRegionProcessingOperationNode (new db::PolygonSizer (dx, dy, mode), input, true /*processor is owned*/, std::max (db::Coord (0), std::max (dx, dy)) /*dist adder*/); + // NOTE: the distance needs to be twice as we may want to see interactions between the post-size features and those interact when + // within twice the size range. + db::Coord dist = 2 * std::max (db::Coord (0), std::max (dx, dy)); + return new db::CompoundRegionProcessingOperationNode (new db::PolygonSizer (dx, dy, mode), input, true /*processor is owned*/, dist); } static db::CompoundRegionOperationNode *new_merged (db::CompoundRegionOperationNode *input, bool min_coherence, unsigned int min_wc) diff --git a/src/db/db/gsiDeclDbContainerHelpers.h b/src/db/db/gsiDeclDbContainerHelpers.h new file mode 100644 index 000000000..a2d4fe961 --- /dev/null +++ b/src/db/db/gsiDeclDbContainerHelpers.h @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_gsiDeclDbContainerHelpers +#define HDR_gsiDeclDbContainerHelpers + +#include "dbPropertiesRepository.h" +#include "tlVariant.h" +#include "gsiDecl.h" + +#include +#include + +namespace gsi +{ + +template +static void enable_properties (Container *c) +{ + c->apply_property_translator (db::PropertiesTranslator::make_pass_all ()); +} + +template +static void remove_properties (Container *c) +{ + c->apply_property_translator (db::PropertiesTranslator::make_remove_all ()); +} + +template +static void filter_properties (Container *c, const std::vector &keys) +{ + if (c->has_properties_repository ()) { + std::set kf; + kf.insert (keys.begin (), keys.end ()); + c->apply_property_translator (db::PropertiesTranslator::make_filter (c->properties_repository (), kf)); + } +} + +template +static void map_properties (Container *c, const std::map &map) +{ + if (c->has_properties_repository ()) { + c->apply_property_translator (db::PropertiesTranslator::make_key_mapper (c->properties_repository (), map)); + } +} + +template +static gsi::Methods +make_property_methods () +{ + return + gsi::method_ext ("enable_properties", &enable_properties, + "@brief Enables properties for the given container.\n" + "This method has an effect mainly on original layers and will import properties from such layers. " + "By default, properties are not enabled on original layers. Alternatively you can apply \\filter_properties " + "or \\map_properties to enable properties with a specific name key.\n" + "\n" + "This method has been introduced in version 0.28.4." + ) + + gsi::method_ext ("remove_properties", &remove_properties, + "@brief Removes properties for the given container.\n" + "This will remove all properties on the given container.\n" + "\n" + "This method has been introduced in version 0.28.4." + ) + + gsi::method_ext ("filter_properties", &filter_properties, gsi::arg ("keys"), + "@brief Filters properties by certain keys.\n" + "Calling this method on a container will reduce the properties to values with name keys from the 'keys' list.\n" + "As a side effect, this method enables properties on original layers.\n" + "\n" + "This method has been introduced in version 0.28.4." + ) + + gsi::method_ext ("map_properties", &map_properties, gsi::arg ("key_map"), + "@brief Maps properties by name key.\n" + "Calling this method on a container will reduce the properties to values with name keys from the 'keys' hash and " + "renames the properties. Properties not listed in the key map will be removed.\n" + "As a side effect, this method enables properties on original layers.\n" + "\n" + "This method has been introduced in version 0.28.4." + ); +} + +} + +#endif diff --git a/src/db/db/gsiDeclDbDeepShapeStore.cc b/src/db/db/gsiDeclDbDeepShapeStore.cc index 376cf609d..ad395cda3 100644 --- a/src/db/db/gsiDeclDbDeepShapeStore.cc +++ b/src/db/db/gsiDeclDbDeepShapeStore.cc @@ -166,6 +166,21 @@ Class decl_dbDeepShapeStore ("db", "DeepShapeStore", gsi::method ("text_enlargement", &db::DeepShapeStore::text_enlargement, "@brief Gets the text enlargement value.\n" ) + + gsi::method ("subcircuit_hierarchy_for_nets=", &db::DeepShapeStore::set_subcircuit_hierarchy_for_nets, gsi::arg ("value"), + "@brief Sets a value indicating whether to build a subcircuit hierarchy per net\n" + "\n" + "\nThis flag is used to determine the way, net subcircuit hierarchies are built:\n" + "when true, subcells are created for subcircuits on a net. Otherwise the net\n" + "shapes are produced flat inside the cell they appear on.\n" + "\n" + "This attribute has been introduced in version 0.28.4" + ) + + gsi::method ("subcircuit_hierarchy_for_nets=", &db::DeepShapeStore::set_subcircuit_hierarchy_for_nets, gsi::arg ("value"), + "@brief Gets a value indicating whether to build a subcircuit hierarchy per net\n" + "See \\subcircuit_hierarchy_for_nets= for details.\n" + "\n" + "This attribute has been introduced in version 0.28.4" + ) + gsi::method ("clear_breakout_cells", &db::DeepShapeStore::clear_breakout_cells, gsi::arg ("layout_index"), "@brief Clears the breakout cells\n" "Breakout cells are a feature by which hierarchy handling can be disabled for specific cells. " diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 2610d1bca..3f4c64032 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -31,6 +31,8 @@ #include "dbEdgesUtils.h" #include "dbEdgePairFilters.h" +#include "gsiDeclDbContainerHelpers.h" + namespace gsi { @@ -912,7 +914,9 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", method_ext ("to_s", &to_string1, gsi::arg ("max_count"), "@brief Converts the edge pair collection to a string\n" "This version allows specification of the maximum number of edge pairs contained in the string." - ), + ) + + gsi::make_property_methods () + , "@brief EdgePairs (a collection of edge pairs)\n" "\n" "Edge pairs are used mainly in the context of the DRC functions (width_check, space_check etc.) of \\Region and \\Edges. " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 67cecfaaa..9f68150f6 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -32,6 +32,8 @@ #include "dbOriginalLayerRegion.h" #include "dbLayoutUtils.h" +#include "gsiDeclDbContainerHelpers.h" + namespace gsi { @@ -1750,7 +1752,9 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", method ("disable_progress", &db::Edges::disable_progress, "@brief Disable progress reporting\n" "Calling this method will disable progress reporting. See \\enable_progress.\n" - ), + ) + + gsi::make_property_methods () + , "@brief A collection of edges (Not necessarily describing closed contours)\n" "\n\n" "This class was introduced to simplify operations on edges sets. " diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index d0f8fe202..bf80bc24e 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -66,27 +66,27 @@ static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) return const_cast (l2n->internal_top_cell ()); } -static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string p = circuit_cell_name_prefix.to_string (); std::string dp = device_cell_name_prefix.to_string (); - l2n->build_net (net, target, target_cell, lmap, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_net (net, target, target_cell, lmap, db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string cp = circuit_cell_name_prefix.to_string (); std::string np = net_cell_name_prefix.to_string (); std::string dp = device_cell_name_prefix.to_string (); - l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void build_nets (const db::LayoutToNetlist *l2n, const std::vector &nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_nets (const db::LayoutToNetlist *l2n, const std::vector &nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string cp = circuit_cell_name_prefix.to_string (); std::string np = net_cell_name_prefix.to_string (); std::string dp = device_cell_name_prefix.to_string (); - l2n->build_nets (&nets, cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_nets (&nets, cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) @@ -315,10 +315,12 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), "@brief Gets the name of the given layer (by index)\n" ) + - gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n"), + gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()), "@brief Names the given layer\n" - "'l' must be a hierarchical \\Region or \\Texts object derived with \\make_layer, \\make_text_layer or \\make_polygon_layer or " - "a region derived from those by boolean operations or other hierarchical operations.\n" + "'l' must be a \\Region or \\Texts object.\n" + "Flat regions or text collections must be registered with this function, before they can be used in \\connect. " + "Registering will copy the shapes into the LayoutToNetlist object in this step to enable " + "netlist extraction.\n" "\n" "Naming a layer allows the system to indicate the layer in various contexts, i.e. " "when writing the data to a file. Named layers are also persisted inside the LayoutToNetlist object. " @@ -574,18 +576,22 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("netlist", &db::LayoutToNetlist::netlist, "@brief gets the netlist extracted (0 if no extraction happened yet)\n" ) + - gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), + gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), "@brief Returns all shapes of a specific net and layer.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4." ) + - gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &, db::properties_id_type) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &, db::properties_id_type, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), gsi::arg ("trans", db::ICplxTrans (), "unity"), "@brief Sends all shapes of a specific net and layer to the given Shapes container.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" "\"prop_id\" is an optional properties ID. If given, this property set will be attached to the shapes." + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4." ) + - gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a net representation in the given layout and cell\n" "\n" "This method puts the shapes of a net into the given target cell using a variety of options\n" @@ -615,7 +621,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@param cell_name_prefix Chooses recursive mode if non-null\n" "@param device_cell_name_prefix See above\n" ) + - gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a full hierarchical representation of the nets\n" "\n" "This method copies all nets into cells corresponding to the circuits. It uses the 'cmap'\n" @@ -657,7 +663,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@param net_cell_name_prefix See method description\n" "@param device_cell_name_prefix See above\n" ) + - gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Like \\build_all_nets, but with the ability to select some nets." ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &, std::vector *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"), @@ -836,14 +842,14 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "This class has been introduced in version 0.26." ); -gsi::EnumIn decl_dbLayoutToNetlist_BuildNetHierarchyMode ("db", "BuildNetHierarchyMode", - gsi::enum_const ("BNH_Flatten", db::LayoutToNetlist::BNH_Flatten, +gsi::EnumIn decl_dbLayoutToNetlist_BuildNetHierarchyMode ("db", "BuildNetHierarchyMode", + gsi::enum_const ("BNH_Flatten", db::BNH_Flatten, "@brief This constant tells \\build_net and \\build_all_nets to flatten the nets (used for the \"hier_mode\" parameter)." ) + - gsi::enum_const ("BNH_Disconnected", db::LayoutToNetlist::BNH_Disconnected, + gsi::enum_const ("BNH_Disconnected", db::BNH_Disconnected, "@brief This constant tells \\build_net and \\build_all_nets to produce local nets without connections to subcircuits (used for the \"hier_mode\" parameter)." ) + - gsi::enum_const ("BNH_SubcircuitCells", db::LayoutToNetlist::BNH_SubcircuitCells, + gsi::enum_const ("BNH_SubcircuitCells", db::BNH_SubcircuitCells, "@brief This constant tells \\build_net and \\build_all_nets to produce a hierarchy of subcircuit cells per net (used for the \"hier_mode\" parameter)." ), "@brief This class represents the LayoutToNetlist::BuildNetHierarchyMode enum\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index fa48bc561..c083786ac 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1405,25 +1405,15 @@ static db::Pin *create_pin (db::Circuit *circuit, const std::string &name) } static std::vector -nets_by_name (db::Circuit *circuit, const std::string &name_pattern) +nets_non_const (const std::vector &nc) { - std::vector res; - if (! circuit) { - return res; + std::vector n; + n.reserve (nc.size ()); + for (auto i = nc.begin (); i != nc.end (); ++i) { + n.push_back (const_cast (*i)); } - tl::GlobPattern glob (name_pattern); - if (circuit->netlist ()) { - glob.set_case_sensitive (circuit->netlist ()->is_case_sensitive ()); - } - for (db::Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { - db::Net *net = n.operator-> (); - if (glob.match (net->name ())) { - res.push_back (net); - } - } - - return res; + return n; } static std::vector @@ -1448,6 +1438,42 @@ nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern) return res; } +static std::vector +nets_by_name (db::Circuit *circuit, const std::string &name_pattern) +{ + return nets_non_const (nets_by_name_const (circuit, name_pattern)); +} + +static std::vector +nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string &name_pattern) +{ + std::vector res; + if (! netlist) { + return res; + } + + tl::GlobPattern glob (name_pattern); + glob.set_case_sensitive (netlist->is_case_sensitive ()); + for (auto c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + bool is_top = (c->begin_parents () == c->end_parents ()); + for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { + const db::Net *net = n.operator-> (); + // NOTE: we only pick root nets (pin_count == 0 or in top cell) + if ((is_top || net->pin_count () == 0) && glob.match (net->name ())) { + res.push_back (net); + } + } + } + + return res; +} + +static std::vector +nets_by_name_from_netlist (db::Netlist *netlist, const std::string &name_pattern) +{ + return nets_non_const (nets_by_name_const_from_netlist (netlist, name_pattern)); +} + Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", gsi::method_ext ("create_pin", &create_pin, gsi::arg ("name"), "@brief Creates a new \\Pin object inside the circuit\n" @@ -1966,7 +1992,7 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Gets the circuit object for a given cell index (const version).\n" "If the cell index is not valid or no circuit is registered with this index, nil is returned." "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::method ("circuit_by_name", (db::Circuit *(db::Netlist::*) (const std::string &)) &db::Netlist::circuit_by_name, gsi::arg ("name"), "@brief Gets the circuit object for a given name.\n" @@ -1976,7 +2002,7 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Gets the circuit object for a given name (const version).\n" "If the name is not a valid circuit name, nil is returned." "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::method_ext ("circuits_by_name", &circuits_by_name, gsi::arg ("name_pattern"), "@brief Gets the circuit objects for a given name filter.\n" @@ -1988,7 +2014,19 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Gets the circuit objects for a given name filter (const version).\n" "The name filter is a glob pattern. This method will return all \\Circuit objects matching the glob pattern.\n" "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." + ) + + gsi::method_ext ("nets_by_name", &nets_by_name_from_netlist, gsi::arg ("name_pattern"), + "@brief Gets the net objects for a given name filter.\n" + "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "\n" + "This method has been introduced in version 0.28.4.\n" + ) + + gsi::method_ext ("nets_by_name", &nets_by_name_const_from_netlist, gsi::arg ("name_pattern"), + "@brief Gets the net objects for a given name filter (const version).\n" + "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "\n\n" + "This constness variant has been introduced in version 0.28.4." ) + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, "@brief Iterates over the circuits top-down\n" @@ -2000,7 +2038,7 @@ Class decl_dbNetlist ("db", "Netlist", "Iterating top-down means the parent circuits come before the child circuits. " "The first \\top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits." "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::iterator ("each_circuit_bottom_up", (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_bottom_up, (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_bottom_up, "@brief Iterates over the circuits bottom-up\n" @@ -2012,7 +2050,7 @@ Class decl_dbNetlist ("db", "Netlist", "Iterating bottom-up means the parent circuits come after the child circuits. " "This is the basically the reverse order as delivered by \\each_circuit_top_down." "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::method ("top_circuit_count", &db::Netlist::top_circuit_count, "@brief Gets the number of top circuits.\n" @@ -2025,7 +2063,7 @@ Class decl_dbNetlist ("db", "Netlist", gsi::iterator ("each_circuit", (db::Netlist::const_circuit_iterator (db::Netlist::*) () const) &db::Netlist::begin_circuits, (db::Netlist::const_circuit_iterator (db::Netlist::*) () const) &db::Netlist::end_circuits, "@brief Iterates over the circuits of the netlist (const version)" "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::method_ext ("add", &gsi::add_device_class, gsi::arg ("device_class"), "@brief Adds the device class to the netlist\n" @@ -2046,7 +2084,7 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Gets the device class for a given name (const version).\n" "If the name is not a valid device class name, nil is returned." "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist" @@ -2054,7 +2092,7 @@ Class decl_dbNetlist ("db", "Netlist", gsi::iterator ("each_device_class", (db::Netlist::const_device_class_iterator (db::Netlist::*) () const) &db::Netlist::begin_device_classes, (db::Netlist::const_device_class_iterator (db::Netlist::*) () const) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist (const version)" "\n\n" - "This constness variant has been introduced in version 0.26.8" + "This constness variant has been introduced in version 0.26.8." ) + gsi::method ("to_s", &db::Netlist::to_string, "@brief Converts the netlist to a string representation.\n" diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index 092ec6502..e97f17bfe 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -172,6 +172,32 @@ static db::Region complex_region (const db::RecursiveShapeIterator *iter) } } +static void enable_properties (db::RecursiveShapeIterator *c) +{ + c->apply_property_translator (db::PropertiesTranslator::make_pass_all ()); +} + +static void remove_properties (db::RecursiveShapeIterator *c) +{ + c->apply_property_translator (db::PropertiesTranslator::make_remove_all ()); +} + +static void filter_properties (db::RecursiveShapeIterator *c, const std::vector &keys) +{ + if (c->layout ()) { + std::set kf; + kf.insert (keys.begin (), keys.end ()); + c->apply_property_translator (db::PropertiesTranslator::make_filter (const_cast (c->layout ())->properties_repository (), kf)); + } +} + +static void map_properties (db::RecursiveShapeIterator *c, const std::map &map) +{ + if (c->layout ()) { + c->apply_property_translator (db::PropertiesTranslator::make_key_mapper (const_cast (c->layout ())->properties_repository (), map)); + } +} + Class decl_RecursiveShapeIterator ("db", "RecursiveShapeIterator", gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), "@brief Creates a recursive, single-layer shape iterator.\n" @@ -547,6 +573,20 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.25.3." ) + + gsi::method ("prop_id", &db::RecursiveShapeIterator::prop_id, + "@brief Gets the effective properties ID\n" + "The shape iterator supports property filtering and translation. This method will deliver " + "the effective property ID after translation. The original property ID can be obtained from " + "'shape.prop_id' and is not changed by installing filters or mappers.\n" + "\n" + "\\prop_id is evaluated by \\Region objects for example, when they are created " + "from a shape iterator.\n" + "\n" + "See \\enable_properties, \\filter_properties, \\remove_properties and \\map_properties for " + "details on this feature.\n" + "\n" + "This attribute has been introduced in version 0.28.4." + ) + gsi::method ("shape", &db::RecursiveShapeIterator::shape, "@brief Gets the current shape\n" "\n" @@ -592,6 +632,51 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "@brief Comparison of iterators - inequality\n" "\n" "Two iterators are not equal if they do not point to the same shape.\n" + ) + + gsi::method_ext ("enable_properties", &enable_properties, + "@brief Enables properties for the given iterator.\n" + "Afer enabling properties, \\prop_id will deliver the effective properties ID for the current shape. " + "By default, properties are not enabled and \\prop_id will always return 0 (no properties attached). " + "Alternatively you can apply \\filter_properties " + "or \\map_properties to enable properties with a specific name key.\n" + "\n" + "Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n" + "\n" + "This feature has been introduced in version 0.28.4." + ) + + gsi::method_ext ("remove_properties", &remove_properties, + "@brief Removes properties for the given container.\n" + "This will remove all properties and \\prop_id will deliver 0 always (no properties attached).\n" + "Alternatively you can apply \\filter_properties " + "or \\map_properties to enable properties with a specific name key.\n" + "\n" + "Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n" + "So effectively after 'remove_properties' you cannot get them back.\n" + "\n" + "This feature has been introduced in version 0.28.4." + ) + + gsi::method_ext ("filter_properties", &filter_properties, gsi::arg ("keys"), + "@brief Filters properties by certain keys.\n" + "Calling this method will reduce the properties to values with name keys from the 'keys' list.\n" + "As a side effect, this method enables properties.\n" + "As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned " + "by \\prop_id, not on the properties ID attached to the shape directly.\n" + "\n" + "Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n" + "\n" + "This feature has been introduced in version 0.28.4." + ) + + gsi::method_ext ("map_properties", &map_properties, gsi::arg ("key_map"), + "@brief Maps properties by name key.\n" + "Calling this method will reduce the properties to values with name keys from the 'keys' hash and " + "renames the properties. Property values with keys not listed in the key map will be removed.\n" + "As a side effect, this method enables properties.\n" + "As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned " + "by \\prop_id, not on the properties ID attached to the shape directly.\n" + "\n" + "Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n" + "\n" + "This feature has been introduced in version 0.28.4." ), "@brief An iterator delivering shapes recursively\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 757e8d175..b8c2813ea 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -35,8 +35,11 @@ #include "dbFillTool.h" #include "dbRegionProcessors.h" #include "dbCompoundOperation.h" +#include "dbLayoutToNetlist.h" #include "tlGlobPattern.h" +#include "gsiDeclDbContainerHelpers.h" + #include #include #include @@ -507,7 +510,7 @@ static db::Region merged_ext2 (db::Region *r, bool min_coherence, int min_wc) return r->merged (min_coherence, std::max (0, min_wc - 1)); } -static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative) +static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative, db::PropertyConstraint prop_constraint) { return r->width_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -517,25 +520,12 @@ static db::EdgePairs width2 (const db::Region *r, db::Region::distance_type d, b shielded, db::NoOppositeFilter, db::NoRectFilter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs space2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) -{ - return r->space_check (d, db::RegionCheckOptions (whole_edges, - metrics, - ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), - min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), - shielded, - opposite, - rect_filter, - negative) - ); -} - -static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative) +static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative, db::PropertyConstraint prop_constraint) { return r->notch_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -545,11 +535,12 @@ static db::EdgePairs notch2 (const db::Region *r, db::Region::distance_type d, b shielded, db::NoOppositeFilter, db::NoRectFilter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) +static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) { return r->isolated_check (d, db::RegionCheckOptions (whole_edges, metrics, @@ -559,11 +550,27 @@ static db::EdgePairs isolated2 (const db::Region *r, db::Region::distance_type d shielded, opposite, rect_filter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) +static db::EdgePairs space2 (const db::Region *r, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) +{ + return r->space_check (d, db::RegionCheckOptions (whole_edges, + metrics, + ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), + min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (), + max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), + shielded, + opposite, + rect_filter, + negative, + prop_constraint) + ); +} + +static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) { return r->inside_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -573,11 +580,12 @@ static db::EdgePairs inside2 (const db::Region *r, const db::Region &other, db:: shielded, opposite, rect_filter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) +static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) { return r->overlap_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -587,11 +595,12 @@ static db::EdgePairs overlap2 (const db::Region *r, const db::Region &other, db: shielded, opposite, rect_filter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) +static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) { return r->enclosing_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -601,11 +610,12 @@ static db::EdgePairs enclosing2 (const db::Region *r, const db::Region &other, d shielded, opposite, rect_filter, - negative) + negative, + prop_constraint) ); } -static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative) +static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, db::Region::distance_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite, db::RectFilter rect_filter, bool negative, db::PropertyConstraint prop_constraint) { return r->separation_check (other, d, db::RegionCheckOptions (whole_edges, metrics, @@ -615,13 +625,14 @@ static db::EdgePairs separation2 (const db::Region *r, const db::Region &other, shielded, opposite, rect_filter, - negative) + negative, + prop_constraint) ); } -static std::vector andnot (const db::Region *r, const db::Region &other) +static std::vector andnot (const db::Region *r, const db::Region &other, db::PropertyConstraint prop_constraint) { - return as_2region_vector (r->andnot (other)); + return as_2region_vector (r->andnot (other, prop_constraint)); } static std::vector split_inside (const db::Region *r, const db::Region &other) @@ -700,14 +711,14 @@ static size_t id (const db::Region *r) } -tl::Variant complex_op (db::Region *region, db::CompoundRegionOperationNode *node) +tl::Variant complex_op (db::Region *region, db::CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint) { if (node->result_type () == db::CompoundRegionOperationNode::Region) { - return tl::Variant (region->cop_to_region (*node)); + return tl::Variant (region->cop_to_region (*node, prop_constraint)); } else if (node->result_type () == db::CompoundRegionOperationNode::Edges) { - return tl::Variant (region->cop_to_edges (*node)); + return tl::Variant (region->cop_to_edges (*node, prop_constraint)); } else if (node->result_type () == db::CompoundRegionOperationNode::EdgePairs) { - return tl::Variant (region->cop_to_edge_pairs (*node)); + return tl::Variant (region->cop_to_edge_pairs (*node, prop_constraint)); } else { return tl::Variant (); } @@ -734,6 +745,12 @@ fill_region_multi (const db::Region *fr, db::Cell *cell, db::cell_index_type fil db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box); } +static db::Region +nets (const db::Region *region, db::LayoutToNetlist &l2n, const tl::Variant &net_prop_name, const std::vector *net_filter) +{ + return region->nets (l2n, net_prop_name.is_nil () ? db::NPM_NoProperties : db::NPM_NetQualifiedNameOnly, net_prop_name, net_filter); +} + static db::Region sized_dvm (const db::Region *region, const db::Vector &dv, unsigned int mode) { @@ -967,10 +984,14 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@brief Gets a flag indicating whether minimum coherence is selected\n" "See \\min_coherence= for a description of this attribute.\n" ) + - method_ext ("complex_op", &complex_op, gsi::arg ("node"), + method_ext ("complex_op", &complex_op, gsi::arg ("node"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Executes a complex operation (see \\CompoundRegionOperationNode for details)\n" "\n" "This method has been introduced in version 0.27." + "\n" + "The 'property_constraint' parameter controls whether properties are considered: with 'SamePropertiesConstraint' " + "the operation is only applied between shapes with identical properties. With 'DifferentPropertiesConstraint' only " + "between shapes with different properties. This option has been introduced in version 0.28.4." ) + method_ext ("with_perimeter", with_perimeter1, gsi::arg ("perimeter"), gsi::arg ("inverse"), "@brief Filter the polygons by perimeter\n" @@ -1595,7 +1616,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + - method_ext ("andnot", &andnot, gsi::arg ("other"), + method_ext ("andnot", &andnot, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Returns the boolean AND and NOT between self and the other region\n" "\n" "@return A two-element array of regions with the first one being the AND result and the second one being the NOT result\n" @@ -1613,6 +1634,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method will compute the boolean AND (intersection) between two regions. " "The result is often but not necessarily always merged.\n" ) + + method ("and", &db::Region::bool_and, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + "@brief Returns the boolean AND between self and the other region\n" + "\n" + "@return The result of the boolean AND operation\n" + "\n" + "This method will compute the boolean AND (intersection) between two regions. " + "The result is often but not necessarily always merged.\n" + "It allows specification of a property constaint - e.g. only performing the boolean operation between " + "shapes with the same user properties.\n" + "\n" + "This variant has been introduced in version 0.28.4." + ) + method ("&=", &db::Region::operator&=, gsi::arg ("other"), "@brief Performs the boolean AND between self and the other region\n" "\n" @@ -1621,6 +1654,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method will compute the boolean AND (intersection) between two regions. " "The result is often but not necessarily always merged.\n" ) + + method ("and_with", &db::Region::bool_and_with, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + "@brief Performs the boolean AND between self and the other region\n" + "\n" + "@return The region after modification (self)\n" + "\n" + "This method will compute the boolean AND (intersection) between two regions. " + "The result is often but not necessarily always merged.\n" + "It allows specification of a property constaint - e.g. only performing the boolean operation between " + "shapes with the same user properties.\n" + "\n" + "This variant has been introduced in version 0.28.4." + ) + method ("-", &db::Region::operator-, gsi::arg ("other"), "@brief Returns the boolean NOT between self and the other region\n" "\n" @@ -1629,6 +1674,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method will compute the boolean NOT (intersection) between two regions. " "The result is often but not necessarily always merged.\n" ) + + method ("not", &db::Region::bool_not, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + "@brief Returns the boolean NOT between self and the other region\n" + "\n" + "@return The result of the boolean NOT operation\n" + "\n" + "This method will compute the boolean NOT (intersection) between two regions. " + "The result is often but not necessarily always merged.\n" + "It allows specification of a property constaint - e.g. only performing the boolean operation between " + "shapes with the same user properties.\n" + "\n" + "This variant has been introduced in version 0.28.4." + ) + method ("-=", &db::Region::operator-=, gsi::arg ("other"), "@brief Performs the boolean NOT between self and the other region\n" "\n" @@ -1637,6 +1694,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method will compute the boolean NOT (intersection) between two regions. " "The result is often but not necessarily always merged.\n" ) + + method ("not_with", &db::Region::bool_not_with, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), + "@brief Performs the boolean NOT between self and the other region\n" + "\n" + "@return The region after modification (self)\n" + "\n" + "This method will compute the boolean NOT (intersection) between two regions. " + "The result is often but not necessarily always merged.\n" + "It allows specification of a property constaint - e.g. only performing the boolean operation between " + "shapes with the same user properties.\n" + "\n" + "This variant has been introduced in version 0.28.4." + ) + method ("^", &db::Region::operator^, gsi::arg ("other"), "@brief Returns the boolean XOR between self and the other region\n" "\n" @@ -2446,7 +2515,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This variant was introduced in version 0.27.\n" ) + - method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("negative", false), + method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs a width check with options\n" "@param d The minimum width for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2456,6 +2525,8 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param shielded Enables shielding\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param property_constraint Only \\IgnoreProperties and \\NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output. " + "Other than 'width' allow more options here.\n" "\n" "This version is similar to the simple version with one parameter. In addition, it allows " "to specify many more options.\n" @@ -2484,9 +2555,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded' and 'negative' options have been introduced in version 0.27." + "The 'shielded' and 'negative' options have been introduced in version 0.27. " + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("space_check", &space2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("space_check", &space2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs a space check with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2497,6 +2569,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2525,9 +2598,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("notch_check", ¬ch2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("negative", false), + method_ext ("notch_check", ¬ch2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs a space check between edges of the same polygon with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2537,6 +2611,8 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param shielded Enables shielding\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" + "@param property_constraint Only \\IgnoreProperties and \\NoPropertyConstraint are allowed - in the last case, properties are copied from the original shapes to the output" "\n" "This version is similar to the simple version with one parameter. In addition, it allows " "to specify many more options.\n" @@ -2565,9 +2641,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded' and 'negative' options have been introduced in version 0.27." + "The 'shielded' and 'negative' options have been introduced in version 0.27.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("isolated_check", &isolated2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("isolated_check", &isolated2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs a space check between edges of different polygons with options\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2578,6 +2655,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2606,9 +2684,10 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs an inside check with options\n" "@param d The minimum distance for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2620,6 +2699,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2656,8 +2736,9 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" "The 'enclosed_check' alias was introduced in version 0.27.5.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs an overlap check with options\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2669,6 +2750,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2704,8 +2786,9 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs an enclosing check with options\n" "@param d The minimum enclosing distance for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2717,6 +2800,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2752,8 +2836,9 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" + "'property_constraint' has been added in version 0.28.4." ) + - method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Performs a separation check with options\n" "@param d The minimum separation for which the polygons are checked\n" "@param other The other region against which to check\n" @@ -2765,6 +2850,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" "@param negative Negative output from the first input\n" + "@param property_constraint Specifies whether to consider only shapes with a certain property relation\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2800,6 +2886,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" + "'property_constraint' has been added in version 0.28.4." ) + method_ext ("area", &area1, "@brief The area of the region\n" @@ -2978,7 +3065,30 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n" "\n" "This method has been introduced in version 0.27.\n" - ), + ) + + gsi::method_ext ("nets", &nets, gsi::arg ("extracted"), gsi::arg ("net_prop_name", tl::Variant (), "nil"), gsi::arg ("net_filter", (const std::vector *) (0), "nil"), + "@brief Pulls the net shapes from a LayoutToNetlist database\n" + "This method will create a new layer with the net shapes from the LayoutToNetlist database, provided that this " + "region was an input to the netlist extraction on this database.\n" + "\n" + "A (circuit name, net name) tuple will be attached as properties to the shapes if 'net_prop_name' is given and not nil. " + "This allows generating unique properties per shape, flagging the net the shape is on. This feature is good for " + "performing net-dependent booleans and DRC checks.\n" + "\n" + "A net filter can be provided with the 'net_filter' argument. If given, only nets from this " + "set are produced. Example:\n" + "\n" + "@code\n" + "connect(metal1, via1)\n" + "connect(via1, metal2)\n" + "\n" + "metal1_all_nets = metal1.nets\n" + "@/code\n" + "\n" + "This method was introduced in version 0.28.4" + ) + + gsi::make_property_methods () + , "@brief A region (a potentially complex area consisting of multiple polygons)\n" "\n\n" "This class was introduced to simplify operations on polygon sets like boolean or sizing operations. " @@ -3015,7 +3125,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This class has been introduced in version 0.23.\n" ); -gsi::EnumIn decl_Region_Metrics ("db", "Metrics", +gsi::Enum decl_Metrics ("db", "Metrics", gsi::enum_const ("Euclidian", db::Euclidian, "@brief Specifies Euclidian metrics for the check functions\n" "This value can be used for the metrics parameter in the check functions, i.e. \\width_check. " @@ -3052,10 +3162,50 @@ gsi::EnumIn decl_Region_Metrics ("db", "Metrics", // Inject the Region::Metrics declarations into Region and Edges: // (Edges injection has to be done here because only here defs() is available) -gsi::ClassExt inject_Metrics_in_parent (decl_Region_Metrics.defs ()); -gsi::ClassExt inject_Metrics_in_Edges (decl_Region_Metrics.defs ()); +gsi::ClassExt inject_Metrics_in_Region (decl_Metrics.defs ()); +gsi::ClassExt inject_Metrics_in_Edges (decl_Metrics.defs ()); -gsi::EnumIn decl_Region_RectFilter ("db", "RectFilter", +gsi::Enum decl_PropertyConstraint ("db", "PropertyConstraint", + gsi::enum_const ("IgnoreProperties", db::IgnoreProperties, + "@brief Specifies to ignore properties\n" + "When using this constraint - for example on a boolean operation - properties are ignored and are not generated in the output." + ) + + gsi::enum_const ("NoPropertyConstraint", db::NoPropertyConstraint, + "@brief Specifies not to apply any property constraint\n" + "When using this constraint - for example on a boolean operation - shapes are considered " + "regardless of their user properties. Properties are generated on the output shapes where applicable." + ) + + gsi::enum_const ("SamePropertiesConstraint", db::SamePropertiesConstraint, + "@brief Specifies to consider shapes only if their user properties are the same\n" + "When using this constraint - for example on a boolean operation - shapes are considered " + "only if their user properties are the same. Properties are generated on the output shapes where applicable." + ) + + gsi::enum_const ("SamePropertiesConstraintDrop", db::SamePropertiesConstraintDrop, + "@brief Specifies to consider shapes only if their user properties are the same\n" + "When using this constraint - for example on a boolean operation - shapes are considered " + "only if their user properties are the same. No properties are generated on the output shapes." + ) + + gsi::enum_const ("DifferentPropertiesConstraint", db::DifferentPropertiesConstraint, + "@brief Specifies to consider shapes only if their user properties are different\n" + "When using this constraint - for example on a boolean operation - shapes are considered " + "only if their user properties are different. Properties are generated on the output shapes where applicable." + ) + + gsi::enum_const ("DifferentPropertiesConstraintDrop", db::DifferentPropertiesConstraintDrop, + "@brief Specifies to consider shapes only if their user properties are different\n" + "When using this constraint - for example on a boolean operation - shapes are considered " + "only if their user properties are the same. No properties are generated on the output shapes." + ), + "@brief This class represents the property constraint for boolean and check functions.\n" + "\n" + "This enum has been introduced in version 0.28.4." +); + +// Inject the Region::PropertyConstraint declarations into Region and Edges: +// (Edges injection has to be done here because only here defs() is available) +gsi::ClassExt inject_PropertyConstraint_in_Region (decl_PropertyConstraint.defs ()); +gsi::ClassExt inject_PropertyConstraint_in_Edges (decl_PropertyConstraint.defs ()); + +gsi::EnumIn decl_RectFilter ("db", "RectFilter", gsi::enum_const ("NoRectFilter", db::RectFilter::NoRectFilter, "@brief Specifies no filtering" ) + @@ -3083,9 +3233,9 @@ gsi::EnumIn decl_Region_RectFilter ("db", "RectFilte ); // Inject the Region::RectFilter declarations into Region: -gsi::ClassExt inject_RectFilter_in_parent (decl_Region_RectFilter.defs ()); +gsi::ClassExt inject_RectFilter_in_Region (decl_RectFilter.defs ()); -gsi::EnumIn decl_Region_OppositeFilter ("db", "OppositeFilter", +gsi::EnumIn decl_OppositeFilter ("db", "OppositeFilter", gsi::enum_const ("NoOppositeFilter", db::OppositeFilter::NoOppositeFilter, "@brief No opposite filtering\n" ) + @@ -3101,6 +3251,6 @@ gsi::EnumIn decl_Region_OppositeFilter ("db", "O ); // Inject the Region::OppositeFilter declarations into Region: -gsi::ClassExt inject_OppositeFilter_in_parent (decl_Region_OppositeFilter.defs ()); +gsi::ClassExt inject_OppositeFilter_in_Region (decl_OppositeFilter.defs ()); } diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index a85853908..7a4801bf2 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -28,6 +28,8 @@ #include "dbDeepTexts.h" #include "dbTextsUtils.h" +#include "gsiDeclDbContainerHelpers.h" + namespace gsi { @@ -506,7 +508,9 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", method_ext ("to_s", &to_string1, gsi::arg ("max_count"), "@brief Converts the text collection to a string\n" "This version allows specification of the maximum number of texts contained in the string." - ), + ) + + gsi::make_property_methods () + , "@brief Texts (a collection of texts)\n" "\n" "Text objects are useful as labels for net names, to identify certain regions and to specify specific locations in general. " diff --git a/src/db/unit_tests/dbAsIfFlatRegionTests.cc b/src/db/unit_tests/dbAsIfFlatRegionTests.cc new file mode 100644 index 000000000..d2d5a1dda --- /dev/null +++ b/src/db/unit_tests/dbAsIfFlatRegionTests.cc @@ -0,0 +1,2024 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "dbEdges.h" +#include "dbRegionUtils.h" +#include "dbRegionProcessors.h" +#include "dbEdgesUtils.h" +#include "dbDeepShapeStore.h" +#include "dbOriginalLayerRegion.h" +#include "dbCellGraphUtils.h" +#include "dbTestSupport.h" +#include "dbCompoundOperation.h" +#include "dbFlatRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1_Basic) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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::Layout target; + + // deliberately using vector to force reallocation ... + std::vector regions; + std::vector target_layers; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + target_layers.push_back (target.insert_layer (*(*li).second)); + + regions.push_back (db::Region (iter)); + + size_t n = 0, nhier = 0; + db::CellCounter cc (&ly); + for (db::Layout::top_down_const_iterator c = ly.begin_top_down (); c != ly.end_top_down (); ++c) { + size_t ns = 0; + for (db::Shapes::shape_iterator is = ly.cell (*c).shapes (li1).begin (db::ShapeIterator::Regions); !is.at_end (); ++is) { + ++ns; + } + n += cc.weight (*c) * ns; + nhier += ns; + } + + EXPECT_EQ (db::Region (iter).count (), n); + EXPECT_EQ (regions.back ().count (), n); + EXPECT_EQ (regions.back ().hier_count (), nhier); + EXPECT_EQ (regions.back ().bbox (), db::Region (iter).bbox ()); + EXPECT_EQ (regions.back ().is_merged (), false); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, target_layers [r - regions.begin ()], *r); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au1.gds"); + + // some operations + 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, ly.cell (top_cell_index), l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, ly.cell (top_cell_index), l3)); + + EXPECT_EQ (r2.is_merged (), false); + r2.merge (); + EXPECT_EQ (r2.is_merged (), true); + r2 += r3; + EXPECT_EQ (r2.is_merged (), false); + EXPECT_EQ (r2.merged ().is_merged (), true); + EXPECT_EQ (r2.is_merged (), false); + r2.merge (); + EXPECT_EQ (r2.is_merged (), true); + r2.flatten (); + EXPECT_EQ (r2.is_merged (), true); + r2.insert (db::Box (0, 0, 1000, 2000)); + EXPECT_EQ (r2.is_merged (), false); +} + +TEST(2) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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::Layout target; + + // deliberately using vector to force reallocation ... + std::vector > regions; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int tl = target.insert_layer (*(*li).second); + + db::RecursiveShapeIterator iter1 (ly, ly.cell (top_cell_index), li1, db::Box (2000, -1000, 6000, 4000)); + regions.push_back (std::make_pair (db::Region (iter1), tl)); + // TODO: currently, original layer regions don't clip - emulate this + regions.back ().first &= db::Region (iter1.region ()); + + db::RecursiveShapeIterator iter2 (ly, ly.cell (top_cell_index), li1, db::Box (14000, 0, 20000, 3000)); + regions.push_back (std::make_pair (db::Region (iter2), tl)); + // TODO: currently, original layer regions don't clip - emulate this + regions.back ().first &= db::Region (iter2.region ()); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector >::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, r->second, r->first); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au2.gds"); +} + +TEST(3_BoolAndNot) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42)); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2minus3 = r2 - r3; + db::Region r2minusbox = r2 - box; + db::Region r2minus42 = r2 - r42; + db::Region rboxminus3 = box - r3; + db::Region r42minus3 = r42 - r3; + db::Region r42minus42 = r42 - r42; + + db::Region tr2minus3 = r2.andnot (r3).second; + db::Region tr2minusbox = r2.andnot (box).second; + db::Region tr2minus42 = r2.andnot (r42).second; + db::Region trboxminus3 = box.andnot (r3).second; + db::Region tr42minus3 = r42.andnot (r3).second; + db::Region tr42minus42 = r42.andnot (r42).second; + + db::Region r2and3 = r2 & r3; + db::Region r2andbox = r2 & box; + db::Region r2and42 = r2 & r42; + db::Region rboxand3 = box & r3; + db::Region r42and3 = r42 & r3; + db::Region r42and42 = r42 & r42; + + db::Region tr2and3 = r2.andnot (r3).first; + db::Region tr2andbox = r2.andnot (box).first; + db::Region tr2and42 = r2.andnot (r42).first; + db::Region trboxand3 = box.andnot (r3).first; + db::Region tr42and3 = r42.andnot (r3).first; + db::Region tr42and42 = r42.andnot (r42).first; + + { + 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 (10, 0)), r2minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2minusbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2minus42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxminus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42minus42); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2andbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2and42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rboxand3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r42and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r42and42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au3.gds"); + } + + { + 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 (10, 0)), tr2minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), tr2minusbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), tr2minus42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), trboxminus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), tr42minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), tr42minus42); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), tr2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), tr2andbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), tr2and42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), trboxand3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), tr42and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), tr42and42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au3b.gds"); + } +} + +TEST(4_Add) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42)); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + db::Region r2box (db::RecursiveShapeIterator (ly, top_cell, l2, box)); + // TODO: currently, original layer regions don't clip - emulate this + r2box &= db::Region (box); + db::Region r3box (db::RecursiveShapeIterator (ly, top_cell, l3, box)); + // TODO: currently, original layer regions don't clip - emulate this + r3box &= db::Region (box); + + // intra-layout + + { + 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 (10, 0)), r2 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r42 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2 + r42); + + db::Region rnew2 = r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), rnew2); + rnew2 += r3; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), rnew2); + rnew2 += r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rnew2); + + db::Region rnew42 = r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), rnew42); + rnew42 += r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), rnew42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au4a.gds"); + } + + // inter-layout + + { + 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 (10, 0)), r2box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2 + r3box); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2box + r3box); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2 + box); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au4b.gds"); + } +} + +TEST(5_BoolXOR) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42)); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2xor3 = r2 ^ r3; + db::Region r2xorbox = r2 ^ box; + db::Region r2xor42 = r2 ^ r42; + db::Region rboxxor3 = box ^ r3; + db::Region r42xor3 = r42 ^ r3; + db::Region r42xor42 = r42 ^ r42; + + EXPECT_EQ (r2xor3.is_merged (), true); + + 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 (10, 0)), r2xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2xorbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2xor42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxxor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42xor42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au5.gds"); +} + +TEST(7_Merge) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6)); + + db::Region r6_merged = r6.merged (); + db::Region r6_merged_minwc = r6.merged (false, 1); + + db::Region r6_minwc = r6; + r6_minwc.merge (false, 1); + + r6.merge (); + + 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 (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6_minwc); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6_merged); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6_merged_minwc); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au7.gds"); +} + +TEST(8_AreaAndPerimeter) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + EXPECT_EQ (r1.area (), db::coord_traits::area_type (9722000000)); + EXPECT_EQ (r1.perimeter (), db::coord_traits::perimeter_type (1360000)); + + EXPECT_EQ (r1.area (r1.bbox ()), db::coord_traits::area_type (9722000000)); + EXPECT_EQ (r1.perimeter (r1.bbox ()), db::coord_traits::perimeter_type (1360000)); + + EXPECT_EQ (r1.area (db::Box (40000, -90000, 50000, -80000)), db::coord_traits::area_type (100000000)); + EXPECT_EQ (r1.perimeter (db::Box (40000, -90000, 50000, -80000)), db::coord_traits::perimeter_type (0)); + EXPECT_EQ (r1.area (db::Box (-40000, -90000, -50000, -80000)), db::coord_traits::area_type (0)); +} + +TEST(9_SizingSimple) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6)); + db::Region r6_sized = r6.sized (-50); + EXPECT_EQ (r6_sized.is_merged (), true); + db::Region r6_sized_aniso = r6.sized (-20, -100, 2); + EXPECT_EQ (r6_sized_aniso.is_merged (), true); + db::Region r6_sized_plus = r6.sized (50); + EXPECT_EQ (r6_sized_plus.is_merged (), false); + db::Region r6_sized_aniso_plus = r6.sized (20, 100, 2); + EXPECT_EQ (r6_sized_aniso_plus.is_merged (), false); + + 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 (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6_sized_aniso); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6_sized_plus); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r6_sized_aniso_plus); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au9a.gds"); +} + +TEST(10_HullsAndHoles) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r1_sized = r1.sized (2000); + r1_sized -= r1; + + db::Region hulls = r1_sized.hulls (); + db::Region holes = r1_sized.holes (); + EXPECT_EQ (hulls.is_merged (), false); + EXPECT_EQ (holes.is_merged (), false); + + 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 (10, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), hulls); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), holes); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au10.gds"); +} + +TEST(11_RoundAndSmoothed) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r1_sized = r1.sized (2000); + r1_sized -= r1; + + db::Region rounded = r1_sized.rounded_corners (3000, 5000, 100); + db::Region smoothed = rounded.smoothed (100, false); + db::Region smoothed_keep_hv = rounded.smoothed (100, true); + + 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 (10, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), rounded); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), smoothed); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), smoothed_keep_hv); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au11.gds"); +} + +TEST(12_GridSnap) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r3snapped = r3.snapped (50, 50); + EXPECT_EQ (r3snapped.is_merged (), false); + + 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 (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3snapped); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au12.gds"); +} + +TEST(13_Edges) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Edges r3edges = r3.edges (); + EXPECT_EQ (r3edges.is_merged (), false); + + db::EdgeLengthFilter f (0, 500, true); + db::Edges r3edges_filtered = r3.edges (f); + + 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 (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r3edges_filtered); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au13.gds"); +} + +TEST(13b_Edges) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_edges.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Edges r1edges = r1.edges (); + EXPECT_EQ (r1edges.is_merged (), false); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Edges r2edges = r2.edges (); + EXPECT_EQ (r2edges.is_merged (), false); + + + 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 (1, 0)), r1); + 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 (11, 0)), r1edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2edges); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au13b.gds"); +} + +TEST(14_Interacting) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6)); + db::Region r1f (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r1r = r1; + r1r.set_merged_semantics (false); + db::Region r2r = r2; + r2r.set_merged_semantics (false); + db::Region r6r = r6; + r6r.set_merged_semantics (false); + + db::Edges r1e = r1.edges (); + db::Edges r1ef = r1f.edges (); + db::Edges r1er = r1r.edges (); + r1er.set_merged_semantics (false); + + { + 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 (10, 0)), r2.selected_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.selected_not_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.selected_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.selected_not_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r2.selected_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r2.selected_not_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r2.selected_overlapping (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2.selected_not_overlapping (r1)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.selected_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r6.selected_not_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r6.selected_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r6.selected_not_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r6.selected_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r6.selected_not_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r6.selected_overlapping (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r6.selected_not_overlapping (r1)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r2.selected_interacting (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r2.selected_not_interacting (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r2.selected_inside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r2.selected_not_inside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), r2.selected_outside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), r2.selected_not_outside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), r2.selected_overlapping (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), r2.selected_not_overlapping (r1f)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r6.selected_interacting (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r6.selected_not_interacting (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r6.selected_inside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), r6.selected_not_inside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), r6.selected_outside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), r6.selected_not_outside (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), r6.selected_overlapping (r1f)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), r6.selected_not_overlapping (r1f)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), r2r.selected_interacting (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), r2r.selected_not_interacting (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), r2r.selected_inside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), r2r.selected_not_inside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (54, 0)), r2r.selected_outside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (55, 0)), r2r.selected_not_outside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (56, 0)), r2r.selected_overlapping (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (57, 0)), r2r.selected_not_overlapping (r1r)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), r6r.selected_interacting (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), r6r.selected_not_interacting (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), r6r.selected_inside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), r6r.selected_not_inside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), r6r.selected_outside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), r6r.selected_not_outside (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (66, 0)), r6r.selected_overlapping (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (67, 0)), r6r.selected_not_overlapping (r1r)); + + EXPECT_EQ (r2.selected_interacting (r1).is_merged (), true); + EXPECT_EQ (r2r.selected_interacting (r1).is_merged (), false); + EXPECT_EQ (r2r.selected_interacting (r1.merged ()).is_merged (), false); + EXPECT_EQ (r2.selected_interacting (r1r).is_merged (), true); + EXPECT_EQ (r2.selected_inside (r1).is_merged (), true); + EXPECT_EQ (r2r.selected_inside (r1).is_merged (), false); + EXPECT_EQ (r2.selected_inside (r1).is_merged (), true); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au14a.gds"); + } + + { + 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 (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1e); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6.selected_interacting (r1e)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6.selected_not_interacting (r1e)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r6.selected_interacting (r1ef)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r6.selected_not_interacting (r1ef)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r6r.selected_interacting (r1er)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r6r.selected_not_interacting (r1er)); + + EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), true); + EXPECT_EQ (r6.selected_interacting (r1er).is_merged (), true); + EXPECT_EQ (r6r.selected_interacting (r1e).is_merged (), false); + EXPECT_EQ (r6r.selected_interacting (r1er).is_merged (), false); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au14b.gds"); + } +} + +TEST(15_Filtered) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::RegionAreaFilter af1 (0, 1000000000, false); + db::Region af1_filtered = r1.filtered (af1); + db::RegionAreaFilter af1inv (0, 1000000000, true); + db::Region af1_else = r1.filtered (af1inv); + EXPECT_EQ (af1_filtered.is_merged (), true); + EXPECT_EQ (af1_else.is_merged (), true); + + { + 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 (10, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), af1_filtered); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), af1_else); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au15a.gds"); + } + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::RegionBBoxFilter bwf (0, 50000, false, db::RegionBBoxFilter::BoxWidth); + db::RegionBBoxFilter bhf (0, 50000, false, db::RegionBBoxFilter::BoxHeight); + db::Region r2_bwf = r2.filtered (bwf); + db::Region r2_bhf = r2.filtered (bhf); + + { + 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 (10, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2_bwf); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2_bhf); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au15b.gds"); + } +} + +TEST(16_MergeWithMinWC) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r1_merged_wc0 = r1.merged (true, 0); + db::Region r1_merged_wc1 = r1.merged (true, 1); + db::Region r1_merged_wc2 = r1.merged (true, 2); + EXPECT_EQ (r1_merged_wc0.is_merged (), true); + EXPECT_EQ (r1_merged_wc1.is_merged (), true); + EXPECT_EQ (r1_merged_wc2.is_merged (), true); + + + { + 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 (10, 0)), r1_merged_wc0); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1_merged_wc1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1_merged_wc2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au16.gds"); + } +} + +TEST(17_SinglePolygonChecks) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6)); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4)); + + { + 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 (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3.width_check (260, db::RegionCheckOptions (false, db::Euclidian, 90, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3.width_check (260, db::RegionCheckOptions (true, db::Projection, 90, 2000))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.notch_check (1300, db::RegionCheckOptions (false, db::Euclidian, 90, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au17.gds"); + } +} + +TEST(18_MultiPolygonChecks) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6)); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4)); + + { + 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 (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3.space_check (500, db::RegionCheckOptions (false, db::Projection, 90, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3.space_check (500, db::RegionCheckOptions (true, db::Projection, 90, 300))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r3.separation_check (r4, 200, db::RegionCheckOptions (false, db::Projection, 90, 0))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r6.enclosing_check (r4, 100, db::RegionCheckOptions (true, db::Projection, 90, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au18.gds"); + } +} + +TEST(19_GridCheck) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/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); + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r3_gc1; + r3.grid_check (25, 25).polygons (r3_gc1, 100); + db::Region r3_gc2; + r3.grid_check (40, 40).polygons (r3_gc2, 100); + + 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 (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3_gc1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r3_gc2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au19.gds"); +} + +TEST(20_AngleCheck) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/angle_check_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::EdgePairs ep1_ac1 = r1.angle_check (0, 91, true); + db::EdgePairs ep1_ac2 = r1.angle_check (0, 45, false); + + 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 (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), ep1_ac1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), ep1_ac2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au20.gds"); +} + +TEST(21_Processors) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + + 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 (1, 0)), r1); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true))); + db::Region ext; + r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 1000, 1000, 2000, 2000); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2000))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::extents_processor (0, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor (1000, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.processed (db::RelativeExtents (0, 0, 1.0, 1.0, 0, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r1.processed (db::RelativeExtents (0.25, 0.4, 0.75, 0.6, 1000, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r1.processed (db::RelativeExtentsAsEdges (0, 0, 1.0, 1.0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r1.processed (db::RelativeExtentsAsEdges (0.5, 0.5, 0.5, 0.5))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r1.processed (db::RelativeExtentsAsEdges (0.25, 0.4, 0.75, 0.6))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.processed (db::minkowski_sum_computation (db::Box (-1000, -2000, 3000, 4000)))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.processed (db::minkowski_sum_computation (db::Edge (-1000, 0, 3000, 0)))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.processed (db::TrapezoidDecomposition (db::TD_htrapezoids))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.processed (db::ConvexDecomposition (db::PO_vertical))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.processed (db::ConvexDecomposition (db::PO_horizontal))); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au21.gds"); +} + +TEST(22_TwoLayoutsWithDifferentDBU) +{ + db::Layout ly1; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly1); + } + + db::cell_index_type top_cell_index1 = *ly1.begin_top_down (); + db::Cell &top_cell1 = ly1.cell (top_cell_index1); + + db::Layout ly2; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_area_peri_l1_dbu2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly2); + } + + db::cell_index_type top_cell_index2 = *ly2.begin_top_down (); + db::Cell &top_cell2 = ly2.cell (top_cell_index2); + + unsigned int l11 = ly1.get_layer (db::LayerProperties (1, 0)); + db::Region r11 (db::RecursiveShapeIterator (ly1, top_cell1, l11)); + + unsigned int l12 = ly2.get_layer (db::LayerProperties (2, 0)); + db::Region r12 (db::RecursiveShapeIterator (ly2, top_cell2, l12), db::ICplxTrans (ly2.dbu () / ly1.dbu ())); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly1.cell_name (top_cell_index1)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r11.sized (1000) ^ r12); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au22.gds"); +} + +TEST(27a_snap) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/scale_and_snap.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + r1.set_merged_semantics (false); + db::Region r2 = r1.snapped (19, 19); + + r2.insert_into (&ly, top_cell_index, ly.get_layer (db::LayerProperties (100, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testdata () + "/algo/flat_region_au27.gds"); +} + +TEST(27b_snap) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/scale_and_snap.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + r1.set_merged_semantics (false); + r1.snap (19, 19); + + r1.insert_into (&ly, top_cell_index, ly.get_layer (db::LayerProperties (100, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testdata () + "/algo/flat_region_au27.gds"); +} + +TEST(28a_snap) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/scale_and_snap.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + r1.set_merged_semantics (false); + db::Region r2 = r1.scaled_and_snapped (19, 2, 10, 19, 2, 10); + + r2.insert_into (&ly, top_cell_index, ly.get_layer (db::LayerProperties (100, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testdata () + "/algo/flat_region_au28.gds"); +} + +TEST(28b_snap) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/scale_and_snap.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + r1.set_merged_semantics (false); + r1.scale_and_snap (19, 2, 10, 19, 2, 10); + + r1.insert_into (&ly, top_cell_index, ly.get_layer (db::LayerProperties (100, 0))); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testdata () + "/algo/flat_region_au28.gds"); +} + +TEST(29_InteractionsWithTexts) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_texts_l2.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); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l8 = ly.get_layer (db::LayerProperties (8, 0)); + + db::Texts texts2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region polygons8 (db::RecursiveShapeIterator (ly, top_cell, l8)); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::Region polygons; + polygons = polygons8.selected_interacting (texts2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), polygons); + + polygons = polygons8.selected_not_interacting (texts2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), polygons); + + { + db::Region polygons8_copy = polygons8; + polygons8_copy.select_interacting (texts2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), polygons8_copy); + } + + { + db::Region polygons8_copy = polygons8; + polygons8_copy.select_not_interacting (texts2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), polygons8_copy); + } + + { + db::Texts t = polygons8.pull_interacting (texts2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), t); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au29.gds"); +} + +TEST(30a_interact_with_count_region) +{ + db::Layout ly; + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + db::cell_index_type ci1 = ly.add_cell ("C1"); + db::Cell &c1 = ly.cell (ci1); + db::cell_index_type ci2 = ly.add_cell ("C2"); + db::Cell &c2 = ly.cell (ci2); + top.insert (db::CellInstArray (db::CellInst (ci1), db::Trans ())); + top.insert (db::CellInstArray (db::CellInst (ci2), db::Trans ())); + + c1.shapes (l1).insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + c1.shapes (l1).insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + + c2.shapes (l2).insert (db::Box (db::Point (-10, -10), db::Point (10, 0))); + c2.shapes (l2).insert (db::Box (db::Point (-10, 0), db::Point (10, 10))); + c2.shapes (l2).insert (db::Box (db::Point (-110, -10), db::Point (-90, 10))); + c2.shapes (l2).insert (db::Box (db::Point (-110, -210), db::Point (-90, -190))); + + ly.copy_layer (l2, l3); + top.shapes (l2).insert (db::Box (db::Point (90, -10), db::Point (110, 10))); + top.shapes (l2).insert (db::Box (db::Point (-110, -110), db::Point (-90, -90))); + + db::Region r (db::RecursiveShapeIterator (ly, top, l1)); + r.set_merged_semantics (true); + r.set_min_coherence (false); + + db::Region empty; + + db::Region rr (db::RecursiveShapeIterator (ly, top, l2)); + db::Region rr2 (db::RecursiveShapeIterator (ly, top, l3)); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 5, 5).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 2, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 4, 5).to_string (), ""); + + EXPECT_EQ (r.selected_not_interacting (empty).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + // Shortcut delivers clone of original, not merged one: + // EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + // so that is: + EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 4, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 5, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 2, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + + r.set_merged_semantics (false); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_interacting (rr), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 1, 4), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 2, 4), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + + EXPECT_EQ (db::compare (r.selected_not_interacting (empty), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_not_interacting (rr, 2, 1), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)"); +} + +TEST(30b_interact_with_count_edge) +{ + db::Layout ly; + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + db::cell_index_type ci1 = ly.add_cell ("C1"); + db::Cell &c1 = ly.cell (ci1); + db::cell_index_type ci2 = ly.add_cell ("C2"); + db::Cell &c2 = ly.cell (ci2); + top.insert (db::CellInstArray (db::CellInst (ci1), db::Trans ())); + top.insert (db::CellInstArray (db::CellInst (ci2), db::Trans ())); + + c1.shapes (l1).insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + c1.shapes (l1).insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + + c2.shapes (l2).insert (db::Edge (db::Point (-10, -10), db::Point (0, 0))); + c2.shapes (l2).insert (db::Edge (db::Point (0, 0), db::Point (10, 10))); + c2.shapes (l2).insert (db::Edge (db::Point (-110, -10), db::Point (-90, 10))); + c2.shapes (l2).insert (db::Edge (db::Point (-110, -210), db::Point (-90, -190))); + + ly.copy_layer (l2, l3); + top.shapes (l2).insert (db::Edge (db::Point (90, -10), db::Point (110, 10))); + top.shapes (l2).insert (db::Edge (db::Point (-110, -110), db::Point (-90, -90))); + + db::Region r (db::RecursiveShapeIterator (ly, top, l1)); + r.set_merged_semantics (true); + r.set_min_coherence (false); + + db::Region empty; + + db::Edges rr (db::RecursiveShapeIterator (ly, top, l2)); + db::Edges rr2 (db::RecursiveShapeIterator (ly, top, l3)); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 5, 5).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 2, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 4, 5).to_string (), ""); + + EXPECT_EQ (r.selected_not_interacting (empty).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + // Shortcut delivers clone of original, not merged one: + // EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + // so that is: + EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 4, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 5, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 2, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + + r.set_merged_semantics (false); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_interacting (rr), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 1, 4), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 2, 4), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + + EXPECT_EQ (db::compare (r.selected_not_interacting (empty), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_not_interacting (rr, 2, 1), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)"); +} + +TEST(30c_interact_with_count_text) +{ + db::Layout ly; + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + db::cell_index_type ci1 = ly.add_cell ("C1"); + db::Cell &c1 = ly.cell (ci1); + db::cell_index_type ci2 = ly.add_cell ("C2"); + db::Cell &c2 = ly.cell (ci2); + top.insert (db::CellInstArray (db::CellInst (ci1), db::Trans ())); + top.insert (db::CellInstArray (db::CellInst (ci2), db::Trans ())); + + c1.shapes (l1).insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + c1.shapes (l1).insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + + c2.shapes (l2).insert (db::Text ("a", db::Trans (db::Vector (0, 0)))); + c2.shapes (l2).insert (db::Text ("b", db::Trans (db::Vector (-100, 0)))); + c2.shapes (l2).insert (db::Text ("c", db::Trans (db::Vector (-100, -200)))); + + ly.copy_layer (l2, l3); + top.shapes (l2).insert (db::Text ("x", db::Trans (db::Vector (100, 0)))); + top.shapes (l2).insert (db::Text ("y", db::Trans (db::Vector (-100, -100)))); + + db::Region r (db::RecursiveShapeIterator (ly, top, l1)); + r.set_merged_semantics (true); + r.set_min_coherence (false); + + db::Region empty; + + db::Texts rr (db::RecursiveShapeIterator (ly, top, l2)); + db::Texts rr2 (db::RecursiveShapeIterator (ly, top, l3)); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr, 5, 5).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 1, 4).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 2, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (rr2, 4, 5).to_string (), ""); + + EXPECT_EQ (r.selected_not_interacting (empty).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + // Shortcut delivers clone of original, not merged one: + // EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + // so that is: + EXPECT_EQ (r.selected_not_interacting (rr, 2, 1).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 4, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 5, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 2).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 2, 5).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr2, 4, 5).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + + r.set_merged_semantics (false); + + EXPECT_EQ (r.selected_interacting (empty).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_interacting (rr), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_interacting (rr, 0, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_interacting (rr, 1, 2).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 1, 4), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (db::compare (r.selected_interacting (rr, 2, 4), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_interacting (rr, 2, 1).to_string (), ""); + EXPECT_EQ (r.selected_interacting (rr, 3, 4).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + + EXPECT_EQ (db::compare (r.selected_not_interacting (empty), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"), true); + EXPECT_EQ (r.selected_not_interacting (rr).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 0, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 2).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_not_interacting (rr, 1, 4).to_string (), ""); + EXPECT_EQ (r.selected_not_interacting (rr, 2, 4).to_string (), ""); + EXPECT_EQ (db::compare (r.selected_not_interacting (rr, 2, 1), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"), true); + EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)"); +} + +TEST(31_in) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_l31.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); // empty + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r1r = r1; + r1r.set_merged_semantics (false); + db::Region r2r = r2; + r2r.set_merged_semantics (false); + + 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 (1, 0)), r1); + 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 (10, 0)), r2.in (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.in (r1, true)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.in (r3, false)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.in (r3, true)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r3.in (r1, false)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r3.in (r1, true)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2r.in (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2r.in (r1, true)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r2.in (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r2.in (r1r, true)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r2r.in (r1r)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r2r.in (r1r, true)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au31.gds"); +} + +TEST(31_in_and_out) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_l31.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); // empty + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + + db::Region r1r = r1; + r1r.set_merged_semantics (false); + db::Region r2r = r2; + r2r.set_merged_semantics (false); + + 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 (1, 0)), r1); + 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 (10, 0)), r2.in_and_out (r1).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.in_and_out (r1).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.in_and_out (r3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.in_and_out (r3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r3.in_and_out (r1).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r3.in_and_out (r1).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2r.in_and_out (r1).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2r.in_and_out (r1).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r2.in_and_out (r1r).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r2.in_and_out (r1r).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r2r.in_and_out (r1r).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r2r.in_and_out (r1r).second); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au31.gds"); +} + +TEST(40_BoolWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_40.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); // empty + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2); + + db::RecursiveShapeIterator si3 (ly, top_cell, l3); + si3.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r3 (si3); + + 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 (1, 0)), r1); + 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 (10, 0)), r1.merged ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.merged ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1 & r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.bool_and (r2, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.bool_and (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r1.bool_and (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r3.bool_and (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r3.bool_and (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r1.bool_and (r3, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r1.bool_and (r3, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 1)), r1.bool_and (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 1)), r1.bool_and (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 1)), r3.bool_and (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 1)), r3.bool_and (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 1)), r1.bool_and (r3, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 1)), r1.bool_and (r3, db::DifferentPropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1 - r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.bool_not (r2, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r1.bool_not (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r1.bool_not (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), r3.bool_not (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), r3.bool_not (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), r1.bool_not (r3, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), r1.bool_not (r3, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r1.bool_not (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 1)), r1.bool_not (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 1)), r3.bool_not (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 1)), r3.bool_not (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 1)), r1.bool_not (r3, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 1)), r1.bool_not (r3, db::DifferentPropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.andnot (r2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.andnot (r2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.andnot (r2, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), r1.andnot (r2, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), r1.andnot (r2, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), r1.andnot (r2, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), r3.andnot (r2, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), r3.andnot (r2, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), r3.andnot (r2, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (49, 0)), r3.andnot (r2, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), r1.andnot (r3, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), r1.andnot (r3, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), r1.andnot (r3, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), r1.andnot (r3, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r1.andnot (r2, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 1)), r1.andnot (r2, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 1)), r1.andnot (r2, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 1)), r1.andnot (r2, db::DifferentPropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 1)), r3.andnot (r2, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 1)), r3.andnot (r2, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 1)), r3.andnot (r2, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (49, 1)), r3.andnot (r2, db::DifferentPropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 1)), r1.andnot (r3, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 1)), r1.andnot (r3, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 1)), r1.andnot (r3, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 1)), r1.andnot (r3, db::DifferentPropertiesConstraintDrop).second); + + db::Box clip (0, 8000, 10000, 15000); + db::Region clip_region; + clip_region.insert (clip); + + db::Region clip_region_wp (new db::FlatRegion ()); + db::property_names_id_type pn = clip_region_wp.properties_repository ().prop_name_id (1); + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (pn, 42)); + db::properties_id_type pid42 = clip_region_wp.properties_repository ().properties_id (ps); + clip_region_wp.insert (db::BoxWithProperties (clip, pid42)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), r1.bool_and (clip_region)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), r1.bool_and (clip_region_wp)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), clip_region.bool_and (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), clip_region_wp.bool_and (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), clip_region_wp.bool_and (clip_region)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), clip_region_wp.bool_and (clip_region_wp)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (70, 0)), r1.bool_and (clip_region, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (71, 0)), r1.bool_and (clip_region_wp, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (72, 0)), clip_region.bool_and (r1, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (73, 0)), clip_region_wp.bool_and (r1, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (74, 0)), clip_region_wp.bool_and (clip_region, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (75, 0)), clip_region_wp.bool_and (clip_region_wp, db::SamePropertiesConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (80, 0)), r1.bool_and (clip_region, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (81, 0)), r1.bool_and (clip_region_wp, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (82, 0)), clip_region.bool_and (r1, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (83, 0)), clip_region_wp.bool_and (r1, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (84, 0)), clip_region_wp.bool_and (clip_region, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (85, 0)), clip_region_wp.bool_and (clip_region_wp, db::SamePropertiesConstraintDrop)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au40.gds"); +} + +TEST(41_EdgesWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_40.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1wp (si1); + db::Region r1wp_nomerge = r1wp; + r1wp_nomerge.set_merged_semantics (false); + + si1 = db::RecursiveShapeIterator (ly, top_cell, l1); + db::Region r1 (si1); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2wp (si2); + db::Region r2wp_nomerge = r2wp; + r2wp_nomerge.set_merged_semantics (false); + + si2.apply_property_translator (db::PropertiesTranslator::make_remove_all ()); + db::Region r2 (si2); + + 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 (1, 0)), r1wp); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2wp); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1wp.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1wp_nomerge.edges ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2wp.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2wp_nomerge.edges ()); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au41.gds"); +} + +TEST(42_DRCWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1); + db::Region r1_nomerge (r1); + r1_nomerge.set_merged_semantics (false); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2); + db::Region r2_nomerge (r2); + r2_nomerge.set_merged_semantics (false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::RegionCheckOptions opt; + opt.metrics = db::Projection; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1_nomerge.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1_nomerge.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r1.separation_check (r2_nomerge, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r1_nomerge.separation_check (r2_nomerge, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2_nomerge.space_check (1000, opt)); + + opt.prop_constraint = db::NoPropertyConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::SamePropertiesConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::SamePropertiesConstraintDrop; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 1)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 1)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::DifferentPropertiesConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::DifferentPropertiesConstraintDrop; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 1)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 1)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r2.space_check (1000, opt)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au42.gds"); +} + +TEST(43_ComplexOpsWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::RegionCheckOptions opt; + opt.metrics = db::Projection; + + db::CompoundRegionOperationSecondaryNode *secondary = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode sep_check (secondary, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + + db::CompoundRegionOperationSecondaryNode *secondary2 = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode *sep_check2 = new db::CompoundRegionCheckOperationNode (secondary2, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + db::CompoundRegionEdgePairToPolygonProcessingOperationNode sep_check2p (new db::EdgePairToPolygonProcessor (0), sep_check2, true); + + db::CompoundRegionOperationSecondaryNode *secondary3 = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode *sep_check3 = new db::CompoundRegionCheckOperationNode (secondary3, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + db::CompoundRegionEdgePairToEdgeProcessingOperationNode sep_check2e (new db::EdgePairToEdgesProcessor (), sep_check3, true); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.cop_to_edge_pairs (sep_check)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.cop_to_region (sep_check2p)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1.cop_to_edges (sep_check2e)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.cop_to_edge_pairs (sep_check, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.cop_to_region (sep_check2p, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.cop_to_edges (sep_check2e, db::NoPropertyConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.cop_to_edge_pairs (sep_check, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.cop_to_region (sep_check2p, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r1.cop_to_edges (sep_check2e, db::SamePropertiesConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 1)), r1.cop_to_edge_pairs (sep_check, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 1)), r1.cop_to_region (sep_check2p, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r1.cop_to_edges (sep_check2e, db::SamePropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.cop_to_edge_pairs (sep_check, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.cop_to_region (sep_check2p, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.cop_to_edges (sep_check2e, db::DifferentPropertiesConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 1)), r1.cop_to_edge_pairs (sep_check, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 1)), r1.cop_to_region (sep_check2p, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r1.cop_to_edges (sep_check2e, db::DifferentPropertiesConstraintDrop)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au43.gds"); +} + +TEST(44_SizeWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2); + + 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 (1, 0)), r1); + 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 (10, 0)), r1.sized (200)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.sized (250, 50)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2.sized (200)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2.sized (250, 50)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au44.gds"); +} + diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 6922b1dc6..89117d591 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -33,6 +33,7 @@ #include "dbOriginalLayerRegion.h" #include "dbCellGraphUtils.h" #include "dbTestSupport.h" +#include "dbCompoundOperation.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -2126,6 +2127,404 @@ TEST(31_in_and_out) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au31.gds"); } +TEST(40_BoolWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_40.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); // empty + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1, dss); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2, dss); + + db::RecursiveShapeIterator si3 (ly, top_cell, l3); + si3.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r3 (si3, dss); + + 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 (1, 0)), r1); + 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 (10, 0)), r1.merged ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.merged ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1 & r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.bool_and (r2, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.bool_and (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r1.bool_and (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r3.bool_and (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r3.bool_and (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r1.bool_and (r3, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r1.bool_and (r3, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 1)), r1.bool_and (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 1)), r1.bool_and (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 1)), r3.bool_and (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 1)), r3.bool_and (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 1)), r1.bool_and (r3, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 1)), r1.bool_and (r3, db::DifferentPropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1 - r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.bool_not (r2, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r1.bool_not (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r1.bool_not (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), r3.bool_not (r2, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), r3.bool_not (r2, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), r1.bool_not (r3, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), r1.bool_not (r3, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r1.bool_not (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 1)), r1.bool_not (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 1)), r3.bool_not (r2, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 1)), r3.bool_not (r2, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 1)), r1.bool_not (r3, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 1)), r1.bool_not (r3, db::DifferentPropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.andnot (r2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.andnot (r2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.andnot (r2, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), r1.andnot (r2, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), r1.andnot (r2, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), r1.andnot (r2, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), r3.andnot (r2, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), r3.andnot (r2, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), r3.andnot (r2, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (49, 0)), r3.andnot (r2, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), r1.andnot (r3, db::SamePropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), r1.andnot (r3, db::SamePropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), r1.andnot (r3, db::DifferentPropertiesConstraint).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), r1.andnot (r3, db::DifferentPropertiesConstraint).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r1.andnot (r2, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 1)), r1.andnot (r2, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 1)), r1.andnot (r2, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 1)), r1.andnot (r2, db::DifferentPropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 1)), r3.andnot (r2, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 1)), r3.andnot (r2, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 1)), r3.andnot (r2, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (49, 1)), r3.andnot (r2, db::DifferentPropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 1)), r1.andnot (r3, db::SamePropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 1)), r1.andnot (r3, db::SamePropertiesConstraintDrop).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 1)), r1.andnot (r3, db::DifferentPropertiesConstraintDrop).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 1)), r1.andnot (r3, db::DifferentPropertiesConstraintDrop).second); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au40.gds"); +} + +TEST(41_EdgesWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_40.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1wp (si1, dss); + db::Region r1wp_nomerge = r1wp; + r1wp_nomerge.set_merged_semantics (false); + + si1 = db::RecursiveShapeIterator (ly, top_cell, l1); + db::Region r1 (si1, dss); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2wp (si2, dss); + db::Region r2wp_nomerge = r2wp; + r2wp_nomerge.set_merged_semantics (false); + + si2.apply_property_translator (db::PropertiesTranslator::make_remove_all ()); + db::Region r2 (si2, dss); + + 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 (1, 0)), r1wp); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2wp); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1wp.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1wp_nomerge.edges ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2wp.edges ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2wp_nomerge.edges ()); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au41.gds"); +} + +TEST(42_DRCWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1, dss); + db::Region r1_nomerge (r1); + r1_nomerge.set_merged_semantics (false); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2, dss); + db::Region r2_nomerge (r2); + r2_nomerge.set_merged_semantics (false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::RegionCheckOptions opt; + opt.metrics = db::Projection; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1_nomerge.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1_nomerge.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r1.separation_check (r2_nomerge, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r1_nomerge.separation_check (r2_nomerge, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2_nomerge.space_check (1000, opt)); + + opt.prop_constraint = db::NoPropertyConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::SamePropertiesConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::SamePropertiesConstraintDrop; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 1)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 1)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::DifferentPropertiesConstraint; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r2.space_check (1000, opt)); + + opt.prop_constraint = db::DifferentPropertiesConstraintDrop; + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 1)), r1.space_check (1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 1)), r1.separation_check (r2, 1000, opt)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r2.space_check (1000, opt)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au42.gds"); +} + +TEST(43_ComplexOpsWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1, dss); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2, dss); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::RegionCheckOptions opt; + opt.metrics = db::Projection; + + db::CompoundRegionOperationSecondaryNode *secondary = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode sep_check (secondary, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + + db::CompoundRegionOperationSecondaryNode *secondary2 = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode *sep_check2 = new db::CompoundRegionCheckOperationNode (secondary2, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + db::CompoundRegionEdgePairToPolygonProcessingOperationNode sep_check2p (new db::EdgePairToPolygonProcessor (0), sep_check2, true); + + db::CompoundRegionOperationSecondaryNode *secondary3 = new db::CompoundRegionOperationSecondaryNode (&r2); + db::CompoundRegionCheckOperationNode *sep_check3 = new db::CompoundRegionCheckOperationNode (secondary3, db::SpaceRelation, true /*==different polygons*/, 1000, opt); + db::CompoundRegionEdgePairToEdgeProcessingOperationNode sep_check2e (new db::EdgePairToEdgesProcessor (), sep_check3, true); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.cop_to_edge_pairs (sep_check)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.cop_to_region (sep_check2p)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1.cop_to_edges (sep_check2e)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.cop_to_edge_pairs (sep_check, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.cop_to_region (sep_check2p, db::NoPropertyConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.cop_to_edges (sep_check2e, db::NoPropertyConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.cop_to_edge_pairs (sep_check, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.cop_to_region (sep_check2p, db::SamePropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r1.cop_to_edges (sep_check2e, db::SamePropertiesConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 1)), r1.cop_to_edge_pairs (sep_check, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 1)), r1.cop_to_region (sep_check2p, db::SamePropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 1)), r1.cop_to_edges (sep_check2e, db::SamePropertiesConstraintDrop)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.cop_to_edge_pairs (sep_check, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.cop_to_region (sep_check2p, db::DifferentPropertiesConstraint)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.cop_to_edges (sep_check2e, db::DifferentPropertiesConstraint)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 1)), r1.cop_to_edge_pairs (sep_check, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 1)), r1.cop_to_region (sep_check2p, db::DifferentPropertiesConstraintDrop)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 1)), r1.cop_to_edges (sep_check2e, db::DifferentPropertiesConstraintDrop)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au43.gds"); +} + +TEST(44_SizeWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1, dss); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2, dss); + + 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 (1, 0)), r1); + 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 (10, 0)), r1.sized (200)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.sized (250, 50)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2.sized (200)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2.sized (250, 50)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au44.gds"); +} + +TEST(45_FlattenWithProperties) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_42.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 l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r1 (si1, dss); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + db::Region r2 (si2, dss); + + 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 (1, 0)), r1); + 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 (10, 0)), r1.flatten ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.flatten ()); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au45.gds"); +} + TEST(100_Integration) { db::Layout ly; diff --git a/src/db/unit_tests/dbHierNetsProcessorTests.cc b/src/db/unit_tests/dbHierNetsProcessorTests.cc new file mode 100644 index 000000000..ce947ade3 --- /dev/null +++ b/src/db/unit_tests/dbHierNetsProcessorTests.cc @@ -0,0 +1,174 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" +#include "dbLayoutToNetlist.h" +#include "dbTestSupport.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +db::Region make_region (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, bool flat) +{ + return flat ? db::Region (si) : db::Region (si, dss); +} + +void run_test (tl::TestBase *_this, bool flat, bool flat_nets, const std::string &au_fn) +{ + db::Layout ly; + db::DeepShapeStore dss; + if (! flat_nets) { + dss.set_subcircuit_hierarchy_for_nets (true); + } + + db::LayerMap lmap; + + unsigned int poly = define_layer (ly, lmap, 1); + unsigned int cont = define_layer (ly, lmap, 2); + unsigned int metal1 = define_layer (ly, lmap, 3); + unsigned int via1 = define_layer (ly, lmap, 4); + unsigned int metal2 = define_layer (ly, lmap, 5); + unsigned int via2 = define_layer (ly, lmap, 6); + unsigned int metal3 = define_layer (ly, lmap, 7); + unsigned int via3 = define_layer (ly, lmap, 8); + unsigned int metal4 = define_layer (ly, lmap, 9); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nets_proc_1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::Region rpoly = make_region (db::RecursiveShapeIterator (ly, tc, poly), dss, flat); + db::Region rcont = make_region (db::RecursiveShapeIterator (ly, tc, cont), dss, flat); + db::Region rmetal1 = make_region (db::RecursiveShapeIterator (ly, tc, metal1), dss, flat); + db::Region rvia1 = make_region (db::RecursiveShapeIterator (ly, tc, via1), dss, flat); + db::Region rmetal2 = make_region (db::RecursiveShapeIterator (ly, tc, metal2), dss, flat); + db::Region rvia2 = make_region (db::RecursiveShapeIterator (ly, tc, via2), dss, flat); + db::Region rmetal3 = make_region (db::RecursiveShapeIterator (ly, tc, metal3), dss, flat); + db::Region rvia3 = make_region (db::RecursiveShapeIterator (ly, tc, via3), dss, flat); + db::Region rmetal4 = make_region (db::RecursiveShapeIterator (ly, tc, metal4), dss, flat); + + std::unique_ptr l2n; + if (! flat) { + l2n.reset (new db::LayoutToNetlist (&dss)); + EXPECT_EQ (dss.has_net_builder_for (0, l2n.get ()), false); + } else { + l2n.reset (new db::LayoutToNetlist (ly.cell_name (tc.cell_index ()), ly.dbu ())); + } + + // net extraction + + if (flat) { + // flat or original layers need to be registered + l2n->register_layer (rpoly); + l2n->register_layer (rcont); + l2n->register_layer (rmetal1); + l2n->register_layer (rvia1); + l2n->register_layer (rmetal2); + l2n->register_layer (rvia2); + l2n->register_layer (rmetal3); + l2n->register_layer (rvia3); + l2n->register_layer (rmetal4); + } + + // Intra-layer + l2n->connect (rpoly); + l2n->connect (rcont); + l2n->connect (rmetal1); + l2n->connect (rvia1); + l2n->connect (rmetal2); + l2n->connect (rvia2); + l2n->connect (rmetal3); + l2n->connect (rvia3); + l2n->connect (rmetal4); + // Inter-layer + l2n->connect (rpoly, rcont); + l2n->connect (rcont, rmetal1); + l2n->connect (rmetal1, rvia1); + l2n->connect (rvia1, rmetal2); + l2n->connect (rmetal2, rvia2); + l2n->connect (rvia2, rmetal3); + l2n->connect (rmetal3, rvia3); + l2n->connect (rvia3, rmetal4); + + l2n->extract_netlist (); + + db::Region rmetal1_nets = rmetal1.nets (*l2n, db::NPM_NetQualifiedNameOnly, tl::Variant (1)); + if (! flat) { + EXPECT_EQ (dss.has_net_builder_for (0, l2n.get ()), true); + } + db::Region rmetal2_nets = rmetal2.nets (*l2n, db::NPM_NetQualifiedNameOnly, tl::Variant (1)); + + db::Region res1 = rmetal1_nets.bool_and (rmetal2_nets, db::SamePropertiesConstraint); + db::Region res2 = rmetal1_nets.bool_and (rmetal2_nets, db::DifferentPropertiesConstraint); + db::Region res3 = rmetal1_nets.bool_and (rmetal2_nets, db::NoPropertyConstraint); + + rmetal1_nets.insert_into (&ly, tc.cell_index (), ly.insert_layer (db::LayerProperties (100, 0))); + rmetal2_nets.insert_into (&ly, tc.cell_index (), ly.insert_layer (db::LayerProperties (101, 0))); + + res1.insert_into (&ly, tc.cell_index (), ly.insert_layer (db::LayerProperties (1000, 0))); + res2.insert_into (&ly, tc.cell_index (), ly.insert_layer (db::LayerProperties (1001, 0))); + res3.insert_into (&ly, tc.cell_index (), ly.insert_layer (db::LayerProperties (1002, 0))); + + // Test auto-unregistration + l2n.reset (0); + if (! flat) { + EXPECT_EQ (dss.has_net_builder_for (0, l2n.get ()), false); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), au_fn); + db::compare_layouts (_this, ly, au_path); +} + +TEST(1_NetSpecificBoolFlat) +{ + run_test (_this, false, true, "net_proc_au1.gds"); +} + +TEST(2_NetSpecificBoolFlatNets) +{ + run_test (_this, false, true, "net_proc_au2.gds"); +} + +TEST(3_NetSpecificBoolFullyHier) +{ + run_test (_this, false, false, "net_proc_au3.gds"); +} diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index e22e96876..7e9fe165f 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -30,6 +30,7 @@ #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "dbLocalOperationUtils.h" +#include "dbRegionLocalOperations.h" #include "dbPolygon.h" static std::string testdata (const std::string &fn) @@ -55,7 +56,7 @@ class BoolAndOrNotWithSizedLocalOperation { public: BoolAndOrNotWithSizedLocalOperation (bool is_and, db::Coord dist) - : BoolAndOrNotLocalOperation (is_and), m_dist (dist) + : db::BoolAndOrNotLocalOperation (is_and), m_dist (dist) { // .. nothing yet .. } @@ -72,7 +73,7 @@ public: } } - BoolAndOrNotLocalOperation::do_compute_local (layout, sized_interactions, results, max_vertex_count, area_ratio); + db::BoolAndOrNotLocalOperation::do_compute_local (layout, sized_interactions, results, max_vertex_count, area_ratio); } db::Coord dist () const diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc index a56a10254..8f4cd26d5 100644 --- a/src/db/unit_tests/dbHierarchyBuilderTests.cc +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -220,7 +220,7 @@ TEST(2_WithClipAndRefGeneration) } db::Layout target; - db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target, 0); db::ClippingHierarchyBuilderShapeReceiver clip(&ref); db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); @@ -259,7 +259,7 @@ TEST(2_WithEmptyResult) } db::Layout target; - db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target, 0); db::ClippingHierarchyBuilderShapeReceiver clip(&ref); db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 4e7ab190e..0d56d2dae 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -74,7 +74,7 @@ TEST(1_ReaderBasic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -104,7 +104,7 @@ TEST(1_ReaderBasic) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets); - l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); + l2n.build_nets (&nets, cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -134,7 +134,7 @@ TEST(1_ReaderBasic) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets); - l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Flatten, 0, "DEVICE_"); + l2n.build_nets (&nets, cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Flatten, 0, "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -164,7 +164,7 @@ TEST(1_ReaderBasic) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets); - l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_nets (&nets, cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -195,7 +195,7 @@ TEST(1_ReaderBasic) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets); - l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0); + l2n.build_nets (&nets, cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -226,7 +226,7 @@ TEST(1_ReaderBasic) db::CellMapping cm = l2n.const_cell_mapping_into (ly2, top2); - l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_nets (&nets, cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -305,13 +305,13 @@ TEST(1c_ReaderBasicShortWithProps) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_AllProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); au = tl::combine_path (au, "l2n_reader_au_p.oas"); - db::compare_layouts (_this, ly2, au, db::WriteOAS); + db::compare_layouts (_this, ly2, au, db::NormalizationMode (db::WriteOAS | db::AsPolygons)); } } @@ -361,7 +361,7 @@ TEST(2_ReaderWithGlobalNets) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -417,7 +417,7 @@ TEST(3_ReaderAbsoluteCoordinates) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -463,7 +463,7 @@ TEST(4_ReaderCombinedDevices) std::map lmap = l2n.create_layermap (ly2, 1000); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index d82a42e7d..f2c1716e6 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -149,22 +149,23 @@ TEST(0_Basic) reg_copy.reset (0); std::unique_ptr reg2 (l2n.make_layer ()); - EXPECT_EQ (l2n.name (1u), ""); - EXPECT_EQ (l2n.name (*reg2), ""); + EXPECT_EQ (l2n.name (1u), "$1"); + EXPECT_EQ (l2n.name (*reg2), "$1"); EXPECT_EQ (l2n.layer_of (*reg2), 1u); EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), true); reg2.reset (0); - EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), false); + // NOTE: deleting the region does not free the layer as we hold it internally inside LayoutToNetlist + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), true); std::unique_ptr reg3 (l2n.make_layer ("l3")); EXPECT_EQ (l2n.name (*reg3), "l3"); - EXPECT_EQ (l2n.layer_of (*reg3), 1u); + EXPECT_EQ (l2n.layer_of (*reg3), 2u); std::string s; for (db::LayoutToNetlist::layer_iterator l = l2n.begin_layers (); l != l2n.end_layers (); ++l) { s += tl::to_string (l->first) + ":" + l->second + ";"; } - EXPECT_EQ (s, "0:l1;1:l3;"); + EXPECT_EQ (s, "0:l1;1:$1;2:l3;"); } TEST(1_BasicExtraction) @@ -395,7 +396,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0); + l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -421,7 +422,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -447,7 +448,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0); + l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -473,7 +474,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (42), db::LayoutToNetlist::BNH_Flatten, 0, 0); + l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_AllProperties, tl::Variant (42), db::BNH_Flatten, 0, 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -499,7 +500,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (42), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0); + l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_AllProperties, tl::Variant (42), db::BNH_SubcircuitCells, "CIRCUIT_", 0); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -525,7 +526,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -636,7 +637,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 423ae0824..68cac78b0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -207,7 +207,7 @@ TEST(1_WriterBasic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); @@ -440,7 +440,7 @@ TEST(2_WriterWithGlobalNets) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testdata (); au = tl::combine_path (au, "algo"); diff --git a/src/db/unit_tests/dbPropertiesRepositoryTests.cc b/src/db/unit_tests/dbPropertiesRepositoryTests.cc index 1a8ba974c..018ef9ae1 100644 --- a/src/db/unit_tests/dbPropertiesRepositoryTests.cc +++ b/src/db/unit_tests/dbPropertiesRepositoryTests.cc @@ -22,6 +22,7 @@ #include "dbPropertiesRepository.h" +#include "dbTestSupport.h" #include "tlString.h" #include "tlUnitTest.h" @@ -259,3 +260,135 @@ TEST(6) EXPECT_EQ (pid2, size_t (2)); } + +TEST(10_PropertiesTranslator) +{ + EXPECT_EQ (db::PropertiesTranslator ().is_null (), true); + EXPECT_EQ (db::PropertiesTranslator ().is_pass (), true); + EXPECT_EQ (db::PropertiesTranslator ().is_empty (), false); + EXPECT_EQ (db::PropertiesTranslator::make_pass_all ().is_null (), false); + EXPECT_EQ (db::PropertiesTranslator::make_pass_all ().is_pass (), true); + EXPECT_EQ (db::PropertiesTranslator::make_pass_all ().is_empty (), false); + EXPECT_EQ (db::PropertiesTranslator::make_remove_all ().is_null (), false); + EXPECT_EQ (db::PropertiesTranslator::make_remove_all ().is_pass (), false); + EXPECT_EQ (db::PropertiesTranslator::make_remove_all ().is_empty (), true); + + db::PropertiesRepository rp; + db::property_names_id_type key1 = rp.prop_name_id (1); + db::property_names_id_type key2 = rp.prop_name_id (2); + db::property_names_id_type key3 = rp.prop_name_id (3); + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1a = rp.properties_id (ps); + EXPECT_EQ (prop2string (rp, prop1a), "1=100\n2=101"); + + ps.clear (); + ps.insert (std::make_pair (key1, 0)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1b = rp.properties_id (ps); + EXPECT_EQ (prop2string (rp, prop1b), "1=0\n2=101"); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key3, 102)); + db::properties_id_type prop2 = rp.properties_id (ps); + EXPECT_EQ (prop2string (rp, prop2), "1=100\n3=102"); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + db::properties_id_type prop3 = rp.properties_id (ps); + EXPECT_EQ (prop2string (rp, prop3), "1=100"); + + db::PropertiesRepository rp_org = rp; + + db::PropertiesTranslator t; + EXPECT_EQ (prop2string (rp, t (prop1a)), "1=100\n2=101"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "1=0\n2=101"); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=100\n3=102"); + EXPECT_EQ (prop2string (rp, t (prop3)), "1=100"); + + t = db::PropertiesTranslator::make_pass_all (); + EXPECT_EQ (prop2string (rp, t (prop1a)), "1=100\n2=101"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "1=0\n2=101"); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=100\n3=102"); + EXPECT_EQ (prop2string (rp, t (prop3)), "1=100"); + + t = db::PropertiesTranslator::make_remove_all (); + EXPECT_EQ (prop2string (rp, t (prop1a)), ""); + EXPECT_EQ (prop2string (rp, t (prop1b)), ""); + EXPECT_EQ (prop2string (rp, t (prop2)), ""); + EXPECT_EQ (prop2string (rp, t (prop3)), ""); + + std::set kf; + kf.insert (1); + t = db::PropertiesTranslator::make_filter (rp, kf); + EXPECT_EQ (prop2string (rp, t (prop1a)), "1=100"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "1=0"); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=100"); + EXPECT_EQ (prop2string (rp, t (prop3)), "1=100"); + + kf.insert (3); + t = db::PropertiesTranslator::make_filter (rp, kf); + EXPECT_EQ (prop2string (rp, t (prop1a)), "1=100"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "1=0"); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=100\n3=102"); + EXPECT_EQ (prop2string (rp, t (prop3)), "1=100"); + + std::map km; + km[1] = 4; + km[3] = 1; + + t = db::PropertiesTranslator::make_key_mapper (rp, km); + EXPECT_EQ (prop2string (rp, t (prop1a)), "4=100"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "4=0"); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=102\n4=100"); + EXPECT_EQ (prop2string (rp, t (prop3)), "4=100"); + + kf.clear (); + kf.insert (4); + t = db::PropertiesTranslator::make_filter (rp, kf) * db::PropertiesTranslator::make_key_mapper (rp, km); + EXPECT_EQ (t.is_empty (), false); + EXPECT_EQ (prop2string (rp, t (prop1a)), "4=100"); + EXPECT_EQ (prop2string (rp, t (prop1b)), "4=0"); + EXPECT_EQ (prop2string (rp, t (prop2)), "4=100"); + EXPECT_EQ (prop2string (rp, t (prop3)), "4=100"); + + kf.clear (); + kf.insert (3); + + t = db::PropertiesTranslator::make_filter (rp, kf) * db::PropertiesTranslator::make_key_mapper (rp, km); + EXPECT_EQ (t.is_empty (), true); + EXPECT_EQ (prop2string (rp, t (prop1a)), ""); + EXPECT_EQ (prop2string (rp, t (prop1b)), ""); + EXPECT_EQ (prop2string (rp, t (prop2)), ""); + EXPECT_EQ (prop2string (rp, t (prop3)), ""); + + t = db::PropertiesTranslator::make_key_mapper (rp, km) * db::PropertiesTranslator::make_filter (rp, kf); + EXPECT_EQ (t.is_empty (), false); + EXPECT_EQ (prop2string (rp, t (prop1a)), ""); + EXPECT_EQ (prop2string (rp, t (prop1b)), ""); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=102"); + EXPECT_EQ (prop2string (rp, t (prop3)), ""); + + rp = rp_org; + + t = db::PropertiesTranslator::make_key_mapper (rp, km); + t = db::PropertiesTranslator::make_filter (rp, kf) * t; + EXPECT_EQ (t.is_empty (), true); + EXPECT_EQ (prop2string (rp, t (prop1a)), ""); + EXPECT_EQ (prop2string (rp, t (prop1b)), ""); + EXPECT_EQ (prop2string (rp, t (prop2)), ""); + EXPECT_EQ (prop2string (rp, t (prop3)), ""); + + rp = rp_org; + + t = db::PropertiesTranslator::make_filter (rp, kf); + t = db::PropertiesTranslator::make_key_mapper (rp, km) * t; + EXPECT_EQ (t.is_empty (), false); + EXPECT_EQ (prop2string (rp, t (prop1a)), ""); + EXPECT_EQ (prop2string (rp, t (prop1b)), ""); + EXPECT_EQ (prop2string (rp, t (prop2)), "1=102"); + EXPECT_EQ (prop2string (rp, t (prop3)), ""); +} diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 826e8a48b..d198d9dac 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -24,6 +24,7 @@ #include "tlUnitTest.h" #include "dbRegion.h" +#include "dbFlatRegion.h" #include "dbRegionUtils.h" #include "dbRegionProcessors.h" #include "dbEdgesUtils.h" @@ -2026,6 +2027,498 @@ TEST(40_with_holes) EXPECT_EQ (r.filtered (db::HoleCountFilter (3, 5, true)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)"); } +TEST(50_PropertiesFlat) +{ + db::Region r; + + // Fill flat region with parts with properties + + r.insert (db::Box (0, 0, 100, 200)); + r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1)); + r.insert (db::Box (10, 20, 110, 220)); + r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42)); + + EXPECT_EQ (r.count (), size_t (4)); + + db::Region::const_iterator s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + // a single property #1 element + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + // a single property #42 element + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + + EXPECT_EQ (s.at_end (), true); +} + +TEST(51_PropertiesFlatFromLayout) +{ + db::Layout ly; + unsigned int li = ly.insert_layer (); + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + + db::Shapes &si = top.shapes (li); + si.insert (db::Box (0, 0, 100, 200)); + si.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1)); + si.insert (db::Box (10, 20, 110, 220)); + si.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42)); + + // NOTE: without specific "property only" selector -> properties are ignored. + + db::Region r (db::RecursiveShapeIterator (ly, top, li)); + + EXPECT_EQ (r.count (), size_t (4)); + + db::Region::const_iterator s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), true); + + // NOTE: now with explicit propertly-only source + db::RecursiveShapeIterator rsi (ly, top, li); + rsi.shape_flags (db::ShapeIterator::AllWithProperties); + r = db::Region (rsi); + + EXPECT_EQ (r.count (), size_t (2)); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(1,2;1,202;11,202;11,212;111,212;111,12;101,12;101,2)"); + ++s; + + // NOTE: now with regarding properties + rsi = db::RecursiveShapeIterator (ly, top, li); + rsi.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + r = db::Region (rsi); + + EXPECT_EQ (r.count (), size_t (4)); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + // a single property #1 element + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + // a single property #42 element + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + + EXPECT_EQ (s.at_end (), true); +} + +TEST(52_PropertiesDeep) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Region r (dss); + + // Fill flat region with parts with properties + + r.insert (db::Box (0, 0, 100, 200)); + r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1)); + r.insert (db::Box (10, 20, 110, 220)); + r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42)); + + EXPECT_EQ (r.count (), size_t (4)); + + db::Region::const_iterator s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (1)); + // a single property #1 element + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (42)); + // a single property #42 element + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + + EXPECT_EQ (s.at_end (), true); +} + +TEST(53_PropertiesDeepFromLayout) +{ + db::DeepShapeStore dss; + + db::Layout ly; + unsigned int li = ly.insert_layer (); + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + + tl::Variant pname ("VALUE"); + db::property_names_id_type pname_id = ly.properties_repository ().prop_name_id (pname); + + db::PropertiesRepository::properties_set ps42; + ps42.insert (std::make_pair (pname_id, tl::Variant (42))); + db::properties_id_type pid42 = ly.properties_repository ().properties_id (ps42); + + db::PropertiesRepository::properties_set ps1; + ps1.insert (std::make_pair (pname_id, tl::Variant (1))); + db::properties_id_type pid1 = ly.properties_repository ().properties_id (ps1); + + db::Shapes &si = top.shapes (li); + si.insert (db::Box (0, 0, 100, 200)); + si.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1)); + si.insert (db::Box (10, 20, 110, 220)); + si.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), pid42)); + + // NOTE: without specific "property only" selector -> properties are ignored. + + db::Region r (db::RecursiveShapeIterator (ly, top, li), dss); + + EXPECT_EQ (r.count (), size_t (4)); + + db::Region::const_iterator s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), true); + + // NOTE: now with explicit propertly-only source + db::RecursiveShapeIterator rsi (ly, top, li); + rsi.shape_flags (db::ShapeIterator::AllWithProperties); + r = db::Region (rsi, dss); + + EXPECT_EQ (r.count (), size_t (2)); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(1,2;1,202;11,202;11,212;111,212;111,12;101,12;101,2)"); + ++s; + + // NOTE: now with regarding properties + rsi = db::RecursiveShapeIterator (ly, top, li); + rsi.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + r = db::Region (rsi, dss); + + EXPECT_EQ (r.count (), size_t (4)); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (prop2string (r.properties_repository (), s.prop_id ()), "VALUE=1"); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (prop2string (r.properties_repository (), s.prop_id ()), "VALUE=42"); + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin_merged (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (s.prop_id (), db::properties_id_type (0)); + // property #0 elements are merged + EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (prop2string (r.properties_repository (), s.prop_id ()), "VALUE=1"); + // a single property #1 element + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (prop2string (r.properties_repository (), s.prop_id ()), "VALUE=42"); + // a single property #42 element + EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)"); + ++s; + + EXPECT_EQ (s.at_end (), true); +} + +TEST(54_PropertiesFilterDeep) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Region r (dss); + + db::PropertiesRepository &rp = r.properties_repository (); + db::property_names_id_type key1 = rp.prop_name_id (1); + db::property_names_id_type key2 = rp.prop_name_id (2); + db::property_names_id_type key3 = rp.prop_name_id (3); + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1a = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 0)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1b = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key3, 102)); + db::properties_id_type prop2 = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + db::properties_id_type prop3 = rp.properties_id (ps); + + r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), prop1a)); + r.insert (db::BoxWithProperties (db::Box (10, 12, 111, 212), prop1b)); + r.insert (db::BoxWithProperties (db::Box (20, 22, 121, 222), prop2)); + r.insert (db::BoxWithProperties (db::Box (30, 32, 131, 232), prop3)); + + db::Region rr = r; + + std::set kf; + kf.insert (1); + + rr.apply_property_translator (db::PropertiesTranslator::make_filter (rr.properties_repository (), kf)); + + db::Region::const_iterator s = rr.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=0"); + EXPECT_EQ (s->to_string (), "(10,12;10,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(20,22;20,222;121,222;121,22)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(30,32;30,232;131,232;131,32)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (r.properties_repository (), s.prop_id ()), "1=100\n2=101"); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); +} + +TEST(55_PropertiesFilterFlat) +{ + // NOTE: we have to force flat as otherwise no properties repo is available + db::Region r (new db::FlatRegion ()); + + db::PropertiesRepository &rp = r.properties_repository (); + db::property_names_id_type key1 = rp.prop_name_id (1); + db::property_names_id_type key2 = rp.prop_name_id (2); + db::property_names_id_type key3 = rp.prop_name_id (3); + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1a = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 0)); + ps.insert (std::make_pair (key2, 101)); + db::properties_id_type prop1b = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + ps.insert (std::make_pair (key3, 102)); + db::properties_id_type prop2 = rp.properties_id (ps); + + ps.clear (); + ps.insert (std::make_pair (key1, 100)); + db::properties_id_type prop3 = rp.properties_id (ps); + + r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), prop1a)); + r.insert (db::BoxWithProperties (db::Box (10, 12, 111, 212), prop1b)); + r.insert (db::BoxWithProperties (db::Box (20, 22, 121, 222), prop2)); + r.insert (db::BoxWithProperties (db::Box (30, 32, 131, 232), prop3)); + + db::Region rr = r; + + std::set kf; + kf.insert (1); + + rr.apply_property_translator (db::PropertiesTranslator::make_filter (rr.properties_repository (), kf)); + + db::Region::const_iterator s = rr.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=0"); + EXPECT_EQ (s->to_string (), "(10,12;10,212;111,212;111,12)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(20,22;20,222;121,222;121,22)"); + ++s; + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (rr.properties_repository (), s.prop_id ()), "1=100"); + EXPECT_EQ (s->to_string (), "(30,32;30,232;131,232;131,32)"); + ++s; + EXPECT_EQ (s.at_end (), true); + + s = r.begin (); + EXPECT_EQ (s.at_end (), false); + EXPECT_EQ (db::prop2string (r.properties_repository (), s.prop_id ()), "1=100\n2=101"); + EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); +} + TEST(100_Processors) { db::Region r; diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 50d493b51..351bf8860 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -13,76 +13,78 @@ SOURCES = \ dbRegionCheckUtilsTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \ - dbLoadLayoutOptionsTests.cc \ - dbSaveLayoutOptionsTests.cc \ - dbHierarchyBuilderTests.cc \ - dbRecursiveShapeIteratorTests.cc \ - dbHierProcessorTests.cc \ - dbDeepRegionTests.cc \ - dbDeepShapeStoreTests.cc \ - dbHierNetworkProcessorTests.cc \ - dbNetlistTests.cc \ - dbNetlistExtractorTests.cc \ - dbNetlistDeviceExtractorTests.cc \ - dbNetlistDeviceClassesTests.cc \ - dbLayoutToNetlistTests.cc \ - dbLayoutToNetlistWriterTests.cc \ - dbLayoutToNetlistReaderTests.cc \ - dbNetlistWriterTests.cc \ - dbCellVariantsTests.cc \ - dbDeepEdgesTests.cc \ - dbDeepEdgePairsTests.cc \ - dbNetlistCompareTests.cc \ - dbNetlistReaderTests.cc \ - dbLayoutVsSchematicTests.cc \ - dbLayoutQueryTests.cc \ - dbPolygonToolsTests.cc \ - dbTechnologyTests.cc \ - dbStreamLayerTests.cc \ - dbVectorTests.cc \ - dbVariableWidthPathTests.cc \ - dbTransTests.cc \ - dbTilingProcessorTests.cc \ - dbTextsTests.cc \ - dbTextTests.cc \ - dbShapesTests.cc \ - dbShapeRepositoryTests.cc \ - dbShapeArrayTests.cc \ - dbShapeTests.cc \ - dbRegionTests.cc \ - dbPropertiesRepositoryTests.cc \ - dbPolygonTests.cc \ - dbPointTests.cc \ - dbPCellsTests.cc \ - dbPathTests.cc \ - dbObjectTests.cc \ - dbMatrixTests.cc \ - dbLibrariesTests.cc \ - dbLayoutUtilsTests.cc \ - dbLayoutDiffTests.cc \ - dbLayoutTests.cc \ - dbLayerMappingTests.cc \ - dbLayerTests.cc \ - dbExpressionTests.cc \ - dbEdgesToContoursTests.cc \ - dbEdgesTests.cc \ - dbEdgeProcessorTests.cc \ - dbEdgePairsTests.cc \ - dbEdgePairRelationsTests.cc \ - dbEdgePairTests.cc \ - dbEdgeTests.cc \ - dbEdgesUtilsTests.cc \ - dbClipTests.cc \ - dbCellMappingTests.cc \ - dbCellHullGeneratorTests.cc \ - dbCellGraphUtilsTests.cc \ - dbCellTests.cc \ - dbBoxTreeTests.cc \ - dbBoxScannerTests.cc \ - dbBoxTests.cc \ - dbArrayTests.cc \ - dbDeepTextsTests.cc \ - dbNetShapeTests.cc + dbLoadLayoutOptionsTests.cc \ + dbSaveLayoutOptionsTests.cc \ + dbHierarchyBuilderTests.cc \ + dbRecursiveShapeIteratorTests.cc \ + dbHierProcessorTests.cc \ + dbDeepRegionTests.cc \ + dbDeepShapeStoreTests.cc \ + dbHierNetworkProcessorTests.cc \ + dbNetlistTests.cc \ + dbNetlistExtractorTests.cc \ + dbNetlistDeviceExtractorTests.cc \ + dbNetlistDeviceClassesTests.cc \ + dbLayoutToNetlistTests.cc \ + dbLayoutToNetlistWriterTests.cc \ + dbLayoutToNetlistReaderTests.cc \ + dbNetlistWriterTests.cc \ + dbCellVariantsTests.cc \ + dbDeepEdgesTests.cc \ + dbDeepEdgePairsTests.cc \ + dbNetlistCompareTests.cc \ + dbNetlistReaderTests.cc \ + dbLayoutVsSchematicTests.cc \ + dbLayoutQueryTests.cc \ + dbPolygonToolsTests.cc \ + dbTechnologyTests.cc \ + dbStreamLayerTests.cc \ + dbVectorTests.cc \ + dbVariableWidthPathTests.cc \ + dbTransTests.cc \ + dbTilingProcessorTests.cc \ + dbTextsTests.cc \ + dbTextTests.cc \ + dbShapesTests.cc \ + dbShapeRepositoryTests.cc \ + dbShapeArrayTests.cc \ + dbShapeTests.cc \ + dbRegionTests.cc \ + dbPropertiesRepositoryTests.cc \ + dbPolygonTests.cc \ + dbPointTests.cc \ + dbPCellsTests.cc \ + dbPathTests.cc \ + dbObjectTests.cc \ + dbMatrixTests.cc \ + dbLibrariesTests.cc \ + dbLayoutUtilsTests.cc \ + dbLayoutDiffTests.cc \ + dbLayoutTests.cc \ + dbLayerMappingTests.cc \ + dbLayerTests.cc \ + dbExpressionTests.cc \ + dbEdgesToContoursTests.cc \ + dbEdgesTests.cc \ + dbEdgeProcessorTests.cc \ + dbEdgePairsTests.cc \ + dbEdgePairRelationsTests.cc \ + dbEdgePairTests.cc \ + dbEdgeTests.cc \ + dbEdgesUtilsTests.cc \ + dbClipTests.cc \ + dbCellMappingTests.cc \ + dbCellHullGeneratorTests.cc \ + dbCellGraphUtilsTests.cc \ + dbCellTests.cc \ + dbBoxTreeTests.cc \ + dbBoxScannerTests.cc \ + dbBoxTests.cc \ + dbArrayTests.cc \ + dbDeepTextsTests.cc \ + dbNetShapeTests.cc \ + dbHierNetsProcessorTests.cc \ + dbAsIfFlatRegionTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 089c1eb5c..91fa1a06e 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1413,6 +1413,72 @@ See Source#polygons for a descr The primary input of the universal DRC function is the layer the Layer#drc function is called on.

+

"props_copy" - Specifies "copy properties" on operations supporting user properties constraints

+ +

+This properties constraint does not constrain the operation, but instructs it to +attach the properties from the primary input to the output objects. +

+See also props_ne and props_eq. +

+

"props_eq" - Specifies "same properties" for operations supporting user properties constraints

+ +

+Some operations such as boolean AND support properties constraints. By giving +a "props_eq" constraint, the operation is performed only on shapes with the same +properties, where "properties" stands for the full set of key/value pairs. +

+Note that you have to enable properties explicitly or generate properties (e.g. +with the DRCLayer#nets method). +

+Example: +

+

+connect(metal1, via1)
+connect(via1, metal2)
+... further connect statements
+
+m1m2_overlap_connected = metal1.nets.and(metal2, props_eq) 
+
+

+"props_eq" can be combined with props_copy. In this case, properties +are transferred to the output shapes and can be used in further processing: +

+

+m1m2_overlap_connected = metal1.nets.and(metal2, props_eq + props_copy) 
+
+

+See also props_ne. +

+

"props_ne" - Specifies "different properties" for operations supporting user properties constraints

+ +

+Some operations such as boolean AND support properties constraints. By giving +a "props_ne" constraint, the operation is performed only on shapes with different +properties, where "properties" stands for the full set of key/value pairs. +

+Note that you have to enable properties explicitly or generate properties (e.g. +with the DRCLayer#nets method). +

+Example: +

+

+connect(metal1, via1)
+connect(via1, metal2)
+... further connect statements
+
+m1m2_overlap_not_connected = metal1.nets.and(metal2, props_ne)
+
+

+"props_ne" can be combined with props_copy. In this case, properties +are transferred to the output shapes and can be used in further processing: +

+

+m1m2_overlap_connected = metal1.nets.and(metal2, props_ne + props_copy) 
+
+

+See also props_eq. +

"rectangles" - Selects all polygons which are rectangles

Usage:

diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index a8e8061e9..4dbd218fa 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -61,11 +61,12 @@ is "
xor". See there for a description of the function.

Usage:

    -
  • layer.and(other)
  • +
  • layer.and(other [, prop_constraint ])

The method computes a boolean AND between self and other. -It is an alias for the "&" operator. +It is an alias for the "&" operator which lacks the ability +to specify a properties constraint.

This method is available for polygon and edge layers. If the first operand is an edge layer and the second is a polygon layer, the @@ -92,6 +93,14 @@ polygons will be written to the output (labels: red, input2: blue): +

+When a properties constraint is given, the operation is performed +only between shapes with the given relation. Together with the +ability to provide net-annotated shapes through the nets method, this +allows constraining the boolean operation to shapes from the same or +from different nets. +

+See prop_eq, prop_ne and prop_copy for details.

"andnot" - Computes Boolean AND and NOT results at the same time

@@ -343,7 +352,7 @@ of the layer's data.

Usage:

    -
  • layer.drc(expression)
  • +
  • layer.drc(expression [, prop_constraint ])

This method implements the universal DRC which offers enhanced abilities, @@ -710,6 +719,24 @@ Note that the first line prepares the operation, but does not execute the area c or the boolean operation. But when the "drc" function executes the loop over the primaries it will only compute the area once per primary as it is represented by the same Ruby object.

+

Properties constraints

+

+The method can be given a properties constraint so that it is only performed +between shapes with the same or different user properties. Note that properties +have to be enabled or generated (e.g. through the DRCLayer#nets method) before they can +be used. +

+Example: +

+

+connect(metal1, via1)
+... 
+
+space_not_connected = metal1.nets.drc(space < 0.4.um, props_ne)
+
+

+See props_eq, props_ne and props_copy for details. +

Outlook

DRC expressions are quite rich and powerful. They provide a more intuitive way of @@ -1639,6 +1666,32 @@ i.e. before computing the length, the edges are merged unless raw mode is chosen (see raw). Hence in clean mode (see clean), overlapping edges are not counted twice.

+

"map_props" - Selects properties with certain keys and allows key mapping

+ +

Usage:

+
    +
  • layer.map_props({ key => key_new, .. })
  • +
+

+Similar to select_props, this method will map or filter properties and +and take the values from certain keys. In addition, this method allows +mapping keys to new keys. Specify a hash argument with old to new keys. +

+Property values with keys not listed in the hash are removed. +

+Note that this method returns a new layer with the new properties. The +original layer will not be touched. +

+For example to map key 2 to 1 (integer name keys) and ignore other keys, +use: +

+

+layer1 = input(1, 0, enable_properties)
+layer1_mapped = layer1.map_props({ 2 => 1 })
+
+

+See also select_props and remove_props. +

"merge" - Merges the layer (modifies the layer)

Usage:

@@ -1740,6 +1793,53 @@ The following images shows the effect of the "moved" method:

+

"nets" - Pulls net shapes from selected or all nets, optionally annotating nets with properties

+ +

Usage:

+
    +
  • layer.nets
  • +
  • layer.nets(net_filter)
  • +
  • layer.nets(circuit_filter, net_filter)
  • +
  • layer.nets(netter, ...)
  • +
  • layer.nets(prop(key), ...)
  • +
  • layer.nets(prop(key), ...)
  • +
+

+This method needs a layer that has been used in a connect statement. +It will take the shapes corresponding to this layer for all or selected nets +and attach the net identity in form of a user property. +

+This way, the resulting shapes can be used in property-constrained boolean operations +or DRC checks to implement operations in connected or non-connected mode. +

+A glob-style name pattern can be supplied to filter nets. Nets are always +complete - subnets from subcircuits are not selected. The net name is taken from +the net's home circuit (to topmost location where all net connections are formed). +You can specify a circuit filter to select nets from certain circuits only or +give a Circuit object explicitly. +

+

+connect(metal1, via1)
+connect(via1, metal2)
+
+metal1_all_nets = metal1.nets
+metal1_vdd      = metal1.nets("VDD")
+metal1_vdd      = metal1.nets("TOPLEVEL", "VDD")
+
+

+By default, the property key used for the net identity is numerical 0 (integer). You +can change the key by giving a property key with the "prop" qualifier. Using "nil" for the key +will disable properties: +

+

+metal1_vdd = metal1.nets("VDD", prop(1))
+# disables properties:
+metal1_vdd = metal1.nets("VDD", prop(nil))
+
+

+If a custom netter object has been used for the construction of the +connectivity, pass it to the "nets" method among the other arguments. +

"non_rectangles" - Selects all polygons from the input which are not rectangles

Usage:

@@ -1785,7 +1885,7 @@ This feature has been introduced in version 0.23.2.

Usage:

    -
  • layer.not(other)
  • +
  • layer.not(other [, prop_constraint ])

The method computes a boolean NOT between self and other. @@ -1816,6 +1916,14 @@ written to the output (labels: red, input2: blue): +

+When a properties constraint is given, the operation is performed +only between shapes with the given relation. Together with the +ability to provide net-annotated shapes through the nets method, this +allows constraining the boolean operation to shapes from the same or +from different nets. +

+See prop_eq, prop_ne and prop_copy for details.

"not_covering" - Selects shapes or regions of self which do not cover (enclose) one or more shapes from the other region

@@ -2390,6 +2498,19 @@ This method is available for polygon layers. By default "merged" semantics appli i.e. all polygons are merged before rectilinear polygons are selected (see
clean and raw). non_rectilinear will select all non-rectangles.

+

"remove_props" - Returns a new layer with all properties removed

+ +

Usage:

+
    +
  • layer.remove_props
  • +
+

+This method will drop all user properties from the layer. +Note that a new layer without properties is returned. The +original layer stays untouched. +

+See also select_props and map_props. +

"rotate" - Rotates a layer (modifies the layer)

Usage:

@@ -2672,6 +2793,42 @@ is
overlapping.

This method is available for polygons only.

+

"select_props" - Enables or selects properties from a property-annotated layer

+ +

Usage:

+
    +
  • layer.select_props(keys)
  • +
+

+This method will select specific property keys +from layers. It returns a new layer with the new properties. The +original layer is not modified. +

+You can specify the user property keys (names) to use. As user properties +in general are a set of key/value pairs and may carry multiple values +under different keys, this feature can be handy to filter out a specific +aspect. To get only the values from key 1 (integer), use: +

+

+layer1 = input(1, 0, enable_properties)
+layer1_filtered = layer1.select_props(1)
+
+

+To get the combined key 1 and 2 properties, use: +

+

+layer1 = input(1, 0, enable_properties)
+layer1_filtered = layer1.select_props(1, 2)
+
+

+Without any arguments, this method will remove all properties. +Note that you can directly filter or map properties on input +which is more efficient than first loading all and then selecting some +properties. See DRCSource#input for details. +

+map_props is a way to change property keys and remove_props +will entirely remove all user properties. +

"sep" - An alias for "separation"

Usage:

@@ -3230,6 +3387,8 @@ but is more intuitive, as "projecting" is written with a condition, like Double-bounded ranges are also available, like: "0.5 <= projecting < 2.0".
  • transparent : performs the check without shielding (polygon layers only)
  • shielded : performs the check with shielding (polygon layers only)
  • +
  • props_eq , props_ne , props_copy : (only props_copy applies to width check) - +see "Properties constraints" below.
  • Note that without the angle_limit, acute corners will always be reported, since two @@ -3292,6 +3451,44 @@ you try to check the distance between features of B vs. A, you cannot use shield because B features which are identical to A features will shield those entirely.

    Shielding is enabled by default, but can be switched off with the "transparent" option. +

    +

    Properties constraints (available on intra-polygon checks such as space, sep etc.)

    +

    +This feature is listed here, because this documentation is generic and used for other checks +as well. props_eq and props_ne are not available on 'width' or 'notch' as these apply to intra-polygon checks - when +pairs of different polygons are involved - something that 'width' or 'notch' does need. +

    +With properties constraints, the check is performed between shapes with the same +or different properties. "properties" refers to the full set of key/value pairs +attached to a shape. +

    +Property constraints are specified by adding props_eq or props_ne to the arguments. +If these literals are present, only shapes with same of different properties are +involved in the check. In connection with the net annotation feature this allows +checking space between connected or disconnected shapes for example: +

    +

    +connect(metal1, via1)
    +... 
    +
    +# attaches net identity as properties
    +metal1_nets = metal1.nets
    +
    +space_not_connected = metal1_nets.space(0.4.um, props_ne)
    +space_connected     = metal1_nets.space(0.4.um, props_eq)
    +
    +

    +props_copy is a special properties constraint that does not alter the behaviour of +the checks, but copies the primary shape's properties to the output markers. +This constraint is applicable to width and notch checks too. The effect is that +the original polygon's properties are copied to the error markers. +props_copy can be combined with props_eq and props_ne to copy the original +shape's properties to the output too: +

    +

    +space_not_connected = metal1_nets.space(0.4.um, props_ne + props_copy)
    +space_connected     = metal1_nets.space(0.4.um, props_eq + props_copy)
    +

    "with_angle" - Selects edges by their angle

    diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index b7947707b..74d119bcb 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -171,6 +171,7 @@ source.global_transform(shift(0, 100.um), rotate(90.0))
  • source.input(layer, datatype)
  • source.input(layer_into)
  • source.input(filter, ...)
  • +
  • source.input(props_spec, ...)
  • Creates a layer with the shapes from the given layer of the source. @@ -209,6 +210,21 @@ is required.

    "input" without any arguments will create a new, empty original layer.

    +If you want to use user properties - for example with properties constraints in DRC checks - +you need to enable properties on input: +

    +

    +input1_with_props = input(1, 0, enable_props)
    +
    +

    +You can also filter or map property keys, similar to the functions available on +layers (DRCLayer#map_props, DRCLayer#select_props). For example to select +property values with key 17 (numerical) only, use: +

    +

    +input1_with_props = input(1, 0, select_props(17))
    +
    +

    Use the global version of "input" without a source object to address the default source.

    "labels" - Gets the labels (texts) from an input layer

    diff --git a/src/doc/doc/manual/drc_runsets.xml b/src/doc/doc/manual/drc_runsets.xml index a8db5cb35..971bf9b79 100644 --- a/src/doc/doc/manual/drc_runsets.xml +++ b/src/doc/doc/manual/drc_runsets.xml @@ -6,6 +6,10 @@ DRC Runsets + + + + @@ -1137,5 +1141,205 @@ puts poly.is_deep? # -> false case this will lead to flattening of the layout and loss of hierarchy.

    +

    DRC and user properties

    + +

    + The DRC feature has some support for user properties. User properties are sets of + key/value pairs attached to shapes. This is a standard feature of KLayout and GDS/OASIS. + The GDS format supports numerical (positive integer) keys and string values while OASIS + supports more types of keys and values - specifically string keys. +

    + +

    + For DRC, the property set attached to a shape is regarded as a whole. + The DRC can act on these properties in specific ways: +

    + +
      +
    • Ignore properties: this is the default mode
    • +
    • Use properties: configure operations such as checks and some boolean operations to + only consider shapes with the same or different properties
    • +
    • Map/filter: change property keys/sets or remove them
    • +
    • Transfer properties: through operations, e.g. "size" or "with_..." selectors
    • +
    + +

    + Specifically, DRC functions can also generate properties. Currently there is + only the "nets" method which attaches net identity information to shapes + involved in a "connect" statement. + This feature opens a path to implementing "connected" or "unconnected" mode + boolean operations and DRC checks. +

    + +

    + As of this writing, user property support is somewhat experimental. + User properties support has a huge potential, so there is more to come. +

    + +

    + Currently, the following operations can be conditioned to act on shapes with same + or different properties: +

    + +
      +
    • Polygon boolean operations: "and", "not" and "andnot"
    • +
    • Polygon-mode DRC checks such as "separation", "space", "isolated" etc.
    • +
    + +

    + A variety of operations can transfer properties, i.e. edge-pair-to-polygon, edge-pair-to-edges, + polygon-to-edges, edge-to-polygon, some filters, the "size" function. It is planned + to enable most features to transfer properties where applicable. +

    + +

    + Property generation is supported currently by: +

    + +
      +
    • Reading properties from input layouts
    • +
    • Using the "nets" feature to generate net identity properties.
    • +
    + +

    + Property manipulation is supported in a very basic way: properties can be removed + entirely from a layer or certain property keys can be selected and optionally + mapped to a different key. + Property values cannot be manipulated currently. +

    + +

    + In general, once a layer has properties, shapes with different properties are + regarded as non-interacting. When shapes are merged, only groups of shapes + with the same properties are merged into bigger chunks. This applies to polygons + and edges. This can have the strange consequence that after merge, still + polygons may overlap. Note that this only applies to the case with properties. + The normal behavior is not changed. +

    + +

    Reading user properties

    + +

    + By default, user properties are not read into the shape containers. + You need to enable them explicitly: +

    + +
    l1 = input(1, 0, enable_props)
    + +

    + At this point you can select certain keys from the set of properties. For example + to select only values with key 17 and 18 (numerical), use: +

    + +
    l1 = input(1, 0, select_props(17, 18))
    + +

    + You can also select and map keys to other keys, like this: +

    + +
    l1 = input(1, 0, map_props({ 17 => 1, 18 => 18 }))
    + +

    + This will map values with key 17 to 1 and read those from 18 while + maintaining the key. Values with other keys are ignored. + See input function documentation + for more details. +

    + +

    Manipulating properties

    + +

    + Once you have a layer with properties, you can remove them: +

    + +
    layer_without_properties = layer.remove_props
    + +

    + You can also apply select_props or map_props to filter values + with certain keys or map keys: +

    + +
    reduced_layer = layer.select_props(17, 18)
    +reduced_layer = layer.map_props({ 17 => 1, 18 => 18 })
    + +

    Generating properties as net identities

    + +

    + The most important application is to use the nets method to + generate net identity properties: +

    + +
    connect(metal1, via1)
    +connect(via1, metal2)
    +
    +metal1_nets = metal1.nets
    + +

    + By default, a unique net identifier (a tuple of circuit and net name) is generated + on property key 0. You can specify the key as well: +

    + +
    metal1_nets = metal1.nets(prop(17))
    + +

    + The "nets" function has a number of options, specifically you can filter certain + nets (by name or circuit + name). This makes the "nets" function useful for other + purposes too. If you do not need properties then, specify "nil" as the property key: +

    + +
    metal1_vdd_net = metal1.nets(prop(nil), "VDD")
    + +

    Using properties in checks and boolean operations

    + +

    + The main purpose of properties is to use them in operations. + To confine a boolean operation to shapes with different properties, + use the props_ne keyword. To confine a boolean operation to shapes + with the same properties, use props_eq: +

    + +
    connect(metal1, via1)
    +connect(via1, metal2)
    +
    +metal1_nets = metal1.nets
    +metal2_nets = metal2.nets
    +
    +metal1_over_metal2_connected = metal1_nets.and(metal2_nets, props_eq)
    +metal1_over_metal2_unconnected = metal1_nets.and(metal2_nets, props_ne)
    + +

    + You can also instruct this operation to emit the original properties + on the output with props_copy: +

    + +
    result_with_props = metal1_nets.and(metal2_nets, props_eq + props_copy)
    + +

    + Similarly, properties can participate in checks: +

    + +
    connect(metal1, via1)
    +connect(via1, metal2)
    +
    +metal1_nets = metal1.nets
    +metal2_nets = metal2.nets
    +
    +metal1_space_connected = metal1_nets.space(0.4.um, props_eq)
    +metal1_space_unconnected = metal1_nets.space(1.um, props_ne)
    + +

    + "props_eq", "props_ne" and "props_copy" are also available on the + generic DRC function (drc), which opens new options, e.g. + detecting potential short locations ("critical area") between unconnected nets: +

    + +
    connect(metal1, via1)
    +connect(via1, metal2)
    +
    +metal1_nets = metal1.nets
    +metal2_nets = metal2.nets
    +
    +critical_area = l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_ne)
    + diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index e0c4f2e32..624f0d506 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -11,7 +11,7 @@ module DRC # %DRC% # @name drc # @brief Provides a generic DRC function for use with \DRC# expressions - # @synopsis layer.drc(expression) + # @synopsis layer.drc(expression [, prop_constraint ]) # # This method implements the universal DRC which offers enhanced abilities, # improved performance in some applications and better readability. @@ -378,6 +378,24 @@ module DRC # or the boolean operation. But when the "drc" function executes the loop over the primaries it will # only compute the area once per primary as it is represented by the same Ruby object. # + # @h3 Properties constraints @/h3 + # + # The method can be given a properties constraint so that it is only performed + # between shapes with the same or different user properties. Note that properties + # have to be enabled or generated (e.g. through the \DRCLayer#nets method) before they can + # be used. + # + # Example: + # + # @code + # connect(metal1, via1) + # ... + # + # space_not_connected = metal1.nets.drc(space < 0.4.um, props_ne) + # @/code + # + # See \global#props_eq, \global#props_ne and \global#props_copy for details. + # # @h3 Outlook @/h3 # # DRC expressions are quite rich and powerful. They provide a more intuitive way of @@ -386,10 +404,11 @@ module DRC # # More formal details about the bits and pieces can be found in the "\DRC" class documentation. - def drc(op) + def drc(op, prop_constraint = DRCPropertiesConstraint::new(RBA::Region::IgnoreProperties)) @engine._context("drc") do requires_region op.is_a?(DRCOpNode) || raise("A DRC expression is required for the argument (got #{op.inspect})") + prop_constraint.is_a?(DRCPropertiesConstraint) || raise("A properties constraint is required for the second argument (got #{prop_constraint.inspect})") node = op.create_node({}) result_cls = nil if node.result_type == RBA::CompoundRegionOperationNode::ResultType::Region @@ -400,7 +419,7 @@ module DRC result_cls = RBA::EdgePairs end if result_cls - DRCLayer::new(@engine, @engine._tcmd(self.data, node.distance, result_cls, :complex_op, node)) + DRCLayer::new(@engine, @engine._tcmd(self.data, node.distance, result_cls, :complex_op, node, prop_constraint.value)) end end end diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 0d645d351..bab1f6acd 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -241,6 +241,127 @@ module DRC DRCRectangleErrorFilter::new(RBA::Region::FourSidesAllowed) end + def prop(k) + self._context("prop") do + DRCPropertyName::new(k) + end + end + + # %DRC% + # @brief Specifies "same properties" for operations supporting user properties constraints + # @name props_eq + # + # Some operations such as boolean AND support properties constraints. By giving + # a "props_eq" constraint, the operation is performed only on shapes with the same + # properties, where "properties" stands for the full set of key/value pairs. + # + # Note that you have to enable properties explicitly or generate properties (e.g. + # with the \DRCLayer#nets method). + # + # Example: + # + # @code + # connect(metal1, via1) + # connect(via1, metal2) + # ... further connect statements + # + # m1m2_overlap_connected = metal1.nets.and(metal2, props_eq) + # @/code + # + # "props_eq" can be combined with \props_copy. In this case, properties + # are transferred to the output shapes and can be used in further processing: + # + # @code + # m1m2_overlap_connected = metal1.nets.and(metal2, props_eq + props_copy) + # @/code + # + # See also \props_ne. + + # %DRC% + # @brief Specifies "different properties" for operations supporting user properties constraints + # @name props_ne + # + # Some operations such as boolean AND support properties constraints. By giving + # a "props_ne" constraint, the operation is performed only on shapes with different + # properties, where "properties" stands for the full set of key/value pairs. + # + # Note that you have to enable properties explicitly or generate properties (e.g. + # with the \DRCLayer#nets method). + # + # Example: + # + # @code + # connect(metal1, via1) + # connect(via1, metal2) + # ... further connect statements + # + # m1m2_overlap_not_connected = metal1.nets.and(metal2, props_ne) + # @/code + # + # "props_ne" can be combined with \props_copy. In this case, properties + # are transferred to the output shapes and can be used in further processing: + # + # @code + # m1m2_overlap_connected = metal1.nets.and(metal2, props_ne + props_copy) + # @/code + # + # See also \props_eq. + + # %DRC% + # @brief Specifies "copy properties" on operations supporting user properties constraints + # @name props_copy + # + # This properties constraint does not constrain the operation, but instructs it to + # attach the properties from the primary input to the output objects. + # + # See also \props_ne and \props_eq. + + def props_eq + self._context("props_eq") do + DRCPropertiesConstraint::new(RBA::Region::SamePropertiesConstraintDrop) + end + end + + def props_ne + self._context("props_ne") do + DRCPropertiesConstraint::new(RBA::Region::DifferentPropertiesConstraintDrop) + end + end + + def props_copy + self._context("props_copy") do + DRCPropertiesConstraint::new(RBA::Region::NoPropertyConstraint) + end + end + + def negative + DRCNegative::new + end + + def enable_props + DRCPropertySelector::new(:enable_properties) + end + + def remove_props + DRCPropertySelector::new(:remove_properties) + end + + def select_props(*keys) + self._context("select_props") do + keys.each do |k| + k.is_a?(String) || k.is_a?(1.class) || raise("Key values need to be integers or strings (got '#{k.inspect}')") + end + DRCPropertySelector::new(:filter_properties, keys) + end + end + + def map_props(hash) + self._context("map_props") do + hash.is_a?(Hash) || raise("Argument needs to be a hash (got '#{hash.inspect}')") + DRCPropertySelector::new(:map_properties, hash) + end + end + def pattern(p) self._context("pattern") do DRCPattern::new(true, p) @@ -2596,6 +2717,10 @@ CODE @dss end + def _default_netter + @netter + end + def _netter @netter ||= DRC::DRCNetter::new(self) end @@ -2719,7 +2844,7 @@ CODE end end - def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, cls) + def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, prop_sel, cls) if layers.empty? && ! @deep @@ -2732,6 +2857,7 @@ CODE else iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers) end + prop_sel.each { |p| p.apply_to(iter) } iter.shape_flags = shape_flags iter.global_dtrans = global_trans @@ -2775,7 +2901,8 @@ CODE if cls == RBA::Region && clip && box # HACK: deep regions will always clip in the constructor, so skip this if ! @deep - r &= RBA::Region::new(box) + # NOTE: NoPropertyConstraint will copy the original properties + r.and_with(RBA::Region::new(box), RBA::Region::NoPropertyConstraint) end end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index db04d00fb..dd6297364 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1744,9 +1744,10 @@ CODE # %DRC% # @name and # @brief Boolean AND operation - # @synopsis layer.and(other) + # @synopsis layer.and(other [, prop_constraint ]) # The method computes a boolean AND between self and other. - # It is an alias for the "&" operator. + # It is an alias for the "&" operator which lacks the ability + # to specify a properties constraint. # # This method is available for polygon and edge layers. # If the first operand is an edge layer and the second is a polygon layer, the @@ -1773,17 +1774,19 @@ CODE # @td @img(/images/drc_textpoly1.png) @/td # @/tr # @/table - - def and(other) - @engine._context("and") do - self & other - end - end + # + # When a properties constraint is given, the operation is performed + # only between shapes with the given relation. Together with the + # ability to provide net-annotated shapes through the \nets method, this + # allows constraining the boolean operation to shapes from the same or + # from different nets. + # + # See \global#prop_eq, \global#prop_ne and \global#prop_copy for details. # %DRC% # @name not # @brief Boolean NOT operation - # @synopsis layer.not(other) + # @synopsis layer.not(other [, prop_constraint ]) # The method computes a boolean NOT between self and other. # It is an alias for the "-" operator. # @@ -1812,10 +1815,42 @@ CODE # @td @img(/images/drc_textpoly2.png) @/td # @/tr # @/table + # + # When a properties constraint is given, the operation is performed + # only between shapes with the given relation. Together with the + # ability to provide net-annotated shapes through the \nets method, this + # allows constraining the boolean operation to shapes from the same or + # from different nets. + # + # See \global#prop_eq, \global#prop_ne and \global#prop_copy for details. - def not(other) - @engine._context("not") do - self - other + def and(other, prop_constraint = nil) + if prop_constraint + @engine._context("and") do + prop_constraint.is_a?(DRCPropertiesConstraint) || raise("The properties constraint needs to be prop_eq, prop_ne or prop_copy") + # currently only available for regions + requires_region + check_is_layer(other) + other.requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :and, other.data, prop_constraint.value)) + end + else + self & other + end + end + + def not(other, prop_constraint = nil) + if prop_constraint + @engine._context("not") do + prop_constraint.is_a?(DRCPropertiesConstraint) || raise("The properties constraint needs to be prop_eq, prop_ne or prop_copy") + # currently only available for regions + requires_region + check_is_layer(other) + other.requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :not, other.data, prop_constraint.value)) + end + else + self - other end end @@ -3654,6 +3689,8 @@ CODE # Double-bounded ranges are also available, like: "0.5 <= projecting < 2.0". @/li # @li @b transparent @/b: performs the check without shielding (polygon layers only) @/li # @li @b shielded @/b: performs the check with shielding (polygon layers only) @/li + # @li @b props_eq @/b, @b props_ne @/b, @b props_copy @/b: (only props_copy applies to width check) - + # see "Properties constraints" below. @/li # @/ul # # Note that without the angle_limit, acute corners will always be reported, since two @@ -3716,6 +3753,45 @@ CODE # because B features which are identical to A features will shield those entirely. # # Shielding is enabled by default, but can be switched off with the "transparent" option. + # + # @h3 Properties constraints (available on intra-polygon checks such as \space, \sep etc.) @/h3 + # + # This feature is listed here, because this documentation is generic and used for other checks + # as well. \props_eq and \props_ne are not available on 'width' or 'notch' as these apply to intra-polygon checks - when + # pairs of different polygons are involved - something that 'width' or 'notch' does need. + # + # With properties constraints, the check is performed between shapes with the same + # or different properties. "properties" refers to the full set of key/value pairs + # attached to a shape. + # + # Property constraints are specified by adding \props_eq or \props_ne to the arguments. + # If these literals are present, only shapes with same of different properties are + # involved in the check. In connection with the net annotation feature this allows + # checking space between connected or disconnected shapes for example: + # + # @code + # connect(metal1, via1) + # ... + # + # # attaches net identity as properties + # metal1_nets = metal1.nets + # + # space_not_connected = metal1_nets.space(0.4.um, props_ne) + # space_connected = metal1_nets.space(0.4.um, props_eq) + # @/code + # + # \props_copy is a special properties constraint that does not alter the behaviour of + # the checks, but copies the primary shape's properties to the output markers. + # This constraint is applicable to \width and \notch checks too. The effect is that + # the original polygon's properties are copied to the error markers. + # \props_copy can be combined with \props_eq and \props_ne to copy the original + # shape's properties to the output too: + # + # @code + # space_not_connected = metal1_nets.space(0.4.um, props_ne + props_copy) + # space_connected = metal1_nets.space(0.4.um, props_eq + props_copy) + # @/code + # # %DRC% # @name space @@ -3754,7 +3830,6 @@ CODE # @td @img(/images/drc_space1.png) @/td # @/tr # @/table - # # %DRC% # @name isolated @@ -4042,8 +4117,10 @@ CODE whole_edges = false other = nil shielded = nil + negative = false opposite_filter = RBA::Region::NoOppositeFilter rect_filter = RBA::Region::NoRectFilter + prop_constraint = RBA::Region::IgnoreProperties n = 1 args.each do |a| @@ -4051,6 +4128,10 @@ CODE metrics = a.value elsif a.is_a?(DRCWholeEdges) whole_edges = a.value + elsif a.is_a?(DRCNegative) + negative = true + elsif a.is_a?(DRCPropertiesConstraint) + prop_constraint = a.value elsif a.is_a?(DRCOppositeErrorFilter) opposite_filter = a.value elsif a.is_a?(DRCRectangleErrorFilter) @@ -4083,11 +4164,19 @@ CODE if :#{f} != :width && :#{f} != :notch args << opposite_filter args << rect_filter - elsif opposite_filter != RBA::Region::NoOppositeFilter - raise("An opposite error filter cannot be used with this check") - elsif rect_filter != RBA::Region::NoRectFilter - raise("A rectangle error filter cannot be used with this check") + else + if opposite_filter != RBA::Region::NoOppositeFilter + raise("An opposite error filter cannot be used with this check") + elsif rect_filter != RBA::Region::NoRectFilter + raise("A rectangle error filter cannot be used with this check") + elsif prop_constraint != RBA::Region::IgnoreProperties && prop_constraint != RBA::Region::NoPropertyConstraint + raise("A specific properties constraint cannot be used with this check (only 'props_copy' can be used)") + end end + args << negative + args << prop_constraint + elsif negative + raise("Negative output can only be used for polygon layers") elsif shielded != nil raise("Shielding can only be used for polygon layers") elsif opposite_filter != RBA::Region::NoOppositeFilter @@ -4699,6 +4788,208 @@ CODE end end + # %DRC% + # @name select_props + # @brief Enables or selects properties from a property-annotated layer + # @synopsis layer.select_props(keys) + # + # This method will select specific property keys + # from layers. It returns a new layer with the new properties. The + # original layer is not modified. + # + # You can specify the user property keys (names) to use. As user properties + # in general are a set of key/value pairs and may carry multiple values + # under different keys, this feature can be handy to filter out a specific + # aspect. To get only the values from key 1 (integer), use: + # + # @code + # layer1 = input(1, 0, enable_properties) + # layer1_filtered = layer1.select_props(1) + # @/code + # + # To get the combined key 1 and 2 properties, use: + # + # @code + # layer1 = input(1, 0, enable_properties) + # layer1_filtered = layer1.select_props(1, 2) + # @/code + # + # Without any arguments, this method will remove all properties. + # Note that you can directly filter or map properties on input + # which is more efficient than first loading all and then selecting some + # properties. See \DRCSource#input for details. + # + # \map_props is a way to change property keys and \remove_props + # will entirely remove all user properties. + + # %DRC% + # @name map_props + # @brief Selects properties with certain keys and allows key mapping + # @synopsis layer.map_props({ key => key_new, .. }) + # + # Similar to \select_props, this method will map or filter properties and + # and take the values from certain keys. In addition, this method allows + # mapping keys to new keys. Specify a hash argument with old to new keys. + # + # Property values with keys not listed in the hash are removed. + # + # Note that this method returns a new layer with the new properties. The + # original layer will not be touched. + # + # For example to map key 2 to 1 (integer name keys) and ignore other keys, + # use: + # + # @code + # layer1 = input(1, 0, enable_properties) + # layer1_mapped = layer1.map_props({ 2 => 1 }) + # @/code + # + # See also \select_props and \remove_props. + + # %DRC% + # @name remove_props + # @brief Returns a new layer with all properties removed + # @synopsis layer.remove_props + # + # This method will drop all user properties from the layer. + # Note that a new layer without properties is returned. The + # original layer stays untouched. + # + # See also \select_props and \map_props. + + def select_props(*args) + @engine._context("select_props") do + keys = args.flatten + if keys.empty? + DRC::DRCLayer::new(@engine, self.data.dup.enable_properties) + else + DRC::DRCLayer::new(@engine, self.data.dup.filter_properties(keys)) + end + end + end + + def remove_props + @engine._context("remove_props") do + DRC::DRCLayer::new(@engine, self.data.dup.remove_properties) + end + end + + def map_props(arg) + @engine._context("map_props") do + arg.is_a?(Hash) || raise("Argument of 'map_props' needs to be a mapping hash") + DRC::DRCLayer::new(@engine, self.data.dup.map_properties(arg)) + end + end + + # %DRC% + # @name nets + # @brief Pulls net shapes from selected or all nets, optionally annotating nets with properties + # @synopsis layer.nets + # @synopsis layer.nets(net_filter) + # @synopsis layer.nets(circuit_filter, net_filter) + # @synopsis layer.nets(netter, ...) + # @synopsis layer.nets(prop(key), ...) + # @synopsis layer.nets(prop(key), ...) + # + # This method needs a layer that has been used in a connect statement. + # It will take the shapes corresponding to this layer for all or selected nets + # and attach the net identity in form of a user property. + # + # This way, the resulting shapes can be used in property-constrained boolean operations + # or DRC checks to implement operations in connected or non-connected mode. + # + # A glob-style name pattern can be supplied to filter nets. Nets are always + # complete - subnets from subcircuits are not selected. The net name is taken from + # the net's home circuit (to topmost location where all net connections are formed). + # You can specify a circuit filter to select nets from certain circuits only or + # give a RBA::Circuit object explicitly. + # + # @code + # connect(metal1, via1) + # connect(via1, metal2) + # + # metal1_all_nets = metal1.nets + # metal1_vdd = metal1.nets("VDD") + # metal1_vdd = metal1.nets("TOPLEVEL", "VDD") + # @/code + # + # By default, the property key used for the net identity is numerical 0 (integer). You + # can change the key by giving a property key with the "prop" qualifier. Using "nil" for the key + # will disable properties: + # + # @code + # metal1_vdd = metal1.nets("VDD", prop(1)) + # # disables properties: + # metal1_vdd = metal1.nets("VDD", prop(nil)) + # @/code + # + # If a custom netter object has been used for the construction of the + # connectivity, pass it to the "nets" method among the other arguments. + + def nets(*args) + + @engine._context("nets") do + + # parse arguments + filters = nil + circuits = nil + prop_id = 0 + netter = nil + args.each do |a| + if a.is_a?(String) + filters ||= [] + filters << a + elsif a.is_a?(1.class) + prop_id = a + elsif a.is_a?(RBA::Circuit) + circuits ||= [] + circuits << a + elsif a.is_a?(DRCPropertyName) + prop_id = a.value + elsif a.is_a?(DRCNetter) + netter = a + else + raise("Invalid argument type for #{a.inspect}") + end + end + + # get netter and netlist + netter ||= @engine._default_netter + if ! netter + raise("No netlist extractor available - did you forget 'connect' statements?") + end + netlist = netter.netlist + if ! netlist + raise("No netlist available - extraction failed?") + end + + # create list of nets + circuit_filter = nil + if filters && filters.size > 1 + circuit_filter = filters.shift + end + if circuit_filter + circuits ||= [] + circuits += netlist.circuits_by_name(circuit_filter) + end + nets = nil + if !circuits + if filters + nets = filters.collect { |f| netlist.nets_by_name(f) }.flatten + end + else + nets = circuits.collect do |circuit| + (filters || ["*"]).collect { |f| circuit.nets_by_name(f) }.flatten + end.flatten + end + + # pulls the net shapes + DRCLayer::new(@engine, @engine._cmd(self.data, :nets, netter._l2n_data, prop_id, nets)) + + end + + end + # %DRC% # @name output # @brief Outputs the content of the layer diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb index 2e969e463..ba1b8a457 100644 --- a/src/drc/drc/built-in-macros/_drc_source.rb +++ b/src/drc/drc/built-in-macros/_drc_source.rb @@ -351,7 +351,7 @@ CODE @layout_var.cells(cell_filter).each do |cell| cell.shapes(tmp).insert(cell.bbox) end - layer = DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, [tmp], @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region)) + layer = DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, [tmp], @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, [], RBA::Region)) else layer = input layer.insert((RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu).transformed(@global_trans)) @@ -374,6 +374,7 @@ CODE # @synopsis source.input(layer, datatype) # @synopsis source.input(layer_into) # @synopsis source.input(filter, ...) + # @synopsis source.input(props_spec, ...) # Creates a layer with the shapes from the given layer of the source. # The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo # object or by a sequence of filters. @@ -410,12 +411,27 @@ CODE # # "input" without any arguments will create a new, empty original layer. # + # If you want to use user properties - for example with properties constraints in DRC checks - + # you need to enable properties on input: + # + # @code + # input1_with_props = input(1, 0, enable_props) + # @/code + # + # You can also filter or map property keys, similar to the functions available on + # layers (\DRCLayer#map_props, \DRCLayer#select_props). For example to select + # property values with key 17 (numerical) only, use: + # + # @code + # input1_with_props = input(1, 0, select_props(17)) + # @/code + # # Use the global version of "input" without a source object to address the default source. def input(*args) @engine._context("input") do - layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region)) + layers, prop_selectors = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, prop_selectors, RBA::Region)) end end @@ -441,8 +457,8 @@ CODE def labels(*args) @engine._context("labels") do - layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, RBA::Texts)) + layers, prop_selectors = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, prop_selectors, RBA::Texts)) end end @@ -467,8 +483,8 @@ CODE def polygons(*args) @engine._context("polygons") do - layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, RBA::Region)) + layers, prop_selectors = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::Region)) end end @@ -496,8 +512,8 @@ CODE def edges(*args) @engine._context("edges") do - layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, RBA::Edges)) + layers, prop_selectors = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, prop_selectors, RBA::Edges)) end end @@ -525,8 +541,8 @@ CODE def edge_pairs(*args) @engine._context("edge_pairs") do - layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, RBA::EdgePairs)) + layers, prop_selectors = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::EdgePairs)) end end @@ -539,7 +555,8 @@ CODE def make_layer layers = [] - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region)) + prop_selectors = [] + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, prop_selectors, RBA::Region)) end # %DRC% @@ -581,7 +598,10 @@ CODE def parse_input_layers(*args) layers = [] - + prop_selectors = args.select { |a| a.is_a?(DRCPropertySelector) } + + args = args.select { |a| !a.is_a?(DRCPropertySelector) } + if args.size == 0 li = @layout.insert_layer(RBA::LayerInfo::new) @@ -615,7 +635,7 @@ CODE end - layers + [ layers, prop_selectors ] end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 677d45852..495f458d1 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -134,6 +134,67 @@ module DRC self.pattern = p end end + + # A wrapper for a property name + # Use "prop(key)" to generate this tag. + class DRCPropertyName + attr_accessor :value + def initialize(k) + self.value = k + end + end + + # A wrapper for a property constraint + # Use "props_eq", "props_ne" or "props_copy" to generate this tag. + class DRCPropertiesConstraint + attr_accessor :value + def initialize(v) + self.value = v + end + def is_eq? + self.value == RBA::Region::SamePropertiesConstraint || self.value == RBA::Region::SamePropertiesConstraintDrop + end + def is_ne? + self.value == RBA::Region::DifferentPropertiesConstraint || self.value == RBA::Region::DifferentPropertiesConstraintDrop + end + def is_copy? + self.value == RBA::Region::NoPropertyConstraint || self.value == RBA::Region::SamePropertiesConstraint || self.value == RBA::Region::DifferentPropertiesConstraint + end + def +(other) + other.is_a?(DRCPropertiesConstraint) || raise("'+' needs to be applied to two properties constraints (got #{other.inspect} for the second one)") + is_eq = self.is_eq? || other.is_eq? + is_ne = self.is_ne? || other.is_ne? + is_copy = self.is_copy? || other.is_copy? + if is_eq == is_ne + DRCPropertiesConstraint::new(is_copy ? RBA::Region::NoPropertyConstraint : RBA::Region::IgnoreProperties) + elsif is_eq + DRCPropertiesConstraint::new(is_copy ? RBA::Region::SamePropertiesConstraint : RBA::Region::SamePropertiesConstraintDrop) + elsif is_ne + DRCPropertiesConstraint::new(is_copy ? RBA::Region::DifferentPropertiesConstraint : RBA::Region::DifferentPropertiesConstraintDrop) + else + nil + end + end + end + + # Negative output on checks + class DRCNegative + def initialize + end + end + + # Property selector for "input" + class DRCPropertySelector + attr_accessor :method + attr_accessor :args + def apply_to(iter) + iter.send(self.method, *self.args) + end + def initialize(method, *args) + self.method = method + self.args = args + end + end # A wrapper for a pair of limit values # This class is used to identify projection limits for DRC diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 88a1f4259..107aa58de 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1396,3 +1396,13 @@ TEST(60d_issue1216) { run_test (_this, "60", true); } + +TEST(70_props) +{ + run_test (_this, "70", false); +} + +TEST(70d_props) +{ + run_test (_this, "70", true); +} diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 2da6b200d..e124d01b0 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -294,7 +294,8 @@ LayoutViewBase::copy_from (lay::LayoutViewBase *source) end_layer_updates (); if (! m_layer_properties_lists.empty ()) { - mp_canvas->set_dither_pattern (m_layer_properties_lists [0]->dither_pattern ()); + mp_canvas->set_dither_pattern (m_layer_properties_lists [0]->dither_pattern ()); + mp_canvas->set_line_styles (m_layer_properties_lists [0]->line_styles ()); } // copy the title diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc index 23e8cdf7d..b29cdc693 100644 --- a/src/layui/layui/layLayoutViewFunctions.cc +++ b/src/layui/layui/layLayoutViewFunctions.cc @@ -1646,7 +1646,7 @@ LayoutViewFunctions::cm_copy_layer () if (! same_layout) { // flat mode (different layouts) - db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + db::PropertyMapper pm (&view ()->cellview (m_copy_cvr)->layout (), &view ()->cellview (m_copy_cva)->layout ()); for (db::RecursiveShapeIterator si (view ()->cellview (m_copy_cva)->layout (), *view ()->cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { target_cell.shapes (m_copy_layerr).insert (*si, si.trans (), pm); } @@ -1693,7 +1693,7 @@ LayoutViewFunctions::cm_copy_layer () } else if (! same_layout) { // current cell only mode (different layouts) - db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + db::PropertyMapper pm (&view ()->cellview (m_copy_cvr)->layout (), &view ()->cellview (m_copy_cva)->layout ()); for (db::Shapes::shape_iterator si = view ()->cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { target_cell.shapes (m_copy_layerr).insert (*si, pm); } diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index 247dd8034..faf4d4000 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -1781,8 +1781,9 @@ NetlistBrowserPage::export_nets (const std::vector *nets) database->build_nets (nets, cm, target_layout, lm, dialog->net_prefix ().empty () ? 0 : dialog->net_prefix ().c_str (), + db::NPM_AllProperties, dialog->net_propname (), - dialog->produce_circuit_cells () ? db::LayoutToNetlist::BNH_SubcircuitCells : db::LayoutToNetlist::BNH_Flatten, + dialog->produce_circuit_cells () ? db::BNH_SubcircuitCells : db::BNH_Flatten, dialog->produce_circuit_cells () ? dialog->circuit_cell_prefix ().c_str () : 0, dialog->produce_device_cells () ? dialog->device_cell_prefix ().c_str () : 0); diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc index 13ee04df9..84738ddd8 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc @@ -829,9 +829,7 @@ NetTracerLayerExpression::make_l2n_region (db::LayoutToNetlist &l2n, std::map get (); } - if (! name.empty ()) { - l2n.register_layer (*res, name); - } + l2n.register_layer (*res, name); return tl::shared_ptr (new NetTracerLayerExpression::RegionHolder (res.release ())); } diff --git a/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc b/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc index 93346fdbc..852560683 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbNetTracerTests.cc @@ -340,7 +340,7 @@ TEST(6) tc.add (connection ("1-10", "2", "3")); tc.add (connection ("3", "4", "5")); - run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (-2250, -900), file_au, "IN_B"); + run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (-2250, -900), file_au, "A"); } TEST(6b) diff --git a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc index b9566e183..cf894f538 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc @@ -78,14 +78,14 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer db::Cell &top_cell = layout_nets.cell (layout_nets.add_cell ("NETS")); db::CellMapping cm = l2ndb.cell_mapping_into (layout_nets, top_cell); - l2ndb.build_all_nets (cm, layout_nets, l2ndb.create_layermap (layout_nets, 1000), "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0); + l2ndb.build_all_nets (cm, layout_nets, l2ndb.create_layermap (layout_nets, 1000), "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", 0); std::string fn (tl::testdata ()); fn += "/net_tracer/"; fn += file_au; CHECKPOINT (); - db::compare_layouts (_this, layout_nets, fn, db::WriteOAS); + db::compare_layouts (_this, layout_nets, fn, db::NormalizationMode (db::WriteOAS | db::AsPolygons)); } TEST(1) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 82e4fe9af..ba970f42f 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -661,7 +661,7 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double { // try to establish a default color from the region's origin if required const db::RecursiveShapeIterator *iter = 0; - const db::OriginalLayerRegion *original = dynamic_cast (data.delegate ()); + const db::OriginalLayerRegion *original = dynamic_cast (data.delegate ()); if (original) { iter = original->iter (); } @@ -677,7 +677,7 @@ D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double z { // try to establish a default color from the region's origin if required const db::RecursiveShapeIterator *iter = 0; - const db::OriginalLayerEdges *original = dynamic_cast (data.delegate ()); + const db::OriginalLayerEdges *original = dynamic_cast (data.delegate ()); if (original) { iter = original->iter (); } @@ -693,7 +693,7 @@ D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, doub { // try to establish a default color from the region's origin if required const db::RecursiveShapeIterator *iter = 0; - const db::OriginalLayerEdgePairs *original = dynamic_cast (data.delegate ()); + const db::OriginalLayerEdgePairs *original = dynamic_cast (data.delegate ()); if (original) { iter = original->iter (); } diff --git a/testdata/algo/deep_region_40.gds b/testdata/algo/deep_region_40.gds new file mode 100644 index 000000000..61140c05f Binary files /dev/null and b/testdata/algo/deep_region_40.gds differ diff --git a/testdata/algo/deep_region_42.gds b/testdata/algo/deep_region_42.gds new file mode 100644 index 000000000..07a1e2ed7 Binary files /dev/null and b/testdata/algo/deep_region_42.gds differ diff --git a/testdata/algo/deep_region_au19.gds b/testdata/algo/deep_region_au19.gds index 2fcc1e133..5a7e3eec8 100644 Binary files a/testdata/algo/deep_region_au19.gds and b/testdata/algo/deep_region_au19.gds differ diff --git a/testdata/algo/deep_region_au40.gds b/testdata/algo/deep_region_au40.gds new file mode 100644 index 000000000..84d6d4969 Binary files /dev/null and b/testdata/algo/deep_region_au40.gds differ diff --git a/testdata/algo/deep_region_au41.gds b/testdata/algo/deep_region_au41.gds new file mode 100644 index 000000000..7e84f976d Binary files /dev/null and b/testdata/algo/deep_region_au41.gds differ diff --git a/testdata/algo/deep_region_au42.gds b/testdata/algo/deep_region_au42.gds new file mode 100644 index 000000000..7a633c745 Binary files /dev/null and b/testdata/algo/deep_region_au42.gds differ diff --git a/testdata/algo/deep_region_au43.gds b/testdata/algo/deep_region_au43.gds new file mode 100644 index 000000000..857661a5b Binary files /dev/null and b/testdata/algo/deep_region_au43.gds differ diff --git a/testdata/algo/deep_region_au44.gds b/testdata/algo/deep_region_au44.gds new file mode 100644 index 000000000..7d8f7ed62 Binary files /dev/null and b/testdata/algo/deep_region_au44.gds differ diff --git a/testdata/algo/deep_region_au45.gds b/testdata/algo/deep_region_au45.gds new file mode 100644 index 000000000..659325c02 Binary files /dev/null and b/testdata/algo/deep_region_au45.gds differ diff --git a/testdata/algo/flat_region_au1.gds b/testdata/algo/flat_region_au1.gds new file mode 100644 index 000000000..901038c3a Binary files /dev/null and b/testdata/algo/flat_region_au1.gds differ diff --git a/testdata/algo/flat_region_au10.gds b/testdata/algo/flat_region_au10.gds new file mode 100644 index 000000000..df975aa83 Binary files /dev/null and b/testdata/algo/flat_region_au10.gds differ diff --git a/testdata/algo/flat_region_au11.gds b/testdata/algo/flat_region_au11.gds new file mode 100644 index 000000000..ebe4a8f6e Binary files /dev/null and b/testdata/algo/flat_region_au11.gds differ diff --git a/testdata/algo/flat_region_au12.gds b/testdata/algo/flat_region_au12.gds new file mode 100644 index 000000000..38fa85056 Binary files /dev/null and b/testdata/algo/flat_region_au12.gds differ diff --git a/testdata/algo/flat_region_au13.gds b/testdata/algo/flat_region_au13.gds new file mode 100644 index 000000000..0fc690ca9 Binary files /dev/null and b/testdata/algo/flat_region_au13.gds differ diff --git a/testdata/algo/flat_region_au13b.gds b/testdata/algo/flat_region_au13b.gds new file mode 100644 index 000000000..8a1030982 Binary files /dev/null and b/testdata/algo/flat_region_au13b.gds differ diff --git a/testdata/algo/flat_region_au14a.gds b/testdata/algo/flat_region_au14a.gds new file mode 100644 index 000000000..7c054c06a Binary files /dev/null and b/testdata/algo/flat_region_au14a.gds differ diff --git a/testdata/algo/flat_region_au14b.gds b/testdata/algo/flat_region_au14b.gds new file mode 100644 index 000000000..1c712e0f5 Binary files /dev/null and b/testdata/algo/flat_region_au14b.gds differ diff --git a/testdata/algo/flat_region_au15a.gds b/testdata/algo/flat_region_au15a.gds new file mode 100644 index 000000000..1ac9891c0 Binary files /dev/null and b/testdata/algo/flat_region_au15a.gds differ diff --git a/testdata/algo/flat_region_au15b.gds b/testdata/algo/flat_region_au15b.gds new file mode 100644 index 000000000..c8ab962d2 Binary files /dev/null and b/testdata/algo/flat_region_au15b.gds differ diff --git a/testdata/algo/flat_region_au16.gds b/testdata/algo/flat_region_au16.gds new file mode 100644 index 000000000..683b753c9 Binary files /dev/null and b/testdata/algo/flat_region_au16.gds differ diff --git a/testdata/algo/flat_region_au17.gds b/testdata/algo/flat_region_au17.gds new file mode 100644 index 000000000..ef1f6d3ab Binary files /dev/null and b/testdata/algo/flat_region_au17.gds differ diff --git a/testdata/algo/flat_region_au18.gds b/testdata/algo/flat_region_au18.gds new file mode 100644 index 000000000..8c7800243 Binary files /dev/null and b/testdata/algo/flat_region_au18.gds differ diff --git a/testdata/algo/flat_region_au19.gds b/testdata/algo/flat_region_au19.gds new file mode 100644 index 000000000..4edc940ea Binary files /dev/null and b/testdata/algo/flat_region_au19.gds differ diff --git a/testdata/algo/flat_region_au2.gds b/testdata/algo/flat_region_au2.gds new file mode 100644 index 000000000..3d16c088c Binary files /dev/null and b/testdata/algo/flat_region_au2.gds differ diff --git a/testdata/algo/flat_region_au20.gds b/testdata/algo/flat_region_au20.gds new file mode 100644 index 000000000..88e0eadf5 Binary files /dev/null and b/testdata/algo/flat_region_au20.gds differ diff --git a/testdata/algo/flat_region_au21.gds b/testdata/algo/flat_region_au21.gds new file mode 100644 index 000000000..61c1aa54d Binary files /dev/null and b/testdata/algo/flat_region_au21.gds differ diff --git a/testdata/algo/flat_region_au22.gds b/testdata/algo/flat_region_au22.gds new file mode 100644 index 000000000..539fea98e Binary files /dev/null and b/testdata/algo/flat_region_au22.gds differ diff --git a/testdata/algo/flat_region_au27.gds b/testdata/algo/flat_region_au27.gds new file mode 100644 index 000000000..f4550fe05 Binary files /dev/null and b/testdata/algo/flat_region_au27.gds differ diff --git a/testdata/algo/flat_region_au28.gds b/testdata/algo/flat_region_au28.gds new file mode 100644 index 000000000..e0d20453b Binary files /dev/null and b/testdata/algo/flat_region_au28.gds differ diff --git a/testdata/algo/flat_region_au29.gds b/testdata/algo/flat_region_au29.gds new file mode 100644 index 000000000..049fba6e7 Binary files /dev/null and b/testdata/algo/flat_region_au29.gds differ diff --git a/testdata/algo/flat_region_au3.gds b/testdata/algo/flat_region_au3.gds new file mode 100644 index 000000000..74fba6e4f Binary files /dev/null and b/testdata/algo/flat_region_au3.gds differ diff --git a/testdata/algo/flat_region_au31.gds b/testdata/algo/flat_region_au31.gds new file mode 100644 index 000000000..efabd4c28 Binary files /dev/null and b/testdata/algo/flat_region_au31.gds differ diff --git a/testdata/algo/flat_region_au3b.gds b/testdata/algo/flat_region_au3b.gds new file mode 100644 index 000000000..b6040526f Binary files /dev/null and b/testdata/algo/flat_region_au3b.gds differ diff --git a/testdata/algo/flat_region_au40.gds b/testdata/algo/flat_region_au40.gds new file mode 100644 index 000000000..889f3249a Binary files /dev/null and b/testdata/algo/flat_region_au40.gds differ diff --git a/testdata/algo/flat_region_au41.gds b/testdata/algo/flat_region_au41.gds new file mode 100644 index 000000000..848b02bbf Binary files /dev/null and b/testdata/algo/flat_region_au41.gds differ diff --git a/testdata/algo/flat_region_au42.gds b/testdata/algo/flat_region_au42.gds new file mode 100644 index 000000000..2d72e0839 Binary files /dev/null and b/testdata/algo/flat_region_au42.gds differ diff --git a/testdata/algo/flat_region_au43.gds b/testdata/algo/flat_region_au43.gds new file mode 100644 index 000000000..5295c4d6a Binary files /dev/null and b/testdata/algo/flat_region_au43.gds differ diff --git a/testdata/algo/flat_region_au44.gds b/testdata/algo/flat_region_au44.gds new file mode 100644 index 000000000..2484aecb1 Binary files /dev/null and b/testdata/algo/flat_region_au44.gds differ diff --git a/testdata/algo/flat_region_au4a.gds b/testdata/algo/flat_region_au4a.gds new file mode 100644 index 000000000..436ba9999 Binary files /dev/null and b/testdata/algo/flat_region_au4a.gds differ diff --git a/testdata/algo/flat_region_au4b.gds b/testdata/algo/flat_region_au4b.gds new file mode 100644 index 000000000..84f875778 Binary files /dev/null and b/testdata/algo/flat_region_au4b.gds differ diff --git a/testdata/algo/flat_region_au5.gds b/testdata/algo/flat_region_au5.gds new file mode 100644 index 000000000..3bcd0e308 Binary files /dev/null and b/testdata/algo/flat_region_au5.gds differ diff --git a/testdata/algo/flat_region_au7.gds b/testdata/algo/flat_region_au7.gds new file mode 100644 index 000000000..12f5c63ea Binary files /dev/null and b/testdata/algo/flat_region_au7.gds differ diff --git a/testdata/algo/flat_region_au9a.gds b/testdata/algo/flat_region_au9a.gds new file mode 100644 index 000000000..72b317c86 Binary files /dev/null and b/testdata/algo/flat_region_au9a.gds differ diff --git a/testdata/algo/net_proc_au1.gds b/testdata/algo/net_proc_au1.gds new file mode 100644 index 000000000..79ac941b5 Binary files /dev/null and b/testdata/algo/net_proc_au1.gds differ diff --git a/testdata/algo/net_proc_au2.gds b/testdata/algo/net_proc_au2.gds new file mode 100644 index 000000000..694462a29 Binary files /dev/null and b/testdata/algo/net_proc_au2.gds differ diff --git a/testdata/algo/net_proc_au3.gds b/testdata/algo/net_proc_au3.gds new file mode 100644 index 000000000..17920b65b Binary files /dev/null and b/testdata/algo/net_proc_au3.gds differ diff --git a/testdata/algo/nets_proc_1.gds b/testdata/algo/nets_proc_1.gds new file mode 100644 index 000000000..e1b911ddb Binary files /dev/null and b/testdata/algo/nets_proc_1.gds differ diff --git a/testdata/drc/drcGenericTests_au8.gds b/testdata/drc/drcGenericTests_au8.gds index ce06f30ff..805de7592 100644 Binary files a/testdata/drc/drcGenericTests_au8.gds and b/testdata/drc/drcGenericTests_au8.gds differ diff --git a/testdata/drc/drcGenericTests_au8d.gds b/testdata/drc/drcGenericTests_au8d.gds index b1933640c..68c86ae3c 100644 Binary files a/testdata/drc/drcGenericTests_au8d.gds and b/testdata/drc/drcGenericTests_au8d.gds differ diff --git a/testdata/drc/drcSimpleTests_70.drc b/testdata/drc/drcSimpleTests_70.drc new file mode 100644 index 000000000..c39b4556f --- /dev/null +++ b/testdata/drc/drcSimpleTests_70.drc @@ -0,0 +1,119 @@ + +# Moved implementation + +source($drc_test_source) +target($drc_test_target) + +if $drc_test_deep + deep +end + +# properties on input +l1 = input(1, 0) +l2 = input(2, 0) +l3_wp = input(3, 0, enable_props) +l3_wp1_input = input(3, 0, select_props(1)) +l3_wp2as1_input = input(3, 0, map_props({ 2 => 1 })) +l3 = input(3, 0) +l4_wp = input(4, 0, enable_props) +l4 = input(4, 0) + +# derived properties +l3_wp1 = l3_wp.select_props(1) +l3_wp2as1 = l3_wp.map_props({ 2 => 1 }) +l3_nowp = l3_wp.remove_props + +# dump to output + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) +l4.output(4, 0) + +l3_wp.output(10, 0) +l3_wp1.output(11, 0) +l3_wp2as1.output(12, 0) +l3_nowp.output(13, 0) +l3_wp1_input.output(14, 0) +l3_wp2as1_input.output(15, 0) +l4_wp.output(16, 0) + +# booleans with properties constraints + +l3_wp.and(l4_wp, props_eq + props_copy).output(20, 0) +l3_wp1.and(l4_wp, props_eq + props_copy).output(21, 0) +l3_wp2as1.and(l4_wp, props_eq + props_copy).output(22, 0) +l3_nowp.and(l4_wp, props_eq + props_copy).output(23, 0) + +l3_wp.and(l4_wp, props_ne + props_copy).output(30, 0) +l3_wp1.and(l4_wp, props_ne + props_copy).output(31, 0) +l3_wp2as1.and(l4_wp, props_ne + props_copy).output(32, 0) +l3_nowp.and(l4_wp, props_ne + props_copy).output(33, 0) + +l3_wp.and(l4_wp, props_copy).output(40, 0) +l3_wp1.and(l4_wp, props_copy).output(41, 0) +l3_wp2as1.and(l4_wp, props_copy).output(42, 0) +l3_nowp.and(l4_wp, props_copy).output(43, 0) + +l3_wp.and(l4_wp).output(50, 0) +l3_wp1.and(l4_wp).output(51, 0) +l3_wp2as1.and(l4_wp).output(52, 0) +l3_nowp.and(l4_wp).output(53, 0) + + +connect(l1, l2) + +l1.nets.output(100, 0) +l1.nets(self._netter).output(101, 0) +l1.nets(prop(1)).output(102, 0) +l1.nets(prop(nil)).output(103, 0) + +l1.nets("X").output(110, 0) +l1.nets("TOP", "X").output(111, 0) +l1.nets("TOP", "NOTEXIST").output(112, 0) +l1.nets("NOTEXIST", "NOTEXIST").output(113, 0) + + +# checks with property constraints + +l1_nets = l1.nets + +l1_nets.space(1.0.um, projection).polygons.output(200, 0) +l1_nets.space(1.0.um, projection, props_eq + props_copy).polygons.output(201, 0) +l1_nets.space(1.0.um, projection, props_ne + props_copy).polygons.output(202, 0) +l1_nets.space(1.0.um, projection, props_copy).polygons.output(203, 0) +l1_nets.width(1.0.um, projection).polygons.output(204, 0) +l1_nets.width(1.0.um, projection, props_copy).polygons.output(205, 0) +l1_nets.space(1.0.um, projection, props_eq).polygons.output(206, 0) +l1_nets.space(1.0.um, projection, props_ne).polygons.output(207, 0) + +l1_nets.drc(space(projection) < 1.0.um).polygons.output(210, 0) +l1_nets.drc(space(projection) < 1.0.um, props_eq + props_copy).polygons.output(211, 0) +l1_nets.drc(space(projection) < 1.0.um, props_ne + props_copy).polygons.output(212, 0) +l1_nets.drc(space(projection) < 1.0.um, props_copy).polygons.output(213, 0) +l1_nets.drc(width(projection) < 1.0.um).polygons.output(214, 0) +l1_nets.drc(width(projection) < 1.0.um, props_copy).polygons.output(215, 0) +l1_nets.drc(space(projection) < 1.0.um, props_eq).polygons.output(216, 0) +l1_nets.drc(space(projection) < 1.0.um, props_ne).polygons.output(217, 0) + +# edge pair to edge/polygon conversion with properties + +l1_nets.space(1.0.um, projection, props_copy).output(220, 0) +l1_nets.space(1.0.um, projection, props_copy).first_edges.output(221, 0) +l1_nets.space(1.0.um, projection, props_copy).second_edges.output(222, 0) +l1_nets.space(1.0.um, projection, props_copy).edges.output(223, 0) +l1_nets.space(1.0.um, projection, props_copy).edges.extended_in(10.nm).output(224, 0) + +# sizing with properties + +l1_nets_sized = l1_nets.sized(0.2.um) +l1_nets_sized.and(l1_nets_sized, props_ne + props_copy).output(300, 0) # overlap of different nets, same layer +l1_nets_sized.and(l1_nets_sized, props_ne).output(301, 0) + +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um)).output(310, 0) +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_ne + props_copy).output(311, 0) +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_eq + props_copy).output(312, 0) +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_copy).output(313, 0) +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_ne).output(314, 0) +l1_nets.drc(primary.sized(0.2.um) & foreign.sized(0.2.um), props_eq).output(315, 0) + diff --git a/testdata/drc/drcSimpleTests_70.gds b/testdata/drc/drcSimpleTests_70.gds new file mode 100644 index 000000000..d3612dddc Binary files /dev/null and b/testdata/drc/drcSimpleTests_70.gds differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.1 b/testdata/drc/drcSimpleTests_au70.gds.1 new file mode 100644 index 000000000..ffe5a38a3 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au70.gds.1 differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.2 b/testdata/drc/drcSimpleTests_au70.gds.2 new file mode 100644 index 000000000..7e83fd948 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au70.gds.2 differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.1 b/testdata/drc/drcSimpleTests_au70d.gds.1 new file mode 100644 index 000000000..a4339e558 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au70d.gds.1 differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.2 b/testdata/drc/drcSimpleTests_au70d.gds.2 new file mode 100644 index 000000000..b9512ba6a Binary files /dev/null and b/testdata/drc/drcSimpleTests_au70d.gds.2 differ diff --git a/testdata/net_tracer/t1_all_nets.oas.gz b/testdata/net_tracer/t1_all_nets.oas.gz index 5a710240c..cc196a89d 100644 Binary files a/testdata/net_tracer/t1_all_nets.oas.gz and b/testdata/net_tracer/t1_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t4_all_nets.oas.gz b/testdata/net_tracer/t4_all_nets.oas.gz index a9a2efba3..d9cb0dcd6 100644 Binary files a/testdata/net_tracer/t4_all_nets.oas.gz and b/testdata/net_tracer/t4_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t4b_all_nets.oas.gz b/testdata/net_tracer/t4b_all_nets.oas.gz index 8461fd262..d3d6064b0 100644 Binary files a/testdata/net_tracer/t4b_all_nets.oas.gz and b/testdata/net_tracer/t4b_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t5_all_nets.oas.gz b/testdata/net_tracer/t5_all_nets.oas.gz index 41d400513..552c59352 100644 Binary files a/testdata/net_tracer/t5_all_nets.oas.gz and b/testdata/net_tracer/t5_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t5b_all_nets.oas.gz b/testdata/net_tracer/t5b_all_nets.oas.gz index d7d70e9f6..3cab442bf 100644 Binary files a/testdata/net_tracer/t5b_all_nets.oas.gz and b/testdata/net_tracer/t5b_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t5c_all_nets.oas.gz b/testdata/net_tracer/t5c_all_nets.oas.gz index 5abaac758..d0ece81d2 100644 Binary files a/testdata/net_tracer/t5c_all_nets.oas.gz and b/testdata/net_tracer/t5c_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t5d_all_nets.oas.gz.1 b/testdata/net_tracer/t5d_all_nets.oas.gz.1 index a0a91d691..ce4bd4f8d 100644 Binary files a/testdata/net_tracer/t5d_all_nets.oas.gz.1 and b/testdata/net_tracer/t5d_all_nets.oas.gz.1 differ diff --git a/testdata/net_tracer/t5d_all_nets.oas.gz.2 b/testdata/net_tracer/t5d_all_nets.oas.gz.2 index c858288fa..17bcf7664 100644 Binary files a/testdata/net_tracer/t5d_all_nets.oas.gz.2 and b/testdata/net_tracer/t5d_all_nets.oas.gz.2 differ diff --git a/testdata/net_tracer/t6.oas.gz b/testdata/net_tracer/t6.oas.gz index 2bcc9d226..c7a709fd5 100644 Binary files a/testdata/net_tracer/t6.oas.gz and b/testdata/net_tracer/t6.oas.gz differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz b/testdata/net_tracer/t6_all_nets.oas.gz new file mode 100644 index 000000000..3fdf25758 Binary files /dev/null and b/testdata/net_tracer/t6_all_nets.oas.gz differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.1 b/testdata/net_tracer/t6_all_nets.oas.gz.1 deleted file mode 100644 index 01198a04c..000000000 Binary files a/testdata/net_tracer/t6_all_nets.oas.gz.1 and /dev/null differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.2 b/testdata/net_tracer/t6_all_nets.oas.gz.2 deleted file mode 100644 index 2c5855ced..000000000 Binary files a/testdata/net_tracer/t6_all_nets.oas.gz.2 and /dev/null differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.3 b/testdata/net_tracer/t6_all_nets.oas.gz.3 deleted file mode 100644 index e80730674..000000000 Binary files a/testdata/net_tracer/t6_all_nets.oas.gz.3 and /dev/null differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.4 b/testdata/net_tracer/t6_all_nets.oas.gz.4 deleted file mode 100644 index c23c41c53..000000000 Binary files a/testdata/net_tracer/t6_all_nets.oas.gz.4 and /dev/null differ diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.5 b/testdata/net_tracer/t6_all_nets.oas.gz.5 deleted file mode 100644 index 3aeeab5cd..000000000 Binary files a/testdata/net_tracer/t6_all_nets.oas.gz.5 and /dev/null differ diff --git a/testdata/net_tracer/t6_net.oas.gz b/testdata/net_tracer/t6_net.oas.gz index 19ed31c1c..10dafc8e8 100644 Binary files a/testdata/net_tracer/t6_net.oas.gz and b/testdata/net_tracer/t6_net.oas.gz differ diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index abd5ffc7a..c40e91b9b 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -634,6 +634,58 @@ END end + def test_15_BuildNetShapes + + l2n = RBA::LayoutToNetlist::new + + input = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_in.txt") + l2n.read(input) + + # build_all_nets using Region#net + + metal1 = l2n.layer_by_name("metal1") + + metal1_all = metal1.nets(l2n) + metal1_vdd = metal1.nets(l2n, nil, l2n.netlist.nets_by_name("VDD")) + metal1_all_wp = metal1.nets(l2n, 1) + + ly = RBA::Layout::new + tc = ly.create_cell("TOP") + metal1_all.insert_into(ly, tc.cell_index, ly.layer(1, 0)) + metal1_vdd.insert_into(ly, tc.cell_index, ly.layer(2, 0)) + metal1_all_wp.insert_into(ly, tc.cell_index, ly.layer(3, 0)) + + si = tc.begin_shapes_rec(ly.layer(1, 0)) + assert_equal(si.each.count, 111) + + si = tc.begin_shapes_rec(ly.layer(1, 0)) + si.each do |i| + assert_equal(i.shape.prop_id, 0) + end + + # VDD net is smaller + si = tc.begin_shapes_rec(ly.layer(2, 0)) + assert_equal(si.each.count, 20) + assert_equal(tc.dbbox(ly.layer(2, 0)).to_s, "(-0.18,2.42;23.94,3.18)") + + si = tc.begin_shapes_rec(ly.layer(3, 0)) + assert_equal(si.each.count, 111) + + # properties are net names + ID + si = tc.begin_shapes_rec(ly.layer(3, 0)) + net_names = [] + si.each do |i| + ly.properties(i.shape.prop_id).each do |k,v| + if k == 1 + net_names << v[0] + end + end + end + + assert_equal(net_names.sort.uniq.join(";"), "$10;$11;$12;$13;$14;$15;$16;$17;$18;$19;$20;$21;$22;$5;$6;$7;$8;$9;FB;OSC;VDD;VSS") + + end + def test_20_Antenna # --- simple antenna check diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 07458e941..71c0df2cd 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1101,6 +1101,73 @@ class DBRegion_TestClass < TestBase end + # Bool with properties + def test_bool_with_properties + + ly = RBA::Layout::new + tc = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + + p1 = ly.properties_id([ [ 1, 17 ] ]) + p2 = ly.properties_id([ [ 1, 42 ] ]) + + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(0, 0), RBA::Point::new(100, 200)), p1) + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(200, 0), RBA::Point::new(300, 200)), p2) + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(400, 0), RBA::Point::new(500, 200))) + + tc.shapes(l2).insert(RBA::Box::new(RBA::Point::new(0, 0), RBA::Point::new(500, 50)), p1) + tc.shapes(l2).insert(RBA::Box::new(RBA::Point::new(0, 50), RBA::Point::new(500, 100))) + + r = RBA::Region::new(tc.begin_shapes_rec(l1)).enable_properties + rr = RBA::Region::new(tc.begin_shapes_rec(l2)).enable_properties + + assert_equal(csort((r & rr).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) + assert_equal(csort(r.and(rr).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) + assert_equal(csort(r.and(rr, RBA::Region::NoPropertyConstraint).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) + assert_equal(csort(r.and(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,0;0,50;100,50;100,0);(400,50;400,100;500,100;500,50)")) + assert_equal(csort(r.and(rr, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(0,50;0,100;100,100;100,50);(200,0;200,100;300,100;300,0);(400,0;400,50;500,50;500,0)")) + + assert_equal(csort(r.not(rr).to_s), csort("(0,100;0,200;100,200;100,100);(200,100;200,200;300,200;300,100);(400,100;400,200;500,200;500,100)")) + assert_equal(csort(r.not(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,50;0,200;100,200;100,50);(400,100;400,200;500,200;500,100);(200,0;200,200;300,200;300,0);(400,0;400,50;500,50;500,0)")) + + r.remove_properties + rr.remove_properties + assert_equal(r.and(rr, RBA::Region::DifferentPropertiesConstraint).to_s, "") + + end + + # Check with properties + def test_check_with_properties + + ly = RBA::Layout::new + tc = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + + p1 = ly.properties_id([ [ 1, 17 ] ]) + p2 = ly.properties_id([ [ 1, 42 ] ]) + + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(0, 0), RBA::Point::new(100, 200)), p1) + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(200, 0), RBA::Point::new(300, 200)), p2) + tc.shapes(l1).insert(RBA::Box::new(RBA::Point::new(400, 0), RBA::Point::new(500, 200))) + + tc.shapes(l2).insert(RBA::Box::new(RBA::Point::new(0, 250), RBA::Point::new(500, 300)), p1) + + r = RBA::Region::new(tc.begin_shapes_rec(l1)).enable_properties + rr = RBA::Region::new(tc.begin_shapes_rec(l2)).enable_properties + + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false).to_s), csort("(400,200;500,200)/(500,250;400,250);(0,200;100,200)/(100,250;0,250);(200,200;300,200)/(300,250;200,250)")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::NoPropertyConstraint).to_s), csort("(400,200;500,200)/(500,250;400,250);(0,200;100,200)/(100,250;0,250);(200,200;300,200)/(300,250;200,250)")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,200;100,200)/(100,250;0,250)")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(400,200;500,200)/(500,250;400,250);(200,200;300,200)/(300,250;200,250)")) + + r.remove_properties + rr.remove_properties + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,200;100,200)/(100,250;0,250);(200,200;300,200)/(300,250;200,250);(400,200;500,200)/(500,250;400,250)")) + + end + end load("test_epilogue.rb")