diff --git a/Changelog b/Changelog index 5a9b54af4..2ac7bcff6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +0.29.0 (2024-04-01): +* TODO + 0.28.17 (2024-02-16): * Enhancement: %GITHUB%/issues/1626 Technology specific grids diff --git a/Changelog.Debian b/Changelog.Debian index 8a6a53a23..735a1aaae 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.29.0-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Fri, 01 Apr 2024 12:00:00 +0100 + klayout (0.28.17-1) unstable; urgency=low * New features and bugfixes diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a4061e230..fe6cbc813 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,6 +32,9 @@ jobs: cp311-cp311-win_amd64.whl: python.version: '3.11' python.architecture: 'x64' + cp312-cp312-win_amd64.whl: + python.version: '3.12' + python.architecture: 'x64' cp36-cp36m-win32.whl: python.version: '3.6' python.architecture: 'x86' @@ -50,6 +53,9 @@ jobs: cp311-cp311-win32.whl: python.version: '3.11' python.architecture: 'x86' + cp312-cp312-win32.whl: + python.version: '3.12' + python.architecture: 'x86' maxParallel: 6 steps: @@ -130,6 +136,11 @@ jobs: vmImage: 'windows-2019' # other options: 'macOS-10.13', 'ubuntu-16.04' steps: - checkout: none #skip checking out the default repository resource + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifacts wheel-3.12.x64' + inputs: + artifactName: 'wheel-3.12.x64' + downloadPath: '$(System.DefaultWorkingDirectory)' - task: DownloadBuildArtifacts@0 displayName: 'Download Build Artifacts wheel-3.11.x64' inputs: @@ -160,6 +171,11 @@ jobs: inputs: artifactName: 'wheel-3.6.x64' downloadPath: '$(System.DefaultWorkingDirectory)' + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifacts wheel-3.12.x86' + inputs: + artifactName: 'wheel-3.12.x86' + downloadPath: '$(System.DefaultWorkingDirectory)' - task: DownloadBuildArtifacts@0 displayName: 'Download Build Artifacts wheel-3.11.x86' inputs: diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index bbc97987e..2799b88e1 100644 --- a/scripts/drc_lvs_doc/create_drc_samples.rb +++ b/scripts/drc_lvs_doc/create_drc_samples.rb @@ -606,6 +606,35 @@ run_demo gen, "input.edges.with_length(0, 3.5)\n .extended(:out => 1.0)", "drc run_demo gen, "input.edges.with_length(0, 3.5)\n .extended(:out => 1.0, :joined => true)", "drc_extended3.png" run_demo gen, "input.edges.with_length(2.0)\n .extended(0.0, -0.5, 1.0, -0.5)", "drc_extended4.png" +class Gen + def produce(s1, s2) + pts = [ + RBA::Point::new(1000, 0), + RBA::Point::new(1000, 5000), + RBA::Point::new(2000, 5000), + RBA::Point::new(2000, 7000), + RBA::Point::new(4000, 7000), + RBA::Point::new(4000, 5000), + RBA::Point::new(5000, 5000), + RBA::Point::new(5000, 0), + RBA::Point::new(4000, 0), + RBA::Point::new(4000, 1000), + RBA::Point::new(2000, 1000), + RBA::Point::new(2000, 0) + ]; + s1.insert(RBA::Polygon::new(pts)) + end +end + +gen = Gen::new + +run_demo gen, "input.edges", "drc_edge_modes1.png" +run_demo gen, "input.edges(convex)", "drc_edge_modes2.png" +run_demo gen, "input.edges(concave)", "drc_edge_modes3.png" +run_demo gen, "input.edges(step)", "drc_edge_modes4.png" +run_demo gen, "input.edges(step_in)", "drc_edge_modes5.png" +run_demo gen, "input.edges(step_out)", "drc_edge_modes6.png" + class Gen def produce(s1, s2) pts = [ diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 9bd72e860..de1f265e4 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -680,13 +680,17 @@ bool run_tiled_xor (const XORData &xor_data) if (ll->second.first < 0) { proc.input (in_a, db::RecursiveShapeIterator ()); } else { - proc.input (in_a, db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first)); + db::RecursiveShapeIterator si (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + si.set_for_merged_input (true); + proc.input (in_a, si); } if (ll->second.second < 0) { proc.input (in_b, db::RecursiveShapeIterator ()); } else { - proc.input (in_b, db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second)); + db::RecursiveShapeIterator si (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + si.set_for_merged_input (true); + proc.input (in_b, si); } std::string expr = "var x=" + in_a + "^" + in_b + "; "; @@ -805,10 +809,12 @@ bool run_deep_xor (const XORData &xor_data) if (ll->second.first >= 0) { ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + ri_a.set_for_merged_input (true); } if (ll->second.second >= 0) { ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + ri_b.set_for_merged_input (true); } db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); diff --git a/src/db/db/built-in-macros/pcell_declaration_helper.lym b/src/db/db/built-in-macros/pcell_declaration_helper.lym index 43754b65d..ba97fac7d 100644 --- a/src/db/db/built-in-macros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-macros/pcell_declaration_helper.lym @@ -118,6 +118,12 @@ Optional, named parameters are @li @b:unit@/b: the unit string @/li + @li + @b:min_value@/b: the minimum value (effective for numerical types and if no choices are present) + @/li + @li + @b:max_value@/b: the maximum value (effective for numerical types and if no choices are present) + @/li @li @b:default@/b: the default value @/li @@ -335,6 +341,8 @@ module RBA # :hidden -> (boolean) true, if the parameter is not shown in the dialog # :readonly -> (boolean) true, if the parameter cannot be edited # :unit -> the unit string + # :min_value -> the minimum value (only effective for numerical types and if no choices are present) + # :max_value -> the maximum value (only effective for numerical types and if no choices are present) # :default -> the default value # :choices -> ([ [ d, v ], ...) choice descriptions/value for choice type # this method defines accessor methods for the parameters @@ -373,6 +381,8 @@ module RBA args[:hidden] && pdecl.hidden = args[:hidden] args[:readonly] && pdecl.readonly = args[:readonly] args[:unit] && pdecl.unit = args[:unit] + args[:min_value] && pdecl.min_value = args[:min_value] + args[:max_value] && pdecl.max_value = args[:max_value] if args[:choices] if !args[:choices].is_a?(Array) raise ":choices value must be an array of two-element arrays (description, value)" diff --git a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym index 462883154..2b3113ca0 100644 --- a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym @@ -127,6 +127,12 @@ Optional, named parameters are @li @bunit@/b: the unit string @/li + @li + @bmin_value@/b: the minimum value (effective for numerical types and if no choices are present) + @/li + @li + @bmax_value@/b: the maximum value (effective for numerical types and if no choices are present) + @/li @li @bdefault@/b: the default value @/li diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 4f38b882e..b428919c6 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -28,6 +28,7 @@ #include "dbEmptyEdges.h" #include "dbEdges.h" #include "dbEdgesUtils.h" +#include "dbEdgesLocalOperations.h" #include "dbEdgeBoolean.h" #include "dbBoxConvert.h" #include "dbRegion.h" @@ -39,6 +40,7 @@ #include "dbPolygonGenerators.h" #include "dbPolygon.h" #include "dbPath.h" +#include "dbHierProcessor.h" #include @@ -94,104 +96,105 @@ AsIfFlatEdges::to_string (size_t nmax) const return os.str (); } -EdgesDelegate * -AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const +namespace { + +class OutputPairHolder { - // shortcuts - if (other.empty () || empty ()) { - return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); - } +public: + OutputPairHolder (int inverse, bool merged_semantics) + { + m_e1.reset (new FlatEdges (merged_semantics)); + m_results.push_back (& m_e1->raw_edges ()); - db::box_scanner2 scanner (report_progress (), progress_desc ()); - - AddressableEdgeDelivery e (begin_merged ()); - - for ( ; ! e.at_end (); ++e) { - scanner.insert1 (e.operator-> (), 0); - } - - AddressablePolygonDelivery p = (mode == EdgesInside ? other.addressable_merged_polygons () : other.addressable_polygons ()); - - for ( ; ! p.at_end (); ++p) { - scanner.insert2 (p.operator-> (), 1); - } - - std::unique_ptr output (new FlatEdges (true)); - - if (! inverse) { - - edge_to_region_interaction_filter filter (output.get (), mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - } else { - - std::set result; - edge_to_region_interaction_filter > filter (&result, mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output->insert (*o); - } + if (inverse == 0) { + m_e2.reset (new FlatEdges (merged_semantics)); + m_results.push_back (& m_e2->raw_edges ()); } - } - return output.release (); + std::pair region_pair () + { + return std::make_pair (m_e1.release (), m_e2.release ()); + } + + const std::vector &results () { return m_results; } + +private: + std::unique_ptr m_e1, m_e2; + std::vector m_results; +}; + } EdgesDelegate * -AsIfFlatEdges::selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const +AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (edges.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); } - db::box_scanner scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert (e.operator-> (), 0); + db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal, min_count, max_count); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + std::vector > others; + // NOTE: with counting the other region needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); + + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); + + return oph.region_pair ().first; +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + + // shortcuts + if (max_count < min_count || other.empty () || empty ()) { + return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); } - // NOTE: "inside" needs merged edges for the other edges as the algorithm works edge by edge - AddressableEdgeDelivery ee = (mode == EdgesInside ? edges.addressable_merged_edges () : edges.addressable_edges ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ()); - for ( ; ! ee.at_end (); ++ee) { - scanner.insert (ee.operator-> (), 1); - } + db::EdgesIterator edges (begin_merged ()); - std::unique_ptr output (new FlatEdges (true)); + db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count); - if (! inverse) { + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - edge_interaction_filter filter (*output, mode); - scanner.process (filter, 1, db::box_convert ()); + std::vector > others; + // NOTE: with counting the other edge collection needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - } else { + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set result; - edge_interaction_filter > filter (result, mode); - scanner.process (filter, 1, db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output->insert (*o); - } - } - - } - - return output.release (); + return oph.region_pair ().first; } std::pair -AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const +AsIfFlatEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (region.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { if (mode != EdgesOutside) { return std::make_pair (new EmptyEdges (), clone ()); } else { @@ -199,43 +202,34 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInte } } - db::box_scanner2 scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (0, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert1 (e.operator-> (), 0); - } + db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both, min_count, max_count); - AddressablePolygonDelivery p = region.addressable_merged_polygons (); + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - for ( ; ! p.at_end (); ++p) { - scanner.insert2 (p.operator-> (), 1); - } + std::vector > others; + // NOTE: with counting the other region needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - std::unique_ptr output (new FlatEdges (true)); - std::unique_ptr output2 (new FlatEdges (true)); + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set result; - edge_to_region_interaction_filter > filter (&result, mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output2->insert (*o); - } else { - output->insert (*o); - } - } - - return std::make_pair (output.release (), output2.release ()); + return oph.region_pair (); } std::pair -AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const +AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (other.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { if (mode != EdgesOutside) { return std::make_pair (new EmptyEdges (), clone ()); } else { @@ -243,36 +237,25 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeIntera } } - db::box_scanner scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (0, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert (e.operator-> (), 0); - } + db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both, min_count, max_count); - AddressableEdgeDelivery ee = other.addressable_merged_edges (); + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - for ( ; ! ee.at_end (); ++ee) { - scanner.insert (ee.operator-> (), 1); - } + std::vector > others; + // NOTE: with counting the other edge collection needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - std::unique_ptr output (new FlatEdges (true)); - std::unique_ptr output2 (new FlatEdges (true)); + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set results; - edge_interaction_filter > filter (results, mode); - scanner.process (filter, 1, db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (results.find (*o) == results.end ()) { - output2->insert (*o); - } else { - output->insert (*o); - } - } - - return std::make_pair (output.release (), output2.release ()); + return oph.region_pair (); } @@ -294,7 +277,7 @@ AsIfFlatEdges::pull_generic (const Edges &edges) const } std::unique_ptr output (new FlatEdges (true)); - edge_interaction_filter filter (*output, EdgesInteract); + edge_interaction_filter filter (*output, EdgesInteract, size_t (1), std::numeric_limits::max ()); scanner.process (filter, 1, db::box_convert ()); return output.release (); @@ -324,7 +307,7 @@ AsIfFlatEdges::pull_generic (const Region &other) const std::unique_ptr output (new FlatRegion (true)); - edge_to_region_interaction_filter filter (output.get (), EdgesInteract); + edge_to_polygon_interaction_filter filter (output.get (), EdgesInteract, size_t (1), std::numeric_limits::max ()); scanner.process (filter, 1, db::box_convert (), db::box_convert ()); return output.release (); @@ -343,111 +326,111 @@ AsIfFlatEdges::pull_interacting (const Region &other) const } EdgesDelegate * -AsIfFlatEdges::selected_interacting (const Edges &other) const +AsIfFlatEdges::selected_interacting (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, false); + return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_not_interacting (const Edges &other) const +AsIfFlatEdges::selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, true); + return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_interacting (const Region &other) const +AsIfFlatEdges::selected_interacting (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, false); + return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_not_interacting (const Region &other) const +AsIfFlatEdges::selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, true); + return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count); } std::pair -AsIfFlatEdges::selected_interacting_pair (const Region &other) const +AsIfFlatEdges::selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_pair_generic (other, EdgesInteract); + return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count); } std::pair -AsIfFlatEdges::selected_interacting_pair (const Edges &other) const +AsIfFlatEdges::selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_pair_generic (other, EdgesInteract); + return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count); } EdgesDelegate * AsIfFlatEdges::selected_outside (const Region &other) const { - return selected_interacting_generic (other, EdgesOutside, false); + return selected_interacting_generic (other, EdgesOutside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_outside (const Region &other) const { - return selected_interacting_generic (other, EdgesOutside, true); + return selected_interacting_generic (other, EdgesOutside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_outside_pair (const Region &other) const { - return selected_interacting_pair_generic (other, EdgesOutside); + return selected_interacting_pair_generic (other, EdgesOutside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_inside (const Region &other) const { - return selected_interacting_generic (other, EdgesInside, false); + return selected_interacting_generic (other, EdgesInside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_inside (const Region &other) const { - return selected_interacting_generic (other, EdgesInside, true); + return selected_interacting_generic (other, EdgesInside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_inside_pair (const Region &other) const { - return selected_interacting_pair_generic (other, EdgesInside); + return selected_interacting_pair_generic (other, EdgesInside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_outside (const Edges &other) const { - return selected_interacting_generic (other, EdgesOutside, false); + return selected_interacting_generic (other, EdgesOutside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_outside (const Edges &other) const { - return selected_interacting_generic (other, EdgesOutside, true); + return selected_interacting_generic (other, EdgesOutside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_outside_pair (const Edges &other) const { - return selected_interacting_pair_generic (other, EdgesOutside); + return selected_interacting_pair_generic (other, EdgesOutside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_inside (const Edges &other) const { - return selected_interacting_generic (other, EdgesInside, false); + return selected_interacting_generic (other, EdgesInside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_inside (const Edges &other) const { - return selected_interacting_generic (other, EdgesInside, true); + return selected_interacting_generic (other, EdgesInside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_inside_pair (const Edges &other) const { - return selected_interacting_pair_generic (other, EdgesInside); + return selected_interacting_pair_generic (other, EdgesInside, size_t (1), std::numeric_limits::max ()); } @@ -769,6 +752,118 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co return result.release (); } +EdgesDelegate * +AsIfFlatEdges::merged () const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (0, EdgeOr); + } +} + +EdgesDelegate * +AsIfFlatEdges::and_with (const Edges &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (&other, EdgeAnd); + } +} + +EdgesDelegate * +AsIfFlatEdges::not_with (const Edges &other) const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeNot); + } +} + +std::pair +AsIfFlatEdges::andnot_with (const Edges &other) const +{ + if (empty ()) { + return std::make_pair (new db::EmptyEdges (), new db::EmptyEdges ()); + } else if (other.empty ()) { + return std::make_pair (new db::EmptyEdges (), clone ()); + } else { + return boolean_andnot (&other); + } +} + +EdgesDelegate * +AsIfFlatEdges::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return edge_region_op (other, db::EdgePolygonOp::Inside, true /*include borders*/).first; + } +} + +EdgesDelegate * +AsIfFlatEdges::not_with (const Region &other) const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else if (other.empty ()) { + return clone (); + } else { + return edge_region_op (other, db::EdgePolygonOp::Outside, true /*include borders*/).first; + } +} + +std::pair +AsIfFlatEdges::andnot_with (const Region &other) const +{ + if (empty ()) { + return std::make_pair (new db::EmptyEdges (), new db::EmptyEdges ()); + } else if (other.empty ()) { + return std::make_pair (new db::EmptyEdges (), clone ()); + } else { + return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/); + } +} + +EdgesDelegate * +AsIfFlatEdges::xor_with (const Edges &other) const +{ + if (empty ()) { + return other.delegate ()->clone (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeXor); + } +} + +EdgesDelegate * +AsIfFlatEdges::or_with (const Edges &other) const +{ + if (empty ()) { + return other.delegate ()->clone (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeOr); + } +} + +EdgesDelegate * +AsIfFlatEdges::intersections (const Edges &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (&other, EdgeIntersections); + } +} + EdgesDelegate * AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const { @@ -781,9 +876,7 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (e.operator-> (), 0); - } + scanner.insert (e.operator-> (), 0); } AddressableEdgeDelivery ee; @@ -791,9 +884,7 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const if (other) { ee = other->addressable_edges (); for ( ; ! ee.at_end (); ++ee) { - if (! ee->is_degenerate ()) { - scanner.insert (ee.operator-> (), 1); - } + scanner.insert (ee.operator-> (), 1); } } @@ -815,9 +906,7 @@ AsIfFlatEdges::boolean_andnot (const Edges *other) const AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (e.operator-> (), 0); - } + scanner.insert (e.operator-> (), 0); } AddressableEdgeDelivery ee; @@ -825,9 +914,7 @@ AsIfFlatEdges::boolean_andnot (const Edges *other) const if (other) { ee = other->addressable_edges (); for ( ; ! ee.at_end (); ++ee) { - if (! ee->is_degenerate ()) { - scanner.insert (ee.operator-> (), 1); - } + scanner.insert (ee.operator-> (), 1); } } @@ -852,6 +939,8 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo db::EdgeProcessor ep (report_progress (), progress_desc ()); + bool has_dots = false; + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { if (p->box ().touches (bbox ())) { ep.insert (*p, 0); @@ -859,7 +948,11 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo } for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - ep.insert (*e, 1); + if (e->is_degenerate ()) { + has_dots = true; + } else { + ep.insert (*e, 1); + } } std::unique_ptr output_second; @@ -874,6 +967,36 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo db::EdgePolygonOp op (mode, include_borders); ep.process (cc, op); + // process dots which are not captured by the booleans using the interaction function + + if (has_dots) { + + std::unique_ptr dots (new FlatEdges (false)); + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (e->is_degenerate ()) { + dots->insert (*e); + } + } + + std::pair res (0, 0); + + if (mode == EdgePolygonOp::Both) { + res = dots->selected_interacting_pair_generic (other, include_borders ? EdgesInteract : EdgesInside, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Inside) { + res.first = dots->selected_interacting_generic (other, include_borders ? EdgesInteract : EdgesInside, false, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Outside) { + res.first = dots->selected_interacting_generic (other, include_borders ? EdgesInteract : EdgesOutside, include_borders, size_t (1), std::numeric_limits::max ()); + } + + if (res.first) { + output->add_in_place (db::Edges (res.first)); + } + if (res.second) { + output_second->add_in_place (db::Edges (res.second)); + } + + } + return std::make_pair (output.release (), output_second.release ()); } diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index b1f9a07d4..fd07227ef 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -107,55 +107,25 @@ public: return merged (); } - virtual EdgesDelegate *merged () const - { - return boolean (0, EdgeOr); - } + virtual EdgesDelegate *merged () const; - virtual EdgesDelegate *and_with (const Edges &other) const - { - return boolean (&other, EdgeAnd); - } + virtual EdgesDelegate *and_with (const Edges &other) const; - virtual EdgesDelegate *not_with (const Edges &other) const - { - return boolean (&other, EdgeNot); - } + virtual EdgesDelegate *not_with (const Edges &other) const; - virtual std::pair andnot_with (const Edges &other) const - { - return boolean_andnot (&other); - } + virtual std::pair andnot_with (const Edges &other) const; - virtual EdgesDelegate *and_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Inside, true /*include borders*/).first; - } + virtual EdgesDelegate *and_with (const Region &other) const; - virtual EdgesDelegate *not_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Outside, true /*include borders*/).first; - } + virtual EdgesDelegate *not_with (const Region &other) const; - virtual std::pair andnot_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/); - } + virtual std::pair andnot_with (const Region &other) const; - virtual EdgesDelegate *xor_with (const Edges &other) const - { - return boolean (&other, EdgeXor); - } + virtual EdgesDelegate *xor_with (const Edges &other) const; - virtual EdgesDelegate *or_with (const Edges &other) const - { - return boolean (&other, EdgeOr); - } + virtual EdgesDelegate *or_with (const Edges &other) const; - virtual EdgesDelegate *intersections (const Edges &other) const - { - return boolean (&other, EdgeIntersections); - } + virtual EdgesDelegate *intersections (const Edges &other) const; virtual EdgesDelegate *add_in_place (const Edges &other) { @@ -183,12 +153,12 @@ public: virtual EdgesDelegate *pull_interacting (const Edges &) const; virtual RegionDelegate *pull_interacting (const Region &) const; - virtual EdgesDelegate *selected_interacting (const Edges &) const; - virtual EdgesDelegate *selected_not_interacting (const Edges &) const; - virtual EdgesDelegate *selected_interacting (const Region &) const; - virtual EdgesDelegate *selected_not_interacting (const Region &) const; - virtual std::pair selected_interacting_pair (const Region &other) const; - virtual std::pair selected_interacting_pair (const Edges &other) const; + virtual EdgesDelegate *selected_interacting (const Edges &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_not_interacting (const Edges &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting (const Region &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_not_interacting (const Region &, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const; virtual EdgesDelegate *selected_outside (const Edges &other) const; virtual EdgesDelegate *selected_not_outside (const Edges &other) const; @@ -217,10 +187,10 @@ protected: EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const EdgesCheckOptions &options) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; virtual RegionDelegate *pull_generic (const Region ®ion) const; - virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const; - virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const; + virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; AsIfFlatEdges &operator= (const AsIfFlatEdges &other); AsIfFlatEdges (const AsIfFlatEdges &other); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 5a86535fd..2171c4f37 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -143,7 +143,7 @@ AsIfFlatRegion::to_string (size_t nmax) const } EdgesDelegate * -AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr result (new FlatEdges ()); db::PropertyMapper pm (result->properties_repository (), properties_repository ()); @@ -154,17 +154,41 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const } result->reserve (n); + std::vector heap; + 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)) { - if (prop_id != 0) { - result->insert (db::EdgeWithProperties (*e, pm (prop_id))); - } else { - result->insert (*e); + + if (proc) { + + heap.clear (); + proc->process (*p, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } + } + } + } + } return result.release (); @@ -195,6 +219,8 @@ AsIfFlatRegion::area (const db::Box &box) const for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { if (box.empty () || p->box ().inside (box)) { a += p->area (); + } else if (p->is_box ()) { + a += (p->box () & box).area (); } else { std::vector clipped; clip_poly (*p, box, clipped); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 5f7ecd203..bc59155a1 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -84,7 +84,7 @@ public: virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) { @@ -137,32 +137,32 @@ public: virtual RegionDelegate *selected_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, Positive, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, 1, false, Positive, size_t (1), std::numeric_limits::max ()).first; } virtual RegionDelegate *selected_not_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, Negative, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, 1, false, Negative, size_t (1), std::numeric_limits::max ()).first; } virtual std::pair selected_outside_pair (const Region &other) const { - return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (0), std::numeric_limits::max ()); + return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (1), std::numeric_limits::max ()); } virtual RegionDelegate *selected_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, Positive, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, -1, true, Positive, size_t (1), std::numeric_limits::max ()).first; } virtual RegionDelegate *selected_not_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, Negative, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, -1, true, Negative, size_t (1), std::numeric_limits::max ()).first; } virtual std::pair selected_inside_pair (const Region &other) const { - return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (0), std::numeric_limits::max ()); + return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (1), std::numeric_limits::max ()); } virtual RegionDelegate *selected_enclosing (const Region &other, size_t min_count, size_t max_count) const diff --git a/src/db/db/dbBox.h b/src/db/db/dbBox.h index 94e2ac58c..cf7d4b233 100644 --- a/src/db/db/dbBox.h +++ b/src/db/db/dbBox.h @@ -254,6 +254,27 @@ struct DB_PUBLIC_TEMPLATE box */ box &operator+= (const point &p); + /** + * @brief Subtraction of boxes. + * + * The -= operator subtracts the argument box from *this. + * Subtraction leaves the bounding box of the region resulting + * from the geometrical NOT of *this and the argument box. + * Subtracting a box from itself gives an empty box. + * Subtracting a box that does not cover a full side of + * *this will not modify the box. + * + * @param b The box to subtract from *this. + * + * @return The result box. + */ + box &operator-= (const box &b); + + /** + * @brief A method version for operator- (mainly for automation purposes) + */ + box subtracted (const box &b) const; + /** * @brief Intersection of boxes. * @@ -784,6 +805,50 @@ box::operator+= (const point &p) return *this; } +template +inline box +box::subtracted (const box &b) const +{ + box r (*this); + r -= b; + return r; +} + +template +inline box & +box::operator-= (const box &bx) +{ + if (bx.empty () || empty ()) { + return *this; + } + + coord_type l = m_p1.x (), r = m_p2.x (); + coord_type b = m_p1.y (), t = m_p2.y (); + + if (bx.bottom () <= bottom () && bx.top () >= top ()) { + if (bx.left () <= left ()) { + l = std::max (bx.right (), left ()); + } + if (bx.right () >= right ()) { + r = std::min (bx.left (), right ()); + } + } + + if (bx.left () <= left () && bx.right () >= right ()) { + if (bx.bottom () <= bottom ()) { + b = std::max (bx.top (), bottom ()); + } + if (bx.top () >= top ()) { + t = std::min (bx.bottom (), top ()); + } + } + + m_p1 = point_type (l, b); + m_p2 = point_type (r, t); + + return *this; +} + template inline box & box::operator&= (const box &b) @@ -1363,6 +1428,23 @@ operator+ (const box &b1, const box &b2) return bb; } +/** + * @brief Box subtraction mapped on the - operator + * + * @param b1 The first box + * @param b2 The second box to subtract from the first + * + * @return The bounding box of the region formed but subtracting b2 from b1 + */ +template +inline box +operator- (const box &b1, const box &b2) +{ + box bb (b1); + bb -= b2; + return bb; +} + /** * @brief "Folding" of two boxes * diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index bcdb9fd02..2b2b06185 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -1079,7 +1079,7 @@ public: m_cl.erase (cli); } - } else if (m_report_single) { + } else if (m_report_single && m_ignore_single.find (obj) == m_ignore_single.end ()) { // single-object entry: create a cluster and feed it a single-object signature Cluster cl (m_cl_template); @@ -1089,6 +1089,13 @@ public: } } + void ignore_single (const Obj *o) + { + if (m_report_single) { + m_ignore_single.insert (o); + } + } + void add_asymm (const Obj *o1, const Prop &p1, const Obj *o2, const Prop &p2) { om_iterator_type om1 = m_om.find (om_key_type (o1, p1)); @@ -1166,6 +1173,7 @@ private: bool m_report_single; cl_type m_cl; om_type m_om; + std::set m_ignore_single; }; } diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index a9608af23..21591eeda 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -914,10 +914,19 @@ EdgesDelegate *DeepEdges::merged () const return res.release (); } -DeepLayer +std::pair DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const { + std::vector output_layers; + DeepLayer dl_out (deep_layer ().derived ()); + output_layers.push_back (dl_out.layer ()); + + DeepLayer dl_out2; + if (op == EdgeAndNot) { + dl_out2 = DeepLayer (deep_layer ().derived ()); + output_layers.push_back (dl_out2.layer ()); + } db::EdgeBoolAndOrNotLocalOperation local_op (op); @@ -927,14 +936,34 @@ DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); - proc.run (&local_op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ()); + proc.run (&local_op, deep_layer ().layer (), other->deep_layer ().layer (), output_layers); - return dl_out; + return std::make_pair (dl_out, dl_out2); } std::pair DeepEdges::edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t mode, bool include_borders) const { + // first, extract dots + + DeepLayer dots (deep_layer ().derived ()); + bool has_dots = false; + + db::Layout &layout = const_cast (dots.layout ()); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::Shapes &s = c->shapes (deep_layer ().layer ()); + db::Shapes &st = c->shapes (dots.layer ()); + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + if (si->edge ().is_degenerate ()) { + st.insert (*si); + has_dots = true; + } + } + } + + // normal processing (dots will vanish) + std::vector output_layers; DeepLayer dl_out (deep_layer ().derived ()); @@ -956,6 +985,29 @@ DeepEdges::edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t mode, proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), output_layers); + if (has_dots) { + + // process dots + + std::pair res (0, 0); + + if (mode == EdgePolygonOp::Both) { + res = db::DeepEdges (dots).selected_interacting_pair_generic_impl (other, include_borders ? EdgesInteract : EdgesInside, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Inside) { + res.first = db::DeepEdges (dots).selected_interacting_generic_impl (other, include_borders ? EdgesInteract : EdgesInside, false, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Outside) { + res.first = db::DeepEdges (dots).selected_interacting_generic_impl (other, include_borders ? EdgesInteract : EdgesOutside, include_borders, size_t (1), std::numeric_limits::max ()); + } + + if (res.first) { + db::DeepEdges (dl_out).add_in_place (db::Edges (res.first)); + } + if (res.second) { + db::DeepEdges (dl_out2).add_in_place (db::Edges (res.second)); + } + + } + return std::make_pair (dl_out, dl_out2); } @@ -963,17 +1015,22 @@ EdgesDelegate *DeepEdges::intersections (const Edges &other) const { const DeepEdges *other_deep = dynamic_cast (other.delegate ()); - if (empty () || other.empty ()) { + if (empty ()) { return clone (); + } else if (other.empty ()) { + + // NOTE: we do not use "EmptyEdges" as we want to maintain + return new DeepEdges (deep_layer ().derived ()); + } else if (! other_deep) { return AsIfFlatEdges::intersections (other); } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections)); + return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections).first); } } @@ -995,9 +1052,13 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const return AsIfFlatEdges::and_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return clone (); + } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeAnd)); + return new DeepEdges (and_or_not_with (other_deep, EdgeAnd).first); } } @@ -1014,9 +1075,13 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const return AsIfFlatEdges::not_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return new DeepEdges (deep_layer ().derived ()); + } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeNot)); + return new DeepEdges (and_or_not_with (other_deep, EdgeNot).first); } } @@ -1092,7 +1157,7 @@ EdgesDelegate *DeepEdges::not_with (const Region &other) const std::pair DeepEdges::andnot_with (const Edges &other) const { - const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (empty ()) { @@ -1107,9 +1172,13 @@ DeepEdges::andnot_with (const Edges &other) const return AsIfFlatEdges::andnot_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return std::make_pair (clone (), new DeepEdges (deep_layer ().derived ())); + } else { - auto res = edge_region_op (other_deep, EdgePolygonOp::Both, true /*include borders*/); + auto res = and_or_not_with (other_deep, EdgeAndNot); return std::make_pair (new DeepEdges (res.first), new DeepEdges (res.second)); } @@ -1131,12 +1200,16 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const return AsIfFlatEdges::xor_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return new DeepEdges (deep_layer ().derived ()); + } 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, EdgeNot)); - DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot)); + DeepLayer n1 (and_or_not_with (other_deep, EdgeNot).first); + DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot).first); n1.add_from (n2); return new DeepEdges (n1); @@ -1146,6 +1219,11 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const EdgesDelegate *DeepEdges::or_with (const Edges &other) const { + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + if (other_deep && other_deep->deep_layer () == deep_layer ()) { + return clone (); + } + // NOTE: in the hierarchical case we don't do a merge on "or": just map to add return add (other); } @@ -1354,362 +1432,8 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t return res.release (); } -namespace -{ - -class Edge2EdgeInteractingLocalOperation - : public local_operation -{ -public: - enum output_mode_t { Normal, Inverse, Both }; - - Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode) - : m_mode (mode), m_output_mode (output_mode) - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == (m_output_mode == Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (m_output_mode == Both) { - result2 = &results[1]; - } - - db::box_scanner scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert (&subject, 0); - } - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - scanner.insert (o.operator-> (), 1); - } - - if (m_output_mode == Inverse || m_output_mode == Both) { - - std::unordered_set interacting; - edge_interaction_filter > filter (interacting, m_mode); - scanner.process (filter, 1, db::box_convert ()); - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - if (interacting.find (subject) == interacting.end ()) { - if (m_output_mode != Both) { - result.insert (subject); - } else { - result2->insert (subject); - } - } else if (m_output_mode == Both) { - result.insert (subject); - } - - } - - } else { - - edge_interaction_filter > filter (result, m_mode); - scanner.process (filter, 1, db::box_convert ()); - - } - - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - if (m_mode == EdgesOutside) { - return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); - } else { - return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); - } - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting edges")); - } - -private: - EdgeInteractionMode m_mode; - output_mode_t m_output_mode; -}; - -class Edge2EdgePullLocalOperation - : public local_operation -{ -public: - Edge2EdgePullLocalOperation () - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::box_scanner scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert (&subject, 1); - } - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - scanner.insert (o.operator-> (), 0); - } - - edge_interaction_filter > filter (result, EdgesInteract); - scanner.process (filter, 1, db::box_convert ()); - - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - return Drop; - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting edges from other")); - } -}; - -class Edge2PolygonInteractingLocalOperation - : public local_operation -{ -public: - enum output_mode_t { Normal, Inverse, Both }; - - Edge2PolygonInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode) - : m_mode (mode), m_output_mode (output_mode) - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (m_output_mode == Both) { - result2 = &results[1]; - } - - db::box_scanner2 scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert1 (&subject, 0); - } - - std::list heap; - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - heap.push_back (o->obj ().transformed (o->trans ())); - scanner.insert2 (& heap.back (), 1); - } - - if (m_output_mode == Inverse || m_output_mode == Both) { - - std::unordered_set interacting; - edge_to_region_interaction_filter > filter (&interacting, m_mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - - if (interacting.find (subject) == interacting.end ()) { - if (m_output_mode != Both) { - result.insert (subject); - } else { - result2->insert (subject); - } - } else if (m_output_mode == Both) { - result.insert (subject); - } - - } - - } else { - - edge_to_region_interaction_filter > filter (&result, m_mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - } - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - if (m_mode == EdgesOutside) { - return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); - } else { - return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); - } - } - - virtual std::string description () const - { - if (m_mode == EdgesInteract) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-interacting edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select interacting edges")); - } else { - return tl::to_string (tr ("Select interacting and non-interacting edges")); - } - } else if (m_mode == EdgesInside) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-inside edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select inside edges")); - } else { - return tl::to_string (tr ("Select inside and non-inside edges")); - } - } else if (m_mode == EdgesOutside) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-outside edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select outside edges")); - } else { - return tl::to_string (tr ("Select outside and non-outside edges")); - } - } - return std::string (); - } - -private: - EdgeInteractionMode m_mode; - output_mode_t m_output_mode; -}; - -struct ResultInserter -{ - typedef db::Polygon value_type; - - ResultInserter (db::Layout *layout, std::unordered_set &result) - : mp_layout (layout), mp_result (&result) - { - // .. nothing yet .. - } - - void insert (const db::Polygon &p) - { - (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ())); - } - -private: - db::Layout *mp_layout; - std::unordered_set *mp_result; -}; - -class Edge2PolygonPullLocalOperation - : public local_operation -{ -public: - Edge2PolygonPullLocalOperation () - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::box_scanner2 scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert1 (&subject, 1); - } - - std::list heap; - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - heap.push_back (o->obj ().transformed (o->trans ())); - scanner.insert2 (& heap.back (), 0); - } - - ResultInserter inserter (layout, result); - edge_to_region_interaction_filter filter (&inserter, EdgesInteract); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - return Drop; - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting regions")); - } -}; - -} - EdgesDelegate * -DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const +DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { std::unique_ptr dr_holder; const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); @@ -1719,23 +1443,33 @@ DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMod other_deep = dr_holder.get (); } + return selected_interacting_generic_impl (other_deep, mode, inverse, min_count, max_count); +} + +EdgesDelegate * +DeepEdges::selected_interacting_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); - db::Edge2PolygonInteractingLocalOperation op (mode, inverse ? db::Edge2PolygonInteractingLocalOperation::Inverse : db::Edge2PolygonInteractingLocalOperation::Normal); + db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); return new db::DeepEdges (dl_out); } std::pair -DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode) const +DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { std::unique_ptr dr_holder; const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); @@ -1745,6 +1479,15 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti other_deep = dr_holder.get (); } + return selected_interacting_pair_generic_impl (other_deep, mode, min_count, max_count); +} + +std::pair +DeepEdges::selected_interacting_pair_generic_impl (const db::DeepRegion *other_deep, EdgeInteractionMode mode, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); @@ -1755,20 +1498,24 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti output_layers.push_back (dl_out.layer ()); output_layers.push_back (dl_out2.layer ()); - db::Edge2PolygonInteractingLocalOperation op (mode, db::Edge2PolygonInteractingLocalOperation::Both); + db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), output_layers); return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2)); } EdgesDelegate * -DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse) const +DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + std::unique_ptr dr_holder; const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (! other_deep) { @@ -1777,24 +1524,44 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if ((mode == EdgesOutside) == inverse) { + return clone (); + } else { + return new DeepEdges (deep_layer ().derived ()); + } + } + const db::DeepLayer &edges = merged_deep_layer (); + // NOTE: with counting the other region needs to be merged + const db::DeepLayer &other_edges = (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()); + DeepLayer dl_out (edges.derived ()); - db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal); + db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); + if (edges == other_edges) { + // with counting and two identical inputs, a copy needs to be made + db::DeepLayer copy (other_edges.copy ()); + proc.run (&op, edges.layer (), copy.layer (), dl_out.layer ()); + } else { + proc.run (&op, edges.layer (), other_edges.layer (), dl_out.layer ()); + } return new db::DeepEdges (dl_out); } std::pair -DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const +DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + std::unique_ptr dr_holder; const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (! other_deep) { @@ -1803,8 +1570,19 @@ DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractio other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if (mode != EdgesOutside) { + return std::make_pair (clone (), new DeepEdges (deep_layer ().derived ())); + } else { + return std::make_pair (new DeepEdges (deep_layer ().derived ()), clone ()); + } + } + const db::DeepLayer &edges = merged_deep_layer (); + // NOTE: with counting the other region needs to be merged + const db::DeepLayer &other_edges = (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()); + DeepLayer dl_out (edges.derived ()); DeepLayer dl_out2 (edges.derived ()); @@ -1813,13 +1591,20 @@ DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractio output_layers.push_back (dl_out.layer ()); output_layers.push_back (dl_out2.layer ()); - db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both); + db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers); + // NOTE: with counting the other region needs to be merged + if (edges == other_edges) { + // with counting and two identical inputs, a copy needs to be made + db::DeepLayer copy (other_edges.copy ()); + proc.run (&op, edges.layer (), copy.layer (), output_layers); + } else { + proc.run (&op, edges.layer (), other_edges.layer (), output_layers); + } return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2)); } @@ -1860,6 +1645,10 @@ EdgesDelegate *DeepEdges::pull_generic (const Edges &other) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return clone (); + } + const db::DeepLayer &edges = deep_layer (); const db::DeepLayer &other_edges = other_deep->merged_deep_layer (); @@ -1886,6 +1675,10 @@ EdgesDelegate *DeepEdges::in (const Edges &other, bool invert) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return invert ? new db::DeepEdges (deep_layer ().derived ()) : clone (); + } + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); @@ -1915,6 +1708,10 @@ std::pair DeepEdges::in_and_out (const Edges & other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return std::make_pair (clone (), new db::DeepEdges (deep_layer ().derived ())); + } + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 0578d704b..91d685695 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -188,15 +188,17 @@ private: void init (); void ensure_merged_edges_valid () const; - DeepLayer and_or_not_with(const DeepEdges *other, EdgeBoolOp op) const; + std::pair and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const; std::pair edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t op, bool include_borders) const; EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const db::EdgesCheckOptions &options) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; virtual RegionDelegate *pull_generic (const Region ®ion) const; - virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const; - virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const; + virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + EdgesDelegate *selected_interacting_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + std::pair selected_interacting_pair_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; DeepEdges *apply_filter (const EdgeFilterBase &filter) const; template OutputContainer *processed_impl (const edge_processor &filter) const; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f44f0fca7..d52df7f9c 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -797,6 +797,10 @@ DeepRegion::and_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::and_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { + + return clone (); + } else { return new DeepRegion (and_or_not_with (other_deep, true, property_constraint)); @@ -817,6 +821,10 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::not_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { + + return new DeepRegion (deep_layer ().derived ()); + } else { return new DeepRegion (and_or_not_with (other_deep, false, property_constraint)); @@ -825,8 +833,13 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain } RegionDelegate * -DeepRegion::or_with (const Region &other, db::PropertyConstraint /*property_constraint*/) const +DeepRegion::or_with (const Region &other, db::PropertyConstraint property_constraint) const { + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (other_deep && other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { + return clone (); + } + // TODO: implement property_constraint RegionDelegate *res = add (other); return res->merged_in_place (); @@ -849,6 +862,10 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr return AsIfFlatRegion::andnot_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { + + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); + } else { std::pair res = and_and_not_with (other_deep, property_constraint); @@ -962,6 +979,10 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { + + return new DeepRegion (deep_layer ().derived ()); + } else { // Implement XOR as (A-B)+(B-A) - only this implementation @@ -1335,7 +1356,7 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy) } EdgesDelegate * -DeepRegion::edges (const EdgeFilterBase *filter) const +DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr res (new db::DeepEdges (deep_layer ().derived ())); @@ -1343,7 +1364,7 @@ DeepRegion::edges (const EdgeFilterBase *filter) const return res.release (); } - if (! filter && merged_semantics () && ! merged_polygons_available ()) { + if (! proc && ! filter && merged_semantics () && ! merged_polygons_available ()) { // Hierarchical edge detector - no pre-merge required @@ -1388,15 +1409,32 @@ DeepRegion::edges (const EdgeFilterBase *filter) const const db::Shapes &s = c->shapes (polygons.layer ()); db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + std::vector heap; + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { db::Polygon poly; si->polygon (poly); - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected ((*e).transformed (tr))) { - st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + if (proc) { + + heap.clear (); + proc->process (poly, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } + } + } } @@ -2110,6 +2148,16 @@ DeepRegion::in_and_out_generic (const Region &other, InteractingOutputMode outpu other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + if (output_mode == PositiveAndNegative) { + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); + } else if (output_mode == Negative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } else { + return std::make_pair (clone (), (RegionDelegate *) 0); + } + } + const db::DeepLayer &polygons = merged_deep_layer (); const db::DeepLayer &other_polygons = other_deep->merged_deep_layer (); @@ -2171,6 +2219,26 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if (mode <= 0 /*inside or interacting*/) { + if (output_mode == PositiveAndNegative) { + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); + } else if (output_mode == Negative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } else { + return std::make_pair (clone (), (RegionDelegate *) 0); + } + } else { + if (output_mode == PositiveAndNegative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), clone ()); + } else if (output_mode == Negative) { + return std::make_pair (clone (), (RegionDelegate *) 0); + } else { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } + } + } + const db::DeepLayer &polygons = merged_deep_layer (); // NOTE: with counting, the other polygons must be merged const db::DeepLayer &other_polygons = counting ? other_deep->merged_deep_layer () : other_deep->deep_layer (); @@ -2188,7 +2256,13 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to bool result_is_merged = (! split_after && (merged_semantics () || is_merged ())); InteractingResultHolder orh (output_mode, result_is_merged, polygons); - proc.run (&op, polygons.layer (), other_polygons.layer (), orh.layers ()); + if (polygons == other_polygons) { + // with counting and two identical inputs we need to create a layer copy + db::DeepLayer other_copy (other_polygons.copy ()); + proc.run (&op, polygons.layer (), other_copy.layer (), orh.layers ()); + } else { + proc.run (&op, polygons.layer (), other_polygons.layer (), orh.layers ()); + } return orh.result_pair (); } @@ -2268,6 +2342,10 @@ DeepRegion::pull_generic (const Region &other, int mode, bool touching) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return clone (); + } + // in "inside" mode, the first argument needs to be merged too const db::DeepLayer &polygons = mode < 0 ? merged_deep_layer () : deep_layer (); const db::DeepLayer &other_polygons = other_deep->merged_deep_layer (); diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 229b2e696..1d5537554 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -119,7 +119,7 @@ public: virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index 11c4e6ed7..d9600f551 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -86,14 +86,14 @@ struct EdgeBooleanCluster { typedef db::Edge::coord_type coord_type; - EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op) - : mp_output (output), mp_output2 (0), m_op (op) + EdgeBooleanCluster (OutputContainer *output, std::set *dots, EdgeBoolOp op) + : mp_output (output), mp_output2 (0), mp_dots (dots), mp_dots2 (0), m_op (op) { // .. nothing yet .. } - EdgeBooleanCluster (OutputContainer *output, OutputContainer *output2, EdgeBoolOp op) - : mp_output (output), mp_output2 (output2), m_op (op) + EdgeBooleanCluster (OutputContainer *output, OutputContainer *output2, std::set *dots, std::set *dots2, EdgeBoolOp op) + : mp_output (output), mp_output2 (output2), mp_dots (dots), mp_dots2 (dots2), m_op (op) { // .. nothing yet .. } @@ -106,24 +106,55 @@ struct EdgeBooleanCluster if (begin () + 1 == end ()) { if (begin ()->second == 0) { if (m_op == EdgeAndNot) { - mp_output2->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots2->insert (begin ()->first->p1 ()); + } + } else if (mp_output2) { + mp_output2->insert (*(begin ()->first)); + } } else if (m_op != EdgeAnd) { - mp_output->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots->insert (begin ()->first->p1 ()); + } + } else if (mp_output) { + mp_output->insert (*(begin ()->first)); + } } } else { if (m_op != EdgeAnd && m_op != EdgeNot && m_op != EdgeAndNot) { - mp_output->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots->insert (begin ()->first->p1 ()); + } + } else if (mp_output) { + mp_output->insert (*(begin ()->first)); + } } } return; } - db::Edge r = *begin ()->first; + // search first non-degenerate, longest + + iterator main = end (); + for (iterator i = begin (); i != end (); ++i) { + if (! i->first->is_degenerate () && (main == end () || main->first->length () < i->first->length ())) { + main = i; + } + } + + if (main == end ()) { + return; + } + + db::Edge r = *main->first; double l1 = 0.0, l2 = r.double_length (); double n = 1.0 / l2; db::Point p1 = r.p1 (), p2 = r.p2 (); - for (iterator o = begin () + 1; o != end (); ++o) { + for (iterator o = begin (); o != end (); ++o) { double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; if (ll1 < l1) { @@ -245,6 +276,7 @@ struct EdgeBooleanCluster private: OutputContainer *mp_output, *mp_output2; + std::set *mp_dots, *mp_dots2; db::EdgeBoolOp m_op; }; @@ -253,15 +285,8 @@ struct EdgeBooleanClusterCollector : public db::cluster_collector > { EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op, OutputContainer *output2 = 0) - : db::cluster_collector > (EdgeBooleanCluster (output, output2, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), - mp_output (output), mp_intersections (op == EdgeIntersections ? output : 0) - { - // .. nothing yet .. - } - - EdgeBooleanClusterCollector (OutputContainer *output, OutputContainer *intersections, EdgeBoolOp op) - : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/), - mp_output (output), mp_intersections (intersections) + : db::cluster_collector > (EdgeBooleanCluster (output, output2, &m_dots, &m_dots2, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), + mp_output (output), mp_output2 (output2), m_op (op) { // .. nothing yet .. } @@ -281,11 +306,78 @@ struct EdgeBooleanClusterCollector db::cluster_collector >::add (o1, p1, o2, p2); - } else if (mp_intersections && p1 != p2) { + } else { + + // dots vs. edge or dot is handled here, no need to copy dots + if (o1->is_degenerate ()) { + this->ignore_single (o1); + } + if (o2->is_degenerate ()) { + this->ignore_single (o2); + } + + if (m_op == EdgeIntersections) { + + if (p1 != p2) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + } + + } else if (m_op == EdgeAndNot) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && (o1->is_degenerate () || o2->is_degenerate ())) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + if (o1->is_degenerate () && ! ip.first) { + m_dots2.insert (o1->p1 ()); + } + } + + } else if (m_op == EdgeAnd) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && (o1->is_degenerate () || o2->is_degenerate ())) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + } + + } else if (m_op == EdgeNot) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && o1->is_degenerate ()) { + std::pair ip = o1->intersect_point (*o2); + if (! ip.first) { + m_dots.insert (o1->p1 ()); + } + } + + } else if (m_op == EdgeOr) { + + // forward dots + if (o1->is_degenerate ()) { + m_dots.insert (o1->p1 ()); + } + if (o2->is_degenerate ()) { + m_dots.insert (o2->p1 ()); + } + + } else if (m_op == EdgeXor) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && o1->is_degenerate () && o2->is_degenerate ()) { + if (o1->p1 () != o2->p1 ()) { + m_dots.insert (o1->p1 ()); + m_dots.insert (o2->p1 ()); + } + } - std::pair ip = o1->intersect_point (*o2); - if (ip.first) { - m_intersections.insert (ip.second); } } @@ -340,35 +432,44 @@ struct EdgeBooleanClusterCollector }; /** - * @brief Finalizes the implementation for "EdgeIntersections" + * @brief Finalizes the implementation for "sections" * This method pushes those points which don't interact with the edges to the output container * as degenerate edges. It needs to be called after the pass has been made. */ void finalize (bool) { - if (m_intersections.empty ()) { + add_orphan_dots (m_dots, mp_output); + if (mp_output2) { + add_orphan_dots (m_dots2, mp_output2); + } + } + +private: + OutputContainer *mp_output, *mp_output2; + EdgeBoolOp m_op; + std::set m_dots, m_dots2; + + static void add_orphan_dots (const std::set &dots, OutputContainer *output) + { + if (dots.empty ()) { return; } - db::box_scanner2 intersections_to_edge_scanner; - for (typename OutputContainer::const_iterator e = mp_output->begin (); e != mp_output->end (); ++e) { - intersections_to_edge_scanner.insert1 (e.operator-> (), 0); + db::box_scanner2 dots_to_edge_scanner; + for (typename OutputContainer::const_iterator e = output->begin (); e != output->end (); ++e) { + dots_to_edge_scanner.insert1 (e.operator-> (), 0); } - for (std::set::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) { - intersections_to_edge_scanner.insert2 (p.operator-> (), 0); + for (std::set::const_iterator p = dots.begin (); p != dots.end (); ++p) { + dots_to_edge_scanner.insert2 (p.operator-> (), 0); } std::set points_to_remove; RemovePointsOnEdges rpoe (points_to_remove); - intersections_to_edge_scanner.process (rpoe, 1, db::box_convert (), db::box_convert ()); + dots_to_edge_scanner.process (rpoe, 1, db::box_convert (), db::box_convert ()); + + std::set_difference (dots.begin (), dots.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (output)); - std::set_difference (m_intersections.begin (), m_intersections.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (mp_intersections)); } - -private: - OutputContainer *mp_output; - OutputContainer *mp_intersections; - std::set m_intersections; }; /** diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index fcfd405f9..6e0d9059e 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -30,6 +30,9 @@ #include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" @@ -93,6 +96,23 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con mp_delegate = new DeepEdgePairs (si, dss, trans); } +void +EdgePairs::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGE_PAIRS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void EdgePairs::insert (const Sh &shape) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 9d1b1aee2..53a499689 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -185,6 +185,14 @@ public: */ explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the edge pair collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 9f1c3f11f..4f0534ae8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -251,6 +251,13 @@ struct CutPoints } + // do not insert points twice + for (auto c = cut_points.begin (); c != cut_points.end (); ++c) { + if (*c == p) { + return; + } + } + cut_points.push_back (p); } @@ -1057,6 +1064,12 @@ EdgeProcessor::reserve (size_t n) mp_work_edges->reserve (n); } +size_t +EdgeProcessor::count () const +{ + return mp_work_edges->size (); +} + void EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index a6fb2a680..30de5b35d 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -695,6 +695,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Insert an edge */ diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e5a6cf007..5cdefe414 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -28,6 +28,9 @@ #include "dbFlatEdges.h" #include "dbEdgesUtils.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" namespace db { @@ -141,6 +144,23 @@ Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) } } +void +Edges::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGES")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + void Edges::clear () { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index e5317f20d..ffb8ab6ff 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -833,6 +833,14 @@ public: return *this; } + /** + * @brief Writes the edge collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Intersections with other edges * Intersections are similar to "AND", but will also report @@ -991,9 +999,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Region &other) + Edges &select_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1002,9 +1010,9 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Region &other) const + Edges selected_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** @@ -1013,9 +1021,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Region &other) + Edges &select_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1024,17 +1032,17 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Region &other) const + Edges selected_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region together with the ones that do not */ - std::pair selected_interacting_differential (const Region &other) const + std::pair selected_interacting_differential (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1280,9 +1288,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Edges &other) + Edges &select_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1291,17 +1299,17 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Edges &other) const + Edges selected_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set together with the ones that do not */ - std::pair selected_interacting_differential (const Edges &other) const + std::pair selected_interacting_differential (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1311,9 +1319,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Edges &other) + Edges &select_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1322,9 +1330,9 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Edges &other) const + Edges selected_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 051d671ee..5215da02f 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -255,12 +255,12 @@ public: virtual std::pair inside_outside_part_pair (const Region &other) const = 0; virtual RegionDelegate *pull_interacting (const Region &) const = 0; virtual EdgesDelegate *pull_interacting (const Edges &) const = 0; - virtual EdgesDelegate *selected_interacting (const Region &other) const = 0; - virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0; - virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0; - virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0; - virtual std::pair selected_interacting_pair (const Region &other) const = 0; - virtual std::pair selected_interacting_pair (const Edges &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0; + virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const = 0; virtual EdgesDelegate *selected_outside (const Region &other) const = 0; virtual EdgesDelegate *selected_not_outside (const Region &other) const = 0; diff --git a/src/db/db/dbEdgesLocalOperations.cc b/src/db/db/dbEdgesLocalOperations.cc index e201df8b5..3572b131c 100644 --- a/src/db/db/dbEdgesLocalOperations.cc +++ b/src/db/db/dbEdgesLocalOperations.cc @@ -94,6 +94,9 @@ EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, db::C if (! is_and) { result.insert (subject); } + if (result2) { + result2->insert (subject); + } } else { scanner.insert (&subject, 0); any_subject = true; @@ -199,5 +202,357 @@ EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell } } +// --------------------------------------------------------------------------------------------- +// Edge2EdgeInteractingLocalOperation implementation + +Edge2EdgeInteractingLocalOperation::Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count) + : m_mode (mode), m_output_mode (output_mode), m_min_count (min_count), m_max_count (max_count) +{ + // .. nothing yet .. +} + +db::Coord Edge2EdgeInteractingLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2EdgeInteractingLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == (m_output_mode == Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (m_output_mode == Both) { + result2 = &results[1]; + } + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (&subject, 0); + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + if (m_output_mode == Inverse || m_output_mode == Both) { + + std::unordered_set interacting; + edge_interaction_filter > filter (interacting, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert ()); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (interacting.find (subject) == interacting.end ()) { + if (m_output_mode != Both) { + result.insert (subject); + } else { + result2->insert (subject); + } + } else if (m_output_mode == Both) { + result.insert (subject); + } + + } + + } else { + + edge_interaction_filter > filter (result, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert ()); + + } + +} + +OnEmptyIntruderHint Edge2EdgeInteractingLocalOperation::on_empty_intruder_hint () const +{ + if (m_mode == EdgesOutside) { + return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); + } else { + return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); + } +} + +std::string Edge2EdgeInteractingLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting edges")); +} + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +Edge2EdgePullLocalOperation::Edge2EdgePullLocalOperation () +{ + // .. nothing yet .. +} + +db::Coord Edge2EdgePullLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2EdgePullLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (&subject, 1); + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 0); + } + + edge_interaction_filter > filter (result, EdgesInteract, size_t (1), std::numeric_limits::max ()); + scanner.process (filter, 1, db::box_convert ()); + +} + +OnEmptyIntruderHint Edge2EdgePullLocalOperation::on_empty_intruder_hint () const +{ + return Drop; +} + +std::string Edge2EdgePullLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting edges from other")); +} + + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +template +edge_to_polygon_interacting_local_operation::edge_to_polygon_interacting_local_operation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count) + : m_mode (mode), m_output_mode (output_mode), m_min_count (min_count), m_max_count (max_count) +{ + // .. nothing yet .. +} + +template +db::Coord edge_to_polygon_interacting_local_operation::dist () const +{ + // touching is sufficient + return 1; +} + +static const db::Polygon *deref (const db::Polygon &poly, std::list &) +{ + return &poly; +} + +static const db::Polygon *deref (const db::PolygonRef &pref, std::list &heap) +{ + heap.push_back (pref.obj ().transformed (pref.trans ())); + return & heap.back (); +} + +template +void edge_to_polygon_interacting_local_operation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (m_output_mode == Both) { + result2 = &results[1]; + } + + db::box_scanner2 scanner; + + std::set others; + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (typename shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert1 (&subject, 0); + } + + std::list heap; + for (typename std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert2 (deref (*o, heap), 1); + } + + if (m_output_mode == Inverse || m_output_mode == Both) { + + std::unordered_set interacting; + edge_to_polygon_interaction_filter > filter (&interacting, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + + if (interacting.find (subject) == interacting.end ()) { + if (m_output_mode != Both) { + result.insert (subject); + } else { + result2->insert (subject); + } + } else if (m_output_mode == Both) { + result.insert (subject); + } + + } + + } else { + + edge_to_polygon_interaction_filter > filter (&result, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + } +} + +template +OnEmptyIntruderHint edge_to_polygon_interacting_local_operation::on_empty_intruder_hint () const +{ + if (m_mode == EdgesOutside) { + return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); + } else { + return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); + } +} + +template +std::string edge_to_polygon_interacting_local_operation::description () const +{ + if (m_mode == EdgesInteract) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-interacting edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select interacting edges")); + } else { + return tl::to_string (tr ("Select interacting and non-interacting edges")); + } + } else if (m_mode == EdgesInside) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-inside edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select inside edges")); + } else { + return tl::to_string (tr ("Select inside and non-inside edges")); + } + } else if (m_mode == EdgesOutside) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-outside edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select outside edges")); + } else { + return tl::to_string (tr ("Select outside and non-outside edges")); + } + } + return std::string (); +} + +template class edge_to_polygon_interacting_local_operation; +template class edge_to_polygon_interacting_local_operation; + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +namespace { + +struct ResultInserter +{ + typedef db::Polygon value_type; + + ResultInserter (db::Layout *layout, std::unordered_set &result) + : mp_layout (layout), mp_result (&result) + { + // .. nothing yet .. + } + + void insert (const db::Polygon &p) + { + (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::unordered_set *mp_result; +}; + +} + +Edge2PolygonPullLocalOperation::Edge2PolygonPullLocalOperation () +{ + // .. nothing yet .. +} + +db::Coord Edge2PolygonPullLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2PolygonPullLocalOperation::do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::box_scanner2 scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert1 (&subject, 1); + } + + std::list heap; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + heap.push_back (o->obj ().transformed (o->trans ())); + scanner.insert2 (& heap.back (), 0); + } + + ResultInserter inserter (layout, result); + edge_to_polygon_interaction_filter filter (&inserter, EdgesInteract, size_t (1), std::numeric_limits::max ()); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); +} + +OnEmptyIntruderHint Edge2PolygonPullLocalOperation::on_empty_intruder_hint () const +{ + return Drop; +} + +std::string Edge2PolygonPullLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting regions")); +} + } diff --git a/src/db/db/dbEdgesLocalOperations.h b/src/db/db/dbEdgesLocalOperations.h index 9a5f9bdc4..b0e6f82f4 100644 --- a/src/db/db/dbEdgesLocalOperations.h +++ b/src/db/db/dbEdgesLocalOperations.h @@ -31,6 +31,7 @@ #include "dbEdgeBoolean.h" #include "dbEdgeProcessor.h" #include "dbLocalOperation.h" +#include "dbEdgesUtils.h" namespace db { @@ -79,6 +80,81 @@ private: bool m_include_borders; }; +/** + * @brief Implements edge-to-edge interactions + */ +class DB_PUBLIC Edge2EdgeInteractingLocalOperation + : public local_operation +{ +public: + enum output_mode_t { Normal, Inverse, Both }; + + Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + EdgeInteractionMode m_mode; + output_mode_t m_output_mode; + size_t m_min_count, m_max_count; +}; + +/** + * @brief Implements edge-to-edge interactions (pull mode) + */ +class DB_PUBLIC Edge2EdgePullLocalOperation + : public local_operation +{ +public: + Edge2EdgePullLocalOperation (); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; +}; + +/** + * @brief Implements edge-to-polygon interactions + */ +template +class DB_PUBLIC edge_to_polygon_interacting_local_operation + : public local_operation +{ +public: + enum output_mode_t { Normal, Inverse, Both }; + + edge_to_polygon_interacting_local_operation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + EdgeInteractionMode m_mode; + output_mode_t m_output_mode; + size_t m_min_count, m_max_count; +}; + +/** + * @brief Implements edge-to-polygon interactions (pull mode) + */ +class DB_PUBLIC Edge2PolygonPullLocalOperation + : public local_operation +{ +public: + Edge2PolygonPullLocalOperation (); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; +}; + } #endif diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 686cce8c9..56d345b2a 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -189,17 +189,22 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector &res) { double l = std::max (edge.double_length () * m_fraction, double (m_length)); + db::DVector ds; + if (! edge.is_degenerate ()) { + ds = db::DVector (edge.d ()) * (l / edge.double_length ()); + } + if (m_mode < 0) { - res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + db::DVector (edge.d ()) * (l / edge.double_length ())))); + res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + ds))); } else if (m_mode > 0) { - res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - db::DVector (edge.d ()) * (l / edge.double_length ())), edge.p2 ())); + res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - ds), edge.p2 ())); } else { - db::DVector dl = db::DVector (edge.d ()) * (0.5 * l / edge.double_length ()); + db::DVector dl = ds * 0.5; db::DPoint center = db::DPoint (edge.p1 ()) + db::DVector (edge.p2 () - edge.p1 ()) * 0.5; res.push_back (db::Edge (db::Point (center - dl), db::Point (center + dl))); @@ -403,16 +408,24 @@ struct DetectTagEdgeSink static bool edge_is_inside_or_outside (bool outside, const db::Edge &a, const db::Polygon &b) { - db::EdgeProcessor ep; - ep.insert (b, 0); + if (a.is_degenerate ()) { - ep.insert (a, 1); + return ((db::inside_poly (b.begin_edge (), a.p1 ()) <= 0) == outside); - DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail - db::EdgePolygonOp op (db::EdgePolygonOp::Both, true /*include borders*/); - ep.process (es, op); + } else { - return es.result; + db::EdgeProcessor ep; + ep.insert (b, 0); + + ep.insert (a, 1); + + DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail + db::EdgePolygonOp op (db::EdgePolygonOp::Both, !outside /*include borders in inside*/); + ep.process (es, op); + + return es.result; + + } } bool edge_is_inside (const db::Edge &a, const db::Polygon &b) diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 939cd6140..531a18fca 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -339,16 +339,39 @@ class edge_interaction_filter : public db::box_scanner_receiver { public: - edge_interaction_filter (OutputContainer &output, EdgeInteractionMode mode) - : mp_output (&output), m_mode (mode) + edge_interaction_filter (OutputContainer &output, EdgeInteractionMode mode, size_t min_count, size_t max_count) + : mp_output (&output), m_mode (mode), m_min_count (min_count), m_max_count (max_count) { - // .. nothing yet .. + // NOTE: "counting" does not really make much sense in Outside mode ... + m_counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + tl_assert (!m_counting || mode != EdgesOutside); } void finish (const db::Edge *o, size_t p) { - if (p == 0 && m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { - mp_output->insert (*o); + if (p != 0) { + return; + } + + if (m_counting) { + + size_t count = 0; + auto i = m_counts.find (o); + if (i != m_counts.end ()) { + count = i->second; + } + + bool match = (count >= m_min_count && count <= m_max_count); + if (match == (m_mode != EdgesOutside)) { + mp_output->insert (*o); + } + + } else { + + if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { + mp_output->insert (*o); + } + } } @@ -363,14 +386,22 @@ public: if ((m_mode == EdgesInteract && db::edge_interacts (*o, *oo)) || (m_mode == EdgesInside && db::edge_is_inside (*o, *oo))) { - if (m_seen.insert (o).second) { - mp_output->insert (*o); + if (m_counting) { + m_counts[o] += 1; + } else { + if (m_seen.insert (o).second) { + mp_output->insert (*o); + } } } else if (m_mode == EdgesOutside && ! db::edge_is_outside (*o, *oo)) { // In this case we need to collect edges which are outside always - we report those on "finished". - m_seen.insert (o); + if (m_counting) { + m_counts[o] += 1; + } else { + m_seen.insert (o); + } } @@ -380,7 +411,10 @@ public: private: OutputContainer *mp_output; std::set m_seen; + std::map m_counts; EdgeInteractionMode m_mode; + size_t m_min_count, m_max_count; + bool m_counting; }; /** @@ -408,20 +442,39 @@ DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Polygon &b); * There is a special box converter which is able to sort that out as well. */ template -class edge_to_region_interaction_filter +class edge_to_polygon_interaction_filter : public db::box_scanner_receiver2 { public: - edge_to_region_interaction_filter (OutputContainer *output, EdgeInteractionMode mode) - : mp_output (output), m_mode (mode) + edge_to_polygon_interaction_filter (OutputContainer *output, EdgeInteractionMode mode, size_t min_count, size_t max_count) + : mp_output (output), m_mode (mode), m_min_count (min_count), m_max_count (max_count) { - // .. nothing yet .. + // NOTE: "counting" does not really make much sense in Outside mode ... + m_counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + tl_assert (!m_counting || mode != EdgesOutside); } void finish (const OutputType *o) { - if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { - mp_output->insert (*o); + if (m_counting) { + + size_t count = 0; + auto i = m_counts.find (o); + if (i != m_counts.end ()) { + count = i->second; + } + + bool match = (count >= m_min_count && count <= m_max_count); + if (match == (m_mode != EdgesOutside)) { + mp_output->insert (*o); + } + + } else { + + if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { + mp_output->insert (*o); + } + } } @@ -448,7 +501,18 @@ public: const OutputType *ep = 0; tl::select (ep, e, p); - if (m_seen.find (ep) == m_seen.end ()) { + if (m_counting) { + + if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) || + (m_mode == EdgesInside && db::edge_is_inside (*e, *p)) || + (m_mode == EdgesOutside && ! db::edge_is_outside (*e, *p))) { + + // we report the result on "finish" here. + m_counts[ep] += 1; + + } + + } else if (m_seen.find (ep) == m_seen.end ()) { if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) || (m_mode == EdgesInside && db::edge_is_inside (*e, *p))) { @@ -468,8 +532,11 @@ public: private: OutputContainer *mp_output; + std::map m_counts; std::set m_seen; EdgeInteractionMode m_mode; + size_t m_min_count, m_max_count; + bool m_counting; }; /** diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 92bab3379..ec265156e 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -94,12 +94,12 @@ public: virtual RegionDelegate *pull_interacting (const Region &) const; virtual EdgesDelegate *pull_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); } - virtual std::pair selected_interacting_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } - virtual std::pair selected_interacting_pair (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } + virtual EdgesDelegate *selected_interacting (const Edges &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Edges &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Region &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Region &, size_t, size_t) const { return new EmptyEdges (); } + virtual std::pair selected_interacting_pair (const Region &, size_t, size_t) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } + virtual std::pair selected_interacting_pair (const Edges &, size_t, size_t) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } virtual EdgesDelegate *selected_outside (const Region &) const { return new EmptyEdges (); } virtual EdgesDelegate *selected_not_outside (const Region &) const { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc index 49f87313b..a9b5e36e0 100644 --- a/src/db/db/dbEmptyRegion.cc +++ b/src/db/db/dbEmptyRegion.cc @@ -176,7 +176,7 @@ EmptyRegion::angle_check (double, double, bool) const } EdgesDelegate * -EmptyRegion::edges (const EdgeFilterBase *) const +EmptyRegion::edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 874945a7b..086ea9837 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -82,7 +82,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return this; } virtual RegionDelegate *scaled_and_snapped (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return new EmptyRegion (); } - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; } diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 085bb255f..8062902fb 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -133,9 +133,7 @@ FlatEdges::ensure_merged_edges_valid () const scanner.reserve (mp_edges->size ()); for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } + scanner.insert (&*e, 0); } scanner.process (cluster_collector, 1, db::box_convert ()); @@ -145,9 +143,7 @@ FlatEdges::ensure_merged_edges_valid () const 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-> ()); - } + edges_by_props [e.prop_id ()].push_back (e.operator-> ()); } for (auto s2p = edges_by_props.begin (); s2p != edges_by_props.end (); ++s2p) { diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index d96bb8a22..0d79dbe3e 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -43,7 +43,6 @@ FlatRegion::FlatRegion (const FlatRegion &other) : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons), mp_properties_repository (other.mp_properties_repository) { init (); - m_is_merged = other.m_is_merged; m_merged_polygons_valid = other.m_merged_polygons_valid; } @@ -52,15 +51,22 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } +FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) +{ + init (); + m_is_merged = is_merged; + transform_generic (trans); + set_merged_semantics (merged_semantics); +} + FlatRegion::FlatRegion (bool is_merged) : 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; } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index d89e53173..55805a8a5 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -52,7 +52,8 @@ public: typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (); - FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (const db::Shapes &polygons, bool is_merged = false); + FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); FlatRegion (bool is_merged); FlatRegion (const FlatRegion &other); diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 75e1ad5b2..b2b5ae467 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -2575,6 +2575,7 @@ 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; @@ -2591,6 +2592,7 @@ 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; @@ -2621,6 +2623,7 @@ 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; @@ -2644,6 +2647,7 @@ 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 @@ -2671,6 +2675,7 @@ 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/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 48961bf32..24b7f4786 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -101,6 +101,7 @@ 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; diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 08e058683..3687ef71a 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -30,6 +30,7 @@ #include "dbLayout.h" #include "tlVariant.h" #include "tlObject.h" +#include "tlOptional.h" namespace db { @@ -267,6 +268,56 @@ public: m_choice_descriptions = choice_descriptions; } + /** + * @brief Sets the minimum value + * + * The minimum value is a visual feature and limits the allowed values for numerical + * entry boxes. This applies to parameters of type int or double. The minimum value + * is not effective if choices are present. + * + * The minimum value is not enforced - for example there is no restriction implemented + * when setting values programmatically. + * + * Setting this attribute to "nil" (the default) implies "no limit". + */ + void set_min_value (const tl::Variant &min) + { + m_min_value = min; + } + + /** + * @brief Gets the minimum value (see \set_min_value) + */ + const tl::Variant &min_value () const + { + return m_min_value; + } + + /** + * @brief Sets the maximum value + * + * The maximum value is a visual feature and limits the allowed values for numerical + * entry boxes. This applies to parameters of type int or double. The maximum value + * is not effective if choices are present. + * + * The maximum value is not enforced - for example there is no restriction implemented + * when setting values programmatically. + * + * Setting this attribute to "nil" (the default) implies "no limit". + */ + void set_max_value (const tl::Variant &max) + { + m_max_value = max; + } + + /** + * @brief Gets the maximum value (see \set_max_value) + */ + const tl::Variant &max_value () const + { + return m_max_value; + } + /** * @brief Equality */ @@ -280,7 +331,9 @@ public: m_type == d.m_type && m_name == d.m_name && m_description == d.m_description && - m_unit == d.m_unit; + m_unit == d.m_unit && + m_min_value == d.m_min_value && + m_max_value == d.m_max_value; } private: @@ -291,6 +344,7 @@ private: type m_type; std::string m_name; std::string m_description, m_unit; + tl::Variant m_min_value, m_max_value; }; /** diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 9a8f6052f..cc02f75a9 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -48,6 +48,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI mp_shape_prop_sel = d.mp_shape_prop_sel; m_shape_inv_prop_sel = d.m_shape_inv_prop_sel; m_overlapping = d.m_overlapping; + m_for_merged_input = d.m_for_merged_input; + m_start = d.m_start; m_stop = d.m_stop; @@ -80,6 +82,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_cells = d.m_cells; m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; + m_skip_shapes_stack = d.m_skip_shapes_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -99,6 +102,7 @@ RecursiveShapeIterator::RecursiveShapeIterator () mp_cell = 0; m_current_layer = 0; m_overlapping = false; + m_for_merged_input = false; m_max_depth = std::numeric_limits::max (); // all m_min_depth = 0; m_shape_flags = shape_iterator::All; @@ -116,6 +120,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes) mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = false; + m_for_merged_input = false; init (); init_region (box_type::world ()); } @@ -127,6 +132,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } @@ -138,11 +144,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -151,11 +158,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -164,11 +172,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -177,11 +186,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -191,11 +201,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -205,11 +216,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -219,11 +231,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -233,11 +246,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -247,11 +261,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -261,6 +276,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } @@ -447,6 +463,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.clear (); m_local_region_stack.push_back (m_global_trans.inverted () * m_region); + m_skip_shapes_stack.clear (); + m_skip_shapes_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -723,11 +741,21 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const if (is_empty) { + // skip entire cell ++m_inst; new_inst (receiver); - } else { - down (receiver); + } else if (!down (receiver)) { + + // skip this instance array member + ++m_inst_array; + new_inst_member (receiver); + + if (m_inst_array.at_end ()) { + ++m_inst; + new_inst (receiver); + } + } } else { @@ -755,9 +783,42 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } } -void +bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { + bool skip_shapes = false; + + if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { + + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + box_type inst_bx; + if (m_inst->size () == 1) { + inst_bx = m_inst->bbox (m_box_convert); + } else { + inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + } + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + skip_shapes = true; + break; + } + ++si; + } + + } + + if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { + return false; + } + tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -779,11 +840,13 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const box_type new_region = box_type::world (); // compute the region inside the new cell - if (new_region != m_region) { - new_region = m_trans.inverted () * m_region; + if (new_region != m_local_region_stack.back ()) { + new_region = m_inst->complex_trans (*m_inst_array).inverted () * m_local_region_stack.back (); new_region &= cell_bbox (cell_index ()); } + m_local_region_stack.push_back (new_region); + m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); if (! m_local_complex_region_stack.empty ()) { @@ -817,11 +880,25 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } - if (receiver) { - receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); - } + // do not descend if the box is empty - new_cell (receiver); + if (m_local_region_stack.back ().empty ()) { + + pop (); + + return false; + + } else { + + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); + + return true; + + } } void @@ -831,6 +908,12 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const receiver->leave_cell (this, cell ()); } + pop (); +} + +void +RecursiveShapeIterator::pop () const +{ m_shape = shape_iterator (); m_shape_quad_id = 0; @@ -846,6 +929,7 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const mp_cell = m_cells.back (); m_cells.pop_back (); m_local_region_stack.pop_back (); + m_skip_shapes_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -870,7 +954,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -901,12 +985,16 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + if (m_overlapping) { + m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); + } else { + m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + } m_inst_quad_id = 0; // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); } @@ -922,7 +1010,7 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const while (! m_inst.at_end ()) { // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); if (m_inst.at_end ()) { break; @@ -950,7 +1038,11 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const // a singular iterator m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); } else if (with_region) { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + if (m_overlapping) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1)), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } } else { m_inst_array = m_inst->cell_inst ().begin (); } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 43f913b1a..532fdd247 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -122,12 +122,13 @@ public: * @param layer The layer from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor @@ -137,13 +138,14 @@ public: * @param layer The layer from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration @@ -153,8 +155,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layer The layer from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -164,12 +167,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -179,13 +183,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -195,12 +200,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -210,13 +216,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -226,8 +233,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -237,8 +245,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input = false); /** * @brief Destructor @@ -427,6 +436,25 @@ public: } } + /** + * @brief Gets a flag indicating whether optimizing for merged input + */ + bool for_merged_input () const + { + return m_for_merged_input; + } + + /** + * @brief Sets a flag indicating whether optimizing for merged input + */ + void set_for_merged_input (bool f) + { + if (m_for_merged_input != f) { + m_for_merged_input = f; + m_needs_reinit = true; + } + } + /** * @brief Sets a global transformation * @@ -812,7 +840,7 @@ private: unsigned int m_shape_flags; const shape_iterator::property_selector *mp_shape_prop_sel; bool m_shape_inv_prop_sel; - bool m_overlapping; + bool m_overlapping, m_for_merged_input; std::set m_start, m_stop; cplx_trans_type m_global_trans; db::PropertiesTranslator m_property_translator; @@ -839,6 +867,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; + mutable std::vector m_skip_shapes_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; @@ -858,7 +887,8 @@ private: void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; void up (RecursiveShapeReceiver *receiver) const; - void down (RecursiveShapeReceiver *receiver) const; + bool down (RecursiveShapeReceiver *receiver) const; + void pop () const; bool is_outside_complex_region (const db::Box &box) const; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 731047698..2d04a2758 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -31,6 +31,9 @@ #include "dbFlatEdges.h" #include "dbPolygonTools.h" #include "dbCompoundOperation.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlGlobPattern.h" // NOTE: include this to provide the symbols for "make_variant" @@ -74,14 +77,42 @@ Region &Region::operator= (const Region &other) return *this; } -Region::Region (const RecursiveShapeIterator &si) +Region::Region (const RecursiveShapeIterator &si, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si); + mp_delegate = new OriginalLayerRegion (si, db::ICplxTrans (), merged_semantics, is_merged); } -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics, is_merged); +} + +Region::Region (const Shapes &shapes, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); +} + +Region::Region (const Shapes &shapes, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s, trans); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); } Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) @@ -101,6 +132,23 @@ Region::Region (DeepShapeStore &dss) mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Region::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("REGION")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + const db::RecursiveShapeIterator & Region::iter () const { @@ -556,16 +604,23 @@ Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); - return Edges (res.release ()); + Edges edges (res.release ()); + edges.set_merged_semantics (false); + return edges; } + db::Edges edges; + text_shape_receiver > pipe = text_shape_receiver > (dot_delivery (), pat, pattern, dr); if (dr && dr->deep_layer ().store () == &store) { - return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); + edges = Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); } else { - return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); + edges = Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); } + + edges.set_merged_semantics (false); + return edges; } Region diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index ee62f4390..145018ef0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -199,7 +199,7 @@ public: * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - explicit Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator with a transformation @@ -208,7 +208,23 @@ public: * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container + * + * Creates a region from a shapes container. + */ + explicit Region (const Shapes &si, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container with a transformation + * + * Creates a region from a recursive shape iterator. This allows feeding a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + explicit Region (const Shapes &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation @@ -232,6 +248,14 @@ public: */ explicit Region (DeepShapeStore &dss); + /** + * @brief Writes the region to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ @@ -770,7 +794,7 @@ public: */ Edges edges () const { - return Edges (mp_delegate->edges (0)); + return Edges (mp_delegate->edges (0, 0)); } /** @@ -783,7 +807,34 @@ public: */ Edges edges (const EdgeFilterBase &filter) const { - return mp_delegate->edges (&filter); + return mp_delegate->edges (&filter, 0); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const db::PolygonToEdgeProcessorBase &proc) const + { + return Edges (mp_delegate->edges (0, &proc)); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * This version allows one to specify a filter by which the edges are filtered before they are + * returned. + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const EdgeFilterBase &filter, const db::PolygonToEdgeProcessorBase &proc) const + { + return mp_delegate->edges (&filter, &proc); } /** diff --git a/src/db/db/dbRegionCheckUtils.cc b/src/db/db/dbRegionCheckUtils.cc index 8b7dbf249..9ace8d834 100644 --- a/src/db/db/dbRegionCheckUtils.cc +++ b/src/db/db/dbRegionCheckUtils.cc @@ -133,7 +133,7 @@ Edge2EdgeCheckBase::finish (const Edge *o, size_t p) std::set partial_edges; - db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); + db::EdgeBooleanCluster > ec (&partial_edges, 0, db::EdgeNot); ec.add (o, 0); for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index b1c32aad0..cfc627cf7 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -211,7 +211,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; - virtual EdgesDelegate *edges (const EdgeFilterBase *filter) const = 0; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const = 0; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0; diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index ce809ac93..63fcdea3b 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -136,10 +136,104 @@ bool RelativeExtentsAsEdges::result_must_not_be_merged () const // ----------------------------------------------------------------------------------- // PolygonToEdgeProcessor implementation +PolygonToEdgeProcessor::PolygonToEdgeProcessor (PolygonToEdgeProcessor::EdgeMode mode) + : m_mode (mode) +{ + // .. nothing yet .. +} + +inline void +next (db::Polygon::contour_type::simple_iterator &iter, const db::Polygon::contour_type &contour) +{ + if (++iter == contour.end ()) { + iter = contour.begin (); + } +} + +static void +contour_to_edges (const db::Polygon::contour_type &contour, PolygonToEdgeProcessor::EdgeMode mode, std::vector &result) +{ + if (contour.size () < 3) { + return; + } + + db::Polygon::contour_type::simple_iterator pm1 = contour.begin (); + db::Polygon::contour_type::simple_iterator p0 = pm1; + next (p0, contour); + db::Polygon::contour_type::simple_iterator p1 = p0; + next (p1, contour); + db::Polygon::contour_type::simple_iterator p2 = p1; + next (p2, contour); + + while (pm1 != contour.end ()) { + + int s1 = db::vprod_sign (*p0 - *pm1, *p1 - *p0); + int s2 = db::vprod_sign (*p1 - *p0, *p2 - *p1); + + bool take = true; + + switch (mode) { + case PolygonToEdgeProcessor::All: + default: + break; + case PolygonToEdgeProcessor::Convex: + take = s1 < 0 && s2 < 0; + break; + case PolygonToEdgeProcessor::NotConvex: + take = ! (s1 < 0 && s2 < 0); + break; + case PolygonToEdgeProcessor::Concave: + take = s1 > 0 && s2 > 0; + break; + case PolygonToEdgeProcessor::NotConcave: + take = ! (s1 > 0 && s2 > 0); + break; + case PolygonToEdgeProcessor::StepOut: + take = s1 > 0 && s2 < 0; + break; + case PolygonToEdgeProcessor::NotStepOut: + take = ! (s1 > 0 && s2 < 0); + break; + case PolygonToEdgeProcessor::StepIn: + take = s1 < 0 && s2 > 0; + break; + case PolygonToEdgeProcessor::NotStepIn: + take = ! (s1 < 0 && s2 > 0); + break; + case PolygonToEdgeProcessor::Step: + take = s1 * s2 < 0; + break; + case PolygonToEdgeProcessor::NotStep: + take = ! (s1 * s2 < 0); + break; + } + + if (take) { + result.push_back (db::Edge (*p0, *p1)); + } + + ++pm1; + next (p0, contour); + next (p1, contour); + next (p2, contour); + + } +} + void PolygonToEdgeProcessor::process (const db::Polygon &poly, std::vector &result) const { - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - result.push_back (*e); + if (m_mode == All) { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + result.push_back (*e); + } + + } else { + + for (unsigned int i = 0; i < poly.holes () + 1; ++i) { + contour_to_edges (poly.contour (i), m_mode, result); + } + } } diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index 74f1a5100..08553ca25 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -293,12 +293,15 @@ class DB_PUBLIC PolygonToEdgeProcessor : public db::PolygonToEdgeProcessorBase { public: - PolygonToEdgeProcessor () - { - // .. nothing yet .. - } + enum EdgeMode { All = 0, Convex, Concave, StepIn, StepOut, Step, + NotConvex, NotConcave, NotStepIn, NotStepOut, NotStep }; + + PolygonToEdgeProcessor (EdgeMode mode = All); void process (const db::Polygon &poly, std::vector &result) const; + +private: + EdgeMode m_mode; }; /** diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 47fc28776..e5c1d7820 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -807,6 +807,61 @@ Shape::box_type Shape::bbox () const } } +Shape::box_type Shape::rectangle () const +{ + if (is_box ()) { + return box (); + } + + switch (m_type) { + case db::Shape::Polygon: + return polygon ().is_box () ? polygon ().box () : box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return polygon_ref ().is_box () ? polygon_ref ().box () : box_type (); + case db::Shape::SimplePolygon: + return simple_polygon ().is_box () ? simple_polygon ().box () : box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return simple_polygon_ref ().is_box () ? simple_polygon_ref ().box () : box_type (); + case db::Shape::Path: + { + const path_type &p = path (); + if (! p.round () && p.points () <= 2 && p.points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + case db::Shape::PathRef: + case db::Shape::PathPtrArrayMember: + { + const path_ref_type &p = path_ref (); + if (! p.ptr ()->round () && p.ptr ()->points () <= 2 && p.ptr ()->points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.ptr ()->points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + default: + break; + } + + return box_type (); +} + std::string Shape::to_string () const { diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 5ab336667..dd003b47e 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2651,6 +2651,16 @@ public: */ box_type bbox () const; + /** + * @brief Returns the box if the object represents a rectangle or an empty box if not + * + * This method returns the rectangle (aka box) the shape represents a polygon + * that is a rectangle, a path with two points and no rounded ends or an actual box. + * + * If not, an empty box is returned. + */ + box_type rectangle () const; + /** * @brief Compute the area of the shape */ diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc index 518aaa9e7..974fd469b 100644 --- a/src/db/db/dbShapeProcessor.cc +++ b/src/db/db/dbShapeProcessor.cc @@ -53,6 +53,12 @@ ShapeProcessor::reserve (size_t n) m_processor.reserve (n); } +size_t +ShapeProcessor::count () const +{ + return m_processor.count (); +} + void ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index a7afde10c..333c53b69 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -196,6 +196,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details) */ diff --git a/src/db/db/dbStream.cc b/src/db/db/dbStream.cc index 3d7cf935d..878198943 100644 --- a/src/db/db/dbStream.cc +++ b/src/db/db/dbStream.cc @@ -30,6 +30,42 @@ namespace db { +// ------------------------------------------------------------------ +// Implementation of StreamFormatDeclaration + +std::string StreamFormatDeclaration::all_formats_string () +{ + std::string fmts = tl::to_string (tr ("All layout files (")); + + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { + if (rdr != tl::Registrar::begin ()) { + fmts += " "; + } + std::string f = rdr->file_format (); + if (!f.empty ()) { + const char *fp = f.c_str (); + while (*fp && *fp != '(') { + ++fp; + } + if (*fp) { + ++fp; + } + while (*fp && *fp != ')') { + fmts += *fp++; + } + } + } + fmts += ")"; + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { + if (!rdr->file_format ().empty ()) { + fmts += ";;"; + fmts += rdr->file_format (); + } + } + + return fmts; +} + // ------------------------------------------------------------------ // Implementation of load_options_xml_element_list diff --git a/src/db/db/dbStream.h b/src/db/db/dbStream.h index 06ce1755d..97dfee6ef 100644 --- a/src/db/db/dbStream.h +++ b/src/db/db/dbStream.h @@ -136,6 +136,11 @@ public: { return 0; } + + /** + * @brief Returns a string for the file dialogs that describes all formats + */ + static std::string all_formats_string (); }; /** diff --git a/src/db/db/dbTechnology.cc b/src/db/db/dbTechnology.cc index 2910fbbab..84bd4118e 100644 --- a/src/db/db/dbTechnology.cc +++ b/src/db/db/dbTechnology.cc @@ -347,12 +347,10 @@ Technology::get_display_string () const return d; } -std::vector -Technology::default_grid_list () const +static void +parse_default_grids (const std::string &s, std::vector &grids, double &default_grid) { - tl::Extractor ex (m_default_grids.c_str ()); - - std::vector grids; + tl::Extractor ex (s.c_str ()); // convert the list of grids to a list of doubles while (! ex.at_end ()) { @@ -361,12 +359,32 @@ Technology::default_grid_list () const break; } grids.push_back (g); + if (ex.test ("!")) { + default_grid = g; + } ex.test (","); } +} +std::vector +Technology::default_grid_list () const +{ + std::vector grids; + double default_grid = 0.0; + parse_default_grids (m_default_grids, grids, default_grid); return grids; } +double +Technology::default_grid () const +{ + std::vector grids; + double default_grid = 0.0; + parse_default_grids (m_default_grids, grids, default_grid); + return default_grid; +} + + tl::XMLElementList Technology::xml_elements () { diff --git a/src/db/db/dbTechnology.h b/src/db/db/dbTechnology.h index e06ddca14..2fa1c6f5e 100644 --- a/src/db/db/dbTechnology.h +++ b/src/db/db/dbTechnology.h @@ -480,6 +480,15 @@ public: */ std::vector default_grid_list () const; + /** + * @brief Gets the default grid (strong grid), parsed from the list + * + * The default grid is the one marked with an exclamation mark in the + * grid list (e.g. "0.01!,0.02,0.05"). If there is not such default + * grid, this method returns zero. + */ + double default_grid () const; + /** * @brief Sets the default default grids */ diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 0b90c8897..388c89696 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -30,7 +30,9 @@ #include "dbOriginalLayerTexts.h" #include "dbEdges.h" #include "dbRegion.h" - +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" #include @@ -90,6 +92,23 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepTexts (si, dss, trans); } +void +Texts::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TEXTS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void Texts::insert (const Sh &shape) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 82d017187..cacda8589 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -181,6 +181,14 @@ public: */ explicit Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the text collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 50dc0d883..d2c5fcc8f 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -324,6 +324,21 @@ struct box_defs "\n" "@return The joined box\n" ) + + method ("-", &C::subtracted, gsi::arg ("box"), + "@brief Subtraction of boxes\n" + "\n" + "\n" + "The - operator subtracts the argument box from self.\n" + "This will return the bounding box of the are covered by self, but not by argument box. " + "Subtracting a box from itself will render an empty box. Subtracting another box from " + "self will modify the first box only if the argument box covers one side entirely.\n" + "\n" + "@param box The box to subtract from this box.\n" + "\n" + "@return The result box\n" + "\n" + "This feature has been introduced in version 0.29." + ) + method ("&", &C::intersection, gsi::arg ("box"), "@brief Returns the intersection of this box with another box\n" "\n" diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 782151553..0ee676e20 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -316,13 +316,13 @@ static db::CompoundRegionOperationNode *new_minkowski_sum_node4 (db::CompoundReg return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation > (p), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input) +static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input, db::PolygonToEdgeProcessor::EdgeMode edge_mode) { check_non_null (input, "input"); if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToEdgesProcessor (), input, true /*processor is owned*/); } else if (input->result_type () == db::CompoundRegionOperationNode::Region) { - return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (), input, true /*processor is owned*/); + return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (edge_mode), input, true /*processor is owned*/); } else { input->keep (); return input; @@ -567,13 +567,13 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_geometrical_boolean", &new_geometrical_boolean, gsi::arg ("op"), gsi::arg ("a"), gsi::arg ("b"), "@brief Creates a node representing a geometrical boolean operation between the inputs.\n" ) + - gsi::constructor ("new_interacting", &new_interacting, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_interacting", &new_interacting, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an interacting selection operation between the inputs.\n" ) + - gsi::constructor ("new_overlapping", &new_overlapping, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_overlapping", &new_overlapping, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an overlapping selection operation between the inputs.\n" ) + - gsi::constructor ("new_enclosing", &new_enclosing, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_enclosing", &new_enclosing, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an inside selection operation between the inputs.\n" ) + gsi::constructor ("new_inside", &new_inside, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), @@ -746,8 +746,12 @@ Class decl_CompoundRegionOperationNode ("db", " "@brief Creates a node filtering the input for rectangular or square shapes.\n" "If 'is_square' is true, only squares will be selected. If 'inverse' is true, the non-rectangle/non-square shapes are returned.\n" ) + - gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), + gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Creates a node converting polygons into its edges.\n" + "The 'mode' argument allows selecting specific edges when generating edges from a polygon. " + "See \\EdgeMode for the various options. By default, all edges are generated from polygons.\n" + "\n" + "The 'mode' argument has been added in version 0.29." ) + gsi::constructor ("new_edge_length_filter", &new_edge_length_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"), "@brief Creates a node filtering edges by their length.\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 4a22f7045..b4f6b5447 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -602,6 +602,12 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + method ("write", &db::EdgePairs::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::EdgePairs::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 8b1041622..5fc83ec49 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -656,14 +656,14 @@ static std::vector split_outside_with_region (const db::Edges *r, con return as_2edges_vector (r->selected_outside_differential (other)); } -static std::vector split_interacting_with_edges (const db::Edges *r, const db::Edges &other) +static std::vector split_interacting_with_edges (const db::Edges *r, const db::Edges &other, size_t min_count, size_t max_count) { - return as_2edges_vector (r->selected_interacting_differential (other)); + return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } -static std::vector split_interacting_with_region (const db::Edges *r, const db::Region &other) +static std::vector split_interacting_with_region (const db::Edges *r, const db::Region &other, size_t min_count, size_t max_count) { - return as_2edges_vector (r->selected_interacting_differential (other)); + return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } @@ -1233,61 +1233,107 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "The 'join_with' alias has been introduced in version 0.28.12." ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_interacting, gsi::arg ("other"), + method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &, size_t, size_t) const) &db::Edges::selected_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges of this edge collection which overlap or touch edges from the other edge collection\n" "\n" "@return A new edge collection containing the edges overlapping or touching edges from the other edge collection\n" + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) edges of the other collection to make the edge selected. An edge is " + "selected by this method if the number of edges interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &, size_t, size_t) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges of this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" "@return A new edge collection containing the edges not overlapping or touching edges from the other edge collection\n" - ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) edges of the other collection to make the edge selected. An edge is " + "not selected by this method if the number of edges interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &, size_t, size_t)) &db::Edges::select_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which overlap or touch edges from the other edge collection\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_interacting, gsi::arg ("other"), + "\n" + "This is the in-place version of \\interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &, size_t, size_t)) &db::Edges::select_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method_ext ("split_interacting", &split_interacting_with_edges, gsi::arg ("other"), + "\n" + "This is the in-place version of \\not_interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method_ext ("split_interacting", &split_interacting_with_edges, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do and do not interact with edges from the other collection\n" "\n" "@return A two-element list of edge collections (first: interacting, second: non-interacting)\n" "\n" "This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. " - "It has been introduced in version 0.28." + "It has been introduced in version 0.28.\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_interacting, gsi::arg ("other"), + method ("interacting", (db::Edges (db::Edges::*) (const db::Region &, size_t, size_t) const) &db::Edges::selected_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges from this edge collection which overlap or touch polygons from the region\n" "\n" "@return A new edge collection containing the edges overlapping or touching polygons from the region\n" - ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) polygons of the other region to make the edge selected. An edge is " + "selected by this method if the number of polygons interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &, size_t, size_t) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges from this edge collection which do not overlap or touch polygons from the region\n" "\n" "@return A new edge collection containing the edges not overlapping or touching polygons from the region\n" - ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) polygons of the other region to make the edge selected. An edge is " + "not selected by this method if the number of polygons interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &, size_t, size_t)) &db::Edges::select_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which overlap or touch polygons from the region\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_interacting, gsi::arg ("other"), + "\n" + "This is the in-place version of \\interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &, size_t, size_t)) &db::Edges::select_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do not overlap or touch polygons from the region\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method_ext ("split_interacting", &split_interacting_with_region, gsi::arg ("other"), + "\n" + "This is the in-place version of \\not_interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method_ext ("split_interacting", &split_interacting_with_region, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do and do not interact with polygons from the other region\n" "\n" "@return A two-element list of edge collections (first: interacting, second: non-interacting)\n" "\n" "This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. " - "It has been introduced in version 0.28." + "It has been introduced in version 0.28.\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + method ("inside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_inside, gsi::arg ("other"), "@brief Returns the edges of this edge collection which are inside (completely covered by) edges from the other edge collection\n" @@ -1521,6 +1567,12 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been added in version 0.28.\n" ) + + method ("write", &db::Edges::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("clear", &db::Edges::clear, "@brief Clears the edge collection\n" ) + diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 8f739a5ee..8fd578392 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -29,6 +29,7 @@ #include "dbPCellDeclaration.h" #include "dbLibrary.h" #include "dbLibraryManager.h" +#include "tlLog.h" namespace gsi { @@ -701,23 +702,23 @@ Class decl_PCellDeclaration (decl_PCellDeclaration_Native, // --------------------------------------------------------------- // db::PCellParameterDeclaration binding -unsigned int get_type (const db::PCellParameterDeclaration *pd) +static unsigned int get_type (const db::PCellParameterDeclaration *pd) { return (unsigned int) pd->get_type (); } -void set_type (db::PCellParameterDeclaration *pd, unsigned int t) +static void set_type (db::PCellParameterDeclaration *pd, unsigned int t) { pd->set_type (db::PCellParameterDeclaration::type (t)); } -void clear_choices (db::PCellParameterDeclaration *pd) +static void clear_choices (db::PCellParameterDeclaration *pd) { pd->set_choices (std::vector ()); pd->set_choice_descriptions (std::vector ()); } -void add_choice (db::PCellParameterDeclaration *pd, const std::string &d, const tl::Variant &v) +static void add_choice (db::PCellParameterDeclaration *pd, const std::string &d, const tl::Variant &v) { std::vector vv = pd->get_choices (); std::vector dd = pd->get_choice_descriptions (); @@ -772,26 +773,7 @@ static unsigned int pd_type_none () return (unsigned int) db::PCellParameterDeclaration::t_none; } -db::PCellParameterDeclaration *ctor_pcell_parameter (const std::string &name, unsigned int type, const std::string &description) -{ - db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration (); - pd->set_name (name); - pd->set_type (db::PCellParameterDeclaration::type (type)); - pd->set_description (description); - return pd; -} - -db::PCellParameterDeclaration *ctor_pcell_parameter_2 (const std::string &name, unsigned int type, const std::string &description, const tl::Variant &def) -{ - db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration (); - pd->set_name (name); - pd->set_type (db::PCellParameterDeclaration::type (type)); - pd->set_description (description); - pd->set_default (def); - return pd; -} - -db::PCellParameterDeclaration *ctor_pcell_parameter_3 (const std::string &name, unsigned int type, const std::string &description, const tl::Variant &def, const std::string &unit) +db::PCellParameterDeclaration *ctor_pcell_parameter (const std::string &name, unsigned int type, const std::string &description, const tl::Variant &def, const std::string &unit) { db::PCellParameterDeclaration *pd = new db::PCellParameterDeclaration (); pd->set_name (name); @@ -803,20 +785,7 @@ db::PCellParameterDeclaration *ctor_pcell_parameter_3 (const std::string &name, } Class decl_PCellParameterDeclaration ("db", "PCellParameterDeclaration", - gsi::constructor ("new", &ctor_pcell_parameter, gsi::arg ("name"), gsi::arg ("type"), gsi::arg ("description"), - "@brief Create a new parameter declaration with the given name and type\n" - "@param name The parameter name\n" - "@param type One of the Type... constants describing the type of the parameter\n" - "@param description The description text\n" - ) + - gsi::constructor ("new", &ctor_pcell_parameter_2, gsi::arg ("name"), gsi::arg ("type"), gsi::arg ("description"), gsi::arg ("default"), - "@brief Create a new parameter declaration with the given name, type and default value\n" - "@param name The parameter name\n" - "@param type One of the Type... constants describing the type of the parameter\n" - "@param description The description text\n" - "@param default The default (initial) value\n" - ) + - gsi::constructor ("new", &ctor_pcell_parameter_3, gsi::arg ("name"), gsi::arg ("type"), gsi::arg ("description"), gsi::arg ("default"), gsi::arg ("unit"), + gsi::constructor ("new", &ctor_pcell_parameter, gsi::arg ("name"), gsi::arg ("type"), gsi::arg ("description"), gsi::arg ("default", tl::Variant (), "nil"), gsi::arg ("unit", std::string ()), "@brief Create a new parameter declaration with the given name, type, default value and unit string\n" "@param name The parameter name\n" "@param type One of the Type... constants describing the type of the parameter\n" @@ -874,6 +843,7 @@ Class decl_PCellParameterDeclaration ("db", "PCel "This method will add the given value with the given description to the list of\n" "choices. If choices are defined, KLayout will show a drop-down box instead of an\n" "entry field in the parameter user interface.\n" + "If a range is already set for this parameter the choice will not be added and a warning message is showed.\n" ) + gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices, "@brief Returns a list of choice values\n" @@ -881,6 +851,44 @@ Class decl_PCellParameterDeclaration ("db", "PCel gsi::method ("choice_descriptions", &db::PCellParameterDeclaration::get_choice_descriptions, "@brief Returns a list of choice descriptions\n" ) + + gsi::method ("min_value", &db::PCellParameterDeclaration::min_value, + "@brief Gets the minimum value allowed\n" + "See \\min_value= for a description of this attribute.\n" + "\n" + "This attribute has been added in version 0.29." + ) + + gsi::method ("min_value=", &db::PCellParameterDeclaration::set_min_value, gsi::arg ("value"), + "@brief Sets the minimum value allowed\n" + "The minimum value is a visual feature and limits the allowed values for numerical\n" + "entry boxes. This applies to parameters of type int or double. The minimum value\n" + "is not effective if choices are present.\n" + "\n" + "The minimum value is not enforced - for example there is no restriction implemented\n" + "when setting values programmatically.\n" + "\n" + "Setting this attribute to \"nil\" (the default) implies \"no limit\".\n" + "\n" + "This attribute has been added in version 0.29." + ) + + gsi::method ("max_value", &db::PCellParameterDeclaration::max_value, + "@brief Gets the maximum value allowed\n" + "See \\max_value= for a description of this attribute.\n" + "\n" + "This attribute has been added in version 0.29." + ) + + gsi::method ("max_value=", &db::PCellParameterDeclaration::set_max_value, gsi::arg ("value"), + "@brief Sets the maximum value allowed\n" + "The maximum value is a visual feature and limits the allowed values for numerical\n" + "entry boxes. This applies to parameters of type int or double. The maximum value\n" + "is not effective if choices are present.\n" + "\n" + "The maximum value is not enforced - for example there is no restriction implemented\n" + "when setting values programmatically.\n" + "\n" + "Setting this attribute to \"nil\" (the default) implies \"no limit\".\n" + "\n" + "This attribute has been added in version 0.29." + ) + gsi::method ("default", &db::PCellParameterDeclaration::get_default, "@brief Gets the default value\n" ) + diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 0c3960bb7..9cfcb1cf6 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -31,6 +31,39 @@ namespace gsi { +template +static std::vector split_poly (const C *p) +{ + std::vector parts; + db::split_polygon (*p, parts); + return parts; +} + +template +static void break_polygon (const C &poly, size_t max_vertex_count, double max_area_ratio, std::vector &result) +{ + if ((max_vertex_count > 0 && poly.vertices () > max_vertex_count) || + (max_area_ratio > 0 && poly.area_ratio () > max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (auto p = split_polygons.begin (); p != split_polygons.end (); ++p) { + break_polygon (*p, max_vertex_count, max_area_ratio, result); + } + + } else { + result.push_back (poly); + } +} + +template +static std::vector break_poly (const C *p, size_t max_vertex_count, double max_area_ratio) +{ + std::vector parts; + break_polygon (*p, max_vertex_count, max_area_ratio, parts); + return parts; +} + // --------------------------------------------------------------- // simple polygon binding @@ -245,13 +278,6 @@ struct simple_polygon_defs return db::interact (*p, spoly); } - static std::vector split_poly (const C *p) - { - std::vector parts; - db::split_polygon (*p, parts); - return parts; - } - static gsi::Methods methods () { return @@ -508,7 +534,7 @@ struct simple_polygon_defs "\n" "This method was introduced in version 0.25.\n" ) + - method_ext ("split", &split_poly, + method_ext ("split", &split_poly, "@brief Splits the polygon into two or more parts\n" "This method will break the polygon into parts. The exact breaking algorithm is unspecified, the " "result are smaller polygons of roughly equal number of points and 'less concave' nature. " @@ -521,6 +547,20 @@ struct simple_polygon_defs "\n" "This method has been introduced in version 0.25.3." ) + + method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"), + "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n" + "The area ratio is the ratio between the bounding box area and the polygon area. Higher values " + "mean more 'skinny' polygons.\n" + "\n" + "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices " + "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the " + "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n" + "\n" + "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the " + "parts satisfy the limits.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method_ext ("area", &area, "@brief Gets the area of the polygon\n" "The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise." @@ -1098,13 +1138,6 @@ struct polygon_defs return db::interact (*p, spoly); } - static std::vector split_spoly (const C *p) - { - std::vector parts; - db::split_polygon (*p, parts); - return parts; - } - static gsi::Methods methods () { return @@ -1520,7 +1553,7 @@ struct polygon_defs "\n" "This method was introduced in version 0.25.\n" ) + - method_ext ("split", &split_spoly, + method_ext ("split", &split_poly, "@brief Splits the polygon into two or more parts\n" "This method will break the polygon into parts. The exact breaking algorithm is unspecified, the " "result are smaller polygons of roughly equal number of points and 'less concave' nature. " @@ -1533,6 +1566,20 @@ struct polygon_defs "\n" "This method has been introduced in version 0.25.3." ) + + method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"), + "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n" + "The area ratio is the ratio between the bounding box area and the polygon area. Higher values " + "mean more 'skinny' polygons.\n" + "\n" + "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices " + "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the " + "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n" + "\n" + "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the " + "parts satisfy the limits.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method_ext ("area", &area, "@brief Gets the area of the polygon\n" "The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise." diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index bc702af0f..50f840eee 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -476,13 +476,29 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("region"), + gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("flag"), "@brief Sets a flag indicating whether overlapping shapes are selected when a region is used\n" "\n" "If this flag is false, shapes touching the search region are returned.\n" "\n" "This method has been introduced in version 0.23.\n" ) + + gsi::method ("for_merged_input?", &db::RecursiveShapeIterator::for_merged_input, + "@brief Gets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "see \\for_merged_input= for details of this attribute.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + gsi::method ("for_merged_input=", &db::RecursiveShapeIterator::set_for_merged_input, gsi::arg ("flag"), + "@brief Sets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "If this flag is set to true, the iterator is allowed to skip shapes it deems irrelevant " + "because they are covered entirely by other shapes. This allows shortcutting hierarchy traversal in " + "some cases.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + gsi::method ("unselect_all_cells", &db::RecursiveShapeIterator::unselect_all_cells, "@brief Unselects all cells.\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 6135f6e9f..c5d6c6570 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -270,15 +270,6 @@ static db::Region *new_path (const db::Path &o) return new db::Region (o); } -static db::Region *new_shapes (const db::Shapes &s) -{ - db::Region *r = new db::Region (); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end (); ++i) { - r->insert (*i); - } - return r; -} - static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); @@ -329,16 +320,26 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } -static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) -{ - return new db::Region (si, dss, area_ratio, max_vertex_count); -} - static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); } +static db::Region *new_sis (const db::Shapes &si) +{ + return new db::Region (si); +} + +static db::Region *new_sis2 (const db::Shapes &si, const db::ICplxTrans &trans) +{ + return new db::Region (si, trans); +} + +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) { return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); @@ -1002,6 +1003,18 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static db::Edges +edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) +{ + if (mode != db::PolygonToEdgeProcessor::All) { + db::PolygonToEdgeProcessor proc (mode); + return region->edges (proc); + } else { + // this version is more efficient in the hierarchical case + return region->edges (); + } +} + static std::vector > rasterize2 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_distance, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) { @@ -1076,13 +1089,6 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from a path.\n" ) + - constructor ("new", &new_shapes, gsi::arg ("shapes"), - "@brief Shapes constructor\n" - "\n" - "This constructor creates a region from a \\Shapes collection.\n" - "\n" - "This constructor has been introduced in version 0.25." - ) + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" "\n" @@ -1114,6 +1120,24 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_sis, gsi::arg ("shapes"), + "@brief Constructor from a shapes container\n" + "\n" + "This constructor creates a region from the shapes container.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor has been introduced in version 0.25 and extended in version 0.29." + ) + + constructor ("new", &new_sis2, gsi::arg ("shapes"), gsi::arg ("trans"), + "@brief Constructor from a shapes container with a transformation\n" + "\n" + "This constructor creates a region from the shapes container after applying the transformation.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor variant has been introduced in version 0.29." + ) + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), "@brief Constructor for a deep region from a hierarchical shape set\n" "\n" @@ -1197,6 +1221,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.26." ) + + method ("write", &db::Region::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + factory_ext ("texts", &texts_as_boxes1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@hide\n" "This method is provided for DRC implementation only." @@ -2493,15 +2523,20 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "If the region is not merged, this method may return false even\n" "if the merged region would be a box.\n" ) + - method ("edges", (db::Edges (db::Region::*) () const) &db::Region::edges, + method_ext ("edges", &edges, gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Returns an edge collection representing all edges of the polygons in this region\n" "This method will decompose the polygons into the individual edges. Edges making up the hulls " "of the polygons are oriented clockwise while edges making up the holes are oriented counterclockwise.\n" "\n" + "The 'mode' parameter allows selecting specific edges, such as convex or concave ones. By default, " + "all edges are selected.\n" + "\n" "The edge collection returned can be manipulated in various ways. See \\Edges for a description of the " - "possibilities of the edge collection.\n" + "features of the edge collection.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "The mode argument has been added in version 0.29." ) + factory_ext ("decompose_convex", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"), "@brief Decomposes the region into convex pieces.\n" @@ -2924,7 +2959,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "The 'shielded' and 'negative' options have been introduced in version 0.27. " "'property_constraint' has been added in version 0.28.4.\n" - "'zero_distance_mode' has been added in version 0.29." + "'zero_distance_mode' has been added in version 0.28.16." ) + 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"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check with options\n" @@ -3577,6 +3612,48 @@ gsi::Enum decl_Metrics ("db", "Metrics", gsi::ClassExt inject_Metrics_in_Region (decl_Metrics.defs ()); gsi::ClassExt inject_Metrics_in_Edges (decl_Metrics.defs ()); +gsi::Enum decl_EdgeMode ("db", "EdgeMode", + gsi::enum_const ("All", db::PolygonToEdgeProcessor::All, + "@brief Selects all edges\n" + ) + + gsi::enum_const ("Concave", db::PolygonToEdgeProcessor::Concave, + "@brief Selects only concave edges\n" + ) + + gsi::enum_const ("NotConcave", db::PolygonToEdgeProcessor::NotConcave, + "@brief Selects only edges which are not concave\n" + ) + + gsi::enum_const ("Convex", db::PolygonToEdgeProcessor::Convex, + "@brief Selects only convex edges\n" + ) + + gsi::enum_const ("NotConvex", db::PolygonToEdgeProcessor::NotConvex, + "@brief Selects only edges which are not convex\n" + ) + + gsi::enum_const ("Step", db::PolygonToEdgeProcessor::Step, + "@brief Selects only step edges leading inside or outside\n" + ) + + gsi::enum_const ("NotStep", db::PolygonToEdgeProcessor::NotStep, + "@brief Selects only edges which are not steps\n" + ) + + gsi::enum_const ("StepIn", db::PolygonToEdgeProcessor::StepIn, + "@brief Selects only step edges leading inside\n" + ) + + gsi::enum_const ("NotStepIn", db::PolygonToEdgeProcessor::NotStepIn, + "@brief Selects only edges which are not steps leading inside\n" + ) + + gsi::enum_const ("StepOut", db::PolygonToEdgeProcessor::StepOut, + "@brief Selects only step edges leading outside\n" + ) + + gsi::enum_const ("NotStepOut", db::PolygonToEdgeProcessor::NotStepOut, + "@brief Selects only edges which are not steps leading outside\n" + ), + "@brief This class represents the edge mode type for \\Region#edges.\n" + "\n" + "This enum has been introduced in version 0.29." +); + +// Inject the Region::EdgeMode declarations into Region: +gsi::ClassExt inject_EdgeMode_in_Region (decl_EdgeMode.defs ()); + gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode", gsi::enum_const ("NeverIncludeZeroDistance", db::NeverIncludeZeroDistance, "@brief Specifies that check functions should never include edges with zero distance.\n" @@ -3590,7 +3667,7 @@ gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode gsi::enum_const ("IncludeZeroDistanceWhenTouching", db::IncludeZeroDistanceWhenTouching, "@brief Specifies that check functions should include edges when they touch\n" "With this specification, the check functions will also check edges if they share at least one common point. " - "This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.29 and later. " + "This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.28.16 and later. " ) + gsi::enum_const ("IncludeZeroDistanceWhenCollinearAndTouching", db::IncludeZeroDistanceWhenCollinearAndTouching, "@brief Specifies that check functions should include edges when they are collinear and touch\n" @@ -3609,7 +3686,7 @@ gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode "if they share at least one common point (\\IncludeZeroDistanceWhenTouching). The latter mode allows activating checks for " "the 'kissing corner' case and is the default mode in most checks." "\n" - "This enum has been introduced in version 0.29." + "This enum has been introduced in version 0.28.16." ); // Inject the Region::ZeroDistanceMode declarations into Region and Edges: diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index c6671d9b7..68f6ff4fb 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -669,6 +669,26 @@ static tl::Variant get_dbox (const db::Shape *s) } } +static tl::Variant get_rectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (b); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_drectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * b); + } else { + return tl::Variant (); + } +} + static tl::Variant get_edge (const db::Shape *s) { db::Shape::edge_type p; @@ -1982,6 +2002,22 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been added in version 0.25.\n" ) + + gsi::method_ext ("rectangle", &get_rectangle, + "@brief Gets the rectangle if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method_ext ("drectangle", &get_drectangle, + "@brief Gets the rectangle in micron units if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("is_user_object?", &db::Shape::is_user_object, "@brief Returns true if the shape is a user defined object\n" ) + diff --git a/src/db/db/gsiDeclDbTechnologies.cc b/src/db/db/gsiDeclDbTechnologies.cc index de670fa4f..bc3d6f9d0 100644 --- a/src/db/db/gsiDeclDbTechnologies.cc +++ b/src/db/db/gsiDeclDbTechnologies.cc @@ -136,7 +136,7 @@ gsi::Class technology_component_decl ("db", "Technology DB_PUBLIC gsi::Class &decl_dbTechnologyComponent () { return technology_component_decl; } static void -set_default_grid_list (db::Technology *tech, const std::vector &grids) +set_default_grid_list2 (db::Technology *tech, const std::vector &grids, double default_grid) { std::string r; for (auto g = grids.begin (); g != grids.end (); ++g) { @@ -144,10 +144,19 @@ set_default_grid_list (db::Technology *tech, const std::vector &grids) r += ","; } r += tl::micron_to_string (*g); + if (db::coord_traits::equals (*g, default_grid)) { + r += "!"; + } } tech->set_default_grids (r); } +static void +set_default_grid_list (db::Technology *tech, const std::vector &grids) +{ + set_default_grid_list2 (tech, grids, 0.0); +} + gsi::Class technology_decl ("db", "Technology", gsi::method ("name", &db::Technology::name, "@brief Gets the name of the technology" @@ -238,12 +247,32 @@ gsi::Class technology_decl ("db", "Technology", "\n" "This property has been introduced in version 0.28.17." ) + + gsi::method ("default_grid", &db::Technology::default_grid, + "@brief Gets the default grid\n" + "\n" + "The default grid is a specific one from the default grid list.\n" + "It indicates the one that is taken if the current grid is not matching one of " + "the default grids.\n" + "\n" + "To set the default grid, use \\set_default_grids.\n" + "\n" + "This property has been introduced in version 0.29." + ) + gsi::method_ext ("default_grids=", &set_default_grid_list, gsi::arg ("grids"), "@brief Sets the default grids\n" "If not empty, this list replaces the global grid list for this technology.\n" + "Note that this method will reset the default grid (see \\default_grid). Use " + "\\set_default_grids to set the default grids and the strong default one.\n" "\n" "This property has been introduced in version 0.28.17." ) + + gsi::method_ext ("set_default_grids", &set_default_grid_list2, gsi::arg ("grids"), gsi::arg ("default_grid", 0.0), + "@brief Sets the default grids and the strong default one\n" + "See \\default_grids and \\default_grid for a description of this property.\n" + "Note that the default grid has to be a member of the 'grids' array to become active.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("layer_properties_file", &db::Technology::layer_properties_file, "@brief Gets the path of the layer properties file\n" "\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 8be9238b8..359249795 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -436,6 +436,12 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "r = RBA::Texts::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + method ("write", &db::Texts::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::Texts::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this texts into the given layout, below the given cell and into the given layer.\n" "If the text collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/unit_tests/dbBoxTests.cc b/src/db/unit_tests/dbBoxTests.cc index d70e942fc..1ac60f799 100644 --- a/src/db/unit_tests/dbBoxTests.cc +++ b/src/db/unit_tests/dbBoxTests.cc @@ -49,6 +49,17 @@ TEST(2) EXPECT_EQ (b & db::Box (110, 220, 120, 250), empty); EXPECT_EQ (b & db::Box (50, 100, 120, 250), db::Box (50, 100, 100, 200)); EXPECT_EQ (b & db::Box (50, 100, 60, 120), db::Box (50, 100, 60, 120)); + EXPECT_EQ (b - b, db::Box ()); + EXPECT_EQ (b - db::Box (), b); + EXPECT_EQ (db::Box () - b, db::Box ()); + EXPECT_EQ (db::Box () - db::Box (), db::Box ()); + EXPECT_EQ (b - db::Box (0, 0, 50, 50), b); + EXPECT_EQ (b - db::Box (0, 0, 50, 200), db::Box (50, 0, 100, 200)); + EXPECT_EQ (b - db::Box (50, 0, 100, 200), db::Box (0, 0, 50, 200)); + EXPECT_EQ (b - db::Box (0, 0, 100, 100), db::Box (0, 100, 100, 200)); + EXPECT_EQ (b - db::Box (0, 100, 100, 200), db::Box (0, 0, 100, 100)); + EXPECT_EQ (db::Box::world () - b, db::Box::world ()); + EXPECT_EQ (b - db::Box::world (), db::Box ()); empty.move (db::Vector (10, 20)); EXPECT_EQ (empty == db::Box (), true); diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 1d9f24594..381ae4fc7 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -133,7 +133,7 @@ TEST(3_Edge2EdgeBooleans) db::Layout ly; { std::string fn (tl::testdata ()); - fn += "/algo/deep_region_l1.gds"; + fn += "/algo/deep_edges_l1.gds"; tl::InputStream stream (fn); db::Reader reader (stream); reader.read (ly); @@ -145,15 +145,23 @@ TEST(3_Edge2EdgeBooleans) db::DeepShapeStore dss; unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l21 = ly.get_layer (db::LayerProperties (2, 1)); unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int lempty = ly.insert_layer (); db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r21 (db::RecursiveShapeIterator (ly, top_cell, l21), dss); db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); db::Region r2and3 = r2 & r3; db::Edges e2 = r2.edges (); + db::Edges e21 = r21.edges (); db::Edges e3 = r3.edges (); + db::Edges e3copy = r3.edges (); db::Edges e2and3 = r2and3.edges (); + db::Edges eempty (db::RecursiveShapeIterator (ly, top_cell, lempty), dss); + db::Edges edots = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + db::Edges edotscopy = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); db::Layout target; unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); @@ -162,11 +170,70 @@ TEST(3_Edge2EdgeBooleans) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), edots.merged ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 - e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 ^ e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.intersections(e2and3)); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), e3.intersections(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 & eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3 & e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), eempty & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), edots & edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), edots & e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), e21 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (28, 0)), edots & e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e3 - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), e3 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), e3 - eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), e3 - e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), eempty - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), edots - edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), edots - e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), e21 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (38, 0)), edots - e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), e3 ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), e3 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), e3 ^ eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), e3 ^ e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), eempty ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), edots ^ edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), edots ^ e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), e21 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), edots ^ e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), e3.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), e3.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), e3.andnot(eempty).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), e3.andnot(e3copy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (54, 0)), eempty.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (55, 0)), edots.andnot(edotscopy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (56, 0)), edots.andnot(e2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (57, 0)), e21.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (58, 0)), edots.andnot(e21).first); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), e3.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), e3.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), e3.andnot(eempty).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), e3.andnot(e3copy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), eempty.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), edots.andnot(edotscopy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (66, 0)), edots.andnot(e2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (67, 0)), e21.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (68, 0)), edots.andnot(e21).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (70, 0)), e3.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (71, 0)), e3.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (72, 0)), e3.intersections(eempty)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (73, 0)), e3.intersections(e3copy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (74, 0)), eempty.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (75, 0)), edots.intersections(edotscopy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (76, 0)), edots.intersections(e2)); + // test, whether dots are not merged + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (77, 0)), edots.intersections(e2).select_interacting(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (78, 0)), e21.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (79, 0)), edots.intersections(e21)); CHECKPOINT(); db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au3.gds"); @@ -1293,6 +1360,201 @@ TEST(20_in_and_out) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au20.gds"); } +TEST(21_EdgeMergeWithDots) +{ + db::Edges e; + e.insert (db::Edge (db::Point(0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + + db::Edges ee = e; + ee.insert (db::Edge (db::Point(100, 0), db::Point (110, 0))); + + db::Edges eee; + eee.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + eee.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + + // make deep + + db::DeepShapeStore dss; + + db::Layout ly; + ly.add_cell ("TOP"); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + unsigned int l3 = ly.insert_layer (); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + e.insert_into (&ly, top_cell.cell_index (), l1); + e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + ee.insert_into (&ly, top_cell.cell_index (), l2); + ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + + eee.insert_into (&ly, top_cell.cell_index (), l3); + eee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + + EXPECT_EQ (e.merged ().to_string (), "(0,0;100,0);(110,0;110,0)"); + // dots do not participate in merge + EXPECT_EQ (ee.merged ().to_string (), "(0,0;110,0)"); + // dots do not participate in merge + EXPECT_EQ (eee.merged ().to_string (), "(110,0;110,0)"); +} + +TEST(22_InteractingWithCount) +{ + db::Edges e; + e.insert (db::Edge (db::Point (0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point (100, 0), db::Point (200, 0))); + e.insert (db::Edge (db::Point (0, 10), db::Point (200, 10))); + e.insert (db::Edge (db::Point (0, 20), db::Point (200, 20))); + e.insert (db::Edge (db::Point (0, 30), db::Point (200, 30))); + + db::Edges e2; + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 10))); + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 30))); + e2.insert (db::Edge (db::Point (110, 10), db::Point (110, 30))); + e2.merge (); + e2.insert (db::Edge (db::Point (120, 20), db::Point (120, 20))); + e2.insert (db::Edge (db::Point (130, 30), db::Point (130, 30))); + e2.set_merged_semantics (false); + + db::Region r2; + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 10))); + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 30))); + r2.insert (db::Box (db::Point (109, 10), db::Point (111, 30))); + r2.insert (db::Box (db::Point (119, 19), db::Point (121, 21))); + r2.insert (db::Box (db::Point (129, 29), db::Point (131, 31))); + + // make deep + + db::DeepShapeStore dss; + + db::Layout ly; + ly.add_cell ("TOP"); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + unsigned int l3 = ly.insert_layer (); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + e.insert_into (&ly, top_cell.cell_index (), l1); + e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + e2.insert_into (&ly, top_cell.cell_index (), l2); + e2 = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + // because it has dots + e2.set_merged_semantics (false); + + r2.insert_into (&ly, top_cell.cell_index (), l3); + r2 = db::Region (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + + + db::Edges edup; + + EXPECT_EQ (db::compare (e.selected_interacting (e2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (e2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting (r2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (r2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); +} + +TEST(23_SameInputs) +{ + 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); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + db::Edges e2 = db::Edges ((db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss)).edges ()); + + EXPECT_EQ ((e2 & e2).to_string (), e2.to_string ()); + EXPECT_EQ ((e2 - e2).to_string (), ""); + EXPECT_EQ (e2.andnot (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.andnot (e2).second.to_string (), ""); + EXPECT_EQ ((e2 | e2).to_string (), e2.to_string ()); + EXPECT_EQ ((e2 ^ e2).to_string (), ""); + EXPECT_EQ (e2.in (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.in (e2, true).to_string (), ""); + EXPECT_EQ (e2.in_and_out (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.in_and_out (e2).second.to_string (), ""); + EXPECT_EQ (e2.selected_interacting (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_not_interacting (e2).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_interacting_differential (e2).second.to_string (), ""); + EXPECT_EQ ((e2.selected_interacting (e2, (size_t) 1, (size_t) 3) ^ e2).to_string (), ""); + EXPECT_EQ ((e2.selected_interacting_differential (e2, (size_t) 1, (size_t) 3).first ^ e2).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2, (size_t) 1, (size_t) 3).second.to_string (), ""); + EXPECT_EQ (e2.selected_interacting (e2, (size_t) 4).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2, (size_t) 4).first.to_string (), ""); + EXPECT_EQ ((e2.selected_interacting_differential (e2, (size_t) 4).second ^ e2).to_string (), ""); + EXPECT_EQ (e2.selected_inside (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_not_inside (e2).to_string (), ""); + EXPECT_EQ (e2.selected_inside_differential (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_inside_differential (e2).second.to_string (), ""); + EXPECT_EQ (e2.selected_outside (e2).to_string (), ""); + EXPECT_EQ (e2.selected_not_outside (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_outside_differential (e2).first.to_string (), ""); + EXPECT_EQ (e2.selected_outside_differential (e2).second.to_string (), e2.to_string ()); + EXPECT_EQ (e2.pull_interacting (e2).to_string (), e2.to_string ()); +} + TEST(deep_edges_and_cheats) { db::Layout ly; diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 1037dc1c7..9212846a5 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -2649,6 +2649,66 @@ TEST(101_DeepFlatCollaboration) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au101.gds"); } +TEST(102_SameInputs) +{ + 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); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + + EXPECT_EQ ((r2 & r2).to_string (), r2.to_string ()); + EXPECT_EQ ((r2 - r2).to_string (), ""); + EXPECT_EQ (r2.andnot (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.andnot (r2).second.to_string (), ""); + EXPECT_EQ ((r2 | r2).to_string (), r2.to_string ()); + EXPECT_EQ ((r2 ^ r2).to_string (), ""); + EXPECT_EQ (r2.in (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.in (r2, true).to_string (), ""); + EXPECT_EQ (r2.in_and_out (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.in_and_out (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_enclosing (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_enclosing (r2).to_string (), ""); + EXPECT_EQ (r2.selected_enclosing_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_enclosing_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_interacting (r2).to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2, (size_t) 1, (size_t) 2).to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 1, (size_t) 2).first.to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 1, (size_t) 2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2, (size_t) 2).to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 2).first.to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 2).second.to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_inside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_inside (r2).to_string (), ""); + EXPECT_EQ (r2.selected_inside_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_inside_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_outside (r2).to_string (), ""); + EXPECT_EQ (r2.selected_not_outside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_outside_differential (r2).first.to_string (), ""); + EXPECT_EQ (r2.selected_outside_differential (r2).second.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_overlapping (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_overlapping (r2).to_string (), ""); + EXPECT_EQ (r2.selected_overlapping_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_overlapping_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.pull_inside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.pull_overlapping (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.pull_interacting (r2).to_string (), r2.to_string ()); +} + TEST(issue_277) { db::Layout ly; diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 132aec5e1..d9cb66589 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -22,12 +22,14 @@ #include "tlUnitTest.h" +#include "tlStream.h" #include "dbEdges.h" #include "dbEdgesUtils.h" #include "dbPolygonTools.h" #include "dbRegion.h" #include "dbTestSupport.h" +#include "dbReader.h" #include @@ -787,27 +789,27 @@ TEST(20) EXPECT_EQ (r2.has_valid_edges (), false); db::Region rr1 (db::RecursiveShapeIterator (ly, ly.cell (top), lp1), db::ICplxTrans (), false); EXPECT_EQ (rr1.has_valid_polygons (), false); - EXPECT_EQ ((r1 & r2).to_string (100), "(80,70;80,40)"); - EXPECT_EQ ((r1 + r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,70;80,40);(80,40;50,40);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;110,70);(110,70;110,40);(110,40;80,40);(110,40;110,70);(110,70;140,70);(140,70;140,40);(140,40;110,40)"); - EXPECT_EQ ((r1 + r2).merged ().to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 | r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 ^ r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 ^ r1).to_string (100), ""); - EXPECT_EQ ((r1 - r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,40;50,40)"); - EXPECT_EQ ((r1 - r1).to_string (100), ""); - EXPECT_EQ (r2.merged ().to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,70;140,40);(140,40;80,40)"); - EXPECT_EQ (rr1.to_string (100), "(0,0;0,30;30,30;30,0);(50,0;50,30;80,30;80,0);(50,40;50,70;80,70;80,40)"); - EXPECT_EQ (r2.selected_interacting (rr1).to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"); - EXPECT_EQ (r2.selected_interacting_differential (rr1).first.to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"); - EXPECT_EQ (r2.selected_not_interacting (rr1).to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); - EXPECT_EQ (r2.selected_interacting_differential (rr1).second.to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); + EXPECT_EQ (db::compare (r1 & r2, "(80,70;80,40)"), true); + EXPECT_EQ (db::compare (r1 + r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,70;80,40);(80,40;50,40);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;110,70);(110,70;110,40);(110,40;80,40);(110,40;110,70);(110,70;140,70);(140,70;140,40);(140,40;110,40)"), true); + EXPECT_EQ (db::compare ((r1 + r2).merged (), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 | r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 ^ r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 ^ r1, ""), true); + EXPECT_EQ (db::compare (r1 - r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 - r1, ""), true); + EXPECT_EQ (db::compare (r2.merged (), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,70;140,40);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (rr1, "(0,0;0,30;30,30;30,0);(50,0;50,30;80,30;80,0);(50,40;50,70;80,70;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting (rr1), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting_differential (rr1).first, "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_not_interacting (rr1), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting_differential (rr1).second, "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); db::Edges r2dup = r2; r2.select_interacting (rr1); EXPECT_EQ (db::compare (r2, "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); r2 = r2dup; r2.select_not_interacting (rr1); - EXPECT_EQ (r2.to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); + EXPECT_EQ (db::compare (r2, "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); r2 = db::Edges (db::RecursiveShapeIterator (ly, ly.cell (top), l2), false); EXPECT_EQ (r2.has_valid_edges (), false); @@ -895,9 +897,9 @@ TEST(22) ee.insert (db::Edge (4000,0,4000,-2000)); ee.insert (db::Edge (4000,-2000,-2000,-2000)); - EXPECT_EQ (db::compare ((e & ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); - EXPECT_EQ (db::compare (e.andnot(ee).first, "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); - EXPECT_EQ (db::compare (e.intersections (ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare ((e & ee), "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare (e.andnot(ee).first, "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare (e.intersections (ee), "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); // Edge/edge intersections ee.clear (); @@ -1157,6 +1159,214 @@ TEST(28) EXPECT_EQ (db::compare (ee.in (e, true), "(0,0;0,2000);(100,1000;0,2000)"), true); } +// edge merge with dots -> dots are merged, but are retained +TEST(29) +{ + db::Edges e; + e.insert (db::Edge (db::Point(0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + EXPECT_EQ (e.merged ().to_string (), "(0,0;100,0);(110,0;110,0)"); + + e.insert (db::Edge (db::Point(100, 0), db::Point (110, 0))); + // dots do not participate in merge + EXPECT_EQ (e.merged ().to_string (), "(0,0;110,0)"); + + e.clear (); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + // dots do not participate in merge + EXPECT_EQ (e.merged ().to_string (), "(110,0;110,0)"); +} + +// interacting with count +TEST(30) +{ + db::Edges e; + e.insert (db::Edge (db::Point (0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point (100, 0), db::Point (200, 0))); + e.insert (db::Edge (db::Point (0, 10), db::Point (200, 10))); + e.insert (db::Edge (db::Point (0, 20), db::Point (200, 20))); + e.insert (db::Edge (db::Point (0, 30), db::Point (200, 30))); + + db::Edges e2; + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 10))); + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 30))); + e2.insert (db::Edge (db::Point (110, 10), db::Point (110, 30))); + e2.merge (); + e2.insert (db::Edge (db::Point (120, 20), db::Point (120, 20))); + e2.insert (db::Edge (db::Point (130, 30), db::Point (130, 30))); + e2.set_merged_semantics (false); + + db::Edges edup; + + EXPECT_EQ (db::compare (e.selected_interacting (e2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (e2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); + + db::Region r2; + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 10))); + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 30))); + r2.insert (db::Box (db::Point (109, 10), db::Point (111, 30))); + r2.insert (db::Box (db::Point (119, 19), db::Point (121, 21))); + r2.insert (db::Box (db::Point (129, 29), db::Point (131, 31))); + + EXPECT_EQ (db::compare (e.selected_interacting (r2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (r2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); +} + +// borrowed from deep edges tests +TEST(31) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_edges_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 l21 = ly.get_layer (db::LayerProperties (2, 1)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int lempty = ly.insert_layer (); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r21 (db::RecursiveShapeIterator (ly, top_cell, l21)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r2and3 = r2 & r3; + + db::Edges e2 = r2.edges (); + db::Edges e21 = r21.edges (); + db::Edges e3 = r3.edges (); + db::Edges e3copy = r3.edges (); + db::Edges e2and3 = r2and3.edges (); + db::Edges eempty (db::RecursiveShapeIterator (ly, top_cell, lempty)); + db::Edges edots = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + db::Edges edotscopy = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), edots.merged ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 & eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3 & e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), eempty & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), edots & edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), edots & e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), e21 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (28, 0)), edots & e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e3 - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), e3 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), e3 - eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), e3 - e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), eempty - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), edots - edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), edots - e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), e21 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (38, 0)), edots - e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), e3 ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), e3 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), e3 ^ eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), e3 ^ e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), eempty ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), edots ^ edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), edots ^ e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), e21 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), edots ^ e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), e3.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), e3.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), e3.andnot(eempty).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), e3.andnot(e3copy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (54, 0)), eempty.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (55, 0)), edots.andnot(edotscopy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (56, 0)), edots.andnot(e2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (57, 0)), e21.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (58, 0)), edots.andnot(e21).first); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), e3.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), e3.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), e3.andnot(eempty).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), e3.andnot(e3copy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), eempty.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), edots.andnot(edotscopy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (66, 0)), edots.andnot(e2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (67, 0)), e21.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (68, 0)), edots.andnot(e21).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (70, 0)), e3.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (71, 0)), e3.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (72, 0)), e3.intersections(eempty)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (73, 0)), e3.intersections(e3copy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (74, 0)), eempty.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (75, 0)), edots.intersections(edotscopy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (76, 0)), edots.intersections(e2)); + // test, whether dots are not merged + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (77, 0)), edots.intersections(e2).select_interacting(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (78, 0)), e21.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (79, 0)), edots.intersections(e21)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au3_flat.gds"); +} + // GitHub issue #72 (Edges/Region NOT issue) TEST(100) { diff --git a/src/db/unit_tests/dbEdgesUtilsTests.cc b/src/db/unit_tests/dbEdgesUtilsTests.cc index 86a0ad53d..bf201b5f1 100644 --- a/src/db/unit_tests/dbEdgesUtilsTests.cc +++ b/src/db/unit_tests/dbEdgesUtilsTests.cc @@ -83,5 +83,25 @@ TEST(1) EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), true); EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 5), db::Point (0, 5)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 10), + db::Point (20, 10), + db::Point (20, -10), + db::Point (10, -10), + db::Point (10, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts + 0, pts + sizeof(pts) / sizeof(pts[0])); + + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 10), db::Point (20, 10)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, -10), db::Point (20, -10)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (11, 0)), poly), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (9, 0)), poly), true); } diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 23833d2ff..4679c5951 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -23,6 +23,7 @@ #include "tlUnitTest.h" #include "tlStream.h" +#include "tlFileUtils.h" #include "dbHierProcessor.h" #include "dbTestSupport.h" #include "dbReader.h" @@ -32,6 +33,9 @@ #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" #include "dbPolygon.h" +#include "dbRecursiveInstanceIterator.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" static std::string testdata (const std::string &fn) { @@ -1284,3 +1288,87 @@ TEST(Arrays) run_test_bool2 (_this, "hlp18.oas", TMNot, 100); } +TEST(XORTool) +{ + test_is_long_runner (); + + std::string fna (tl::combine_path (tl::testdata_private (), "xor/a.gds.gz")); + std::string fnb (tl::combine_path (tl::testdata_private (), "xor/b.gds.gz")); + std::string fn_au (tl::combine_path (tl::testdata_private (), "xor/xor_au.oas.gz")); + + db::Layout lya, lyb; + + unsigned int l1, l2; + + db::LayerMap lmap; + + lmap.map (db::LDPair (1, 0), l1 = lya.insert_layer ()); + lyb.insert_layer (); + + lmap.map (db::LDPair (2, 0), l2 = lya.insert_layer ()); + lyb.insert_layer (); + + { + tl::InputStream stream (fna); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lya, options); + } + + { + tl::InputStream stream (fnb); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lyb, options); + } + + db::Layout ly_out; + db::cell_index_type top_out = ly_out.add_cell ("TOP"); + unsigned int l1_out = ly_out.insert_layer (db::LayerProperties (1, 0)); + unsigned int l2_out = ly_out.insert_layer (db::LayerProperties (2, 0)); + + db::DeepShapeStore dss; + dss.set_wants_all_cells (true); // saves time for less cell mapping operations + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l1); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l1); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (12)); + + xor_res.insert_into (&ly_out, top_out, l1_out); + } + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l2); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l2); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (15984)); + + xor_res.insert_into (&ly_out, top_out, l2_out); + } + + db::compare_layouts (_this, ly_out, fn_au, db::WriteOAS); +} diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c89af7605..6184176db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -26,6 +26,10 @@ #include "dbLayoutDiff.h" #include "tlString.h" #include "tlUnitTest.h" +#include "tlFileUtils.h" +#include "tlStream.h" +#include "dbReader.h" +#include "dbWriter.h" #include @@ -1554,3 +1558,225 @@ TEST(11_LayoutIsWeakPointer) x = collect(i1, *g); EXPECT_EQ (x, ""); } + +TEST(12_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + db::Cell &c2 (g->cell (g->add_cell ())); + db::Cell &c3 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2000)); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + std::vector lv; + lv.push_back (0); + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + lv.push_back (1); // empty, but kills "for merged" optimization + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + // no longer optimized + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); + i1.set_overlapping (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); +} + + +TEST(13_ForMergedPerformance) +{ + test_is_long_runner (); + + std::string fn (tl::combine_path (tl::testdata_private (), "oasis/caravel.oas.gz")); + + db::Layout ly; + + { + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + unsigned l1 = ly.get_layer (db::LayerProperties (66, 20)); + unsigned l2 = ly.get_layer (db::LayerProperties (235, 4)); + + db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); + db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + + size_t n1_expected_full = db::default_editable_mode () ? 1203072 : 1203078; + size_t n2_expected_full = 10; + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1218378)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (57462)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (n1_expected_full)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (n2_expected_full)); + } + + si1.set_for_merged_input (false); + si1.set_region (db::Box (0, 0, 1000000, 1000000)); + si2.set_for_merged_input (false); + si2.set_region (db::Box (0, 0, 1000000, 1000000)); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218823)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (2578)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + size_t n1_expected = db::default_editable_mode () ? 218068 : 218069; + size_t n2_expected = 2; + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (n1_expected)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (n2_expected)); + } + + { + tl::SelfTimer timer ("XOR on tile of 66/20"); + si1.set_for_merged_input (false); + db::Region r1 (si1); + si1.set_for_merged_input (true); + db::Region r2 (si1); + + EXPECT_EQ (r1.count (), size_t (218823)); + EXPECT_EQ (r2.count (), size_t (n1_expected)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } + + { + tl::SelfTimer timer ("XOR on tile of 235/4"); + si2.set_for_merged_input (false); + db::Region r1 (si2); + si2.set_for_merged_input (true); + db::Region r2 (si2); + + EXPECT_EQ (r1.count (), size_t (2578)); + EXPECT_EQ (r2.count (), size_t (n2_expected)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } +} + diff --git a/src/db/unit_tests/dbRegionProcessorTests.cc b/src/db/unit_tests/dbRegionProcessorTests.cc new file mode 100644 index 000000000..da534f5a9 --- /dev/null +++ b/src/db/unit_tests/dbRegionProcessorTests.cc @@ -0,0 +1,80 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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 "tlString.h" + +#include "dbRegionProcessors.h" + + +TEST(1_RegionToEdgesProcessor) +{ + db::Point hull[] = { + db::Point (0, 0), + db::Point (0, 1000), + db::Point (1000, 1000), + db::Point (1000, 2000), + db::Point (2000, 2000), + db::Point (2000, 1000), + db::Point (3000, 1000), + db::Point (3000, 0) + }; + + db::Point hole[] = { + db::Point (100, 100), + db::Point (2900, 100), + db::Point (2900, 900), + db::Point (100, 900) + }; + + db::Polygon poly; + poly.assign_hull (hull + 0, hull + sizeof (hull) / sizeof (hull[0])); + poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); + + std::vector result; + + result.clear (); + db::PolygonToEdgeProcessor ().process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,0;0,1000);(0,1000;1000,1000);(1000,1000;1000,2000);(1000,2000;2000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000);(3000,1000;3000,0);(3000,0;0,0);(100,100;2900,100);(2900,100;2900,900);(2900,900;100,900);(100,900;100,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Concave).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(2900,100;2900,900);(2900,900;100,900);(100,900;100,100);(100,100;2900,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Convex).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,2000;2000,2000);(3000,1000;3000,0);(3000,0;0,0);(0,0;0,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Step).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(1000,1000;1000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepOut).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,1000;1000,2000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepIn).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(2000,2000;2000,1000)"); +} diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 554f25956..70ac453b2 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2537,6 +2537,35 @@ TEST(55_PropertiesFilterFlat) EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); } +TEST(56_RegionsFromShapes) +{ + db::Shapes shapes; + + shapes.insert (db::Box (0, 0, 100, 200)); + shapes.insert (db::Box (50, 50, 150, 250)); + + EXPECT_EQ (db::Region (shapes).area (), 32500); + EXPECT_EQ (db::Region (shapes, false).area (), 40000); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5), false).area (), 10000); + + // for cross-checking: same for RecursiveShapeIterator + + db::Layout layout; + unsigned int l1 = layout.insert_layer (); + db::Cell &top = layout.cell (layout.add_cell ("TOP")); + + top.shapes (l1).insert (db::Box (0, 0, 100, 200)); + top.shapes (l1).insert (db::Box (50, 50, 150, 250)); + + db::RecursiveShapeIterator si (layout, top, l1); + + EXPECT_EQ (db::Region (si).area (), 32500); + EXPECT_EQ (db::Region (si, false).area (), 40000); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000); +} + TEST(100_Processors) { db::Region r; diff --git a/src/db/unit_tests/dbShapeTests.cc b/src/db/unit_tests/dbShapeTests.cc index b6f8b514c..1d4a19003 100644 --- a/src/db/unit_tests/dbShapeTests.cc +++ b/src/db/unit_tests/dbShapeTests.cc @@ -948,3 +948,89 @@ TEST(9) EXPECT_EQ (si.at_end (), true); } +// Rectangle +TEST(10) +{ + db::Manager m (true); + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::ShapeIterator si; + + s.insert (db::Point (100, 200)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Edge (db::Point (100, 200), db::Point (200, 400))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::EdgePair (db::Edge (db::Point (100, 200), db::Point (200, 400)), db::Edge (db::Point (0, 300), db::Point (100, 500)))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Box (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::ShortBox (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::SimplePolygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + db::Point pts1 [1] = { db::Point (0, 0) }; + db::Point pts2 [2] = { db::Point (0, 0), db::Point (1000, 0) }; + db::Point pts2b [2] = { db::Point (0, 0), db::Point (1000, 1000) }; + db::Point pts3 [3] = { db::Point (0, 0), db::Point (1000, 0), db::Point (1000, 1000) }; + + s.clear (); + s.insert (db::Path (pts1 + 0, pts1 + 1, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 1500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500, true)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts2b + 0, pts2b + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts3 + 0, pts3 + 3, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); +} diff --git a/src/db/unit_tests/dbTransTests.cc b/src/db/unit_tests/dbTransTests.cc index 85f9a5188..622b94428 100644 --- a/src/db/unit_tests/dbTransTests.cc +++ b/src/db/unit_tests/dbTransTests.cc @@ -353,9 +353,9 @@ TEST(11) EXPECT_EQ (x.try_read (tt2), true); EXPECT_EQ (x.test ("a"), true); EXPECT_EQ (tt2.to_string (), t2.to_string ()); - x = tl::Extractor ("m22.5 *0.55 12.4,-17 ++"); + x = tl::Extractor ("m22.5 *0.55 12.4,-17 ##"); EXPECT_EQ (x.try_read (tt2), true); - EXPECT_EQ (x.test ("++"), true); + EXPECT_EQ (x.test ("##"), true); EXPECT_EQ (tt2.to_string (), "m22.5 *0.55 12.4,-17"); EXPECT_EQ (tt2.to_string (), t3.to_string ()); } diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 2884fc920..a5ef9690e 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -87,6 +87,7 @@ SOURCES = \ dbDeepTextsTests.cc \ dbNetShapeTests.cc \ dbHierNetsProcessorTests.cc \ + dbRegionProcessorTests.cc \ dbAsIfFlatRegionTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 3fab7774b..6b080c952 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -358,7 +358,7 @@ The plain function is equivalent to "primary.bbox_width". This method acts on edge expressions and delivers a specific part of each edge. See layer#centers for details about this functionality.

-

"corners" - Applies smoothing

+

"corners" - Selects corners of polygons

Usage:

"edges" - Decomposes the layer into single edges

+

Usage:

+
    +
  • layer.edges
  • +
  • layer.edges(mode)
  • +

Edge pair collections are decomposed into the individual edges that make up the edge pairs. Polygon layers are decomposed into the edges making up the @@ -791,6 +796,36 @@ is called on.

Merged semantics applies, i.e. the result reflects merged polygons rather than individual ones unless raw mode is chosen. +

+The "mode" argument allows selecting specific edges from polygons. +Allowed values are: "convex", "concave", "step", "step_in" and "step_out". +"step" generates edges only if they provide a step between two other +edges. "step_in" creates edges that make a step towards the inside of +the polygon and "step_out" creates edges that make a step towards the +outside: +

+

+out = in.edges(convex)
+
+

+This feature is only available for polygon layers. +

+The following images show the effect of the mode argument: +

+ + + + + + + + + + + + + +

"edges?" - Returns true, if the layer is an edge layer

diff --git a/src/doc/doc/images/drc_edge_modes1.png b/src/doc/doc/images/drc_edge_modes1.png new file mode 100644 index 000000000..f5270d944 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes1.png differ diff --git a/src/doc/doc/images/drc_edge_modes2.png b/src/doc/doc/images/drc_edge_modes2.png new file mode 100644 index 000000000..5005e888d Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes2.png differ diff --git a/src/doc/doc/images/drc_edge_modes3.png b/src/doc/doc/images/drc_edge_modes3.png new file mode 100644 index 000000000..cbc9c978b Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes3.png differ diff --git a/src/doc/doc/images/drc_edge_modes4.png b/src/doc/doc/images/drc_edge_modes4.png new file mode 100644 index 000000000..94cf50140 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes4.png differ diff --git a/src/doc/doc/images/drc_edge_modes5.png b/src/doc/doc/images/drc_edge_modes5.png new file mode 100644 index 000000000..706c18fa4 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes5.png differ diff --git a/src/doc/doc/images/drc_edge_modes6.png b/src/doc/doc/images/drc_edge_modes6.png new file mode 100644 index 000000000..e90b4380f Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes6.png differ diff --git a/src/doc/docDRCLVSResources.qrc b/src/doc/docDRCLVSResources.qrc index e4449b7ed..905f12d19 100644 --- a/src/doc/docDRCLVSResources.qrc +++ b/src/doc/docDRCLVSResources.qrc @@ -59,6 +59,12 @@ doc/images/drc_extended2.png doc/images/drc_extended3.png doc/images/drc_extended4.png + doc/images/drc_edge_modes1.png + doc/images/drc_edge_modes2.png + doc/images/drc_edge_modes3.png + doc/images/drc_edge_modes4.png + doc/images/drc_edge_modes5.png + doc/images/drc_edge_modes6.png doc/images/drc_extents1.png doc/images/drc_extents2.png doc/images/drc_inside.png diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index c3674e3d2..3f093df45 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -756,15 +756,17 @@ CODE # %DRC% # @name corners - # @brief Applies smoothing + # @brief Selects corners of polygons # @synopsis expression.corners # @synopsis expression.corners(as_dots) # @synopsis expression.corners(as_boxes) # # This operation acts on polygons and selects the corners of the polygons. # It can be put into a condition to select corners by their angles. The angle of - # a corner is positive for a turn to the left if walking a polygon counterclockwise - # and negative for the turn to the right. Angles take values between -180 and 180 degree. + # a corner is positive for a turn to the left if walking a polygon clockwise + # and negative for the turn to the right. Hence positive angles indicate concave + # (inner) corners, negative ones indicate convex (outer) corners. + # Angles take values between -180 and 180 degree. # # When using "as_dots" for the argument, the operation will return single-point edges at # the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be @@ -780,8 +782,8 @@ CODE # The following example selects all inner corners: # # @code - # out = in.drc(corners < 0) - # out = in.drc(primary.corners < 0) # equivalent + # out = in.drc(corners > 0) + # out = in.drc(primary.corners > 0) # equivalent # @/code # # The "corners" method is available as a plain function or as a method on \DRC# expressions. @@ -989,6 +991,7 @@ CODE # @name edges # @brief Converts the input shapes into edges # @synopsis expression.edges + # @synopsis expression.edges(mode) # # Polygons will be separated into edges forming their contours. Edge pairs will be # decomposed into individual edges. @@ -1001,9 +1004,38 @@ CODE # @code # out = in.drc(primary.edges) # @/code + # + # The "mode" argument allows selecting specific edges from polygons. + # Allowed values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only if they provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside: + # + # @code + # out = in.drc(primary.edges(convex)) + # @/code + # + # In addition, "not_.." variants are available which selects edges + # not qualifying for the specific mode: + # + # @code + # out = in.drc(primary.edges(not_convex)) + # @/code + # + # The mode argument is ignored when translating other objects than + # polygons. - def edges - return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges") + def edges(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end + return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges", mode) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 9bc76081e..81e325ff3 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -255,6 +255,46 @@ module DRC DRCJoinFlag::new(true) end + def convex + DRCEdgeMode::new(RBA::EdgeMode::Convex) + end + + def not_convex + DRCEdgeMode::new(RBA::EdgeMode::NotConvex) + end + + def concave + DRCEdgeMode::new(RBA::EdgeMode::Concave) + end + + def not_concave + DRCEdgeMode::new(RBA::EdgeMode::NotConcave) + end + + def step_in + DRCEdgeMode::new(RBA::EdgeMode::StepIn) + end + + def not_step_in + DRCEdgeMode::new(RBA::EdgeMode::NotStepIn) + end + + def step_out + DRCEdgeMode::new(RBA::EdgeMode::StepOut) + end + + def not_step_out + DRCEdgeMode::new(RBA::EdgeMode::NotStepOut) + end + + def step + DRCEdgeMode::new(RBA::EdgeMode::Step) + end + + def not_step + DRCEdgeMode::new(RBA::EdgeMode::NotStep) + end + def padding_zero DRCDensityPadding::new(:zero) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index ffa80b943..870591330 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1232,7 +1232,7 @@ CODE # This method produces markers on the corners of the polygons. An angle criterion can be given which # selects corners based on the angle of the connecting edges. Positive angles indicate a left turn # while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles - # indicate concave corners while negative ones indicate convex corners. + # indicate concave (inner) corners while negative ones indicate convex (outer) corners # # The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default. # @@ -3387,6 +3387,8 @@ CODE # %DRC% # @name edges # @brief Decomposes the layer into single edges + # @synopsis layer.edges + # @synopsis layer.edges(mode) # # Edge pair collections are decomposed into the individual edges that make up # the edge pairs. Polygon layers are decomposed into the edges making up the @@ -3395,13 +3397,61 @@ CODE # # Merged semantics applies, i.e. the result reflects merged polygons rather than # individual ones unless raw mode is chosen. + # + # The "mode" argument allows selecting specific edges from polygons. + # Allowed values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only if they provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside: + # + # @code + # out = in.edges(convex) + # @/code + # + # In addition, "not_.." variants are available which selects edges + # not qualifying for the specific mode: + # + # @code + # out = in.edges(not_convex) + # @/code + # + # The mode argument is only available for polygon layers. + # + # The following images show the effect of the mode argument: + # + # @table + # @tr + # @td @img(/images/drc_edge_modes1.png) @/td + # @td @img(/images/drc_edge_modes2.png) @/td + # @/tr + # @tr + # @td @img(/images/drc_edge_modes3.png) @/td + # @td @img(/images/drc_edge_modes4.png) @/td + # @/tr + # @tr + # @td @img(/images/drc_edge_modes5.png) @/td + # @td @img(/images/drc_edge_modes6.png) @/td + # @/tr + # @/table %w(edges).each do |f| eval <<"CODE" - def #{f} + def #{f}(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + if ! self.data.is_a?(RBA::Region) + raise "The mode argument is only available for polygon layers" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end @engine._context("#{f}") do if self.data.is_a?(RBA::Region) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, mode)) elsif self.data.is_a?(RBA::EdgePairs) DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) else diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index fd774ebd6..482f18db8 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -57,6 +57,14 @@ module DRC end end + # A wrapper for the edge mode value for Region#edges + class DRCEdgeMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the join flag for extended class DRCJoinFlag attr_accessor :value diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 1362acee0..1c8180a8d 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1672,3 +1672,33 @@ TEST(92_issue1594_dual_top) CHECKPOINT (); compare_netlists (_this, output, au); } + +TEST(100_edge_interaction_with_count) +{ + run_test (_this, "100", false); +} + +TEST(100d_edge_interaction_with_count) +{ + run_test (_this, "100", true); +} + +TEST(101_edge_booleans_with_dots) +{ + run_test (_this, "101", false); +} + +TEST(101d_edge_booleans_with_dots) +{ + run_test (_this, "101", true); +} + +TEST(102_edge_modes) +{ + run_test (_this, "102", false); +} + +TEST(102d_edge_modes) +{ + run_test (_this, "102", true); +} diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 2e5488d2e..bcd9ae8a2 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -399,6 +399,16 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P m_icon_widgets.push_back (icon_label); m_all_widgets.back ().push_back (icon_label); + std::string range; + + if (! p->min_value ().is_nil () || ! p->max_value ().is_nil ()) { + range = tl::sprintf ( + " [%s, %s]" , + p->min_value ().is_nil () ? "-\u221e" /*infinity*/ : p->min_value ().to_string (), + p->max_value ().is_nil () ? "\u221e" /*infinity*/ : p->max_value ().to_string () + ); + } + if (p->get_type () != db::PCellParameterDeclaration::t_callback) { std::string leader; @@ -406,7 +416,8 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P leader = tl::sprintf ("[%s] ", p->get_name ()); } - QLabel *l = new QLabel (tl::to_qstring (leader + description), inner_frame); + QLabel *l = new QLabel (tl::to_qstring (leader + description + range), inner_frame); + inner_grid->addWidget (l, row, 1); m_all_widgets.back ().push_back (l); @@ -702,9 +713,11 @@ PCellParametersPage::do_parameter_changed () bool ok = true; db::ParameterStates states = m_states; get_parameters (states, &ok); // includes coerce - update_widgets_from_states (states); - if (ok && ! lazy_evaluation ()) { - emit edited (); + if (ok) { + update_widgets_from_states (states); + if (! lazy_evaluation ()) { + emit edited (); + } } } @@ -762,6 +775,8 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool ps.set_value (tl::Variant (v)); lay::indicate_error (le, (tl::Exception *) 0); + check_range(tl::Variant (v), *p); + } catch (tl::Exception &ex) { lay::indicate_error (le, &ex); @@ -786,6 +801,8 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool ps.set_value (tl::Variant (v)); lay::indicate_error (le, (tl::Exception *) 0); + check_range(tl::Variant (v), *p); + } catch (tl::Exception &ex) { lay::indicate_error (le, &ex); @@ -1085,6 +1102,18 @@ PCellParametersPage::states_from_parameters (db::ParameterStates &states, const } } +void +PCellParametersPage::check_range (const tl::Variant &value, const db::PCellParameterDeclaration &decl) +{ + if (! decl.min_value ().is_nil () && value < decl.min_value ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("The value is lower than the minimum allowed value: given value is %s, minimum value is %s")), value.to_string (), decl.min_value ().to_string ())); + } + + if (! decl.max_value ().is_nil () && ! (value < decl.max_value () || value == decl.max_value ())) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("The value is higher than the maximum allowed value: given value is %s, maximum value is %s")), value.to_string (), decl.max_value ().to_string ())); + } +} + } #endif diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index e6e501399..a7b528371 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -181,6 +181,7 @@ private: void get_parameters_internal (db::ParameterStates &states, bool &edit_error); std::vector parameter_from_states (const db::ParameterStates &states) const; void states_from_parameters (db::ParameterStates &states, const std::vector ¶meters); + void check_range (const tl::Variant& value, const db::PCellParameterDeclaration &decl); }; } diff --git a/src/lay/lay/MainConfigPage3.ui b/src/lay/lay/MainConfigPage3.ui index 54420b273..614606a58 100644 --- a/src/lay/lay/MainConfigPage3.ui +++ b/src/lay/lay/MainConfigPage3.ui @@ -1,67 +1,75 @@ - + + MainConfigPage3 - - + + 0 0 - 475 - 81 + 504 + 180 - + Settings - - - 9 - - + + 6 + + 9 + - - + + Default Grids - - + + 9 - + 6 - - - + + + + + 0 + 0 + + + + Grids for "View" menu + + + + + + µm (g1,g2,...) - - - - - 7 - 0 + + + + 0 0 - - - - - 5 - 5 - 0 - 0 - + + + + <html>You can declare one grid a strong default to enforce an editing grid from this list. To do so, add an exclamation mark - e.g. "0.01!,0.02,0.05". +<br/><b>Note</b>: the general default grids can be overridden by technology specific default grids.</html> - - Grids for "View" menu + + true @@ -70,7 +78,7 @@ - + diff --git a/src/lay/lay/TechBaseEditorPage.ui b/src/lay/lay/TechBaseEditorPage.ui index 668e04ffe..17d89749d 100644 --- a/src/lay/lay/TechBaseEditorPage.ui +++ b/src/lay/lay/TechBaseEditorPage.ui @@ -7,7 +7,7 @@ 0 0 625 - 587 + 616 @@ -331,6 +331,9 @@ properties The default database unit is used as database unit for freshly created layouts + + true + @@ -352,7 +355,10 @@ properties - These grids are available for selection from the "View" menu + These grids are available for selection from the "View" menu and will override the general ones. You can declare one grid as a strong default to enforce an editing grid from this list. To do so, add an exclamation mark to the grid - e.g. "0.01!,0.02,0.05". + + + true diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index a7634e4e5..95e724196 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -525,7 +525,7 @@ CustomizeMenuConfigPage::commit (lay::Dispatcher *dispatcher) std::map::iterator cb = m_current_bindings.find (kb->first); if (cb != m_current_bindings.end ()) { lay::Action *a = dispatcher->menu ()->action (kb->first); - if (cb->second != a->get_default_shortcut ()) { + if (a && cb->second != a->get_default_shortcut ()) { if (cb->second.empty ()) { kb->second = lay::Action::no_shortcut (); } else { diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0eb2eb397..d9c7fe0a7 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -175,9 +175,11 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) m_disable_tab_selected (false), m_exited (false), dm_do_update_menu (this, &MainWindow::do_update_menu), + dm_do_update_grids (this, &MainWindow::do_update_grids), dm_do_update_mru_menus (this, &MainWindow::do_update_mru_menus), dm_exit (this, &MainWindow::exit), m_grid_micron (0.001), + m_default_grid (0.0), m_default_grids_updated (true), m_new_layout_current_panel (false), m_synchronized_views (false), @@ -498,34 +500,9 @@ MainWindow::~MainWindow () std::string MainWindow::all_layout_file_formats () const { - std::string fmts = tl::to_string (QObject::tr ("All layout files (")); - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - if (rdr != tl::Registrar::begin ()) { - fmts += " "; - } - std::string f = rdr->file_format (); - if (!f.empty ()) { - const char *fp = f.c_str (); - while (*fp && *fp != '(') { - ++fp; - } - if (*fp) { - ++fp; - } - while (*fp && *fp != ')') { - fmts += *fp++; - } - } - } - fmts += ");;"; - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - if (!rdr->file_format ().empty ()) { - fmts += rdr->file_format (); - fmts += ";;"; - } - } - fmts += tl::to_string (QObject::tr ("All files (*)")); - + std::string fmts = db::StreamFormatDeclaration::all_formats_string (); + fmts += ";;"; + fmts += tl::to_string (tr ("All files (*)")); return fmts; } @@ -583,7 +560,7 @@ MainWindow::technology_changed () } m_default_grids_updated = true; // potentially ... - dm_do_update_menu (); + dm_do_update_grids (); } void @@ -939,7 +916,7 @@ MainWindow::config_finalize () // Update the default grids menu if necessary if (m_default_grids_updated) { - dm_do_update_menu (); + dm_do_update_grids (); } // make the changes visible in the setup form if the form is visible @@ -972,6 +949,7 @@ MainWindow::configure (const std::string &name, const std::string &value) tl::Extractor ex (value.c_str ()); m_default_grids.clear (); + m_default_grid = 0.0; m_default_grids_updated = true; // convert the list of grids to a list of doubles @@ -980,6 +958,9 @@ MainWindow::configure (const std::string &name, const std::string &value) if (! ex.try_read (g)) { break; } + if (ex.test ("!")) { + m_default_grid = g; + } m_default_grids.push_back (g); ex.test (","); } @@ -4041,6 +4022,38 @@ MainWindow::menu_changed () dm_do_update_menu (); } +void +MainWindow::do_update_grids () +{ + const std::vector *grids = &m_default_grids; + double default_grid = m_default_grid; + + std::vector tech_grids; + lay::TechnologyController *tc = lay::TechnologyController::instance (); + if (tc && tc->active_technology ()) { + tech_grids = tc->active_technology ()->default_grid_list (); + if (! tech_grids.empty ()) { + grids = &tech_grids; + default_grid = tc->active_technology ()->default_grid (); + } + } + + if (default_grid > db::epsilon) { + for (auto g = grids->begin (); g != grids->end (); ++g) { + if (db::coord_traits::equals (*g, m_grid_micron)) { + default_grid = 0.0; + break; + } + } + } + + if (default_grid > db::epsilon) { + dispatcher ()->config_set (cfg_grid, default_grid); + } + + do_update_menu (); +} + void MainWindow::do_update_menu () { @@ -4082,7 +4095,7 @@ MainWindow::do_update_menu () lay::Action *ga = new lay::ConfigureAction (gs, cfg_grid, tl::to_string (*g)); ga->set_checkable (true); - ga->set_checked (fabs (*g - m_grid_micron) < 1e-10); + ga->set_checked (db::coord_traits::equals (*g, m_grid_micron)); for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { menu ()->insert_item (*t + ".end", name, ga); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 326bc3c07..64e457c89 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -706,6 +706,7 @@ protected slots: protected: void update_content (); void do_update_menu (); + void do_update_grids (); void do_update_mru_menus (); bool eventFilter (QObject *watched, QEvent *event); @@ -753,6 +754,7 @@ private: bool m_disable_tab_selected; bool m_exited; tl::DeferredMethod dm_do_update_menu; + tl::DeferredMethod dm_do_update_grids; tl::DeferredMethod dm_do_update_mru_menus; tl::DeferredMethod dm_exit; QTimer m_message_timer; @@ -765,6 +767,7 @@ private: std::string m_initial_technology; double m_grid_micron; std::vector m_default_grids; + double m_default_grid; bool m_default_grids_updated; std::vector > m_key_bindings; bool m_new_layout_current_panel; diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index 792818c65..76c20d842 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -419,12 +419,6 @@ LineStyleSelectionButton::update_menu () unsigned int n = palette.style_by_index (i); if (int (n) < std::distance (patterns.begin (), patterns.end ())) { -#if QT_VERSION > 0x050000 - double dpr = devicePixelRatio (); -#else - double dpr = 1.0; -#endif - lay::LineStyleInfo info = patterns.begin () [n]; std::string name (info.name ()); @@ -1226,13 +1220,14 @@ ColorButton::set_color_internal (QColor c) double dpr = 1.0; #endif - QPixmap pixmap (rt.width () * dpr, rt.height () * dpr); + QImage image (rt.width () * dpr, rt.height () * dpr, QImage::Format_ARGB32); #if QT_VERSION >= 0x50000 - pixmap.setDevicePixelRatio (dpr); + image.setDevicePixelRatio (dpr); #endif + image.fill (Qt::transparent); QColor text_color = palette ().color (QPalette::Active, QPalette::Text); - QPainter pxpainter (&pixmap); + QPainter pxpainter (&image); QPen frame_pen (text_color); frame_pen.setWidthF (1.0); frame_pen.setJoinStyle (Qt::MiterJoin); @@ -1253,7 +1248,7 @@ ColorButton::set_color_internal (QColor c) } - QPushButton::setIcon (QIcon (pixmap)); + QPushButton::setIcon (QIcon (QPixmap::fromImage (image))); } QColor diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 459803767..800025a13 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -35,6 +35,7 @@ #include "layConfigurationDialog.h" #include "dbLayoutUtils.h" #include "dbRecursiveShapeIterator.h" +#include "dbStream.h" #include "ui_MarkerBrowserDialog.h" @@ -420,11 +421,17 @@ BEGIN_PROTECTED // collect the formats available ... std::string fmts = tl::to_string (QObject::tr ("All files (*)")); for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - fmts += ";;" + rdr->file_format (); + fmts += ";;"; + fmts += rdr->file_format (); } + // also provide the stream formats + fmts += ";;"; + fmts += db::StreamFormatDeclaration::all_formats_string (); + // prepare and open the file dialog lay::FileDialog open_dialog (this, tl::to_string (QObject::tr ("Load Marker Database File")), fmts); + if (open_dialog.get_open (m_open_filename)) { std::unique_ptr db (new rdb::Database ()); @@ -731,111 +738,17 @@ MarkerBrowserDialog::deactivated () void MarkerBrowserDialog::scan_layer () { - std::vector layers = view ()->selected_layers (); - if (layers.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected to get shapes from"))); - } - - int cv_index = -1; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children ()) { - if (cv_index < 0) { - cv_index = (*l)->cellview_index (); - } else if ((*l)->cellview_index () >= 0) { - if (cv_index != (*l)->cellview_index ()) { - throw tl::Exception (tl::to_string (QObject::tr ("All layers must originate from the same layout"))); - } - } - } - } - - if (cv_index < 0) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected"))); - } - - tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); - progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); - progress.set_unit (10000); - - const lay::CellView &cv = view ()->cellview (cv_index); - const db::Layout &layout = cv->layout (); - - std::unique_ptr rdb (new rdb::Database ()); - rdb->set_name ("Shapes"); - rdb->set_top_cell_name (layout.cell_name (cv.cell_index ())); - rdb::Cell *rdb_top_cell = rdb->create_cell (rdb->top_cell_name ()); - - std::string desc; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - if (! desc.empty ()) { - desc += ", "; - } - desc += layout.get_properties ((*l)->layer_index ()).to_string (); - } - } - desc = tl::to_string (tr ("Hierarchical shapes of layer(s) ")) + desc; - desc += " "; - desc += tl::to_string (tr ("from cell ")); - desc += cv->layout ().cell_name (cv.cell_index ()); - rdb->set_description (desc); - - std::set called_cells; - called_cells.insert (cv.cell_index ()); - cv->layout ().cell (cv.cell_index ()).collect_called_cells (called_cells); - - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - - rdb::Category *cat = rdb->create_category (layout.get_properties ((*l)->layer_index ()).to_string ()); - - for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { - - if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { - continue; - } - - const db::Cell &cell = *cid; - if (cell.shapes ((*l)->layer_index ()).size () > 0) { - - std::string cn = layout.cell_name (cell.cell_index ()); - const rdb::Cell *rdb_cell = rdb->cell_by_qname (cn); - if (! rdb_cell) { - - rdb::Cell *rdb_cell_nc = rdb->create_cell (cn); - rdb_cell = rdb_cell_nc; - - std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cv.cell_index ()); - if (ctx.first) { - db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); - rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); - } - - } - - for (db::ShapeIterator shape = cell.shapes ((*l)->layer_index ()).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { - - rdb::create_item_from_shape (rdb.get (), rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); - - ++progress; - - } - - } - - } - - } - - } - - unsigned int rdb_index = view ()->add_rdb (rdb.release ()); - view ()->open_rdb_browser (rdb_index, cv_index); + scan_layer_flat_or_hierarchical (false); } -void +void MarkerBrowserDialog::scan_layer_flat () +{ + scan_layer_flat_or_hierarchical (true); +} + +void +MarkerBrowserDialog::scan_layer_flat_or_hierarchical (bool flat) { std::vector layers = view ()->selected_layers (); if (layers.empty ()) { @@ -859,52 +772,19 @@ MarkerBrowserDialog::scan_layer_flat () throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected"))); } - tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); - progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); - progress.set_unit (10000); - const lay::CellView &cv = view ()->cellview (cv_index); const db::Layout &layout = cv->layout (); + std::vector > layer_indexes; + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { + layer_indexes.push_back (std::make_pair ((*l)->layer_index (), (*l)->name ())); + } + } + std::unique_ptr rdb (new rdb::Database ()); - rdb->set_name ("Shapes"); - rdb->set_top_cell_name (layout.cell_name (cv.cell_index ())); - rdb::Cell *rdb_top_cell = rdb->create_cell (rdb->top_cell_name ()); - std::string desc; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - if (! desc.empty ()) { - desc += ", "; - } - desc += layout.get_properties ((*l)->layer_index ()).to_string (); - } - } - desc = tl::to_string (tr ("Flat shapes of layer(s) ")) + desc; - desc += " "; - desc += tl::to_string (tr ("from cell ")); - desc += cv->layout ().cell_name (cv.cell_index ()); - rdb->set_description (desc); - - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - - rdb::Category *cat = rdb->create_category (layout.get_properties ((*l)->layer_index ()).to_string ()); - - db::RecursiveShapeIterator shape (layout, *cv.cell (), (*l)->layer_index ()); - while (! shape.at_end ()) { - - rdb::create_item_from_shape (rdb.get (), rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); - - ++progress; - ++shape; - - } - - } - - } + rdb->scan_layout (layout, cv.cell_index (), layer_indexes, flat); unsigned int rdb_index = view ()->add_rdb (rdb.release ()); view ()->open_rdb_browser (rdb_index, cv_index); diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index 7988d6070..482913f6a 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -36,9 +36,16 @@ namespace Ui class MarkerBrowserDialog; } +namespace db +{ + class Layout; +} + namespace rdb { +class Database; + class LAYUI_PUBLIC MarkerBrowserDialog : public lay::Browser { @@ -101,6 +108,7 @@ private: void update_content (); void scan_layer (); void scan_layer_flat (); + void scan_layer_flat_or_hierarchical (bool flat); }; } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 5f0394057..803cca456 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1117,7 +1117,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::map::const_iterator l = layers.begin (); l != layers.end (); ++l) { @@ -1176,7 +1176,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::mapsecond == ViaGeometry) { @@ -1284,7 +1284,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::mapsecond == All) { @@ -1364,21 +1364,22 @@ LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPu m_layers.insert (std::make_pair (std::make_pair (n, LayerDetailsKey (purpose, mask)), ll)); if (ll.empty () && ! has_fallback (purpose)) { + std::string msg; if (n.empty ()) { - tl::warn << tl::to_string (tr ("No mapping for purpose")) << " '" << purpose_to_name (purpose) << "'" << tl::noendl; + msg = tl::to_string (tr ("No mapping for purpose")) + " '" + purpose_to_name (purpose) + "'"; } else { - tl::warn << tl::to_string (tr ("No mapping for layer")) << " '" << n << "', purpose '" << purpose_to_name (purpose) << "'" << tl::noendl; + msg = tl::to_string (tr ("No mapping for layer")) + " '" + n + "', purpose '" + purpose_to_name (purpose) + "'"; } if (mask > 0) { - tl::warn << tl::to_string (tr (" Mask ")) << mask << tl::noendl; + msg += tl::to_string (tr (" Mask ")) + tl::to_string (mask); } // not printing via size - too confusing? #if 0 if (via_size != db::DVector ()) { - tl::warn << tl::to_string (tr (" Via size ")) << via_size.to_string () << tl::noendl; + msg += tl::to_string (tr (" Via size ")) + via_size.to_string (); } #endif - tl::warn << tl::to_string (tr (" - layer is ignored")); + common_reader_warn (msg + tl::to_string (tr (" - layer is ignored"))); } return ll; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 1f7ab2b93..d9fd91ddd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -1080,6 +1080,12 @@ OASISReader::do_read (db::Layout &layout) LNameJoinOp2 op2; layer_names ().add (l1, l2 + 1, dt_map, op2); + // for singular layers, force a layer entry: + // this way we can have empty, but existing layers. + if (l1 == l2 && dt1 == dt2) { + open_dl (layout, db::LDPair (l1, dt1)); + } + reset_modal_variables (); // ignore properties attached to this name item diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc index f9e4bfefc..ae287e6f0 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc @@ -22,6 +22,7 @@ #include "dbOASISReader.h" +#include "dbOASISWriter.h" #include "dbTextWriter.h" #include "dbTestSupport.h" #include "tlLog.h" @@ -487,21 +488,18 @@ TEST(99) // XGEOMTRY tests (#773) TEST(100) { - const char *expected = + const char *expected = "begin_lib 0.0001\n" "begin_cell {mask}\n" - "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n" - "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n" - "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n" - "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n" - "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n" - "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n" - "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n" - "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n" "boundary 1 0 {17922 6288} {17922 6510} {18150 6510} {18150 6288} {17922 6288}\n" "boundary 1 0 {18157 647} {18157 676} {21630 676} {21630 647} {18157 647}\n" "boundary 1 0 {21956 0} {21956 89} {22047 89} {22047 0} {21956 0}\n" + "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n" + "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n" "boundary 3 0 {15392 1744} {15392 1774} {15672 1774} {15672 1744} {15392 1744}\n" + "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n" + "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n" + "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n" "boundary 5 1 {15550 1658} {15550 1673} {15570 1673} {15570 1658} {15550 1658}\n" "boundary 5 1 {15661 1657} {15641 1659} {15642 1671} {15662 1669} {15661 1657}\n" "boundary 5 1 {18150 7440} {18150 7460} {18162 7460} {18162 7440} {18150 7440}\n" @@ -519,6 +517,9 @@ TEST(100) "boundary 5 1 {25710 1978} {25710 1998} {25722 1998} {25722 1978} {25710 1978}\n" "boundary 5 1 {25710 2800} {25710 2820} {25722 2820} {25722 2800} {25710 2800}\n" "boundary 5 2 {18074 6408} {17971 6486} {17983 6502} {18086 6424} {18074 6408}\n" + "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n" + "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n" + "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n" "end_cell\n" "end_lib\n" ; @@ -543,6 +544,41 @@ TEST(100) EXPECT_EQ (std::string (os.string ()), std::string (expected)) } +// Empty layers through LAYERMAP +TEST(101) +{ + db::Layout ly; + ly.add_cell ("TOP"); + ly.insert_layer (db::LayerProperties (1, 0, "A")); + ly.insert_layer (db::LayerProperties (2, 0, "")); + ly.insert_layer (db::LayerProperties (3, 0, "C")); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISReader101.oas"); + + { + tl::OutputStream stream (tmp_file); + db::OASISWriter writer; + db::SaveLayoutOptions options; + writer.write (ly, stream, options); + } + + db::Layout ly_new; + + { + tl::InputStream stream (tmp_file); + db::Reader reader (stream); + reader.read (ly_new); + } + + // NOTE: only named layers are written into layer table + EXPECT_EQ (ly_new.cell_by_name ("TOP").first, true); + EXPECT_EQ (int (ly_new.layers ()), 2); + if (int (ly_new.layers ()) == 2) { + EXPECT_EQ (ly_new.get_properties (0).to_string (), "A (1/0)"); + EXPECT_EQ (ly_new.get_properties (1).to_string (), "C (3/0)"); + } +} + TEST(Bug_121_1) { db::Manager m (false); diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 6df3ff111..c85a7f600 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -255,6 +255,13 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::Reader reader (stream); reader.read (layout); + // named layers create a mismatch between GDS and OASIS, so we unname them here + for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { + db::LayerProperties lp = layout.get_properties ((*l).first); + lp.name.clear (); + layout.set_properties ((*l).first, lp); + } + db::SaveLayoutOptions options; db::OASISWriterOptions oasis_options; oasis_options.compression_level = compr; diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index d78cbc9cf..83d2c66e1 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -52,6 +52,12 @@ namespace lay { +bool merge_before_bool () +{ + // $KLAYOUT_XOR_MERGE_BEFORE_BOOLEAN + return tl::app_flag ("xor-merge-before-boolean"); +} + std::string cfg_xor_input_mode ("xor-input-mode"); std::string cfg_xor_output_mode ("xor-output-mode"); std::string cfg_xor_nworkers ("xor-num-workers"); @@ -766,6 +772,9 @@ XORWorker::do_perform_deep (const XORTask *xor_task) db::RecursiveShapeIterator s_a (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, xor_task->region_a ()); db::RecursiveShapeIterator s_b (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, xor_task->region_b ()); + s_a.set_for_merged_input (true); + s_b.set_for_merged_input (true); + db::Region ra (s_a, dss, db::ICplxTrans (mp_job->cva ()->layout ().dbu () / mp_job->dbu ())); db::Region rb (s_b, dss, db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ())); @@ -794,6 +803,8 @@ XORWorker::do_perform_deep (const XORTask *xor_task) dbu_scale = db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ()); } + s.set_for_merged_input (true); + rr = db::Region (s, dss, dbu_scale); } @@ -861,40 +872,11 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if ((!la.empty () && !lb.empty ()) || mp_job->el_handling () == XORJob::EL_process) { - if (! mp_job->has_tiles ()) { + tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); + size_t n; - tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); -#if 0 - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), op, true, false, true); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); + if (! merge_before_bool ()) { - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); - } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif - - } else { - - tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); - size_t n; - -#if 0 // Straightforward implementation sp.clear (); @@ -902,20 +884,36 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), mp_job->cva ().cell (), la, region_a); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); + db::RecursiveShapeIterator s_a; + if (mp_job->has_tiles ()) { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s_a.set_for_merged_input (true); + for ( ; ! s_a.at_end (); ++s_a, ++n) { + sp.insert (s_a.shape (), dbu_scale_a * s_a.trans (), n * 2); } n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), mp_job->cvb ().cell (), lb, region_b); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); + db::RecursiveShapeIterator s_b; + if (mp_job->has_tiles ()) { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s_b.set_for_merged_input (true); + for (; ! s_b.at_end (); ++s_b, ++n) { + sp.insert (s_b.shape (), dbu_scale_b * s_b.trans (), n * 2 + 1); } db::BooleanOp bool_op (mp_job->op ()); db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, mp_job->op ()); -#else + sp.process (out, bool_op); + + } else { + // This implementation is faster when a lot of overlapping shapes are involved db::Layout merge_helper; merge_helper.dbu (mp_job->dbu ()); @@ -931,7 +929,14 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { sp.insert (s.shape (), dbu_scale * s.trans (), n); } @@ -949,7 +954,14 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { sp.insert (s.shape (), dbu_scale * s.trans (), n); } @@ -960,10 +972,9 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif } @@ -990,6 +1001,8 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) dbu_scale = db::CplxTrans (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); } + s.set_for_merged_input (true); + for (; ! s.at_end (); ++s) { if (s->is_polygon () || s->is_box () || s->is_path ()) { db::Polygon p; diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 72e4eb7d2..77f3c1728 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -350,9 +350,9 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i))); if (! arg) { is_valid = a->spec ()->has_default (); - } else if (test_arg (*a, arg.get (), false /*strict*/)) { + } else if (test_arg (*a, arg.get (), false /*strict*/, false /*object substitution*/)) { ++sc; - } else if (test_arg (*a, arg.get (), true /*loose*/)) { + } else if (test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { // non-scoring match } else { is_valid = false; @@ -405,7 +405,7 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st int i = 0; for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments (); ++a, ++i) { PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i))); - if (arg && ! test_arg (*a, arg.get (), true /*loose*/)) { + if (arg && ! test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { return 0; } } @@ -1116,7 +1116,8 @@ property_setter_impl (int mid, PyObject *self, PyObject *value) // check arguments (count and type) bool is_valid = (*m)->compatible_with_num_args (1); - if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, pass != 0 /*loose in the second pass*/)) { + bool loose = (pass != 0); // loose in the second pass + if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, loose)) { is_valid = false; } diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 41cc134c0..e6ebed0ed 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -1046,7 +1046,7 @@ size_t PythonBasedMapAdaptor::serial_size () const template struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { @@ -1083,7 +1083,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *, const gsi::ArgType &, bool) + void operator() (bool *ret, PyObject *, const gsi::ArgType &, bool, bool) { // we assume we can convert everything into a variant *ret = true; @@ -1093,7 +1093,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &, bool) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &, bool, bool) { #if PY_MAJOR_VERSION < 3 if (PyString_Check (arg)) { @@ -1117,7 +1117,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { // for ptr or cptr, null is an allowed value @@ -1138,7 +1138,7 @@ struct test_arg_func size_t n = PyTuple_Size (arg); for (size_t i = 0; i < n && *ret; ++i) { - if (! test_arg (ainner, PyTuple_GetItem (arg, i), loose)) { + if (! test_arg (ainner, PyTuple_GetItem (arg, i), loose, true /*issue-1651*/)) { *ret = false; } } @@ -1147,7 +1147,7 @@ struct test_arg_func size_t n = PyList_Size (arg); for (size_t i = 0; i < n && *ret; ++i) { - if (! test_arg (ainner, PyList_GetItem (arg, i), loose)) { + if (! test_arg (ainner, PyList_GetItem (arg, i), loose, true /*issue-1651*/)) { *ret = false; } } @@ -1159,7 +1159,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { // for ptr or cptr, null is an allowed value @@ -1184,11 +1184,11 @@ struct test_arg_func PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(arg, &pos, &key, &value)) { - if (! test_arg (ainner_k, key, loose)) { + if (! test_arg (ainner_k, key, loose, true /*issue-1651*/)) { *ret = false; break; } - if (! test_arg (ainner, value, loose)) { + if (! test_arg (ainner, value, loose, true /*issue-1651*/)) { *ret = false; break; } @@ -1199,7 +1199,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { const gsi::ClassBase *acls = atype.cls (); @@ -1209,7 +1209,7 @@ struct test_arg_func return; } - if (loose && (PyTuple_Check (arg) || PyList_Check (arg))) { + if (object_substitution && (PyTuple_Check (arg) || PyList_Check (arg))) { // we may implicitly convert a tuple into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the list given. @@ -1247,10 +1247,10 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose) +test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose, bool object_substitution) { bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/pya/pya/pyaMarshal.h b/src/pya/pya/pyaMarshal.h index ea61acaef..e0e529382 100644 --- a/src/pya/pya/pyaMarshal.h +++ b/src/pya/pya/pyaMarshal.h @@ -69,7 +69,7 @@ PythonRef pull_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObje * @return True, if the type match */ bool -test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose); +test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose, bool object_substitution); /** * @brief Correct constness if a reference is const and a non-const reference is required diff --git a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py index 5bdb73e4e..5beff2043 100644 --- a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py +++ b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py @@ -66,7 +66,7 @@ class _PCellDeclarationHelperMixin: self.layer = None self.cell = None - def param(self, name, value_type, description, hidden = False, readonly = False, unit = None, default = None, choices = None): + def param(self, name, value_type, description, hidden = False, readonly = False, unit = None, default = None, choices = None, min_value = None, max_value = None): """ Defines a parameter name -> the short name of the parameter @@ -76,6 +76,8 @@ class _PCellDeclarationHelperMixin: hidden -> (boolean) true, if the parameter is not shown in the dialog readonly -> (boolean) true, if the parameter cannot be edited unit -> the unit string + min_value -> the minimum value (only effective for numerical types and if no choices are present) + max_value -> the maximum value (only effective for numerical types and if no choices are present) default -> the default value choices -> ([ [ d, v ], ...) choice descriptions/value for choice type this method defines accessor methods for the parameters @@ -102,6 +104,8 @@ class _PCellDeclarationHelperMixin: pdecl.readonly = readonly if not (default is None): pdecl.default = default + pdecl.min_value = min_value + pdecl.max_value = max_value if not (unit is None): pdecl.unit = unit if not (choices is None): diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 8cd688a4f..b6244d346 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -3941,12 +3941,16 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -3958,6 +3962,10 @@ class CompoundRegionOperationNode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: CompoundRegionOperationNode.GeometricalOp) -> bool: r""" @@ -3986,6 +3994,10 @@ class CompoundRegionOperationNode: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -4034,6 +4046,10 @@ class CompoundRegionOperationNode: r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -4044,6 +4060,10 @@ class CompoundRegionOperationNode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: CompoundRegionOperationNode.LogicalOp) -> bool: r""" @@ -4072,6 +4092,10 @@ class CompoundRegionOperationNode: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -4125,12 +4149,16 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -4142,6 +4170,10 @@ class CompoundRegionOperationNode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: CompoundRegionOperationNode.ParameterType) -> bool: r""" @@ -4170,6 +4202,10 @@ class CompoundRegionOperationNode: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -4215,12 +4251,16 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -4232,6 +4272,10 @@ class CompoundRegionOperationNode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: CompoundRegionOperationNode.RatioParameterType) -> bool: r""" @@ -4260,6 +4304,10 @@ class CompoundRegionOperationNode: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -4305,12 +4353,16 @@ class CompoundRegionOperationNode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -4322,6 +4374,10 @@ class CompoundRegionOperationNode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: CompoundRegionOperationNode.ResultType) -> bool: r""" @@ -4335,12 +4391,12 @@ class CompoundRegionOperationNode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -4350,6 +4406,10 @@ class CompoundRegionOperationNode: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -5069,11 +5129,12 @@ class CplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod - def from_dtrans(cls, trans: DCplxTrans) -> CplxTrans: + def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @classmethod def from_s(cls, s: str) -> CplxTrans: @@ -5091,7 +5152,7 @@ class CplxTrans: """ @overload @classmethod - def new(cls, c: CplxTrans, m: Optional[float] = ..., u: Optional[DVector] = ...) -> CplxTrans: + def new(cls, c: CplxTrans, mag: Optional[float] = ..., u: Optional[DVector] = ...) -> CplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -5104,7 +5165,7 @@ class CplxTrans: """ @overload @classmethod - def new(cls, c: CplxTrans, m: float, x: int, y: int) -> CplxTrans: + def new(cls, c: CplxTrans, mag: Optional[float] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> CplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -5118,15 +5179,7 @@ class CplxTrans: """ @overload @classmethod - def new(cls, m: float) -> CplxTrans: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, u: DVector) -> CplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> CplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -5140,7 +5193,7 @@ class CplxTrans: """ @overload @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, x: float, y: float) -> CplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> CplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -5155,15 +5208,7 @@ class CplxTrans: """ @overload @classmethod - def new(cls, t: Trans) -> CplxTrans: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - @classmethod - def new(cls, t: Trans, m: float) -> CplxTrans: + def new(cls, t: Trans, mag: Optional[float] = ...) -> CplxTrans: r""" @brief Creates a transformation from a simple transformation and a magnification @@ -5171,27 +5216,30 @@ class CplxTrans: """ @overload @classmethod - def new(cls, trans: DCplxTrans) -> CplxTrans: + def new(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: ICplxTrans) -> CplxTrans: + def new(cls, trans: ICplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the output space from integer units to floating-point units. Formally, the CplxTrans transformation is initialized with 'from_dbu * trans' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: VCplxTrans) -> CplxTrans: + def new(cls, trans: VCplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input and output space from integer units to floating-point units and vice versa. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod @@ -5240,7 +5288,7 @@ class CplxTrans: @brief Creates a unit transformation """ @overload - def __init__(self, c: CplxTrans, m: Optional[float] = ..., u: Optional[DVector] = ...) -> None: + def __init__(self, c: CplxTrans, mag: Optional[float] = ..., u: Optional[DVector] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -5252,7 +5300,7 @@ class CplxTrans: @param u The Additional displacement """ @overload - def __init__(self, c: CplxTrans, m: float, x: int, y: int) -> None: + def __init__(self, c: CplxTrans, mag: Optional[float] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -5265,14 +5313,7 @@ class CplxTrans: @param y The Additional displacement (y) """ @overload - def __init__(self, m: float) -> None: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - def __init__(self, mag: float, rot: float, mirrx: bool, u: DVector) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -5285,7 +5326,7 @@ class CplxTrans: @param u The displacement """ @overload - def __init__(self, mag: float, rot: float, mirrx: bool, x: float, y: float) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -5299,39 +5340,35 @@ class CplxTrans: @param y The y displacement """ @overload - def __init__(self, t: Trans) -> None: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - def __init__(self, t: Trans, m: float) -> None: + def __init__(self, t: Trans, mag: Optional[float] = ...) -> None: r""" @brief Creates a transformation from a simple transformation and a magnification Creates a magnifying transformation from a simple transformation and a magnification. """ @overload - def __init__(self, trans: DCplxTrans) -> None: + def __init__(self, trans: DCplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: ICplxTrans) -> None: + def __init__(self, trans: ICplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the output space from integer units to floating-point units. Formally, the CplxTrans transformation is initialized with 'from_dbu * trans' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: VCplxTrans) -> None: + def __init__(self, trans: VCplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input and output space from integer units to floating-point units and vice versa. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload def __init__(self, u: DVector) -> None: @@ -5769,9 +5806,13 @@ class CplxTrans: r""" @brief Converts the transformation to another transformation with integer input and output coordinates - The database unit can be specified to translate the floating-point coordinate displacement in micron units to an integer-coordinate displacement in database units. The displacement's' coordinates will be divided by the database unit. + This method is redundant with the conversion constructors. Instead of 'to_itrans' use the conversion constructor: - This method has been introduced in version 0.25. + @code + itrans = RBA::ICplxTrans::new(trans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_s(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @@ -5781,19 +5822,29 @@ class CplxTrans: The lazy and DBU arguments have been added in version 0.27.6. """ - def to_trans(self) -> DCplxTrans: + def to_trans(self, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point input coordinates - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors. Instead of 'to_trans' use the conversion constructor: + + @code + dtrans = RBA::DCplxTrans::new(trans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_vtrans(self, dbu: Optional[float] = ...) -> VCplxTrans: r""" @brief Converts the transformation to another transformation with integer output and floating-point input coordinates - The database unit can be specified to translate the floating-point coordinate displacement in micron units to an integer-coordinate displacement in database units. The displacement's' coordinates will be divided by the database unit. + This method is redundant with the conversion constructors. Instead of 'to_vtrans' use the conversion constructor: - This method has been introduced in version 0.25. + @code + vtrans = RBA::VCplxTrans::new(trans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ @overload def trans(self, box: Box) -> DBox: @@ -7236,11 +7287,12 @@ class DCplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod - def from_itrans(cls, trans: CplxTrans) -> DCplxTrans: + def from_itrans(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from integer units to floating-point units. Formally, the DCplxTrans transformation is initialized with 'trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_itrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @classmethod def from_s(cls, s: str) -> DCplxTrans: @@ -7258,7 +7310,7 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, c: DCplxTrans, m: Optional[float] = ..., u: Optional[DVector] = ...) -> DCplxTrans: + def new(cls, c: DCplxTrans, mag: Optional[float] = ..., u: Optional[DVector] = ...) -> DCplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -7271,7 +7323,7 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, c: DCplxTrans, m: float, x: float, y: float) -> DCplxTrans: + def new(cls, c: DCplxTrans, mag: Optional[float] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -7285,15 +7337,7 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, m: float) -> DCplxTrans: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, u: DVector) -> DCplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> DCplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -7307,7 +7351,7 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, x: float, y: float) -> DCplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -7322,15 +7366,7 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, t: DTrans) -> DCplxTrans: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - @classmethod - def new(cls, t: DTrans, m: float) -> DCplxTrans: + def new(cls, t: DTrans, mag: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a transformation from a simple transformation and a magnification @@ -7338,27 +7374,30 @@ class DCplxTrans: """ @overload @classmethod - def new(cls, trans: CplxTrans) -> DCplxTrans: + def new(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from integer units to floating-point units. Formally, the DCplxTrans transformation is initialized with 'trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_itrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: ICplxTrans) -> DCplxTrans: + def new(cls, trans: ICplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input and output space from integer units to floating-point units and vice versa. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: VCplxTrans) -> DCplxTrans: + def new(cls, trans: VCplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the output space from integer units to floating-point units. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod @@ -7407,7 +7446,7 @@ class DCplxTrans: @brief Creates a unit transformation """ @overload - def __init__(self, c: DCplxTrans, m: Optional[float] = ..., u: Optional[DVector] = ...) -> None: + def __init__(self, c: DCplxTrans, mag: Optional[float] = ..., u: Optional[DVector] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -7419,7 +7458,7 @@ class DCplxTrans: @param u The Additional displacement """ @overload - def __init__(self, c: DCplxTrans, m: float, x: float, y: float) -> None: + def __init__(self, c: DCplxTrans, mag: Optional[float] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -7432,14 +7471,7 @@ class DCplxTrans: @param y The Additional displacement (y) """ @overload - def __init__(self, m: float) -> None: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - def __init__(self, mag: float, rot: float, mirrx: bool, u: DVector) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -7452,7 +7484,7 @@ class DCplxTrans: @param u The displacement """ @overload - def __init__(self, mag: float, rot: float, mirrx: bool, x: float, y: float) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -7466,39 +7498,35 @@ class DCplxTrans: @param y The y displacement """ @overload - def __init__(self, t: DTrans) -> None: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - def __init__(self, t: DTrans, m: float) -> None: + def __init__(self, t: DTrans, mag: Optional[float] = ...) -> None: r""" @brief Creates a transformation from a simple transformation and a magnification Creates a magnifying transformation from a simple transformation and a magnification. """ @overload - def __init__(self, trans: CplxTrans) -> None: + def __init__(self, trans: CplxTrans, dbu: Optional[float] = ...) -> None: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input space from integer units to floating-point units. Formally, the DCplxTrans transformation is initialized with 'trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_itrans'. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: ICplxTrans) -> None: + def __init__(self, trans: ICplxTrans, dbu: Optional[float] = ...) -> None: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the input and output space from integer units to floating-point units and vice versa. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: VCplxTrans) -> None: + def __init__(self, trans: VCplxTrans, dbu: Optional[float] = ...) -> None: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour + The 'dbu' argument is used to transform the output space from integer units to floating-point units. Formally, the DCplxTrans transformation is initialized with 'from_dbu * trans' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. - This constructor has been introduced in version 0.25. + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload def __init__(self, u: DVector) -> None: @@ -7928,7 +7956,13 @@ class DCplxTrans: The database unit can be specified to translate the floating-point coordinate displacement in micron units to an integer-coordinate displacement in database units. The displacement's' coordinates will be divided by the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors. Instead of 'to_itrans' use the conversion constructor: + + @code + itrans = RBA::ICplxTrans::new(dtrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_s(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @@ -7938,11 +7972,17 @@ class DCplxTrans: The lazy and DBU arguments have been added in version 0.27.6. """ - def to_trans(self) -> CplxTrans: + def to_trans(self, dbu: Optional[float] = ...) -> CplxTrans: r""" @brief Converts the transformation to another transformation with integer input coordinates - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors. Instead of 'to_trans' use the conversion constructor: + + @code + trans = RBA::CplxTrans::new(dtrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_vtrans(self, dbu: Optional[float] = ...) -> VCplxTrans: r""" @@ -7950,7 +7990,13 @@ class DCplxTrans: The database unit can be specified to translate the floating-point coordinate displacement in micron units to an integer-coordinate displacement in database units. The displacement's' coordinates will be divided by the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors. Instead of 'to_vtrans' use the conversion constructor: + + @code + vtrans = RBA::VCplxTrans::new(dtrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ @overload def trans(self, box: DBox) -> DBox: @@ -11339,8 +11385,7 @@ class DText: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: float r""" @@ -11886,7 +11931,7 @@ class DTrans: """ @overload @classmethod - def new(cls, c: DTrans, x: float, y: float) -> DTrans: + def new(cls, c: DTrans, x: Optional[float] = ..., y: Optional[float] = ...) -> DTrans: r""" @brief Creates a transformation from another transformation plus a displacement @@ -11900,7 +11945,7 @@ class DTrans: """ @overload @classmethod - def new(cls, rot: int, mirr: Optional[bool] = ..., u: Optional[DVector] = ...) -> DTrans: + def new(cls, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> DTrans: r""" @brief Creates a transformation using angle and mirror flag @@ -11913,7 +11958,7 @@ class DTrans: """ @overload @classmethod - def new(cls, rot: int, mirr: bool, x: float, y: float) -> DTrans: + def new(cls, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> DTrans: r""" @brief Creates a transformation using angle and mirror flag and two coordinate values for displacement @@ -11987,7 +12032,7 @@ class DTrans: @param u The Additional displacement """ @overload - def __init__(self, c: DTrans, x: float, y: float) -> None: + def __init__(self, c: DTrans, x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a displacement @@ -12000,7 +12045,7 @@ class DTrans: @param y The Additional displacement (y) """ @overload - def __init__(self, rot: int, mirr: Optional[bool] = ..., u: Optional[DVector] = ...) -> None: + def __init__(self, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., u: Optional[DVector] = ...) -> None: r""" @brief Creates a transformation using angle and mirror flag @@ -12012,7 +12057,7 @@ class DTrans: @param u The displacement """ @overload - def __init__(self, rot: int, mirr: bool, x: float, y: float) -> None: + def __init__(self, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., x: Optional[float] = ..., y: Optional[float] = ...) -> None: r""" @brief Creates a transformation using angle and mirror flag and two coordinate values for displacement @@ -19038,6 +19083,10 @@ class Edges(ShapeCollection): r""" @brief Compares an enum with an integer value """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -19048,6 +19097,10 @@ class Edges(ShapeCollection): r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Edges.EdgeType) -> bool: r""" @@ -19076,6 +19129,10 @@ class Edges(ShapeCollection): r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -21852,12 +21909,16 @@ class HAlign: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -21869,6 +21930,10 @@ class HAlign: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: HAlign) -> bool: r""" @@ -21882,12 +21947,12 @@ class HAlign: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -21959,6 +22024,10 @@ class HAlign: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -22090,11 +22159,13 @@ class ICplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod - def from_dtrans(cls, trans: DCplxTrans) -> ICplxTrans: + def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + The 'dbu' argument is used to transform the input space and output space from floating-point units to integer units and vice versa. Formally, the ICplxTrans transformation is initialized with 'to_dbu * trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)' and 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @classmethod def from_s(cls, s: str) -> ICplxTrans: @@ -22105,11 +22176,13 @@ class ICplxTrans: This method has been added in version 0.23. """ @classmethod - def from_trans(cls, trans: CplxTrans) -> ICplxTrans: + def from_trans(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_trans'. + The 'dbu' argument is used to transform the output space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'to_dbu * trans' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod @@ -22119,7 +22192,7 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, c: ICplxTrans, m: Optional[float] = ..., u: Optional[Vector] = ...) -> ICplxTrans: + def new(cls, c: ICplxTrans, mag: Optional[float] = ..., u: Optional[Vector] = ...) -> ICplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -22132,7 +22205,7 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, c: ICplxTrans, m: float, x: int, y: int) -> ICplxTrans: + def new(cls, c: ICplxTrans, mag: Optional[float] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> ICplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -22146,15 +22219,7 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, m: float) -> ICplxTrans: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, u: Vector) -> ICplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> ICplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -22168,7 +22233,7 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, x: int, y: int) -> ICplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> ICplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -22183,15 +22248,7 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, t: Trans) -> ICplxTrans: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - @classmethod - def new(cls, t: Trans, m: float) -> ICplxTrans: + def new(cls, t: Trans, mag: Optional[float] = ...) -> ICplxTrans: r""" @brief Creates a transformation from a simple transformation and a magnification @@ -22199,27 +22256,33 @@ class ICplxTrans: """ @overload @classmethod - def new(cls, trans: CplxTrans) -> ICplxTrans: + def new(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_trans'. + The 'dbu' argument is used to transform the output space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'to_dbu * trans' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: DCplxTrans) -> ICplxTrans: + def new(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + The 'dbu' argument is used to transform the input space and output space from floating-point units to integer units and vice versa. Formally, the ICplxTrans transformation is initialized with 'to_dbu * trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)' and 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: VCplxTrans) -> ICplxTrans: + def new(cls, trans: VCplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25. + The 'dbu' argument is used to transform the input space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload @classmethod @@ -22268,7 +22331,7 @@ class ICplxTrans: @brief Creates a unit transformation """ @overload - def __init__(self, c: ICplxTrans, m: Optional[float] = ..., u: Optional[Vector] = ...) -> None: + def __init__(self, c: ICplxTrans, mag: Optional[float] = ..., u: Optional[Vector] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -22280,7 +22343,7 @@ class ICplxTrans: @param u The Additional displacement """ @overload - def __init__(self, c: ICplxTrans, m: float, x: int, y: int) -> None: + def __init__(self, c: ICplxTrans, mag: Optional[float] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -22293,14 +22356,7 @@ class ICplxTrans: @param y The Additional displacement (y) """ @overload - def __init__(self, m: float) -> None: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - def __init__(self, mag: float, rot: float, mirrx: bool, u: Vector) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -22313,7 +22369,7 @@ class ICplxTrans: @param u The displacement """ @overload - def __init__(self, mag: float, rot: float, mirrx: bool, x: int, y: int) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -22327,39 +22383,38 @@ class ICplxTrans: @param y The y displacement """ @overload - def __init__(self, t: Trans) -> None: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - def __init__(self, t: Trans, m: float) -> None: + def __init__(self, t: Trans, mag: Optional[float] = ...) -> None: r""" @brief Creates a transformation from a simple transformation and a magnification Creates a magnifying transformation from a simple transformation and a magnification. """ @overload - def __init__(self, trans: CplxTrans) -> None: + def __init__(self, trans: CplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_trans'. + The 'dbu' argument is used to transform the output space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'to_dbu * trans' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: DCplxTrans) -> None: + def __init__(self, trans: DCplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. + The 'dbu' argument is used to transform the input space and output space from floating-point units to integer units and vice versa. Formally, the ICplxTrans transformation is initialized with 'to_dbu * trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)' and 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: VCplxTrans) -> None: + def __init__(self, trans: VCplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates an integer coordinate transformation from another coordinate flavour - This constructor has been introduced in version 0.25. + The 'dbu' argument is used to transform the input space from floating-point units to integer units. Formally, the CplxTrans transformation is initialized with 'trans * from_dbu' where 'from_dbu' is the transformation into micrometer space, or more precisely 'CplxTrans(mag=dbu)'. + + This constructor has been introduced in version 0.25. The 'dbu' argument has been added in version 0.29. """ @overload def __init__(self, u: Vector) -> None: @@ -22789,7 +22844,13 @@ class ICplxTrans: The database unit can be specified to translate the integer coordinate displacement in database units to a floating-point displacement in micron units. The displacement's' coordinates will be multiplied with the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_itrans' use the conversion constructor: + + @code + dtrans = RBA::DCplxTrans::new(itrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_s(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @@ -22799,11 +22860,17 @@ class ICplxTrans: The lazy and DBU arguments have been added in version 0.27.6. """ - def to_trans(self) -> VCplxTrans: + def to_trans(self, dbu: Optional[float] = ...) -> VCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point input coordinates - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_trans' use the conversion constructor: + + @code + vtrans = RBA::VCplxTrans::new(itrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ def to_vtrans(self, dbu: Optional[float] = ...) -> CplxTrans: r""" @@ -22811,7 +22878,13 @@ class ICplxTrans: The database unit can be specified to translate the integer coordinate displacement in database units to a floating-point displacement in micron units. The displacement's' coordinates will be multiplied with the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_vtrans' use the conversion constructor: + + @code + trans = RBA::CplxTrans::new(itrans, dbu) + @/code + + This method has been introduced in version 0.25 and was deprecated in version 0.29. """ @overload def trans(self, box: Box) -> Box: @@ -23917,11 +23990,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'b' axis + @brief Sets the displacement vector for the 'b' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ cell: Cell r""" @@ -24096,10 +24169,9 @@ class Instance: @brief Gets the transformation of the instance or the first instance in the array The transformation returned is only valid if the array does not represent a complex transformation array Setter: - @brief Sets the transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ @classmethod def new(cls) -> Instance: @@ -25906,7 +25978,7 @@ class LayerMap: The LayerMap class has been introduced in version 0.18. Target layer have been introduced in version 0.20. 1:n mapping and unmapping has been introduced in version 0.27. """ @classmethod - def from_string(cls, arg0: str) -> LayerMap: + def from_string(cls, s: str) -> LayerMap: r""" @brief Creates a layer map from the given string The format of the string is that used in layer mapping files: one mapping entry per line, comments are allowed using '#' or '//'. The format of each line is that used in the 'map(string, index)' method. @@ -29212,6 +29284,10 @@ class LayoutToNetlist: r""" @brief Compares an enum with an integer value """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -29222,6 +29298,10 @@ class LayoutToNetlist: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: LayoutToNetlist.BuildNetHierarchyMode) -> bool: r""" @@ -29235,12 +29315,12 @@ class LayoutToNetlist: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -29250,6 +29330,10 @@ class LayoutToNetlist: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -30524,12 +30608,16 @@ class LoadLayoutOptions: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -30541,6 +30629,10 @@ class LoadLayoutOptions: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: LoadLayoutOptions.CellConflictResolution) -> bool: r""" @@ -30569,6 +30661,10 @@ class LoadLayoutOptions: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -32740,6 +32836,10 @@ class Metrics: r""" @brief Compares an enum with an integer value """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -32750,6 +32850,10 @@ class Metrics: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Metrics) -> bool: r""" @@ -32840,6 +32944,10 @@ class Metrics: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -33260,14 +33368,14 @@ class NetPinRef: @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this pin reference is attached to. """ @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to. + @brief Gets the net this pin reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ def pin(self) -> Pin: r""" @@ -33488,12 +33596,6 @@ class NetTerminalRef: The latter may happen, if the object is owned by a C++ object which got destroyed itself. """ @overload - def device(self) -> Device: - r""" - @brief Gets the device reference. - Gets the device object that this connection is made to. - """ - @overload def device(self) -> Device: r""" @brief Gets the device reference (non-const version). @@ -33501,6 +33603,12 @@ class NetTerminalRef: This constness variant has been introduced in version 0.26.8 """ + @overload + def device(self) -> Device: + r""" + @brief Gets the device reference. + Gets the device object that this connection is made to. + """ def device_class(self) -> DeviceClass: r""" @brief Gets the class of the device which is addressed. @@ -33518,14 +33626,14 @@ class NetTerminalRef: @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to. + @brief Gets the net this terminal reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this terminal reference is attached to. """ def terminal_def(self) -> DeviceTerminalDefinition: r""" @@ -34314,6 +34422,14 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload + def circuits_by_name(self, name_pattern: str) -> List[Circuit]: + r""" + @brief Gets the circuit objects for a given name filter. + The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. + + This method has been introduced in version 0.26.4. + """ + @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" @brief Gets the circuit objects for a given name filter (const version). @@ -34322,14 +34438,6 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ - @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: - r""" - @brief Gets the circuit objects for a given name filter. - The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This method has been introduced in version 0.26.4. - """ def combine_devices(self) -> None: r""" @brief Combines devices where possible @@ -34384,24 +34492,16 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ @overload - def each_circuit_bottom_up(self) -> Iterator[Circuit]: - r""" - @brief Iterates over the circuits bottom-up (const version) - 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. - - This constness variant has been introduced in version 0.26.8. - """ - @overload def each_circuit_bottom_up(self) -> Iterator[Circuit]: r""" @brief Iterates over the circuits bottom-up 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. """ @overload - def each_circuit_top_down(self) -> Iterator[Circuit]: + def each_circuit_bottom_up(self) -> Iterator[Circuit]: r""" - @brief Iterates over the circuits top-down (const version) - 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. + @brief Iterates over the circuits bottom-up (const version) + 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. This constness variant has been introduced in version 0.26.8. """ @@ -34412,6 +34512,14 @@ class 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. """ @overload + def each_circuit_top_down(self) -> Iterator[Circuit]: + r""" + @brief Iterates over the circuits top-down (const version) + 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. + + This constness variant has been introduced in version 0.26.8. + """ + @overload def each_device_class(self) -> Iterator[DeviceClass]: r""" @brief Iterates over the device classes of the netlist @@ -34440,7 +34548,7 @@ class Netlist: @brief Flattens circuits matching a certain pattern This method will substitute all instances (subcircuits) of all circuits with names matching the given name pattern. The name pattern is a glob expression. For example, 'flatten_circuit("np*")' will flatten all circuits with names starting with 'np'. """ - def flatten_circuits(self, arg0: Sequence[Circuit]) -> None: + def flatten_circuits(self, circuits: Sequence[Circuit]) -> None: r""" @brief Flattens all given circuits of the netlist This method is equivalent to calling \flatten_circuit for all given circuits, but more efficient. @@ -35084,6 +35192,10 @@ class NetlistCrossReference(NetlistCompareLogger): r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -35094,6 +35206,10 @@ class NetlistCrossReference(NetlistCompareLogger): r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: NetlistCrossReference.Status) -> bool: r""" @@ -35122,6 +35238,10 @@ class NetlistCrossReference(NetlistCompareLogger): r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -36013,7 +36133,7 @@ class NetlistSpiceWriter(NetlistWriter): """ @overload @classmethod - def new(cls, arg0: NetlistSpiceWriterDelegate) -> NetlistSpiceWriter: + def new(cls, delegate: NetlistSpiceWriterDelegate) -> NetlistSpiceWriter: r""" @brief Creates a new writer with a delegate. """ @@ -36031,7 +36151,7 @@ class NetlistSpiceWriter(NetlistWriter): @brief Creates a new writer without delegate. """ @overload - def __init__(self, arg0: NetlistSpiceWriterDelegate) -> None: + def __init__(self, delegate: NetlistSpiceWriterDelegate) -> None: r""" @brief Creates a new writer with a delegate. """ @@ -36597,6 +36717,46 @@ class PCellParameterDeclaration: Setter: @brief Makes the parameter hidden if this attribute is set to true """ + max_value: Any + r""" + Getter: + @brief Gets the maximum value allowed + See \max_value= for a description of this attribute. + + This attribute has been added in version 0.29. + Setter: + @brief Sets the maximum value allowed + The maximum value is a visual feature and limits the allowed values for numerical + entry boxes. This applies to parameters of type int or double. The maximum value + is not effective if choices are present. + + The maximum value is not enforced - for example there is no restriction implemented + when setting values programmatically. + + Setting this attribute to "nil" (the default) implies "no limit". + + This attribute has been added in version 0.29. + """ + min_value: Any + r""" + Getter: + @brief Gets the minimum value allowed + See \min_value= for a description of this attribute. + + This attribute has been added in version 0.29. + Setter: + @brief Sets the minimum value allowed + The minimum value is a visual feature and limits the allowed values for numerical + entry boxes. This applies to parameters of type int or double. The minimum value + is not effective if choices are present. + + The minimum value is not enforced - for example there is no restriction implemented + when setting values programmatically. + + Setting this attribute to "nil" (the default) implies "no limit". + + This attribute has been added in version 0.29. + """ name: str r""" Getter: @@ -36632,28 +36792,8 @@ class PCellParameterDeclaration: @brief Sets the unit string The unit string is shown right to the edit fields for numeric parameters. """ - @overload @classmethod - def new(cls, name: str, type: int, description: str) -> PCellParameterDeclaration: - r""" - @brief Create a new parameter declaration with the given name and type - @param name The parameter name - @param type One of the Type... constants describing the type of the parameter - @param description The description text - """ - @overload - @classmethod - def new(cls, name: str, type: int, description: str, default: Any) -> PCellParameterDeclaration: - r""" - @brief Create a new parameter declaration with the given name, type and default value - @param name The parameter name - @param type One of the Type... constants describing the type of the parameter - @param description The description text - @param default The default (initial) value - """ - @overload - @classmethod - def new(cls, name: str, type: int, description: str, default: Any, unit: str) -> PCellParameterDeclaration: + def new(cls, name: str, type: int, description: str, default: Optional[Any] = ..., unit: Optional[str] = ...) -> PCellParameterDeclaration: r""" @brief Create a new parameter declaration with the given name, type, default value and unit string @param name The parameter name @@ -36670,25 +36810,7 @@ class PCellParameterDeclaration: r""" @brief Creates a copy of self """ - @overload - def __init__(self, name: str, type: int, description: str) -> None: - r""" - @brief Create a new parameter declaration with the given name and type - @param name The parameter name - @param type One of the Type... constants describing the type of the parameter - @param description The description text - """ - @overload - def __init__(self, name: str, type: int, description: str, default: Any) -> None: - r""" - @brief Create a new parameter declaration with the given name, type and default value - @param name The parameter name - @param type One of the Type... constants describing the type of the parameter - @param description The description text - @param default The default (initial) value - """ - @overload - def __init__(self, name: str, type: int, description: str, default: Any, unit: str) -> None: + def __init__(self, name: str, type: int, description: str, default: Optional[Any] = ..., unit: Optional[str] = ...) -> None: r""" @brief Create a new parameter declaration with the given name, type, default value and unit string @param name The parameter name @@ -36740,6 +36862,7 @@ class PCellParameterDeclaration: This method will add the given value with the given description to the list of choices. If choices are defined, KLayout will show a drop-down box instead of an entry field in the parameter user interface. + If a range is already set for this parameter the choice will not be added and a warning message is showed. """ def assign(self, other: PCellParameterDeclaration) -> None: r""" @@ -36837,6 +36960,10 @@ class PCellParameterState: r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -36847,6 +36974,10 @@ class PCellParameterState: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: PCellParameterState.ParameterStateIcon) -> bool: r""" @@ -36860,12 +36991,12 @@ class PCellParameterState: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -36875,6 +37006,10 @@ class PCellParameterState: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -39459,6 +39594,10 @@ class PreferredOrientation: r""" @brief Compares an enum with an integer value """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -39469,6 +39608,10 @@ class PreferredOrientation: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: PreferredOrientation) -> bool: r""" @@ -39559,6 +39702,10 @@ class PreferredOrientation: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -39637,12 +39784,16 @@ class PropertyConstraint: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -39654,6 +39805,10 @@ class PropertyConstraint: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: PropertyConstraint) -> bool: r""" @@ -39667,12 +39822,12 @@ class PropertyConstraint: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -39744,6 +39899,10 @@ class PropertyConstraint: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -41112,6 +41271,10 @@ class Region(ShapeCollection): r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -41122,6 +41285,10 @@ class Region(ShapeCollection): r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Region.OppositeFilter) -> bool: r""" @@ -41135,12 +41302,12 @@ class Region(ShapeCollection): @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -41150,6 +41317,10 @@ class Region(ShapeCollection): r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -41218,6 +41389,10 @@ class Region(ShapeCollection): r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -41228,6 +41403,10 @@ class Region(ShapeCollection): r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Region.RectFilter) -> bool: r""" @@ -41241,12 +41420,12 @@ class Region(ShapeCollection): @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -41256,6 +41435,10 @@ class Region(ShapeCollection): r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -42142,6 +42325,32 @@ class Region(ShapeCollection): This method has been introduced in version 0.25. """ + @overload + def delaunay(self) -> Region: + r""" + @brief Computes a constrained Delaunay triangulation from the given region + + @return A new region holding the triangles of the constrained Delaunay triangulation. + + Note that the result is a region in raw mode as otherwise the triangles are likely to get merged later on. + + This method has been introduced in version 0.29. + """ + @overload + def delaunay(self, max_area: float, min_b: Optional[float] = ...) -> Region: + r""" + @brief Computes a refined, constrained Delaunay triangulation from the given region + + @return A new region holding the triangles of the refined, constrained Delaunay triangulation. + + Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles larger than this area will be split. In addition 'skinny' triangles will be resolved where possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). A minimum number for b can be given. The default of 1.0 corresponds to a minimum angle of 30 degree and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to a minimum angle of abouth 37 degree. + + The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + Note that the result is a region in raw mode as otherwise the triangles are likely to get merged later on. + + This method has been introduced in version 0.29. + """ def destroy(self) -> None: r""" @brief Explicitly destroys the object @@ -43246,6 +43455,40 @@ class Region(ShapeCollection): This method has been introduced in version 0.26.1 """ + @overload + def rasterize(self, origin: Point, pixel_distance: Vector, pixel_size: Vector, nx: int, ny: int) -> List[List[float]]: + r""" + @brief A version of 'rasterize' that allows a pixel step distance which is larger than the pixel size + This version behaves like the first variant of 'rasterize', but the pixel distance (pixel-to-pixel step raster) + can be specified separately from the pixel size. Currently, the pixel size must be equal or smaller than the + pixel distance - i.e. the pixels must not overlap. + + This method has been added in version 0.29. + """ + @overload + def rasterize(self, origin: Point, pixel_size: Vector, nx: int, ny: int) -> List[List[float]]: + r""" + @brief A grayscale rasterizer delivering the area covered per pixel + @param origin The lower-left corner of the lowest-left pixel + @param pixel_size The dimension of each pixel (the x component gives the width, the y component the height) + @param nx The number of pixels in horizontal direction + @param ny The number of pixels in vertical direction + The method will create a grayscale, high-resolution density map of a rectangular region. + The scan region is defined by the origin, the pixel size and the number of pixels in horizontal (nx) and + vertical (ny) direction. The resulting array will contain the area covered by polygons from the region + in square database units. + + For non-overlapping polygons, the maximum density value is px*py. Overlapping polygons are counted multiple + times, so the actual values may be larger. If you want overlaps removed, you have to + merge the region before. Merge semantics does not apply for the 'rasterize' method. + + The resulting area values are precise within the limits of double-precision floating point arithmetics. + + A second version exists that allows specifying an active pixel size which is smaller than the + pixel distance hence allowing pixels samples that do not cover the full area, but leave gaps between the pixels. + + This method has been added in version 0.29. + """ def rectangles(self) -> Region: r""" @brief Returns all polygons which are rectangles @@ -44891,6 +45134,10 @@ class Severity: r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -44901,6 +45148,10 @@ class Severity: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Severity) -> bool: r""" @@ -44991,6 +45242,10 @@ class Severity: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -45265,11 +45520,12 @@ class Shape: This method has been introduced in version 0.23. Setter: - @brief Sets the upper right point of the box + @brief Sets the upper right corner of the box with the point being given in micrometer units Applies to boxes only. Changes the upper right point of the box and throws an exception if the shape is not a box. + Translation from micrometer units to database units is done internally. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ box_width: int r""" @@ -45588,11 +45844,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a polygon. Setter: - @brief Replaces the shape by the given polygon object - This method replaces the shape by the given polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given polygon (in micrometer units) + This method replaces the shape by the given polygon, like \polygon= with a \Polygon argument does. This version translates the polygon from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ prop_id: int r""" @@ -48839,12 +49094,6 @@ class SubCircuit(NetlistObject): Usually it's not required to call this method. It has been introduced in version 0.24. """ @overload - def circuit(self) -> Circuit: - r""" - @brief Gets the circuit the subcircuit lives in. - This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - """ - @overload def circuit(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in (non-const version). @@ -48853,6 +49102,12 @@ class SubCircuit(NetlistObject): This constness variant has been introduced in version 0.26.8 """ @overload + def circuit(self) -> Circuit: + r""" + @brief Gets the circuit the subcircuit lives in. + This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + """ + @overload def circuit_ref(self) -> Circuit: r""" @brief Gets the circuit referenced by the subcircuit (non-const version). @@ -48958,6 +49213,20 @@ class Technology: Setter: @hide """ + default_grids: List[float] + r""" + Getter: + @brief Gets the default grids + + See \default_grids for details. + + This property has been introduced in version 0.28.17. + Setter: + @brief Sets the default grids + If not empty, this list replaces the global grid list for this technology. + + This property has been introduced in version 0.28.17. + """ description: str r""" Getter: @@ -49410,8 +49679,7 @@ class Text: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: int r""" @@ -49447,7 +49715,8 @@ class Text: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: int r""" @@ -49886,7 +50155,7 @@ class TextGenerator: @brief Creates a new object of this class """ @classmethod - def set_font_paths(cls, arg0: Sequence[str]) -> None: + def set_font_paths(cls, paths: Sequence[str]) -> None: r""" @brief Sets the paths where to look for font files This function sets the paths where to look for font files. After setting such a path, each font found will render a specific generator. The generator can be found under the font file's name. As the text generator is also the basis for the Basic.TEXT PCell, using this function also allows configuring custom fonts for this library cell. @@ -51267,6 +51536,14 @@ class TilingProcessor: @param edges The \Edges object to which the data is sent """ @overload + def output(self, name: str, image: lay.BasicImage) -> None: + r""" + @brief Specifies output to an image + This method will establish an output channel which delivers float data to image data. The image is a monochrome image where each pixel corresponds to a single tile. This method for example is useful to collect density information into an image. The image is configured such that each pixel covers one tile. + + The name is the name which must be used in the _output function of the scripts in order to address that channel. + """ + @overload def output(self, name: str, layout: Layout, cell: int, layer_index: int) -> None: r""" @brief Specifies output to a layout layer @@ -51293,6 +51570,14 @@ class TilingProcessor: @param lp The layer specification where the output will be sent to """ @overload + def output(self, name: str, rdb: rdb.ReportDatabase, cell_id: int, category_id: int) -> None: + r""" + @brief Specifies output to a report database + This method will establish an output channel for the processor. The output sent to that channel will be put into the report database given by the "rdb" parameter. "cell_id" specifies the cell and "category_id" the category to use. + + The name is the name which must be used in the _output function of the scripts in order to address that channel. + """ + @overload def output(self, name: str, rec: TileOutputReceiverBase) -> None: r""" @brief Specifies output for the tiling processor @@ -51553,7 +51838,7 @@ class Trans: """ @overload @classmethod - def new(cls, c: Trans, x: int, y: int) -> Trans: + def new(cls, c: Trans, x: Optional[int] = ..., y: Optional[int] = ...) -> Trans: r""" @brief Creates a transformation from another transformation plus a displacement @@ -51575,7 +51860,7 @@ class Trans: """ @overload @classmethod - def new(cls, rot: int, mirr: Optional[bool] = ..., u: Optional[Vector] = ...) -> Trans: + def new(cls, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> Trans: r""" @brief Creates a transformation using angle and mirror flag @@ -51588,7 +51873,7 @@ class Trans: """ @overload @classmethod - def new(cls, rot: int, mirr: bool, x: int, y: int) -> Trans: + def new(cls, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> Trans: r""" @brief Creates a transformation using angle and mirror flag and two coordinate values for displacement @@ -51654,7 +51939,7 @@ class Trans: @param u The Additional displacement """ @overload - def __init__(self, c: Trans, x: int, y: int) -> None: + def __init__(self, c: Trans, x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a displacement @@ -51674,7 +51959,7 @@ class Trans: This constructor has been introduced in version 0.25 and replaces the previous static method 'from_dtrans'. """ @overload - def __init__(self, rot: int, mirr: Optional[bool] = ..., u: Optional[Vector] = ...) -> None: + def __init__(self, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> None: r""" @brief Creates a transformation using angle and mirror flag @@ -51686,7 +51971,7 @@ class Trans: @param u The displacement """ @overload - def __init__(self, rot: int, mirr: bool, x: int, y: int) -> None: + def __init__(self, rot: Optional[int] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation using angle and mirror flag and two coordinate values for displacement @@ -52202,12 +52487,16 @@ class TrapezoidDecompositionMode: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -52219,6 +52508,10 @@ class TrapezoidDecompositionMode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: TrapezoidDecompositionMode) -> bool: r""" @@ -52232,12 +52525,12 @@ class TrapezoidDecompositionMode: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ def __repr__(self) -> str: r""" @@ -52309,6 +52602,10 @@ class TrapezoidDecompositionMode: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -52511,12 +52808,16 @@ class VAlign: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -52528,6 +52829,10 @@ class VAlign: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: VAlign) -> bool: r""" @@ -52541,12 +52846,12 @@ class VAlign: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -52618,6 +52923,10 @@ class VAlign: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -52766,7 +53075,7 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, c: VCplxTrans, m: Optional[float] = ..., u: Optional[Vector] = ...) -> VCplxTrans: + def new(cls, c: VCplxTrans, mag: Optional[float] = ..., u: Optional[Vector] = ...) -> VCplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -52779,7 +53088,7 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, c: VCplxTrans, m: float, x: float, y: float) -> VCplxTrans: + def new(cls, c: VCplxTrans, mag: Optional[float] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> VCplxTrans: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -52793,15 +53102,7 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, m: float) -> VCplxTrans: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, u: Vector) -> VCplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> VCplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -52815,7 +53116,7 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, mag: float, rot: float, mirrx: bool, x: int, y: int) -> VCplxTrans: + def new(cls, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> VCplxTrans: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -52830,15 +53131,7 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, t: DTrans) -> VCplxTrans: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - @classmethod - def new(cls, t: DTrans, m: float) -> VCplxTrans: + def new(cls, t: DTrans, mag: Optional[float] = ...) -> VCplxTrans: r""" @brief Creates a transformation from a simple transformation and a magnification @@ -52846,21 +53139,33 @@ class VCplxTrans: """ @overload @classmethod - def new(cls, trans: CplxTrans) -> VCplxTrans: + def new(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> VCplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the input and output space from floating-point units to integer units and vice versa. Formally, the VCplxTrans transformation is initialized with 'to_dbu * trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: DCplxTrans) -> VCplxTrans: + def new(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> VCplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the output space from floating-point units to integer units. Formally, the VCplxTrans transformation is initialized with 'to_dbu * trans' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload @classmethod - def new(cls, trans: ICplxTrans) -> VCplxTrans: + def new(cls, trans: ICplxTrans, dbu: Optional[float] = ...) -> VCplxTrans: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the input and output space from floating-point units to integer units and vice versa. Formally, the VCplxTrans transformation is initialized with 'trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload @classmethod @@ -52909,7 +53214,7 @@ class VCplxTrans: @brief Creates a unit transformation """ @overload - def __init__(self, c: VCplxTrans, m: Optional[float] = ..., u: Optional[Vector] = ...) -> None: + def __init__(self, c: VCplxTrans, mag: Optional[float] = ..., u: Optional[Vector] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -52921,7 +53226,7 @@ class VCplxTrans: @param u The Additional displacement """ @overload - def __init__(self, c: VCplxTrans, m: float, x: float, y: float) -> None: + def __init__(self, c: VCplxTrans, mag: Optional[float] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation from another transformation plus a magnification and displacement @@ -52934,14 +53239,7 @@ class VCplxTrans: @param y The Additional displacement (y) """ @overload - def __init__(self, m: float) -> None: - r""" - @brief Creates a transformation from a magnification - - Creates a magnifying transformation without displacement and rotation given the magnification m. - """ - @overload - def __init__(self, mag: float, rot: float, mirrx: bool, u: Vector) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., u: Optional[Vector] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -52954,7 +53252,7 @@ class VCplxTrans: @param u The displacement """ @overload - def __init__(self, mag: float, rot: float, mirrx: bool, x: int, y: int) -> None: + def __init__(self, mag: Optional[float] = ..., rot: Optional[float] = ..., mirrx: Optional[bool] = ..., x: Optional[int] = ..., y: Optional[int] = ...) -> None: r""" @brief Creates a transformation using magnification, angle, mirror flag and displacement @@ -52968,33 +53266,38 @@ class VCplxTrans: @param y The y displacement """ @overload - def __init__(self, t: DTrans) -> None: - r""" - @brief Creates a transformation from a simple transformation alone - - Creates a magnifying transformation from a simple transformation and a magnification of 1.0. - """ - @overload - def __init__(self, t: DTrans, m: float) -> None: + def __init__(self, t: DTrans, mag: Optional[float] = ...) -> None: r""" @brief Creates a transformation from a simple transformation and a magnification Creates a magnifying transformation from a simple transformation and a magnification. """ @overload - def __init__(self, trans: CplxTrans) -> None: + def __init__(self, trans: CplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the input and output space from floating-point units to integer units and vice versa. Formally, the VCplxTrans transformation is initialized with 'to_dbu * trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: DCplxTrans) -> None: + def __init__(self, trans: DCplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the output space from floating-point units to integer units. Formally, the VCplxTrans transformation is initialized with 'to_dbu * trans' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload - def __init__(self, trans: ICplxTrans) -> None: + def __init__(self, trans: ICplxTrans, dbu: Optional[float] = ...) -> None: r""" - @brief Creates a floating-point coordinate transformation from another coordinate flavour + @brief Creates a floating-point to integer coordinate transformation from another coordinate flavour + + The 'dbu' argument is used to transform the input and output space from floating-point units to integer units and vice versa. Formally, the VCplxTrans transformation is initialized with 'trans * to_dbu' where 'to_dbu' is the transformation into DBU space, or more precisely 'VCplxTrans(mag=1/dbu)'. + + The 'dbu' argument has been added in version 0.29. """ @overload def __init__(self, u: Vector) -> None: @@ -53434,7 +53737,13 @@ class VCplxTrans: The database unit can be specified to translate the integer coordinate displacement in database units to a floating-point displacement in micron units. The displacement's' coordinates will be multiplied with the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_itrans' use the conversion constructor: + + @code + dtrans = RBA::DCplxTrans::new(vtrans, dbu) + @/code + + This method has been deprecated in version 0.29. """ def to_s(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @@ -53444,11 +53753,17 @@ class VCplxTrans: The lazy and DBU arguments have been added in version 0.27.6. """ - def to_trans(self) -> ICplxTrans: + def to_trans(self, arg0: float) -> ICplxTrans: r""" @brief Converts the transformation to another transformation with integer input coordinates - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_trans' use the conversion constructor: + + @code + itrans = RBA::ICplxTrans::new(vtrans, dbu) + @/code + + This method has been deprecated in version 0.29. """ def to_vtrans(self, dbu: Optional[float] = ...) -> CplxTrans: r""" @@ -53456,7 +53771,13 @@ class VCplxTrans: The database unit can be specified to translate the integer coordinate displacement in database units to an floating-point displacement in micron units. The displacement's' coordinates will be multiplied with the database unit. - This method has been introduced in version 0.25. + This method is redundant with the conversion constructors and is ill-named. Instead of 'to_vtrans' use the conversion constructor: + + @code + trans = RBA::CplxTrans::new(vtrans, dbu) + @/code + + This method has been deprecated in version 0.29. """ @overload def trans(self, box: DBox) -> Box: @@ -53961,6 +54282,10 @@ class ZeroDistanceMode: r""" @brief Compares an enum with an integer value """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -53971,6 +54296,10 @@ class ZeroDistanceMode: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: ZeroDistanceMode) -> bool: r""" @@ -54061,6 +54390,10 @@ class ZeroDistanceMode: r""" @brief Creates a copy of self """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string diff --git a/src/pymod/distutils_src/klayout/laycore.pyi b/src/pymod/distutils_src/klayout/laycore.pyi index f5e2077b4..9ca149db3 100644 --- a/src/pymod/distutils_src/klayout/laycore.pyi +++ b/src/pymod/distutils_src/klayout/laycore.pyi @@ -838,6 +838,13 @@ class Annotation(BasicAnnotation): This constant has been introduced in version 0.25 """ + RulerModeAutoMetricEdge: ClassVar[int] + r""" + @brief Specifies edge-sensitive auto-metric ruler mode for the \register_template method + In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the edge it is placed on. + + This constant has been introduced in version 0.29 + """ RulerModeNormal: ClassVar[int] r""" @brief Specifies normal ruler mode for the \register_template method @@ -4249,7 +4256,7 @@ class LayerProperties: This method has been introduced in version 0.22. """ @overload - def lower_hier_level_mode(self, arg0: bool) -> int: + def lower_hier_level_mode(self, real: bool) -> int: r""" @brief Gets the mode for the lower hierarchy level. @param real If true, the computed value is returned, otherwise the local node value @@ -4994,6 +5001,10 @@ class LayoutViewBase: r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -5004,6 +5015,10 @@ class LayoutViewBase: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: LayoutViewBase.SelectionMode) -> bool: r""" @@ -5032,6 +5047,10 @@ class LayoutViewBase: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -6364,6 +6383,13 @@ class LayoutViewBase: This method returns true, if self is a const reference. In that case, only const methods may be called on self. """ + def is_dirty(self) -> bool: + r""" + @brief Gets a flag indicating whether one of the layouts displayed needs saving + A layout is 'dirty' if it is modified and needs saving. This method returns true if this is the case for at least one of the layouts shown in the view. + + This method has been introduced in version 0.29. + """ def is_editable(self) -> bool: r""" @brief Returns true if the view is in editable mode @@ -6681,7 +6707,7 @@ class LayoutViewBase: See \set_title and \title for a description about how titles are handled. """ - def resize(self, arg0: int, arg1: int) -> None: + def resize(self, w: int, h: int) -> None: r""" @brief Resizes the layout view to the given dimension @@ -7062,7 +7088,7 @@ class LayoutViewBase: It is very important to stop the redraw thread before applying changes to the layout or the cell views and the LayoutView configuration. This is usually done automatically. For rare cases, where this is not the case, this method is provided. """ - def switch_mode(self, arg0: str) -> None: + def switch_mode(self, mode: str) -> None: r""" @brief Switches the mode. @@ -7204,12 +7230,16 @@ class Macro: @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares two enums + @brief Compares an enum with an integer value """ @overload def __eq__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer value + @brief Compares two enums + """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum """ @overload def __init__(self, i: int) -> None: @@ -7221,6 +7251,10 @@ class Macro: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Macro.Format) -> bool: r""" @@ -7249,6 +7283,10 @@ class Macro: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string @@ -7308,6 +7346,10 @@ class Macro: r""" @brief Compares two enums """ + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ @overload def __init__(self, i: int) -> None: r""" @@ -7318,6 +7360,10 @@ class Macro: r""" @brief Creates an enum from a string value """ + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ @overload def __lt__(self, other: Macro.Interpreter) -> bool: r""" @@ -7331,12 +7377,12 @@ class Macro: @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares two enums for inequality + @brief Compares an enum with an integer for inequality """ @overload def __ne__(self, other: object) -> bool: r""" - @brief Compares an enum with an integer for inequality + @brief Compares two enums for inequality """ def __repr__(self) -> str: r""" @@ -7346,6 +7392,10 @@ class Macro: r""" @brief Gets the symbolic string from an enum """ + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ def inspect(self) -> str: r""" @brief Converts an enum to a visual string diff --git a/src/pymod/distutils_src/klayout/tlcore.pyi b/src/pymod/distutils_src/klayout/tlcore.pyi index ae853b477..f821e70bf 100644 --- a/src/pymod/distutils_src/klayout/tlcore.pyi +++ b/src/pymod/distutils_src/klayout/tlcore.pyi @@ -308,7 +308,7 @@ class ArgType: r""" @brief Creates a copy of self """ - def __eq__(self, arg0: object) -> bool: + def __eq__(self, other: object) -> bool: r""" @brief Equality of two types """ @@ -316,7 +316,7 @@ class ArgType: r""" @brief Creates a new object of this class """ - def __ne__(self, arg0: object) -> bool: + def __ne__(self, other: object) -> bool: r""" @brief Inequality of two types """ @@ -1423,6 +1423,20 @@ class Method: r""" @brief Creates a new object of this class """ + def __repr__(self) -> str: + r""" + @brief Describes the method + This attribute returns a string description of the method and its signature. + + This method has been introduced in version 0.29. + """ + def __str__(self) -> str: + r""" + @brief Describes the method + This attribute returns a string description of the method and its signature. + + This method has been introduced in version 0.29. + """ def _create(self) -> None: r""" @brief Ensures the C++ object is created @@ -1460,7 +1474,7 @@ class Method: Usually it's not required to call this method. It has been introduced in version 0.24. """ - def accepts_num_args(self, arg0: int) -> bool: + def accepts_num_args(self, n: int) -> bool: r""" @brief True, if this method is compatible with the given number of arguments @@ -1569,6 +1583,13 @@ class Method: r""" @brief The return type of this method """ + def to_s(self) -> str: + r""" + @brief Describes the method + This attribute returns a string description of the method and its signature. + + This method has been introduced in version 0.29. + """ class MethodOverload: r""" diff --git a/src/pymod/pymod.pri b/src/pymod/pymod.pri index 7670517de..e030d0528 100644 --- a/src/pymod/pymod.pri +++ b/src/pymod/pymod.pri @@ -84,6 +84,8 @@ INSTALLS = lib_target QMAKE_POST_LINK += && $(MKDIR) $$DESTDIR_PYMOD/$$REALMODULE && $(COPY) $$PWD/distutils_src/klayout/$$REALMODULE/*.py $$DESTDIR_PYMOD/$$REALMODULE } + POST_TARGETDEPS += $$files($$PWD/distutils_src/klayout/$$REALMODULE/*.py, false) + # INSTALLS needs to be inside a lib or app templates. modsrc_target.path = $$PREFIX/pymod/klayout/$$REALMODULE # This would be nice: diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 8bd642f04..0cef54c62 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -479,9 +479,9 @@ private: VALUE arg = i >= argc ? get_kwarg (*a, kwargs) : argv[i]; if (arg == Qundef) { is_valid = a->spec ()->has_default (); - } else if (test_arg (*a, arg, false /*strict*/)) { + } else if (test_arg (*a, arg, false /*strict*/, false /*with object substitution*/)) { ++sc; - } else if (test_arg (*a, arg, true /*loose*/)) { + } else if (test_arg (*a, arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match } else { is_valid = false; diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 5cacd22e1..7083fd1c7 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -1053,7 +1053,7 @@ pull_arg (const gsi::ArgType &atype, Proxy *self, gsi::SerialArgs &aserial, tl:: template struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { @@ -1101,7 +1101,7 @@ struct test_vector unsigned int len = RARRAY_LEN(arr); VALUE *el = RARRAY_PTR(arr); while (len-- > 0) { - if (! test_arg (ainner, *el++, loose)) { + if (! test_arg (ainner, *el++, loose, true /*issue-1651*/)) { *ret = false; break; } @@ -1112,7 +1112,7 @@ struct test_vector template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for pointers to vectors, nil is a valid value @@ -1141,11 +1141,11 @@ struct HashTestKeyValueData static int hash_test_value_key (VALUE key, VALUE value, VALUE a) { HashTestKeyValueData *args = (HashTestKeyValueData *)a; - if (! test_arg (*args->ainner_k, key, args->loose)) { + if (! test_arg (*args->ainner_k, key, args->loose, true /*issue-1651*/)) { *(args->ret) = false; return ST_STOP; } - if (! test_arg (*args->ainner, value, args->loose)) { + if (! test_arg (*args->ainner, value, args->loose, true /*issue-1651*/)) { *(args->ret) = false; return ST_STOP; } @@ -1155,7 +1155,7 @@ static int hash_test_value_key (VALUE key, VALUE value, VALUE a) template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for pointers to maps, nil is a valid value @@ -1183,14 +1183,14 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for const X * or X *, nil is an allowed value *ret = true; - } else if (loose && TYPE (arg) == T_ARRAY) { + } else if (object_substitution && TYPE (arg) == T_ARRAY) { // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. @@ -1234,10 +1234,10 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, VALUE arg, bool loose) +test_arg (const gsi::ArgType &atype, VALUE arg, bool loose, bool object_substitution) { bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/rba/rba/rbaMarshal.h b/src/rba/rba/rbaMarshal.h index 9e8ea4e07..436ef01cd 100644 --- a/src/rba/rba/rbaMarshal.h +++ b/src/rba/rba/rbaMarshal.h @@ -62,7 +62,7 @@ void push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, VALUE arg, t * otherwise: * argument must be of the requested type */ -bool test_arg (const gsi::ArgType &atype, VALUE arg, bool loose); +bool test_arg (const gsi::ArgType &atype, VALUE arg, bool loose, bool object_substitution); } diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 00392affa..2b467f2ed 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -88,6 +88,49 @@ private: rdb::Database::const_item_ref_iterator m_iter; }; +class ItemRefUnwrappingNonConstIterator +{ +public: + typedef rdb::Database::const_item_ref_iterator::iterator_category iterator_category; + typedef rdb::Database::const_item_ref_iterator::difference_type difference_type; + typedef rdb::Item value_type; + typedef rdb::Item &reference; + typedef rdb::Item *pointer; + + ItemRefUnwrappingNonConstIterator (rdb::Database::item_ref_iterator i) + : m_iter (i) + { } + + bool operator== (const ItemRefUnwrappingNonConstIterator &d) const + { + return m_iter == d.m_iter; + } + + bool operator!= (const ItemRefUnwrappingNonConstIterator &d) const + { + return m_iter != d.m_iter; + } + + ItemRefUnwrappingNonConstIterator &operator++ () + { + ++m_iter; + return *this; + } + + rdb::Item &operator* () const + { + return (*m_iter).operator* (); + } + + rdb::Item *operator-> () const + { + return (*m_iter).operator-> (); + } + +private: + rdb::Database::item_ref_iterator m_iter; +}; + // --------------------------------------------------------------- // rdb::Reference binding @@ -105,7 +148,12 @@ Class decl_RdbReference ("rdb", "RdbReference", "\n" "This method has been introduced in version 0.23." ) + - gsi::method ("trans", &rdb::Reference::trans, + gsi::method ("database", (rdb::Database *(rdb::Reference::*)()) &rdb::Reference::database, + "@brief Gets the database object that category is associated with (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method ("trans", &rdb::Reference::trans, "@brief Gets the transformation for this reference\n" "The transformation describes the transformation of the child cell into the parent cell. In that sense that is the " "usual transformation of a cell reference.\n" @@ -141,6 +189,16 @@ static rdb::References::const_iterator end_references (const rdb::Cell *cell) return cell->references ().end (); } +static rdb::References::iterator begin_references_nc (rdb::Cell *cell) +{ + return cell->references ().begin (); +} + +static rdb::References::iterator end_references_nc (rdb::Cell *cell) +{ + return cell->references ().end (); +} + static void add_reference (rdb::Cell *cell, const rdb::Reference &ref) { cell->references ().insert (ref); @@ -163,6 +221,18 @@ ItemRefUnwrappingIterator cell_items_end (const rdb::Cell *cell) return cell->database ()->items_by_cell (cell->id ()).second; } +ItemRefUnwrappingNonConstIterator cell_items_begin_non_const (rdb::Cell *cell) +{ + tl_assert (cell->database ()); + return cell->database ()->items_by_cell (cell->id ()).first; +} + +ItemRefUnwrappingNonConstIterator cell_items_end_non_const (rdb::Cell *cell) +{ + tl_assert (cell->database ()); + return cell->database ()->items_by_cell (cell->id ()).second; +} + Class decl_RdbCell ("rdb", "RdbCell", gsi::method ("rdb_id", &rdb::Cell::id, "@brief Gets the cell ID\n" @@ -175,12 +245,22 @@ Class decl_RdbCell ("rdb", "RdbCell", "\n" "This method has been introduced in version 0.23." ) + + gsi::method ("database", (rdb::Database *(rdb::Cell::*)()) &rdb::Cell::database, + "@brief Gets the database object that category is associated with (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::iterator_ext ("each_item", &cell_items_begin, &cell_items_end, "@brief Iterates over all items inside the database which are associated with this cell\n" "\n" "This method has been introduced in version 0.23." ) + - gsi::method ("name", &rdb::Cell::name, + gsi::iterator_ext ("each_item", &cell_items_begin_non_const, &cell_items_end_non_const, + "@brief Iterates over all items inside the database which are associated with this cell (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method ("name", &rdb::Cell::name, "@brief Gets the cell name\n" "The cell name is an string that identifies the category in the database. " "Additionally, a cell may carry a variant identifier which is a string that uniquely identifies a cell " @@ -215,6 +295,11 @@ Class decl_RdbCell ("rdb", "RdbCell", ) + gsi::iterator_ext ("each_reference", &begin_references, &end_references, "@brief Iterates over all references\n" + ) + + gsi::iterator_ext ("each_reference", &begin_references_nc, &end_references_nc, + "@brief Iterates over all references (non-const version)\n" + "\n" + "This method has been introduced in version 0.23." ), "@brief A cell inside the report database\n" "This class represents a cell in the report database. There is not necessarily a 1:1 correspondence of RDB cells " @@ -226,12 +311,22 @@ Class decl_RdbCell ("rdb", "RdbCell", // --------------------------------------------------------------- // rdb::Category binding -static rdb::Categories::iterator begin_sub_categories (rdb::Category *cat) +static rdb::Categories::const_iterator begin_sub_categories (const rdb::Category *cat) { return cat->sub_categories ().begin (); } -static rdb::Categories::iterator end_sub_categories (rdb::Category *cat) +static rdb::Categories::const_iterator end_sub_categories (const rdb::Category *cat) +{ + return cat->sub_categories ().end (); +} + +static rdb::Categories::iterator begin_sub_categories_non_const (rdb::Category *cat) +{ + return cat->sub_categories ().begin (); +} + +static rdb::Categories::iterator end_sub_categories_non_const (rdb::Category *cat) { return cat->sub_categories ().end (); } @@ -248,6 +343,18 @@ ItemRefUnwrappingIterator category_items_end (const rdb::Category *cat) return cat->database ()->items_by_category (cat->id ()).second; } +ItemRefUnwrappingNonConstIterator category_items_begin_non_const (rdb::Category *cat) +{ + tl_assert (cat->database ()); + return cat->database ()->items_by_category (cat->id ()).first; +} + +ItemRefUnwrappingNonConstIterator category_items_end_non_const (rdb::Category *cat) +{ + tl_assert (cat->database ()); + return cat->database ()->items_by_category (cat->id ()).second; +} + static void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell, int levels, bool with_properties) { rdb::scan_layer (cat, layout, layer, from_cell, levels, with_properties); @@ -299,6 +406,11 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "\n" "This method has been introduced in version 0.23." ) + + gsi::iterator_ext ("each_item", &category_items_begin_non_const, &category_items_end_non_const, + "@brief Iterates over all items inside the database which are associated with this category (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method_ext ("scan_shapes", &scan_shapes, gsi::arg ("iter"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Scans the polygon or edge shapes from the shape iterator into the category\n" "Creates RDB items for each polygon or edge shape read from the iterator and puts them into this category.\n" @@ -379,12 +491,23 @@ Class decl_RdbCategory ("rdb", "RdbCategory", ) + gsi::iterator_ext ("each_sub_category", &begin_sub_categories, &end_sub_categories, "@brief Iterates over all sub-categories\n" + "\n" + "The const version has been added in version 0.29." ) + - gsi::method ("parent", (rdb::Category *(rdb::Category::*) ()) &rdb::Category::parent, + gsi::iterator_ext ("each_sub_category", &begin_sub_categories_non_const, &end_sub_categories_non_const, + "@brief Iterates over all sub-categories (non-const version)\n" + ) + + gsi::method ("parent", (const rdb::Category *(rdb::Category::*) () const) &rdb::Category::parent, "@brief Gets the parent category of this category\n" "@return The parent category or nil if this category is a top-level category\n" + "\n" + "The const version has been added in version 0.29." ) + - gsi::method ("num_items", &rdb::Category::num_items, + gsi::method ("parent", (rdb::Category *(rdb::Category::*) ()) &rdb::Category::parent, + "@brief Gets the parent category of this category (non-const version)\n" + "@return The parent category or nil if this category is a top-level category\n" + ) + + gsi::method ("num_items", &rdb::Category::num_items, "@brief Gets the number of items in this category\n" "The number of items includes the items in sub-categories of this category.\n" ) + @@ -923,6 +1046,16 @@ rdb::Items::const_iterator database_items_end (const rdb::Database *db) return db->items ().end (); } +rdb::Items::iterator database_items_begin_nc (rdb::Database *db) +{ + return db->items_non_const ().begin (); +} + +rdb::Items::iterator database_items_end_nc (rdb::Database *db) +{ + return db->items_non_const ().end (); +} + ItemRefUnwrappingIterator database_items_begin_cell (const rdb::Database *db, rdb::id_type cell_id) { return db->items_by_cell (cell_id).first; @@ -933,6 +1066,16 @@ ItemRefUnwrappingIterator database_items_end_cell (const rdb::Database *db, rdb: return db->items_by_cell (cell_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cell_nc (rdb::Database *db, rdb::id_type cell_id) +{ + return db->items_by_cell (cell_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cell_nc (rdb::Database *db, rdb::id_type cell_id) +{ + return db->items_by_cell (cell_id).second; +} + ItemRefUnwrappingIterator database_items_begin_cat (const rdb::Database *db, rdb::id_type cat_id) { return db->items_by_category (cat_id).first; @@ -943,6 +1086,16 @@ ItemRefUnwrappingIterator database_items_end_cat (const rdb::Database *db, rdb:: return db->items_by_category (cat_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cat_nc (rdb::Database *db, rdb::id_type cat_id) +{ + return db->items_by_category (cat_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cat_nc (rdb::Database *db, rdb::id_type cat_id) +{ + return db->items_by_category (cat_id).second; +} + ItemRefUnwrappingIterator database_items_begin_cc (const rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) { return db->items_by_cell_and_category (cell_id, cat_id).first; @@ -953,6 +1106,16 @@ ItemRefUnwrappingIterator database_items_end_cc (const rdb::Database *db, rdb::i return db->items_by_cell_and_category (cell_id, cat_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cc_nc (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) +{ + return db->items_by_cell_and_category (cell_id, cat_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cc_nc (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) +{ + return db->items_by_cell_and_category (cell_id, cat_id).second; +} + rdb::Categories::const_iterator database_begin_categories (const rdb::Database *db) { return db->categories ().begin (); @@ -963,6 +1126,16 @@ rdb::Categories::const_iterator database_end_categories (const rdb::Database *db return db->categories ().end (); } +rdb::Categories::iterator database_end_categories_nc (rdb::Database *db) +{ + return db->categories_non_const ().end (); +} + +rdb::Categories::iterator database_begin_categories_nc (rdb::Database *db) +{ + return db->categories_non_const ().begin (); +} + rdb::Cells::const_iterator database_begin_cells (const rdb::Database *db) { return db->cells ().begin (); @@ -973,6 +1146,16 @@ rdb::Cells::const_iterator database_end_cells (const rdb::Database *db) return db->cells ().end (); } +rdb::Cells::iterator database_begin_cells_nc (rdb::Database *db) +{ + return db->cells_non_const ().begin (); +} + +rdb::Cells::iterator database_end_cells_nc (rdb::Database *db) +{ + return db->cells_non_const ().end (); +} + const std::string &database_tag_name (const rdb::Database *db, rdb::id_type tag) { return db->tags ().tag (tag).name (); @@ -1121,6 +1304,11 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", gsi::iterator_ext ("each_category", &database_begin_categories, &database_end_categories, "@brief Iterates over all top-level categories\n" ) + + gsi::iterator_ext ("each_category", &database_begin_categories_nc, &database_end_categories_nc, + "@brief Iterates over all top-level categories (non-const version)\n" + "\n" + "The non-const variant has been added in version 0.29." + ) + gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (const std::string &)) &rdb::Database::create_category, gsi::arg ("name"), "@brief Creates a new top level category\n" "@param name The name of the category\n" @@ -1135,10 +1323,23 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param path The full path to the category starting from the top level (subcategories separated by dots)\n" "@return The (const) category object or nil if the name is not valid\n" ) + + gsi::method ("category_by_path", &rdb::Database::category_by_name_non_const, gsi::arg ("path"), + "@brief Gets a category by path (non-const version)\n" + "@param path The full path to the category starting from the top level (subcategories separated by dots)\n" + "@return The (const) category object or nil if the name is not valid\n" + "\n" + "This non-const variant has been introduced in version 0.29." + ) + gsi::method ("category_by_id", &rdb::Database::category_by_id, gsi::arg ("id"), "@brief Gets a category by ID\n" "@return The (const) category object or nil if the ID is not valid\n" ) + + gsi::method ("category_by_id", &rdb::Database::category_by_id_non_const, gsi::arg ("id"), + "@brief Gets a category by ID (non-const version)\n" + "@return The (const) category object or nil if the ID is not valid\n" + "\n" + "This non-const variant has been introduced in version 0.29." + ) + gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), "@brief Creates a new cell\n" "@param name The name of the cell\n" @@ -1158,14 +1359,33 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param qname The qualified name of the cell (name plus variant name optionally)\n" "@return The cell object or nil if no such cell exists\n" ) + + gsi::method ("cell_by_qname", &rdb::Database::cell_by_qname_non_const, gsi::arg ("qname"), + "@brief Returns the cell for a given qualified name (non-const version)\n" + "@param qname The qualified name of the cell (name plus variant name optionally)\n" + "@return The cell object or nil if no such cell exists\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::method ("cell_by_id", &rdb::Database::cell_by_id, gsi::arg ("id"), "@brief Returns the cell for a given ID\n" "@param id The ID of the cell\n" "@return The cell object or nil if no cell with that ID exists\n" ) + + gsi::method ("cell_by_id", &rdb::Database::cell_by_id_non_const, gsi::arg ("id"), + "@brief Returns the cell for a given ID (non-const version)\n" + "@param id The ID of the cell\n" + "@return The cell object or nil if no cell with that ID exists\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::iterator_ext ("each_cell", &database_begin_cells, &database_end_cells, "@brief Iterates over all cells\n" ) + + gsi::iterator_ext ("each_cell", &database_begin_cells_nc, &database_end_cells_nc, + "@brief Iterates over all cells (non-const version)\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::method ("num_items", (size_t (rdb::Database::*) () const) &rdb::Database::num_items, "@brief Returns the number of items inside the database\n" "@return The total number of items\n" @@ -1351,19 +1571,43 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", gsi::iterator_ext ("each_item", &database_items_begin, &database_items_end, "@brief Iterates over all items inside the database\n" ) + + gsi::iterator_ext ("each_item", &database_items_begin_nc, &database_items_end_nc, + "@brief Iterates over all items inside the database (non-const version)\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_cell", &database_items_begin_cell, &database_items_end_cell, gsi::arg ("cell_id"), "@brief Iterates over all items inside the database which are associated with the given cell\n" "@param cell_id The ID of the cell for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_cell", &database_items_begin_cell_nc, &database_items_end_cell_nc, gsi::arg ("cell_id"), + "@brief Iterates over all items inside the database which are associated with the given cell (non-const version)\n" + "@param cell_id The ID of the cell for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_category", &database_items_begin_cat, &database_items_end_cat, gsi::arg ("category_id"), "@brief Iterates over all items inside the database which are associated with the given category\n" "@param category_id The ID of the category for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_category", &database_items_begin_cat_nc, &database_items_end_cat_nc, gsi::arg ("category_id"), + "@brief Iterates over all items inside the database which are associated with the given category (non-const version)\n" + "@param category_id The ID of the category for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_cell_and_category", &database_items_begin_cc, &database_items_end_cc, gsi::arg ("cell_id"), gsi::arg ("category_id"), "@brief Iterates over all items inside the database which are associated with the given cell and category\n" "@param cell_id The ID of the cell for which all associated items should be retrieved\n" "@param category_id The ID of the category for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_cell_and_category", &database_items_begin_cc_nc, &database_items_end_cc_nc, gsi::arg ("cell_id"), gsi::arg ("category_id"), + "@brief Iterates over all items inside the database which are associated with the given cell and category\n" + "@param cell_id The ID of the cell for which all associated items should be retrieved\n" + "@param category_id The ID of the category for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::method ("set_item_visited", &rdb::Database::set_item_visited, gsi::arg ("item"), gsi::arg ("visited"), "@brief Modifies the visited state of an item\n" "@param item The item to modify\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 887383198..0a532328f 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -23,11 +23,13 @@ #include "rdb.h" #include "rdbReader.h" +#include "rdbUtils.h" #include "tlString.h" #include "tlAssert.h" #include "tlStream.h" #include "tlLog.h" #include "tlBase64.h" +#include "tlProgress.h" #include "dbPolygon.h" #include "dbBox.h" #include "dbEdge.h" @@ -35,6 +37,10 @@ #include "dbPath.h" #include "dbText.h" #include "dbShape.h" +#include "dbLayout.h" +#include "dbLayoutUtils.h" +#include "dbReader.h" +#include "dbRecursiveShapeIterator.h" #if defined(HAVE_QT) # include @@ -1488,7 +1494,18 @@ Database::items_by_cell_and_category (id_type cell_id, id_type category_id) cons } } -std::pair +std::pair +Database::items_by_cell_and_category (id_type cell_id, id_type category_id) +{ + std::map , std::list >::iterator i = m_items_by_cell_and_category_id.find (std::make_pair (cell_id, category_id)); + if (i != m_items_by_cell_and_category_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +std::pair Database::items_by_cell (id_type cell_id) const { std::map >::const_iterator i = m_items_by_cell_id.find (cell_id); @@ -1499,7 +1516,18 @@ Database::items_by_cell (id_type cell_id) const } } -std::pair +std::pair +Database::items_by_cell (id_type cell_id) +{ + std::map >::iterator i = m_items_by_cell_id.find (cell_id); + if (i != m_items_by_cell_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +std::pair Database::items_by_category (id_type category_id) const { std::map >::const_iterator i = m_items_by_category_id.find (category_id); @@ -1510,7 +1538,18 @@ Database::items_by_category (id_type category_id) const } } -size_t +std::pair +Database::items_by_category (id_type category_id) +{ + std::map >::iterator i = m_items_by_category_id.find (category_id); + if (i != m_items_by_category_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +size_t Database::num_items (id_type cell_id, id_type category_id) const { std::map , size_t>::const_iterator n = m_num_items_by_cell_and_category.find (std::make_pair (cell_id, category_id)); @@ -1566,16 +1605,49 @@ Database::clear () mp_categories->set_database (this); } +static void +read_db_from_layout (rdb::Database *db, tl::InputStream &is) +{ + // try reading a layout file + db::Layout layout; + db::Reader reader (is); + + reader.read (layout); + + std::vector > layers; + for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { + layers.push_back (std::make_pair ((*l).first, std::string ())); + } + + if (layout.begin_top_down () != layout.end_top_down ()) { + db->scan_layout (layout, *layout.begin_top_down (), layers, false /*hierarchical*/); + } +} + void Database::load (const std::string &fn) { tl::log << "Loading RDB from " << fn; - tl::InputStream stream (fn); - rdb::Reader reader (stream); - clear (); - reader.read (*this); + + tl::InputStream stream (fn); + + bool ok = false; + try { + // try reading a stream file + read_db_from_layout (this, stream); + ok = true; + } catch (tl::Exception &) { + stream.reset (); + } + + if (! ok) { + // try reading a DB file + clear (); + rdb::Reader reader (stream); + reader.read (*this); + } set_filename (stream.absolute_path ()); set_name (stream.filename ()); @@ -1587,5 +1659,128 @@ Database::load (const std::string &fn) } } +void +Database::scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) +{ + tl::AbsoluteProgress progress (tl::to_string (tr ("Shapes To Markers")), 10000); + progress.set_format (tl::to_string (tr ("%.0f0000 markers"))); + progress.set_unit (10000); + + set_name ("Shapes"); + set_top_cell_name (layout.cell_name (cell_index)); + rdb::Cell *rdb_top_cell = create_cell (top_cell_name ()); + + std::string desc; + + if (layers_and_descriptions.size () == 1) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layer ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layer ")); + } + + desc += layout.get_properties (layers_and_descriptions.front ().first).to_string (); + + } else if (layers_and_descriptions.size () < 4 && layers_and_descriptions.size () > 0) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layers ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layers ")); + } + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + if (l != layers_and_descriptions.begin ()) { + desc += ","; + } + desc += layout.get_properties (l->first).to_string (); + } + + } else { + + if (flat) { + desc = tl::sprintf (tl::to_string (tr ("Flat shapes of %d layers")), int (layers_and_descriptions.size ())); + } else { + desc = tl::sprintf (tl::to_string (tr ("Hierarchical shapes of %d layers")), int (layers_and_descriptions.size ())); + } + + } + + desc += " "; + desc += tl::to_string (tr ("from cell ")); + desc += layout.cell_name (cell_index); + set_description (desc); + + if (flat) { + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + + rdb::Category *cat = create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); + + db::RecursiveShapeIterator shape (layout, layout.cell (cell_index), l->first); + while (! shape.at_end ()) { + + rdb::create_item_from_shape (this, rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); + + ++progress; + ++shape; + + } + + } + + } else { + + std::set called_cells; + called_cells.insert (cell_index); + layout.cell (cell_index).collect_called_cells (called_cells); + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + + rdb::Category *cat = create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); + + for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { + + if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { + continue; + } + + const db::Cell &cell = *cid; + if (! cell.shapes (l->first).empty ()) { + + std::string cn = layout.cell_name (cell.cell_index ()); + const rdb::Cell *rdb_cell = cell_by_qname (cn); + if (! rdb_cell) { + + rdb::Cell *rdb_cell_nc = create_cell (cn); + rdb_cell = rdb_cell_nc; + + std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cell_index); + if (ctx.first) { + db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); + rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); + } + + } + + for (db::ShapeIterator shape = cell.shapes (l->first).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { + + rdb::create_item_from_shape (this, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); + + ++progress; + + } + + } + + } + + } + + } +} + + } // namespace rdb diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index a7859f60e..b15558a96 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -50,6 +50,7 @@ namespace tl namespace db { class Shape; + class Layout; } namespace rdb @@ -2082,6 +2083,14 @@ public: return *mp_categories; } + /** + * @brief Get the reference to the categories collection (non-const version) + */ + Categories &categories_non_const () + { + return *mp_categories; + } + /** * @brief Import categories * @@ -2129,6 +2138,20 @@ public: return const_cast (this)->category_by_id_non_const (id); } + /** + * @brief Get the category pointer for a category name (non-const version) + * + * This method returns 0 if the category name is invalid. + */ + Category *category_by_name_non_const (const std::string &name); + + /** + * @brief Get the category pointer for a category id (non-const version) + * + * This method returns 0 if the category is invalid. + */ + Category *category_by_id_non_const (id_type id); + /** * @brief Access to the cell collection (const) */ @@ -2137,6 +2160,14 @@ public: return m_cells; } + /** + * @brief Access to the cell collection + */ + Cells &cells_non_const () + { + return m_cells; + } + /** * @brief Import cells * @@ -2190,6 +2221,20 @@ public: return const_cast (this)->cell_by_id_non_const (id); } + /** + * @brief Get the cell pointer for a cell name or name:variant combination (non-const version) + * + * This method returns 0 if the cell name or name:variant combination is invalid. + */ + Cell *cell_by_qname_non_const (const std::string &qname); + + /** + * @brief Get the cell pointer for a cell id (non-const version) + * + * This method returns 0 if the cell id is invalid. + */ + Cell *cell_by_id_non_const (id_type id); + /** * @brief Report the number of items in total */ @@ -2266,6 +2311,14 @@ public: return *mp_items; } + /** + * @brief Get the items collection (non-const version) + */ + Items &items_non_const () + { + return *mp_items; + } + /** * @brief Set the items collection * @@ -2279,16 +2332,31 @@ public: */ std::pair items_by_cell (id_type cell_id) const; + /** + * @brief Get an iterator pair that delivers the non-const items (ItemRef) for a given cell + */ + std::pair items_by_cell (id_type cell_id); + /** * @brief Get an iterator that delivers the const items (ItemRef) for a given category */ std::pair items_by_category (id_type category_id) const; + /** + * @brief Get an iterator that delivers the non-const items (ItemRef) for a given category + */ + std::pair items_by_category (id_type category_id); + /** * @brief Get an iterator that delivers the const items (ItemRef) for a given cell and category */ std::pair items_by_cell_and_category (id_type cell_id, id_type category_id) const; + /** + * @brief Get an iterator that delivers the non-const items (ItemRef) for a given cell and category + */ + std::pair items_by_cell_and_category (id_type cell_id, id_type category_id); + /** * @brief Returns true, if the database was modified */ @@ -2317,6 +2385,16 @@ public: */ void load (const std::string &filename); + /** + * @brief Scans a layout into this RDB + * + * @param layout The layout to scan + * @param cell_index The top cell to scan + * @param layers_and_descriptions The layers and (optional) descriptions/names of the layer to scan + * @param flat True, to perform a flat scan + */ + void scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat); + private: std::string m_generator; std::string m_filename; @@ -2349,14 +2427,6 @@ private: m_modified = true; } - /** - * @brief Get the items collection (non-const version) - */ - Items &items_non_const () - { - return *mp_items; - } - /** * @brief Get the reference to the tags collection (non-const version) */ @@ -2364,50 +2434,6 @@ private: { return m_tags; } - - /** - * @brief Get the reference to the categories collection (non-const version) - */ - Categories &categories_non_const () - { - return *mp_categories; - } - - /** - * @brief Get the category pointer for a category name - * - * This method returns 0 if the category name is invalid. - */ - Category *category_by_name_non_const (const std::string &name); - - /** - * @brief Get the category pointer for a category id - * - * This method returns 0 if the category is invalid. - */ - Category *category_by_id_non_const (id_type id); - - /** - * @brief Access to the cell collection - */ - Cells &cells_non_const () - { - return m_cells; - } - - /** - * @brief Get the cell pointer for a cell name or name:variant combination (non-const version) - * - * This method returns 0 if the cell name or name:variant combination is invalid. - */ - Cell *cell_by_qname_non_const (const std::string &qname); - - /** - * @brief Get the cell pointer for a cell id (non-const version) - * - * This method returns 0 if the cell id is invalid. - */ - Cell *cell_by_id_non_const (id_type id); }; } diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index 5a9f73562..7d1cf0c15 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -55,7 +55,8 @@ SOURCES = \ tlEquivalenceClusters.cc \ tlUniqueName.cc \ tlRecipe.cc \ - tlEnv.cc + tlEnv.cc \ + tlOptional.cc HEADERS = \ tlAlgorithm.h \ @@ -121,7 +122,8 @@ HEADERS = \ tlUniqueName.h \ tlRecipe.h \ tlSelect.h \ - tlEnv.h + tlEnv.h \ + tlOptional.h equals(HAVE_GIT2, "1") { diff --git a/src/tl/tl/tlGit.cc b/src/tl/tl/tlGit.cc index 3e749e633..6278bccfe 100644 --- a/src/tl/tl/tlGit.cc +++ b/src/tl/tl/tlGit.cc @@ -30,6 +30,9 @@ #include "tlEnv.h" #include +#if LIBGIT2_VER_MAJOR > 1 || (LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR >= 8) + #include +#endif #include namespace tl diff --git a/src/tl/tl/tlHttpStream.h b/src/tl/tl/tlHttpStream.h index a85dc3348..529e81d85 100644 --- a/src/tl/tl/tlHttpStream.h +++ b/src/tl/tl/tlHttpStream.h @@ -112,7 +112,6 @@ public: * @brief Polling: call this function regularly to explicitly establish polling * (in the Qt framework, this is done automatically within the event loop) * May throw a tl::CancelException to stop. - * Returns true if a message has arrived. */ void tick (); @@ -209,6 +208,21 @@ private: InputHttpStreamCallback *mp_callback; }; +/** + * @brief A HTTP stream with .gz support + */ +class TL_PUBLIC InflatingInputHttpStream + : public inflating_input_stream +{ +public: + /** + * @brief Open a stream with the given URL + */ + InflatingInputHttpStream (const std::string &url) + : inflating_input_stream (new InputHttpStream (url)) + { } +}; + } #endif diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc new file mode 100644 index 000000000..d7ee370b2 --- /dev/null +++ b/src/tl/tl/tlOptional.cc @@ -0,0 +1,30 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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 "tlOptional.h" + +namespace tl +{ + +const nullopt_t nullopt = nullopt_t (); + +} // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h new file mode 100644 index 000000000..3893d6ac0 --- /dev/null +++ b/src/tl/tl/tlOptional.h @@ -0,0 +1,156 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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_tlOptional +#define HDR_tlOptional + +#include "tlAssert.h" +#include "tlString.h" +#include "tlCommon.h" + +#include + +namespace tl +{ + +struct nullopt_t {}; + +extern TL_PUBLIC const nullopt_t nullopt; + +/** + * @brief Poor man's partial implementation of C++17's std::optional + */ +template +class TL_PUBLIC_TEMPLATE optional +{ +public: + optional () : + m_value (), + m_is_valid (false) + {} + + optional (const nullopt_t &) : + m_value (), + m_is_valid (false) + {} + + optional (const T &value) : + m_value (value), + m_is_valid (true) + {} + + void reset () + { + m_is_valid = false; + } + + bool has_value() const { return m_is_valid; } + + T &value () + { + tl_assert (m_is_valid); + + return m_value; + } + + const T &value () const + { + tl_assert (m_is_valid); + + return m_value; + } + + T& operator* () + { + return value (); + } + + const T& operator* () const + { + return value (); + } + + T* operator-> () + { + return m_is_valid ? &m_value : 0; + } + + const T* operator-> () const + { + return m_is_valid ? &m_value : 0; + } + +private: + T m_value; + bool m_is_valid; +}; + +template +optional make_optional (const T &value) +{ + return optional (value); +} + +template +bool operator== (const optional &lhs, const optional &rhs) +{ + if (lhs.has_value () != rhs.has_value ()) { + return false; + } + if (!lhs.has_value ()) { + return true; + } + + return lhs.value() == rhs.value(); +} + +template +bool operator!= (const optional &lhs, const optional &rhs) +{ + return !(lhs == rhs); +} + +template +std::ostream &operator<< (std::ostream &ostr, const optional &rhs) +{ + if (rhs.has_value()) { + ostr << rhs.value(); + } else { + ostr << ""; + } + + return ostr; +} + +template +std::string to_string (const optional &opt) +{ + if (opt.has_value ()) { + return tl::to_string (*opt); + } else { + return std::string (); + } +} + +} // namespace tl + +#endif /* HDR_tlOptional */ diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index dc57b3a98..751c4841d 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -47,6 +47,7 @@ #include "tlException.h" #include "tlString.h" #include "tlUri.h" +#include "tlHttpStream.h" #if defined(HAVE_QT) # include @@ -140,6 +141,129 @@ public: gzFile zs; }; +// --------------------------------------------------------------- +// inflating_input_stream implementation + +/** + * @brief A wrapper that adds generic .gz support + */ +template +inflating_input_stream::inflating_input_stream (Base *delegate) + : m_inflating_stream (delegate), m_is_compressed (false), mp_delegate (delegate) +{ + enter_inflate (); +} + +template +size_t +inflating_input_stream::read (char *b, size_t n) +{ + // TODO: this is somewhat inefficient, but we only use it for pipe and HTTP streams + size_t i = 0; + while (i < n) { + + if (m_is_compressed || m_inflating_stream.blen () == 0) { + + const char *read = m_inflating_stream.get (1); + if (! read) { + break; + } + *b++ = *read; + i += 1; + + } else { + + size_t nn = std::min (n - i, m_inflating_stream.blen ()); + const char *read = m_inflating_stream.get (nn); + tl_assert (read != 0); + memcpy (b, read, nn); + b += nn; + i += nn; + + } + + } + return i; +} + +template +void +inflating_input_stream::enter_inflate () +{ + // identify and skip header for .gz file + if (auto_detect_gz ()) { + m_is_compressed = true; + m_inflating_stream.inflate (true /* stop after inflated block */); + } else { + m_inflating_stream.unget (m_inflating_stream.pos ()); + } +} + +template +bool +inflating_input_stream::auto_detect_gz () +{ + std::string header = m_inflating_stream.read_all (10); + if (header.size () < 10) { + return false; + } + + const unsigned char *header_data = (const unsigned char *) header.c_str (); + unsigned char flags = header_data[3]; + if (header_data[0] != 0x1f || header_data[1] != 0x8b || header_data[2] != 0x08 || (flags & 0xe0) != 0) { + return false; + } + + // .gz signature found + + bool has_fhcrc = (flags & 0x02) != 0; + bool has_extra = (flags & 0x04) != 0; + bool has_fname = (flags & 0x08) != 0; + bool has_comment = (flags & 0x10) != 0; + + if (has_extra) { + const unsigned char *xlen = (const unsigned char *) m_inflating_stream.get (2); + if (! xlen) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing XLEN field"))); + } + const char *xdata = m_inflating_stream.get (size_t (xlen[0]) + (size_t (xlen[1]) << 8)); + if (! xdata) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing EXTRA data"))); + } + } + + if (has_fname) { + const char *c; + while ((c = m_inflating_stream.get (1)) != 0 && *c) + ; + if (! c) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing FNAME data trailing zero byte"))); + } + } + + if (has_comment) { + const char *c; + while ((c = m_inflating_stream.get (1)) != 0 && *c) + ; + if (! c) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing COMMENT data trailing zero byte"))); + } + } + + if (has_fhcrc) { + const char *crc16 = m_inflating_stream.get (2); + if (! crc16) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing CRC16 data"))); + } + } + + return true; +} + +// explicit instantiations +template class inflating_input_stream; +template class inflating_input_stream; + // --------------------------------------------------------------- // InputStream implementation @@ -175,7 +299,7 @@ public: } InputStream::InputStream (InputStreamBase &delegate) - : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -183,7 +307,7 @@ InputStream::InputStream (InputStreamBase &delegate) } InputStream::InputStream (InputStreamBase *delegate) - : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -191,7 +315,7 @@ InputStream::InputStream (InputStreamBase *delegate) } InputStream::InputStream (const std::string &abstract_path) - : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -252,7 +376,7 @@ InputStream::InputStream (const std::string &abstract_path) } else if (ex.test ("pipe:")) { - mp_delegate = new InputPipe (ex.get ()); + mp_delegate = new InflatingInputPipe (ex.get ()); } else { @@ -260,7 +384,7 @@ InputStream::InputStream (const std::string &abstract_path) if (uri.scheme () == "http" || uri.scheme () == "https") { #if defined(HAVE_CURL) || defined(HAVE_QT) - mp_delegate = new InputHttpStream (abstract_path); + mp_delegate = new InflatingInputHttpStream (abstract_path); #else throw tl::Exception (tl::to_string (tr ("HTTP support not enabled - HTTP/HTTPS paths are not available"))); #endif @@ -337,9 +461,16 @@ InputStream::get (size_t n, bool bypass_inflate) tl_assert (r != 0); // since deflate did not report at_end() return r; + } else if (m_stop_after_inflate) { + + // report EOF after the inflator has finished + return 0; + } else { + delete mp_inflate; mp_inflate = 0; + } } @@ -384,9 +515,16 @@ InputStream::get (size_t n, bool bypass_inflate) void InputStream::unget (size_t n) { + if (n == 0) { + return; + } + if (mp_inflate) { + // TODO: this will not work if mp_inflate just got destroyed + // (no unget into previous compressed block) mp_inflate->unget (n); } else { + tl_assert (mp_buffer + n <= mp_bptr); mp_bptr -= n; m_blen += n; m_pos -= n; @@ -476,10 +614,11 @@ void InputStream::copy_to (tl::OutputStream &os) } void -InputStream::inflate () +InputStream::inflate (bool stop_after) { tl_assert (mp_inflate == 0); mp_inflate = new tl::InflateFilter (*this); + m_stop_after_inflate = stop_after; } void @@ -1334,11 +1473,11 @@ OutputPipe::write (const char *b, size_t n) // --------------------------------------------------------------- // InputPipe delegate implementation -InputPipe::InputPipe (const std::string &path) +InputPipe::InputPipe (const std::string &source) : m_file (NULL) { - m_source = path; - m_file = popen (tl::string_to_system (path).c_str (), "r"); + m_source = source; + m_file = popen (tl::string_to_system (source).c_str (), "r"); if (m_file == NULL) { throw FilePOpenErrorException (m_source, errno); } diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 2412c4663..c738c84ff 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -310,10 +310,9 @@ public: * an error occurs - commonly if the command cannot be executed. * This implementation is based on popen (). * - * @param cmd The command to execute - * @param read True, if the file should be read, false on write. + * @param source The command to execute */ - InputPipe (const std::string &path); + InputPipe (const std::string &source); /** * @brief Close the pipe @@ -348,8 +347,7 @@ public: */ virtual std::string source () const { - // No source (in the sense of a file name) is available .. - return std::string (); + return m_source; } virtual std::string absolute_path () const @@ -474,8 +472,11 @@ public: * the uncompressed data rather than the raw data, until the * compressed block is finished. * The stream must not be in inflate state yet. + * + * If "stop_after" is true, the stream will stop after the inflated + * block has finished. */ - void inflate (); + void inflate (bool stop_after = false); /** * @brief Enables "inflate" right from the beginning @@ -578,12 +579,87 @@ private: // inflate support InflateFilter *mp_inflate; bool m_inflate_always; + bool m_stop_after_inflate; // No copying currently InputStream (const InputStream &); InputStream &operator= (const InputStream &); }; +/** + * @brief A wrapper that adds generic .gz support + */ +template +class TL_PUBLIC inflating_input_stream + : public InputStreamBase +{ +public: + inflating_input_stream (Base *delegate); + + Base *delegate () + { + return mp_delegate; + } + + virtual size_t read (char *b, size_t n); + + virtual void reset () + { + m_inflating_stream.reset (); + enter_inflate (); + } + + virtual void close () + { + m_inflating_stream.close (); + } + + virtual std::string source () const + { + return m_inflating_stream.source (); + } + + virtual std::string absolute_path () const + { + return m_inflating_stream.absolute_path (); + } + + virtual std::string filename () const + { + return m_inflating_stream.filename (); + } + +private: + tl::InputStream m_inflating_stream; + bool m_is_compressed; + Base *mp_delegate; + + void enter_inflate (); + bool auto_detect_gz (); +}; + +/** + * @brief A pipe stream with .gz support + */ +class TL_PUBLIC InflatingInputPipe + : public inflating_input_stream +{ +public: + /** + * @brief Open a stream by connecting with the stdout of a given command + * + * Opening a pipe is a prerequisite for reading from the + * object. open() will throw a FilePOpenErrorException if + * an error occurs - commonly if the command cannot be executed. + * This implementation is based on popen (). + * + * @param source The command to execute + */ + InflatingInputPipe (const std::string &source) + : inflating_input_stream (new InputPipe (source)) + { } +}; + // --------------------------------------------------------------------------------- /** diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index ab327c00f..8a0379e6e 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -322,10 +322,8 @@ static double local_strtod (const char *cp, const char *&cp_new) if (*cp == '-') { s = -1.0; ++cp; - /* } else if (*cp == '+') { ++cp; - */ } // Extract upper digits diff --git a/src/tl/unit_tests/tlHttpStreamTests.cc b/src/tl/unit_tests/tlHttpStreamTests.cc index e05c7e5fe..203ff8e1e 100644 --- a/src/tl/unit_tests/tlHttpStreamTests.cc +++ b/src/tl/unit_tests/tlHttpStreamTests.cc @@ -24,8 +24,10 @@ #include "tlHttpStream.h" #include "tlUnitTest.h" #include "tlTimer.h" +#include "tlStream.h" static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text"); +static std::string test_url1_gz ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata2/text.gz"); static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1"); TEST(1) @@ -125,3 +127,29 @@ TEST(3) EXPECT_EQ (res, "hello, world.\n"); } +// tl::Stream embedding +TEST(4) +{ + if (! tl::InputHttpStream::is_available ()) { + throw tl::CancelException (); + } + + tl::InputStream stream (test_url1); + + std::string res = stream.read_all (); + EXPECT_EQ (res, "hello, world.\n"); +} + +// tl::Stream embedding with automatic unzip +TEST(5) +{ + if (! tl::InputHttpStream::is_available ()) { + throw tl::CancelException (); + } + + tl::InputStream stream (test_url1_gz); + + std::string res = stream.read_all (); + EXPECT_EQ (res, "hello, world.\n"); +} + diff --git a/src/tl/unit_tests/tlOptionalTests.cc b/src/tl/unit_tests/tlOptionalTests.cc new file mode 100644 index 000000000..182d0048d --- /dev/null +++ b/src/tl/unit_tests/tlOptionalTests.cc @@ -0,0 +1,97 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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 "tlOptional.h" +#include "tlUnitTest.h" + +namespace +{ + +TEST(1_Basic) +{ + tl::optional opt; + + // value not set + + EXPECT_EQ (opt.has_value (), false); + EXPECT_EQ (opt.operator-> (), (int *) 0); + EXPECT_EQ (((const tl::optional &) opt).operator-> (), (const int *) 0); + EXPECT_EQ (tl::to_string (opt), ""); + + try { + opt.value (); // asserts + EXPECT_EQ (true, false); + } catch (...) { + } + + // make_optional, assignment + + opt = tl::make_optional (17); + + // value set + + EXPECT_EQ (opt.has_value (), true); + EXPECT_EQ (opt.value (), 17); + EXPECT_EQ (tl::to_string (opt), "17"); + EXPECT_EQ (((const tl::optional &) opt).value (), 17); + EXPECT_EQ (*opt, 17); + EXPECT_EQ (*((const tl::optional &) opt), 17); + EXPECT_EQ (*(opt.operator-> ()), 17); + EXPECT_EQ (*(((const tl::optional &) opt).operator-> ()), 17); + + // compare operators + + EXPECT_EQ (opt == tl::make_optional (-1), false); + EXPECT_EQ (opt == tl::make_optional (17), true); + EXPECT_EQ (opt == tl::optional (), false); + + EXPECT_EQ (opt != tl::make_optional (-1), true); + EXPECT_EQ (opt != tl::make_optional (17), false); + EXPECT_EQ (opt != tl::optional (), true); + + // copy ctor + + tl::optional opt2 (opt); + + EXPECT_EQ (opt2.has_value (), true); + EXPECT_EQ (opt2.value (), 17); + + // reset method + + opt = tl::make_optional (17); + opt.reset (); + + EXPECT_EQ (opt.has_value (), false); + EXPECT_EQ (opt == tl::optional (), true); + EXPECT_EQ (opt != tl::optional (), false); + + // tl::nullopt tag + + opt = tl::make_optional (17); + opt = tl::optional (tl::nullopt); + + EXPECT_EQ (opt.has_value (), false); + EXPECT_EQ (opt == tl::optional (), true); + EXPECT_EQ (opt != tl::optional (), false); +} + +} diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index e51a7d4c4..517d71615 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -52,6 +52,13 @@ TEST(InputPipe2) EXPECT_NE (ret, 0); } +TEST(InputPipe3) +{ + tl::InputStream str ("pipe:echo HELLOWORLD"); + tl::TextInputStream tstr (str); + EXPECT_EQ (tstr.get_line (), "HELLOWORLD"); +} + TEST(OutputPipe1) { std::string tf = tmp_file ("pipe_out"); @@ -455,3 +462,5 @@ TEST(Backups) } } + + diff --git a/src/tl/unit_tests/tlStringTests.cc b/src/tl/unit_tests/tlStringTests.cc index e6078ec3a..aafab46e6 100644 --- a/src/tl/unit_tests/tlStringTests.cc +++ b/src/tl/unit_tests/tlStringTests.cc @@ -305,6 +305,25 @@ TEST(6) EXPECT_EQ (x3.test (":"), true); } +TEST(6_double) +{ + Extractor x (" 5.5 -2.5 \n+0.125 (no number)"); + + EXPECT_EQ (x.at_end (), false); + + double d = 0.0; + + EXPECT_EQ (x.try_read (d), true); + EXPECT_EQ (d, 5.5); + + x.read (d); + EXPECT_EQ (d, -2.5); + x.read (d); + EXPECT_EQ (d, 0.125); + + x.expect ("("); +} + TEST(7) { EXPECT_EQ (tl::to_quoted_string ("a_word!"), "'a_word!'"); diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 0e4bcc449..3d8b06676 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -31,6 +31,7 @@ SOURCES = \ tlLongIntTests.cc \ tlMathTests.cc \ tlObjectTests.cc \ + tlOptionalTests.cc \ tlPixelBufferTests.cc \ tlResourcesTests.cc \ tlReuseVectorTests.cc \ diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds index 4bb15fdec..3742b8f31 100644 Binary files a/testdata/algo/deep_edges_au3.gds and b/testdata/algo/deep_edges_au3.gds differ diff --git a/testdata/algo/deep_edges_au3_flat.gds b/testdata/algo/deep_edges_au3_flat.gds new file mode 100644 index 000000000..ca8d210ec Binary files /dev/null and b/testdata/algo/deep_edges_au3_flat.gds differ diff --git a/testdata/algo/deep_edges_l1.gds b/testdata/algo/deep_edges_l1.gds new file mode 100644 index 000000000..7a9bada7e Binary files /dev/null and b/testdata/algo/deep_edges_l1.gds differ diff --git a/testdata/drc/drcSimpleTests_100.drc b/testdata/drc/drcSimpleTests_100.drc new file mode 100644 index 000000000..74d09c10f --- /dev/null +++ b/testdata/drc/drcSimpleTests_100.drc @@ -0,0 +1,89 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +e1 = l1.edges +e2 = l2.edges + +e1.output(10, 0) +e2.output(11, 0) + +e1.interacting(l2).output(100, 0) +e1.interacting(l2, 2).output(101, 0) +e1.interacting(l2, 2, 2).output(102, 0) +e1.interacting(l2, 3).output(103, 0) + +e1.split_interacting(l2)[0].output(110, 0) +e1.split_interacting(l2, 2)[0].output(111, 0) +e1.split_interacting(l2, 2, 2)[0].output(112, 0) +e1.split_interacting(l2, 3)[0].output(113, 0) + +e1.interacting(e2).output(200, 0) +e1.interacting(e2, 2).output(201, 0) +e1.interacting(e2, 2, 2).output(202, 0) +e1.interacting(e2, 3).output(203, 0) +e1.interacting(e2, 3..4).output(204, 0) + +e1.split_interacting(e2)[0].output(210, 0) +e1.split_interacting(e2, 2)[0].output(211, 0) +e1.split_interacting(e2, 2, 2)[0].output(212, 0) +e1.split_interacting(e2, 3)[0].output(213, 0) +e1.split_interacting(e2, 3..4)[0].output(214, 0) + +e1.not_interacting(l2).output(300, 0) +e1.not_interacting(l2, 2).output(301, 0) +e1.not_interacting(l2, 2, 2).output(302, 0) +e1.not_interacting(l2, 3).output(303, 0) + +e1.split_interacting(l2)[1].output(310, 0) +e1.split_interacting(l2, 2)[1].output(311, 0) +e1.split_interacting(l2, 2, 2)[1].output(312, 0) +e1.split_interacting(l2, 3)[1].output(313, 0) + +e1.not_interacting(e2).output(400, 0) +e1.not_interacting(e2, 2).output(401, 0) +e1.not_interacting(e2, 2, 2).output(402, 0) +e1.not_interacting(e2, 3).output(403, 0) +e1.not_interacting(e2, 3..4).output(404, 0) + +e1.split_interacting(e2)[1].output(410, 0) +e1.split_interacting(e2, 2)[1].output(411, 0) +e1.split_interacting(e2, 2, 2)[1].output(412, 0) +e1.split_interacting(e2, 3)[1].output(413, 0) +e1.split_interacting(e2, 3..4)[1].output(414, 0) + +# convex detection (the initial problem) + +c90 = l2.corners(as_dots, 90) +cm90 = l2.corners(as_dots, -90) +c90.output(1000, 0) +cm90.output(1001, 0) + +e2.interacting(c90, 2, 2).output(1100, 0) +e2.interacting(cm90, 2, 2).output(1101, 0) +e2.interacting(c90, 1, 1).output(1102, 0) +e2.interacting(cm90, 1, 1).output(1103, 0) + +c90.interacting(e1).output(1200, 0) +c90.not_interacting(e1).output(1201, 0) +c90.interacting(e2).output(1202, 0) +c90.not_interacting(e2).output(1203, 0) + +c90.interacting(l1).output(1300, 0) +c90.not_interacting(l1).output(1301, 0) +c90.interacting(l2).output(1302, 0) +c90.not_interacting(l2).output(1303, 0) + +c90.split_interacting(l1)[0].output(1310, 0) +c90.split_interacting(l1)[1].output(1311, 0) + diff --git a/testdata/drc/drcSimpleTests_100.gds b/testdata/drc/drcSimpleTests_100.gds new file mode 100644 index 000000000..e0c678c17 Binary files /dev/null and b/testdata/drc/drcSimpleTests_100.gds differ diff --git a/testdata/drc/drcSimpleTests_101.drc b/testdata/drc/drcSimpleTests_101.drc new file mode 100644 index 000000000..745d17f88 --- /dev/null +++ b/testdata/drc/drcSimpleTests_101.drc @@ -0,0 +1,52 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +e1 = l1.edges +e2 = l2.edges + +e1.output(10, 0) +e2.output(11, 0) + +c1 = l1.corners(as_dots) +c2 = l2.corners(as_dots) + +c1.output(20, 0) +c2.output(21, 0) + +(c2 & l1).output(100, 0) +(c2 - l1).output(101, 0) + +c2.andnot(l1)[0].output(110, 0) +c2.andnot(l1)[1].output(111, 0) + +c2.inside_part(l1).output(200, 0) +c2.outside_part(l1).output(201, 0) + +c2.inside_outside_part(l1)[0].output(210, 0) +c2.inside_outside_part(l1)[1].output(211, 0) + +c2.interacting(l1).output(300, 0) +c2.not_interacting(l1).output(301, 0) + +c2.split_interacting(l1)[0].output(310, 0) +c2.split_interacting(l1)[1].output(311, 0) + +(c2 & e1).output(400, 0) +(c2 - e1).output(401, 0) +(c2 ^ e1).output(402, 0) +(c2 | e1).output(403, 0) + +c2.andnot(e1)[0].output(410, 0) +c2.andnot(e1)[1].output(411, 0) + diff --git a/testdata/drc/drcSimpleTests_101.gds b/testdata/drc/drcSimpleTests_101.gds new file mode 100644 index 000000000..b3dc99ddb Binary files /dev/null and b/testdata/drc/drcSimpleTests_101.gds differ diff --git a/testdata/drc/drcSimpleTests_102.drc b/testdata/drc/drcSimpleTests_102.drc new file mode 100644 index 000000000..7187fe7aa --- /dev/null +++ b/testdata/drc/drcSimpleTests_102.drc @@ -0,0 +1,40 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l2.edges.output(100, 0) +l2.edges(convex).output(101, 0) +l2.edges(concave).output(102, 0) +l2.edges(step).output(103, 0) +l2.edges(step_in).output(104, 0) +l2.edges(step_out).output(105, 0) + +l2.edges(not_convex).output(111, 0) +l2.edges(not_concave).output(112, 0) +l2.edges(not_step).output(113, 0) +l2.edges(not_step_in).output(114, 0) +l2.edges(not_step_out).output(115, 0) + +l2.drc(primary.edges).output(200, 0) +l2.drc(primary.edges(convex)).output(201, 0) +l2.drc(primary.edges(concave)).output(202, 0) +l2.drc(primary.edges(step)).output(203, 0) +l2.drc(primary.edges(step_in)).output(204, 0) +l2.drc(primary.edges(step_out)).output(205, 0) + +l2.drc(primary.edges(not_convex)).output(211, 0) +l2.drc(primary.edges(not_concave)).output(212, 0) +l2.drc(primary.edges(not_step)).output(213, 0) +l2.drc(primary.edges(not_step_in)).output(214, 0) +l2.drc(primary.edges(not_step_out)).output(215, 0) + diff --git a/testdata/drc/drcSimpleTests_102.gds b/testdata/drc/drcSimpleTests_102.gds new file mode 100644 index 000000000..b3dc99ddb Binary files /dev/null and b/testdata/drc/drcSimpleTests_102.gds differ diff --git a/testdata/drc/drcSimpleTests_au100.gds b/testdata/drc/drcSimpleTests_au100.gds new file mode 100644 index 000000000..dd25563fb Binary files /dev/null and b/testdata/drc/drcSimpleTests_au100.gds differ diff --git a/testdata/drc/drcSimpleTests_au100d.gds b/testdata/drc/drcSimpleTests_au100d.gds new file mode 100644 index 000000000..e977f6009 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au100d.gds differ diff --git a/testdata/drc/drcSimpleTests_au101.gds b/testdata/drc/drcSimpleTests_au101.gds new file mode 100644 index 000000000..f9d618e85 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au101.gds differ diff --git a/testdata/drc/drcSimpleTests_au101d.gds b/testdata/drc/drcSimpleTests_au101d.gds new file mode 100644 index 000000000..d61b0a5ae Binary files /dev/null and b/testdata/drc/drcSimpleTests_au101d.gds differ diff --git a/testdata/drc/drcSimpleTests_au102.gds b/testdata/drc/drcSimpleTests_au102.gds new file mode 100644 index 000000000..26068b9b3 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au102.gds differ diff --git a/testdata/drc/drcSimpleTests_au102d.gds b/testdata/drc/drcSimpleTests_au102d.gds new file mode 100644 index 000000000..e14a2cce2 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au102d.gds differ diff --git a/testdata/drc/drcSuiteTests_au1.oas b/testdata/drc/drcSuiteTests_au1.oas index 9e56a12cf..ef96649d3 100644 Binary files a/testdata/drc/drcSuiteTests_au1.oas and b/testdata/drc/drcSuiteTests_au1.oas differ diff --git a/testdata/drc/drcSuiteTests_au2.oas b/testdata/drc/drcSuiteTests_au2.oas index 067d6154c..81cb3d835 100644 Binary files a/testdata/drc/drcSuiteTests_au2.oas and b/testdata/drc/drcSuiteTests_au2.oas differ diff --git a/testdata/drc/drcSuiteTests_au3.oas b/testdata/drc/drcSuiteTests_au3.oas index 0960a1669..3d996bb5c 100644 Binary files a/testdata/drc/drcSuiteTests_au3.oas and b/testdata/drc/drcSuiteTests_au3.oas differ diff --git a/testdata/drc/drcSuiteTests_au4.oas b/testdata/drc/drcSuiteTests_au4.oas index d10bdf656..7c98f4486 100644 Binary files a/testdata/drc/drcSuiteTests_au4.oas and b/testdata/drc/drcSuiteTests_au4.oas differ diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas index 990671ce6..2ad58c4ed 100644 Binary files a/testdata/drc/drcSuiteTests_au5.oas and b/testdata/drc/drcSuiteTests_au5.oas differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index bf7421344..d4e846c31 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/lefdef/blend_mode/au1.oas.gz b/testdata/lefdef/blend_mode/au1.oas.gz index e0e95c3a5..a1115551b 100644 Binary files a/testdata/lefdef/blend_mode/au1.oas.gz and b/testdata/lefdef/blend_mode/au1.oas.gz differ diff --git a/testdata/lefdef/blend_mode/au2.oas.gz b/testdata/lefdef/blend_mode/au2.oas.gz index b05825a58..091ede9a8 100644 Binary files a/testdata/lefdef/blend_mode/au2.oas.gz and b/testdata/lefdef/blend_mode/au2.oas.gz differ diff --git a/testdata/lefdef/blend_mode/au3.oas.gz b/testdata/lefdef/blend_mode/au3.oas.gz index b8ceb61e2..e52346650 100644 Binary files a/testdata/lefdef/blend_mode/au3.oas.gz and b/testdata/lefdef/blend_mode/au3.oas.gz differ diff --git a/testdata/python/dbPolygonTest.py b/testdata/python/dbPolygonTest.py index ec1c4a3b6..fb5349d45 100644 --- a/testdata/python/dbPolygonTest.py +++ b/testdata/python/dbPolygonTest.py @@ -746,6 +746,16 @@ class DBPolygonTests(unittest.TestCase): poly = pya.Polygon(hull).insert_hole(hole1).insert_hole(hole2) self.assertEqual(str(poly), "(0,0;0,3000;6000,3000;6000,0/1000,1000;2000,1000;2000,2000;1000,2000/3000,1000;4000,1000;4000,2000;3000,2000)") + def test_argumentShortcuts(self): + + # implicit conversion to a Point array: + poly = pya.Polygon([ (0,0), (0,1000), (1000,1000) ]) + self.assertEqual(str(poly), "(0,0;0,1000;1000,1000)") + + # issue 1651 - no binding to Box constructor + poly = pya.Polygon([ (0,0), (0,1000), (1000,1000), (1000,0) ]) + self.assertEqual(str(poly), "(0,0;0,1000;1000,1000;1000,0)") + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBPolygonTests) diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 8e9a916e9..1a207ef81 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -146,6 +146,9 @@ class DBBox_TestClass < TestBase assert_equal( a + b, b ) assert_equal( (b + c).to_s, "(1,-10;22,22)" ) + assert_equal( b - a, b ) + assert_equal( (b - c).to_s, "(1,-1;17,22)" ) + assert_equal( a + RBA::DPoint::new( 1, -5 ), RBA::DBox::new( 1, -5, 1, -5 ) ) assert_equal( (b + RBA::DPoint::new( 1, -5 )).to_s, "(1,-5;17,22)" ) diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index ac96c68d3..d65150618 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -190,6 +190,78 @@ def norm_hash(hash) end +class DBPCellAPI_TestClass < TestBase + + def test_1 + + # PCellParameterDeclaration + + decl = RBA::PCellParameterDeclaration::new("name", RBA::PCellParameterDeclaration::TypeString, "description") + + assert_equal(decl.name, "name") + assert_equal(decl.description, "description") + assert_equal(decl.default.inspect, "nil") + assert_equal(decl.unit, "") + assert_equal(decl.type, RBA::PCellParameterDeclaration::TypeString) + + decl = RBA::PCellParameterDeclaration::new("name", RBA::PCellParameterDeclaration::TypeString, "description", "17") + + assert_equal(decl.name, "name") + assert_equal(decl.description, "description") + assert_equal(decl.type, RBA::PCellParameterDeclaration::TypeString) + assert_equal(decl.default.to_s, "17") + assert_equal(decl.unit, "") + + decl = RBA::PCellParameterDeclaration::new("name", RBA::PCellParameterDeclaration::TypeString, "description", "17", "unit") + + assert_equal(decl.name, "name") + assert_equal(decl.description, "description") + assert_equal(decl.type, RBA::PCellParameterDeclaration::TypeString) + assert_equal(decl.default.to_s, "17") + assert_equal(decl.unit, "unit") + + decl.name = "n" + assert_equal(decl.name, "n") + decl.description = "d" + assert_equal(decl.description, "d") + decl.unit = "u" + assert_equal(decl.unit, "u") + decl.type = RBA::PCellParameterDeclaration::TypeBoolean + assert_equal(decl.type, RBA::PCellParameterDeclaration::TypeBoolean) + decl.default = true + assert_equal(decl.default.to_s, "true") + + decl.type = RBA::PCellParameterDeclaration::TypeInt + assert_equal(decl.min_value.inspect, "nil") + assert_equal(decl.max_value.inspect, "nil") + decl.min_value = "-1" + assert_equal(decl.min_value.to_s, "-1") + decl.max_value = "42" + assert_equal(decl.max_value.to_s, "42") + decl.min_value = nil + decl.max_value = nil + assert_equal(decl.min_value.inspect, "nil") + assert_equal(decl.max_value.inspect, "nil") + + assert_equal(decl.hidden?, false) + decl.hidden = true + assert_equal(decl.hidden?, true) + + assert_equal(decl.readonly?, false) + decl.readonly = true + assert_equal(decl.readonly?, true) + + decl.add_choice("first", 42) + assert_equal(decl.choice_values, [42]) + assert_equal(decl.choice_descriptions, ["first"]) + decl.clear_choices + assert_equal(decl.choice_values, []) + assert_equal(decl.choice_descriptions, []) + + end + +end + class DBPCell_TestClass < TestBase def test_1 diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index bdd290f01..ef598737a 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -821,6 +821,54 @@ class DBPolygon_TestClass < TestBase end + def test_breakPolygon + + pts = [] + pts << RBA::Point::new(0, 0) + pts << RBA::Point::new(0, 1000) + pts << RBA::Point::new(100, 1000) + pts << RBA::Point::new(100, 100) + pts << RBA::Point::new(1000, 100) + pts << RBA::Point::new(1000, 0) + + split = RBA::Polygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::SimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + pts = [] + pts << RBA::DPoint::new(0, 0) + pts << RBA::DPoint::new(0, 1000) + pts << RBA::DPoint::new(100, 1000) + pts << RBA::DPoint::new(100, 100) + pts << RBA::DPoint::new(1000, 100) + pts << RBA::DPoint::new(1000, 0) + + split = RBA::DPolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::DSimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + end + def test_voidMethodsReturnSelf hull = [ RBA::Point::new(0, 0), RBA::Point::new(6000, 0), @@ -834,6 +882,18 @@ class DBPolygon_TestClass < TestBase end + def test_argumentShortcuts + + # implicit conversion to a Point array: + poly = RBA::Polygon.new([ [0,0], [0,1000], [1000,1000] ]) + assert_equal(poly.to_s, "(0,0;0,1000;1000,1000)") + + # issue 1651 - no binding to Box constructor + poly = RBA::Polygon.new([ [0,0], [0,1000], [1000,1000], [1000,0] ]) + assert_equal(poly.to_s, "(0,0;0,1000;1000,1000;1000,0)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index 29724541c..f4d2d9b9b 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -322,6 +322,38 @@ END end + def test_3 + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(RBA::Box.new(0, 0, 3000, 2000)) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Vector.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Vector.new(1100, 0)))) + + ii = RBA::RecursiveShapeIterator.new(l, c0, 0) + assert_equal(ii.for_merged_input, false) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.for_merged_input = true + assert_equal(ii.for_merged_input, true) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c3](-1200,0;-100,1000)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 0d81b5efd..8c59d587c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1066,6 +1066,47 @@ class DBRegion_TestClass < TestBase end + # regions from Shapes + def test_regions_from_shapes + + shapes = RBA::Shapes::new; + + shapes.insert(RBA::Box::new(0, 0, 100, 200)) + shapes.insert(RBA::Box::new(50, 50, 150, 250)) + + assert_equal(RBA::Region::new(shapes).area, 32500) + region = RBA::Region::new(shapes) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + # for cross-checking: same for RecursiveShapeIterator + + layout = RBA::Layout::new + l1 = layout.insert_layer(RBA::LayerInfo::new(1, 0)) + top = layout.create_cell("TOP") + + top.shapes(l1).insert (RBA::Box::new(0, 0, 100, 200)) + top.shapes(l1).insert (RBA::Box::new(50, 50, 150, 250)) + + si = RBA::RecursiveShapeIterator::new(layout, top, l1) + + assert_equal(RBA::Region::new(si).area, 32500) + region = RBA::Region::new(si) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(si, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(si, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + end + # deep region tests def test_deep1 diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 3417c8688..bca7672cf 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -178,6 +178,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].is_polygon?, false ) assert_equal( arr[0].is_box?, true ) assert_equal( arr[0].box.to_s, "(10,-10;50,40)" ) + assert_equal( arr[0].rectangle.to_s, "(10,-10;50,40)" ) assert_equal( arr[0].bbox.to_s, "(10,-10;50,40)" ) # edges @@ -198,6 +199,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].edge.to_s, "(-1,2;5,2)" ) assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].rectangle.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) assert_equal( arr[0].edge == a, true ) @@ -533,6 +535,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.inspect, "nil" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.to_s, "(0.01,-0.01;0.05,0.04)" ) + assert_equal( arr[0].drectangle.to_s, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].is_polygon?, false ) @@ -557,6 +560,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.to_s, "(-0.001,0.002;0.005,0.002)" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].drectangle.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].dbbox.to_s, "(-0.001,0.002;0.005,0.002)" ) diff --git a/testdata/ruby/dbTechnologies.rb b/testdata/ruby/dbTechnologies.rb index d1dc9cf93..3305eae6a 100644 --- a/testdata/ruby/dbTechnologies.rb +++ b/testdata/ruby/dbTechnologies.rb @@ -105,10 +105,29 @@ END tech.default_grids = [] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "") + assert_equal("%.12g" % tech.default_grid, "0") tech.default_grids = [0.001, 0.01, 0.2] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.001,0.01,0.2") tech.default_grids = [1] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "1") + assert_equal("%.12g" % tech.default_grid, "0") + + # setting the default grid + tech.set_default_grids([0.01,0.02,0.05], 0.02) + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.01,0.02,0.05") + assert_equal("%.12g" % tech.default_grid, "0.02") + + # default grid must be a member of the default grid list + tech.set_default_grids([0.01,0.02,0.05], 0.2) + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.01,0.02,0.05") + assert_equal("%.12g" % tech.default_grid, "0") + + # "default_grids=" resets the default grid + tech.set_default_grids([0.01,0.02,0.05], 0.02) + assert_equal("%.12g" % tech.default_grid, "0.02") + tech.default_grids = [1] + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "1") + assert_equal("%.12g" % tech.default_grid, "0") tech.default_base_path = "/default/path" assert_equal(tech.default_base_path, "/default/path") diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index e6e1ace7d..abc40519f 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -935,6 +935,78 @@ class RDB_TestClass < TestBase end + def test_13 + + # manipulations + rdb = RBA::ReportDatabase::new("") + + _cell = rdb.create_cell("CELL") + _cat = rdb.create_category("cat") + _subcat = rdb.create_category(_cat, "subcat") + _subcat.description = "subcat_d" + _item1 = rdb.create_item(_cell.rdb_id, _subcat.rdb_id) + _item1.add_value(17.5) + _item1.add_value("string") + _item2 = rdb.create_item(_cell.rdb_id, _subcat.rdb_id) + _item2.add_value("b") + _subsubcat = rdb.create_category(_subcat, "subsubcat") + _cat2 = rdb.create_category("cat2") + + cell = rdb.cell_by_id(_cell.rdb_id) + assert_equal(cell._is_const_object?, false) + assert_equal(rdb.each_cell.to_a[0]._is_const_object?, false) + + cell = rdb.cell_by_qname("CELL") + assert_equal(cell._is_const_object?, false) + + cat = rdb.category_by_id(_cat.rdb_id) + assert_equal(cat._is_const_object?, false) + + cat = rdb.category_by_path("cat") + assert_equal(cat._is_const_object?, false) + subcat = rdb.category_by_path("cat.subcat") + + assert_equal(rdb.each_category.to_a[0]._is_const_object?, false) + assert_equal(rdb.each_category.collect { |c| c.name }.join(","), "cat,cat2") + assert_equal(subcat._is_const_object?, false) + assert_equal(subcat.database._is_const_object?, false) + assert_equal(subcat.name, "subcat") + assert_equal(subcat.parent.name, "cat") + + assert_equal(subcat.description, "subcat_d") + subcat.description = "changed" + assert_equal(subcat.description, "changed") + + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), "float: 17.5/text: string;text: b") + + item1 = rdb.each_item_per_category(subcat.rdb_id).to_a[0] + assert_equal(item1._is_const_object?, false) + item1.clear_values + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), ";text: b") + item1.add_value("x") + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), "text: x;text: b") + item1.add_tag(17) + assert_equal(item1.has_tag?(17), true) + assert_equal(item1.has_tag?(16), false) + + item1 = rdb.each_item.to_a[0] + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = rdb.each_item_per_cell(cell.rdb_id).to_a[0] + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = rdb.each_item_per_cell_and_category(cell.rdb_id, subcat.rdb_id).to_a[0] + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = cell.each_item.to_a[0] + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + end + end load("test_epilogue.rb") diff --git a/version.sh b/version.sh index 9163a6f1d..43b2ae5b6 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.28.17" +KLAYOUT_VERSION="0.29.0" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.28.17" +KLAYOUT_PYPI_VERSION="0.29.0" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")