diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d9c02da0..21b403ca1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,7 @@ jobs: dotnet: true haskell: true large-packages: true - - uses: hmarr/debug-action@v2 + - uses: hmarr/debug-action@v3 - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 @@ -97,7 +97,7 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.8.11 + - uses: pypa/gh-action-pypi-publish@v1.8.12 continue-on-error: true # might fail if we don't bump the version with: user: __token__ @@ -114,7 +114,7 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.8.11 + - uses: pypa/gh-action-pypi-publish@v1.8.12 with: user: __token__ password: ${{ secrets.pypi_password }} 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/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/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index cf9c3621b..d0f098e27 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -147,6 +147,24 @@ void AsIfFlatEdgePairs::invalidate_bbox () m_bbox_valid = false; } +EdgePairsDelegate * +AsIfFlatEdgePairs::processed (const EdgePairProcessorBase &filter) const +{ + std::unique_ptr edge_pairs (new FlatEdgePairs ()); + + std::vector res_edge_pairs; + + for (EdgePairsIterator e = begin (); ! e.at_end (); ++e) { + res_edge_pairs.clear (); + filter.process (*e, res_edge_pairs); + for (std::vector::const_iterator er = res_edge_pairs.begin (); er != res_edge_pairs.end (); ++er) { + edge_pairs->insert (*er); + } + } + + return edge_pairs.release (); +} + RegionDelegate * AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const { diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h index 29f18f6f1..2a69bb035 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.h +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -53,8 +53,14 @@ public: virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; - virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const; - virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const; + virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) + { + return processed (proc); + } + + virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &proc) const; + virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &proc) const; + virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &proc) const; virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) { 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..9ee803c2a 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 (); 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/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index 0e38590a6..8c73f1c1a 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -164,6 +164,24 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const return new_texts.release (); } +TextsDelegate * +AsIfFlatTexts::processed (const TextProcessorBase &filter) const +{ + std::unique_ptr texts (new FlatTexts ()); + + std::vector res_texts; + + for (TextsIterator e = begin (); ! e.at_end (); ++e) { + res_texts.clear (); + filter.process (*e, res_texts); + for (std::vector::const_iterator er = res_texts.begin (); er != res_texts.end (); ++er) { + texts->insert (*er); + } + } + + return texts.release (); +} + RegionDelegate * AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const { diff --git a/src/db/db/dbAsIfFlatTexts.h b/src/db/db/dbAsIfFlatTexts.h index ffff931e1..07c023741 100644 --- a/src/db/db/dbAsIfFlatTexts.h +++ b/src/db/db/dbAsIfFlatTexts.h @@ -55,6 +55,12 @@ public: virtual TextsDelegate *filtered (const TextFilterBase &) const; + virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) + { + return processed (proc); + } + + virtual TextsDelegate *processed (const TextProcessorBase &proc) const; virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const; virtual TextsDelegate *add_in_place (const Texts &other) 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/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index d19a08fa2..090644265 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -451,6 +451,18 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const return res.release (); } +EdgePairsDelegate *DeepEdgePairs::process_in_place (const EdgePairProcessorBase &filter) +{ + // TODO: implement to be really in-place + return processed (filter); +} + +EdgePairsDelegate * +DeepEdgePairs::processed (const EdgePairProcessorBase &filter) const +{ + return shape_collection_processed_impl (deep_layer (), filter); +} + RegionDelegate * DeepEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const { diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index ccc73a762..8ec686da0 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -78,6 +78,8 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &); + virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const; virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const; virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const; diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index a9608af23..c94839621 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); } } @@ -997,7 +1054,7 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeAnd)); + return new DeepEdges (and_or_not_with (other_deep, EdgeAnd).first); } } @@ -1016,7 +1073,7 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeNot)); + return new DeepEdges (and_or_not_with (other_deep, EdgeNot).first); } } @@ -1092,7 +1149,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 ()) { @@ -1109,7 +1166,7 @@ DeepEdges::andnot_with (const Edges &other) const } 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)); } @@ -1135,8 +1192,8 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const // Implement XOR as (A-B)+(B-A) - only this implementation // is compatible with the local processor scheme - DeepLayer n1 (and_or_not_with (other_deep, 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); @@ -1354,362 +1411,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 +1422,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 +1458,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 +1477,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) { @@ -1781,20 +1507,24 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode 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 ()); + // 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 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) { @@ -1813,13 +1543,14 @@ 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 + 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)); } 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..50173c49e 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1335,7 +1335,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 +1343,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 +1388,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 ()))); + } + } + } } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 1bef5efb1..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; @@ -182,8 +182,6 @@ private: std::pair and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const; DeepRegion *apply_filter (const PolygonFilterBase &filter) const; - template OutputContainer *processed_impl (const polygon_processor &filter) const; - template void configure_proc (Proc &proc) const { diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index f328452ea..ebf6daccd 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -474,6 +474,18 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const return res.release (); } +TextsDelegate *DeepTexts::process_in_place (const TextProcessorBase &filter) +{ + // TODO: implement to be really in-place + return processed (filter); +} + +TextsDelegate * +DeepTexts::processed (const TextProcessorBase &filter) const +{ + return shape_collection_processed_impl (deep_layer (), filter); +} + RegionDelegate * DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const { diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 1bd5bb66c..44c367e07 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -80,6 +80,8 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &filter); virtual TextsDelegate *filtered (const TextFilterBase &) const; + virtual TextsDelegate *process_in_place (const TextProcessorBase &); + virtual TextsDelegate *processed (const TextProcessorBase &) const; virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const; virtual TextsDelegate *add_in_place (const Texts &other); 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 0451006ef..fcfd405f9 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -169,14 +169,19 @@ EdgePairs::properties_repository () return *r; } -void EdgePairs::processed (Region &output, const EdgePairToPolygonProcessorBase &filter) const +EdgePairs EdgePairs::processed (const EdgePairProcessorBase &proc) const { - output = Region (mp_delegate->processed_to_polygons (filter)); + return EdgePairs (mp_delegate->processed (proc)); } -void EdgePairs::processed (Edges &output, const EdgePairToEdgeProcessorBase &filter) const +void EdgePairs::processed (Region &output, const EdgePairToPolygonProcessorBase &proc) const { - output = Edges (mp_delegate->processed_to_edges (filter)); + output = Region (mp_delegate->processed_to_polygons (proc)); +} + +void EdgePairs::processed (Edges &output, const EdgePairToEdgeProcessorBase &proc) const +{ + output = Edges (mp_delegate->processed_to_edges (proc)); } void EdgePairs::polygons (Region &output, db::Coord e) const diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 38e32d499..9d1b1aee2 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -328,13 +328,31 @@ public: return EdgePairs (mp_delegate->filtered (filter)); } + /** + * @brief Processes the edge pairs in-place + * + * This method will run the processor over all edge pairs and replace the collection by the results. + */ + EdgePairs &process (const EdgePairProcessorBase &proc) + { + set_delegate (mp_delegate->process_in_place (proc)); + return *this; + } + + /** + * @brief Processes the edge pairs + * + * This method will run the processor over all edge pairs return a new edge pair collection with the results. + */ + EdgePairs processed (const EdgePairProcessorBase &proc) const; + /** * @brief Processes the edge pairs into polygons * * This method will run the processor over all edge pairs and return a region * with the outputs of the processor. */ - void processed (Region &output, const EdgePairToPolygonProcessorBase &filter) const; + void processed (Region &output, const EdgePairToPolygonProcessorBase &proc) const; /** * @brief Processes the edge pairs into edges @@ -342,7 +360,7 @@ public: * This method will run the processor over all edge pairs and return a edge collection * with the outputs of the processor. */ - void processed (Edges &output, const EdgePairToEdgeProcessorBase &filter) const; + void processed (Edges &output, const EdgePairToEdgeProcessorBase &proc) const; /** * @brief Transforms the edge pair set diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index 32fa03670..189ff293e 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -39,6 +39,7 @@ class RegionDelegate; class EdgesDelegate; class Layout; +typedef shape_collection_processor EdgePairProcessorBase; typedef shape_collection_processor EdgePairToPolygonProcessorBase; typedef shape_collection_processor EdgePairToEdgeProcessorBase; @@ -194,8 +195,10 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0; virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0; - virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const = 0; - virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const = 0; + virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) = 0; + virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &proc) const = 0; + virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &proc) const = 0; + virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &proc) const = 0; virtual RegionDelegate *polygons (db::Coord e) const = 0; virtual EdgesDelegate *edges () const = 0; diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index e5317f20d..28decbbb6 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -991,9 +991,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Region &other) + Edges &select_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1002,9 +1002,9 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Region &other) const + Edges selected_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** @@ -1013,9 +1013,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Region &other) + Edges &select_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1024,17 +1024,17 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Region &other) const + Edges selected_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region together with the ones that do not */ - std::pair selected_interacting_differential (const Region &other) const + std::pair selected_interacting_differential (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1280,9 +1280,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Edges &other) + Edges &select_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1291,17 +1291,17 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Edges &other) const + Edges selected_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set together with the ones that do not */ - std::pair selected_interacting_differential (const Edges &other) const + std::pair selected_interacting_differential (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1311,9 +1311,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Edges &other) + Edges &select_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1322,9 +1322,9 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Edges &other) const + Edges selected_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 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/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index 6680ec6a3..16be5fa99 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -56,6 +56,8 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; } virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); } + virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &) { return this; } + virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const { return new EmptyEdgePairs (); } virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const; virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const; 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/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index c224dadaa..9bd206de9 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -57,6 +57,8 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &) { return this; } virtual TextsDelegate *filtered (const TextFilterBase &) const { return new EmptyTexts (); } + virtual TextsDelegate *process_in_place (const TextProcessorBase &) { return this; } + virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); } virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &) const; virtual RegionDelegate *polygons (db::Coord e) const; 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/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/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 62d2dc287..44b817a18 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -473,69 +473,114 @@ void LayoutToNetlist::check_must_connect (const db::Circuit &c, const db::Net &a return; } - if (c.begin_refs () != c.end_refs ()) { + std::vector path; + check_must_connect_impl (c, a, b, c, a, b, path); +} + +static std::string path_msg (const std::vector &path, const db::Circuit &c_org) +{ + if (path.empty ()) { + return std::string (); + } + + std::string msg (".\n" + tl::to_string (tr ("Instance path: "))); + + for (auto p = path.rbegin (); p != path.rend (); ++p) { + if (p != path.rbegin ()) { + msg += "/"; + } + msg += (*p)->circuit ()->name () + ":" + (*p)->expanded_name () + "[" + (*p)->trans ().to_string () + "]"; + } + + msg += "/"; + msg += c_org.name (); + + return msg; +} + +void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::Net &a, const db::Net &b, const db::Circuit &c_org, const db::Net &a_org, const db::Net &b_org, std::vector &path) +{ + if (c.begin_refs () != c.end_refs () && path.empty ()) { + if (a.begin_pins () == a.end_pins ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a_org.expanded_name ())); error.set_cell_name (c.name ()); error.set_category_name ("must-connect"); log_entry (error); } if (b.begin_pins () == b.end_pins ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a_org.expanded_name ())); error.set_cell_name (c.name ()); error.set_category_name ("must-connect"); log_entry (error); } - } else { - if (a.expanded_name () == b.expanded_name ()) { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a.expanded_name ())); - warn.set_cell_name (c.name ()); - warn.set_category_name ("must-connect"); - log_entry (warn); + + } else if (c.begin_refs () == c.end_refs () || a.begin_pins () == a.end_pins () || b.begin_pins () == b.end_pins ()) { + + if (a_org.expanded_name () == b_org.expanded_name ()) { + if (path.empty ()) { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name ()) + path_msg (path, c_org)); + warn.set_cell_name (c.name ()); + warn.set_category_name ("must-connect"); + log_entry (warn); + } else { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), c_org.name ()) + path_msg (path, c_org)); + warn.set_cell_name (c.name ()); + warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); + warn.set_category_name ("must-connect"); + log_entry (warn); + } } else { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a.expanded_name (), b.expanded_name ())); - warn.set_cell_name (c.name ()); - warn.set_category_name ("must-connect"); - log_entry (warn); + if (path.empty ()) { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name ()) + path_msg (path, c_org)); + warn.set_cell_name (c.name ()); + warn.set_category_name ("must-connect"); + log_entry (warn); + } else { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name (), c_org.name ()) + path_msg (path, c_org)); + warn.set_cell_name (c.name ()); + warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); + warn.set_category_name ("must-connect"); + log_entry (warn); + } } + } if (a.begin_pins () != a.end_pins () && b.begin_pins () != b.end_pins ()) { + for (auto ref = c.begin_refs (); ref != c.end_refs (); ++ref) { + const db::SubCircuit &sc = *ref; + // TODO: consider the case of multiple pins on a net (rare) const db::Net *net_a = sc.net_for_pin (a.begin_pins ()->pin_id ()); const db::Net *net_b = sc.net_for_pin (b.begin_pins ()->pin_id ()); + if (net_a == 0) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a.expanded_name (), c.name (), subcircuit_to_string (sc))); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); log_entry (error); } + if (net_b == 0) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b.expanded_name (), c.name (), subcircuit_to_string (sc))); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); log_entry (error); } + if (net_a && net_b && net_a != net_b) { - if (a.expanded_name () == b.expanded_name ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s are not connected%s")), a.expanded_name (), c.name (), subcircuit_to_string (sc))); - error.set_cell_name (sc.circuit ()->name ()); - error.set_geometry (subcircuit_geometry (sc, internal_layout ())); - error.set_category_name ("must-connect"); - log_entry (error); - } else { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s are not connected%s")), a.expanded_name (), b.expanded_name (), c.name (), subcircuit_to_string (sc))); - error.set_cell_name (sc.circuit ()->name ()); - error.set_geometry (subcircuit_geometry (sc, internal_layout ())); - error.set_category_name ("must-connect"); - log_entry (error); - } + path.push_back (&sc); + check_must_connect_impl (*sc.circuit (), *net_a, *net_b, c_org, a_org, b_org, path); + path.pop_back (); } + } + } } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 493450194..2856de3e9 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1041,6 +1041,7 @@ private: void join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p); void join_nets_from_pattern (db::Circuit &c, const std::set &p); void check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b); + void check_must_connect_impl (const db::Circuit &c, const db::Net &a, const db::Net &b, const db::Circuit &c_org, const db::Net &a_org, const db::Net &b_org, std::vector &path); // implementation of NetlistManipulationCallbacks virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); 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/dbNetlist.cc b/src/db/db/dbNetlist.cc index 9c7c724ba..f0f8d177c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -126,20 +126,6 @@ void Netlist::set_case_sensitive (bool f) m_case_sensitive = f; } -int Netlist::name_compare (bool case_sensitive, const std::string &n1, const std::string &n2) -{ - // TODO: unicode support? - if (case_sensitive) { - return strcmp (n1.c_str (), n2.c_str ()); - } else { -#if defined(_WIN32) - return _stricmp (n1.c_str (), n2.c_str ()); -#else - return strcasecmp (n1.c_str (), n2.c_str ()); -#endif - } -} - std::string Netlist::normalize_name (bool case_sensitive, const std::string &n) { if (case_sensitive) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 397417d04..9e5221796 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -515,11 +515,6 @@ public: */ void combine_devices (); - /** - * @brief Compares two names with the given case sensitivity - */ - static int name_compare (bool case_sensitive, const std::string &n1, const std::string &n2); - /** * @brief Normalizes a name with the given case sensitivity */ diff --git a/src/db/db/dbNetlistCompareCore.cc b/src/db/db/dbNetlistCompareCore.cc index 9e8abaef5..49558ad4d 100644 --- a/src/db/db/dbNetlistCompareCore.cc +++ b/src/db/db/dbNetlistCompareCore.cc @@ -1355,7 +1355,7 @@ static size_t distance3 (const NetGraphNode &a, const NetGraphNode &b1, const Ne static void analyze_nodes_for_close_matches (const std::multimap &nodes_by_edges1, const std::multimap &nodes_by_edges2, bool layout2ref, db::NetlistCompareLogger *logger, const db::NetGraph &g2) { - size_t max_search = 100; + size_t max_search = 100000; double max_fuzz_factor = 0.25; size_t max_fuzz_count = 3; size_t max_edges_split = 3; // by how many edges joining will reduce the edge count at max @@ -1368,7 +1368,9 @@ analyze_nodes_for_close_matches (const std::multimap 0; ++i) { if (i->first < min_edges) { continue; @@ -1376,7 +1378,7 @@ analyze_nodes_for_close_matches (const std::multimap seen; - for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end (); ++j) { + for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end () && tries > 0; ++j) { seen.insert (j->second); @@ -1407,7 +1409,6 @@ analyze_nodes_for_close_matches (const std::multimapfirst + k->first < i->first + max_fuzz_count + max_edges_split && tries > 0; ++k) { if (seen.find (k->second) != seen.end ()) { @@ -1436,6 +1437,8 @@ analyze_nodes_for_close_matches (const std::multimap= 21, "Analyzing failed matches"); + // Determine the range of nodes with same identity std::vector no_edges; diff --git a/src/db/db/dbNetlistCompareUtils.cc b/src/db/db/dbNetlistCompareUtils.cc index b4b50b9ca..97951ca25 100644 --- a/src/db/db/dbNetlistCompareUtils.cc +++ b/src/db/db/dbNetlistCompareUtils.cc @@ -125,9 +125,40 @@ const std::string &extended_net_name (const db::Net *n) } } +static int net_name_compare (bool case_sensitive, const std::string &n1, const std::string &n2) +{ + const char *n1p = n1.c_str (); + const char *n2p = n2.c_str (); + + while (*n1p && *n2p) { + + uint32_t c1 = tl::utf32_from_utf8 (n1p); + uint32_t c2 = tl::utf32_from_utf8 (n2p); + + if (! case_sensitive) { + c1 = tl::utf32_downcase (c1); + c2 = tl::utf32_downcase (c2); + } + + if (c1 != c2) { + return c1 < c2 ? -1 : 1; + } + + } + + // colon terminates net name, such that NET:I is identical to NET. + if (*n2p && *n2p != ':') { + return -1; + } else if (*n1p && *n1p != ':') { + return 1; + } else { + return 0; + } +} + int name_compare (const db::Net *a, const db::Net *b) { - return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), extended_net_name (a), extended_net_name (b)); + return net_name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), extended_net_name (a), extended_net_name (b)); } bool net_names_are_different (const db::Net *a, const db::Net *b) diff --git a/src/db/db/dbNetlistCompareUtils.h b/src/db/db/dbNetlistCompareUtils.h index bb4bd2948..55614577a 100644 --- a/src/db/db/dbNetlistCompareUtils.h +++ b/src/db/db/dbNetlistCompareUtils.h @@ -92,30 +92,30 @@ std::string nets2string (const std::pair &np); /** * @brief Derives the common case sensitivity for two netlists */ -bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b); +DB_PUBLIC bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b); /** * @brief Gets the extended net name * This name is used for comparing the net names and also employs the pin name if one is given */ -const std::string &extended_net_name (const db::Net *n); +DB_PUBLIC const std::string &extended_net_name (const db::Net *n); /** * @brief Compare two nets by name */ -int name_compare (const db::Net *a, const db::Net *b); +DB_PUBLIC int name_compare (const db::Net *a, const db::Net *b); /** * @brief Returns a value indicating whether two nets are different by name * Two unnamed nets are never different. */ -bool net_names_are_different (const db::Net *a, const db::Net *b); +DB_PUBLIC bool net_names_are_different (const db::Net *a, const db::Net *b); /** * @brief Returns a value indicating whether two nets are equal by name * Two unnamed nets are never equal. */ -bool net_names_are_equal (const db::Net *a, const db::Net *b); +DB_PUBLIC bool net_names_are_equal (const db::Net *a, const db::Net *b); // -------------------------------------------------------------------------------------------------------------------- // DeviceCompare definition and implementation 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/dbRegion.cc b/src/db/db/dbRegion.cc index 731047698..488fbc460 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -556,16 +556,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..39880b2ca 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -770,7 +770,7 @@ public: */ Edges edges () const { - return Edges (mp_delegate->edges (0)); + return Edges (mp_delegate->edges (0, 0)); } /** @@ -783,7 +783,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 ab8597646..cfc627cf7 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -106,62 +106,6 @@ public: virtual bool wants_variants () const = 0; }; -/** - * @brief A template base class for polygon processors - * - * A polygon processor can turn a polygon into something else. - */ -template -class DB_PUBLIC polygon_processor -{ -public: - /** - * @brief Constructor - */ - polygon_processor () { } - - /** - * @brief Destructor - */ - virtual ~polygon_processor () { } - - /** - * @brief Performs the actual processing - * This method will take the input polygon from "polygon" and puts the results into "res". - * "res" can be empty - in this case, the polygon will be skipped. - */ - virtual void process (const db::Polygon &polygon, std::vector &res) const = 0; - - /** - * @brief Returns the transformation reducer for building cell variants - * This method may return 0. In this case, not cell variants are built. - */ - virtual const TransformationReducer *vars () const = 0; - - /** - * @brief Returns true, if the result of this operation can be regarded "merged" always. - */ - virtual bool result_is_merged () const = 0; - - /** - * @brief Returns true, if the result of this operation must not be merged. - * This feature can be used, if the result represents "degenerated" objects such - * as point-like edges. These must not be merged. Otherwise they disappear. - */ - virtual bool result_must_not_be_merged () const = 0; - - /** - * @brief Returns true, if the processor wants raw (not merged) input - */ - virtual bool requires_raw_input () const = 0; - - /** - * @brief Returns true, if the processor wants to build variants - * If not true, the processor accepts shape propagation as variant resolution. - */ - virtual bool wants_variants () const = 0; -}; - typedef shape_collection_processor PolygonProcessorBase; typedef shape_collection_processor PolygonToEdgeProcessorBase; typedef shape_collection_processor PolygonToEdgePairProcessorBase; @@ -267,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/dbShapeCollectionUtils.h b/src/db/db/dbShapeCollectionUtils.h index 0d2a448f9..c300bb052 100644 --- a/src/db/db/dbShapeCollectionUtils.h +++ b/src/db/db/dbShapeCollectionUtils.h @@ -48,6 +48,9 @@ class DB_PUBLIC_TEMPLATE shape_collection_processor : public tl::Object { public: + typedef Shape shape_type; + typedef Result result_type; + /** * @brief Constructor */ diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 31d6ae0a8..0b90c8897 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -199,6 +199,11 @@ MutableTexts *Texts::mutable_texts () return texts; } +Texts Texts::processed (const TextProcessorBase &proc) const +{ + return Texts (mp_delegate->processed (proc)); +} + void Texts::processed (Region &output, const TextToPolygonProcessorBase &filter) const { output = Region (mp_delegate->processed_to_polygons (filter)); diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 3ed3cf1f8..82d017187 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -316,7 +316,25 @@ public: } /** - * @brief Processes the edges into polygons + * @brief Processes the edge pairs in-place + * + * This method will run the processor over all texts and replace the collection by the results. + */ + Texts &process (const TextProcessorBase &proc) + { + set_delegate (mp_delegate->process_in_place (proc)); + return *this; + } + + /** + * @brief Processes the texts + * + * This method will run the processor over all texts and return a new text collection with the results. + */ + Texts processed (const TextProcessorBase &proc) const; + + /** + * @brief Processes the texts into polygons * * This method will run the processor over all edges and return a region * with the outputs of the processor. diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index 6a818cec5..05f26188e 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -40,6 +40,7 @@ class RegionDelegate; class EdgesDelegate; class Layout; +typedef shape_collection_processor TextProcessorBase; typedef shape_collection_processor TextToPolygonProcessorBase; typedef db::generic_shape_iterator_delegate_base TextsIteratorDelegate; @@ -94,7 +95,9 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &filter) = 0; virtual TextsDelegate *filtered (const TextFilterBase &filter) const = 0; - virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const = 0; + virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) = 0; + virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0; + virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0; virtual RegionDelegate *polygons (db::Coord e) const = 0; virtual EdgesDelegate *edges () const = 0; 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/gsiDeclDbContainerHelpers.h b/src/db/db/gsiDeclDbContainerHelpers.h index e16e91be8..7c78def79 100644 --- a/src/db/db/gsiDeclDbContainerHelpers.h +++ b/src/db/db/gsiDeclDbContainerHelpers.h @@ -25,6 +25,7 @@ #define HDR_gsiDeclDbContainerHelpers #include "dbPropertiesRepository.h" +#include "dbCellVariants.h" #include "tlVariant.h" #include "gsiDecl.h" @@ -100,6 +101,361 @@ make_property_methods () ); } +// --------------------------------------------------------------------------------- +// Generic shape filter declarations + +template +class shape_filter_impl + : public FilterBase +{ +public: + shape_filter_impl () + { + mp_vars = &m_mag_and_orient; + m_wants_variants = true; + m_requires_raw_input = false; + } + + // overrides virtual method + virtual const db::TransformationReducer *vars () const + { + return mp_vars; + } + + // maybe overrides virtual method + virtual bool requires_raw_input () const + { + return m_requires_raw_input; + } + + void set_requires_raw_input (bool f) + { + m_requires_raw_input = f; + } + + // overrides virtual method + virtual bool wants_variants () const + { + return m_wants_variants; + } + + void set_wants_variants (bool f) + { + m_wants_variants = f; + } + + void is_isotropic () + { + mp_vars = &m_mag; + } + + void is_scale_invariant () + { + mp_vars = &m_orientation; + } + + void is_isotropic_and_scale_invariant () + { + mp_vars = 0; + } + + static gsi::Methods method_decls (bool with_requires_raw_input) + { + gsi::Methods decls; + + if (with_requires_raw_input) { + decls = + method ("requires_raw_input?", &shape_filter_impl::requires_raw_input, + "@brief Gets a value indicating whether the filter needs raw (unmerged) input\n" + "See \\requires_raw_input= for details.\n" + ) + + method ("requires_raw_input=", &shape_filter_impl::set_requires_raw_input, gsi::arg ("flag"), + "@brief Sets a value indicating whether the filter needs raw (unmerged) input\n" + "This flag must be set before using this filter. It tells the filter implementation whether the " + "filter wants to have raw input (unmerged). The default value is 'false', meaning that\n" + "the filter will receive merged polygons ('merged semantics').\n" + "\n" + "Setting this value to false potentially saves some CPU time needed for merging the polygons.\n" + "Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, " + "empty or degenerated polygons are preserved." + ); + } + + decls += + method ("wants_variants?", &shape_filter_impl::wants_variants, + "@brief Gets a value indicating whether the filter prefers cell variants\n" + "See \\wants_variants= for details.\n" + ) + + method ("wants_variants=", &shape_filter_impl::set_wants_variants, gsi::arg ("flag"), + "@brief Sets a value indicating whether the filter prefers cell variants\n" + "This flag must be set before using this filter for hierarchical applications (deep mode). " + "It tells the filter implementation whether cell variants should be created (true, the default) " + "or shape propagation will be applied (false).\n" + "\n" + "This decision needs to be made, if the filter indicates that it will deliver different results\n" + "for scaled or rotated versions of the shape (see \\is_isotropic and the other hints). If a cell\n" + "is present with different qualities - as seen from the top cell - the respective instances\n" + "need to be differentiated. Cell variant formation is one way, shape propagation the other way.\n" + "Typically, cell variant formation is less expensive, but the hierarchy will be modified." + ) + + method ("is_isotropic", &shape_filter_impl::is_isotropic, + "@brief Indicates that the filter has isotropic properties\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "Examples for isotropic (polygon) filters are area or perimeter filters. The area or perimeter of a polygon " + "depends on the scale, but not on the orientation of the polygon." + ) + + method ("is_scale_invariant", &shape_filter_impl::is_scale_invariant, + "@brief Indicates that the filter is scale invariant\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the scale of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "An example for a scale invariant (polygon) filter is the bounding box aspect ratio (height/width) filter. " + "The definition of heigh and width depends on the orientation, but the ratio is independent on scale." + ) + + method ("is_isotropic_and_scale_invariant", &shape_filter_impl::is_isotropic_and_scale_invariant, + "@brief Indicates that the filter is isotropic and scale invariant\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the scale and orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "An example for such a (polygon) filter is the square selector. Whether a polygon is a square or not does not depend on " + "the polygon's orientation nor scale." + ); + + return decls; + } + +private: + const db::TransformationReducer *mp_vars; + db::OrientationReducer m_orientation; + db::MagnificationReducer m_mag; + db::MagnificationAndOrientationReducer m_mag_and_orient; + bool m_requires_raw_input; + bool m_wants_variants; +}; + +// --------------------------------------------------------------------------------- +// Generic shape processor declarations + +template +class shape_processor_impl + : public ProcessorBase +{ +public: + typedef typename ProcessorBase::shape_type shape_type; + typedef typename ProcessorBase::result_type result_type; + + shape_processor_impl () + { + mp_vars = &m_mag_and_orient; + m_wants_variants = true; + m_requires_raw_input = false; + m_result_is_merged = false; + m_result_must_not_be_merged = false; + } + + // overrides virtual method + virtual const db::TransformationReducer *vars () const + { + return mp_vars; + } + + // maybe overrides virtual method + virtual bool requires_raw_input () const + { + return m_requires_raw_input; + } + + void set_requires_raw_input (bool f) + { + m_requires_raw_input = f; + } + + // overrides virtual method + virtual bool wants_variants () const + { + return m_wants_variants; + } + + void set_wants_variants (bool f) + { + m_wants_variants = f; + } + + // overrides virtual method + virtual bool result_is_merged () const + { + return m_result_is_merged; + } + + void set_result_is_merged (bool f) + { + m_result_is_merged = f; + } + + // overrides virtual method + virtual bool result_must_not_be_merged () const + { + return m_result_must_not_be_merged; + } + + void set_result_must_not_be_merged (bool f) + { + m_result_must_not_be_merged = f; + } + + void is_isotropic () + { + mp_vars = &m_mag; + } + + void is_scale_invariant () + { + mp_vars = &m_orientation; + } + + void is_isotropic_and_scale_invariant () + { + mp_vars = 0; + } + + virtual void process (const shape_type &shape, std::vector &res) const + { + res = do_process (shape); + } + + std::vector issue_do_process (const shape_type &) const + { + return std::vector (); + } + + std::vector do_process (const shape_type &shape) const + { + if (f_process.can_issue ()) { + return f_process.issue, const shape_type &> (&shape_processor_impl::issue_do_process, shape); + } else { + return issue_do_process (shape); + } + } + + gsi::Callback f_process; + + static gsi::Methods method_decls (bool with_merged_options) + { + gsi::Methods decls = + callback ("process", &shape_processor_impl::issue_do_process, &shape_processor_impl::f_process, gsi::arg ("shape"), + "@brief Processes a shape\n" + "This method is the actual payload. It needs to be reimplemented in a derived class.\n" + "If needs to process the input shape and deliver a list of output shapes.\n" + "The output list may be empty to entirely discard the input shape. It may also contain more than a single shape.\n" + "In that case, the number of total shapes may grow during application of the processor.\n" + ); + + if (with_merged_options) { + decls += + method ("requires_raw_input?", &shape_processor_impl::requires_raw_input, + "@brief Gets a value indicating whether the processor needs raw (unmerged) input\n" + "See \\requires_raw_input= for details.\n" + ) + + method ("requires_raw_input=", &shape_processor_impl::set_requires_raw_input, gsi::arg ("flag"), + "@brief Sets a value indicating whether the processor needs raw (unmerged) input\n" + "This flag must be set before using this processor. It tells the processor implementation whether the " + "processor wants to have raw input (unmerged). The default value is 'false', meaning that\n" + "the processor will receive merged polygons ('merged semantics').\n" + "\n" + "Setting this value to false potentially saves some CPU time needed for merging the polygons.\n" + "Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, " + "empty or degenerated polygons are preserved." + ) + + method ("result_is_merged?", &shape_processor_impl::result_is_merged, + "@brief Gets a value indicating whether the processor delivers merged output\n" + "See \\result_is_merged= for details.\n" + ) + + method ("result_is_merged=", &shape_processor_impl::set_result_is_merged, gsi::arg ("flag"), + "@brief Sets a value indicating whether the processor delivers merged output\n" + "This flag must be set before using this processor. If the processor maintains the merged condition\n" + "by design (output is merged if input is), it is a good idea to set this predicate to 'true'.\n" + "This will avoid additional merge steps when the resulting collection is used in further operations\n" + "that need merged input\n." + ) + + method ("result_must_not_be_merged?", &shape_processor_impl::result_must_not_be_merged, + "@brief Gets a value indicating whether the processor's output must not be merged\n" + "See \\result_must_not_be_merged= for details.\n" + ) + + method ("result_must_not_be_merged=", &shape_processor_impl::set_result_must_not_be_merged, gsi::arg ("flag"), + "@brief Sets a value indicating whether the processor's output must not be merged\n" + "This flag must be set before using this processor. The processor can set this flag if it wants to\n" + "deliver shapes that must not be merged - e.g. point-like edges or strange or degenerated polygons.\n." + ); + } + + decls += + method ("wants_variants?", &shape_processor_impl::wants_variants, + "@brief Gets a value indicating whether the filter prefers cell variants\n" + "See \\wants_variants= for details.\n" + ) + + method ("wants_variants=", &shape_processor_impl::set_wants_variants, gsi::arg ("flag"), + "@brief Sets a value indicating whether the filter prefers cell variants\n" + "This flag must be set before using this filter for hierarchical applications (deep mode). " + "It tells the filter implementation whether cell variants should be created (true, the default) " + "or shape propagation will be applied (false).\n" + "\n" + "This decision needs to be made, if the filter indicates that it will deliver different results\n" + "for scaled or rotated versions of the shape (see \\is_isotropic and the other hints). If a cell\n" + "is present with different qualities - as seen from the top cell - the respective instances\n" + "need to be differentiated. Cell variant formation is one way, shape propagation the other way.\n" + "Typically, cell variant formation is less expensive, but the hierarchy will be modified." + ) + + method ("is_isotropic", &shape_processor_impl::is_isotropic, + "@brief Indicates that the filter has isotropic properties\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "Examples for isotropic (polygon) processors are size or shrink operators. Size or shrink is not dependent " + "on orientation unless size or shrink needs to be different in x and y direction." + ) + + method ("is_scale_invariant", &shape_processor_impl::is_scale_invariant, + "@brief Indicates that the filter is scale invariant\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the scale of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "An example for a scale invariant (polygon) processor is the rotation operator. Rotation is not depending on scale, " + "but on the original orientation as mirrored versions need to be rotated differently." + ) + + method ("is_isotropic_and_scale_invariant", &shape_processor_impl::is_isotropic_and_scale_invariant, + "@brief Indicates that the filter is isotropic and scale invariant\n" + "Call this method before using the filter to indicate that the selection is independent of " + "the scale and orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in " + "hierarchical mode.\n" + "\n" + "An example for such a (polygon) processor is the convex decomposition operator. The decomposition of a polygon into " + "convex parts is an operation that is not depending on scale nor orientation." + ); + + return decls; + } + +private: + const db::TransformationReducer *mp_vars; + db::OrientationReducer m_orientation; + db::MagnificationReducer m_mag; + db::MagnificationAndOrientationReducer m_mag_and_orient; + bool m_requires_raw_input; + bool m_wants_variants; + bool m_result_is_merged; + bool m_result_must_not_be_merged; + + // No copying + shape_processor_impl &operator= (const shape_processor_impl &); + shape_processor_impl (const shape_processor_impl &); +}; + } #endif diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 870991c4d..4a22f7045 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -36,7 +36,182 @@ namespace gsi { -static db::EdgePairs *new_v () +// --------------------------------------------------------------------------------- +// EdgePairFilter binding + +class EdgePairFilterImpl + : public shape_filter_impl +{ +public: + EdgePairFilterImpl () { } + + bool issue_selected (const db::EdgePair &) const + { + return false; + } + + virtual bool selected (const db::EdgePair &edge_pair) const + { + if (f_selected.can_issue ()) { + return f_selected.issue (&EdgePairFilterImpl::issue_selected, edge_pair); + } else { + return issue_selected (edge_pair); + } + } + + gsi::Callback f_selected; + +private: + // No copying + EdgePairFilterImpl &operator= (const EdgePairFilterImpl &); + EdgePairFilterImpl (const EdgePairFilterImpl &); +}; + +Class decl_EdgePairFilterImpl ("db", "EdgePairFilter", + EdgePairFilterImpl::method_decls (false) + + callback ("selected", &EdgePairFilterImpl::issue_selected, &EdgePairFilterImpl::f_selected, gsi::arg ("text"), + "@brief Selects an edge pair\n" + "This method is the actual payload. It needs to be reimplemented in a derived class.\n" + "It needs to analyze the edge pair and return 'true' if it should be kept and 'false' if it should be discarded." + ), + "@brief A generic edge pair filter adaptor\n" + "\n" + "EdgePair filters are an efficient way to filter edge pairs from a EdgePairs collection. To apply a filter, derive your own " + "filter class and pass an instance to \\EdgePairs#filter or \\EdgePairs#filtered method.\n" + "\n" + "Conceptually, these methods take each edge pair from the collection and present it to the filter's 'selected' method.\n" + "Based on the result of this evaluation, the edge pair is kept or discarded.\n" + "\n" + "The magic happens when deep mode edge pair collections are involved. In that case, the filter will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You " + "need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using the filter.\n" + "\n" + "You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that filters edge pairs where the edges are perpendicular:" + "\n" + "@code\n" + "class PerpendicularEdgesFilter < RBA::EdgePairFilter\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n" + " end\n" + " \n" + " # Select edge pairs where the edges are perpendicular\n" + " def selected(edge_pair)\n" + " return edge_pair.first.d.sprod_sign(edge_pair.second.d) == 0\n" + " end\n" + "\n" + "end\n" + "\n" + "edge_pairs = ... # some EdgePairs object\n" + "perpendicular_only = edge_pairs.filtered(PerpendicularEdgesFilter::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// EdgePairProcessor binding + +Class > decl_EdgePairProcessor ("db", "EdgePairOperator", + shape_processor_impl::method_decls (false), + "@brief A generic edge-pair operator\n" + "\n" + "Edge pair processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\EdgePairs#processed or \\EdgePairs#process method.\n" + "\n" + "Conceptually, these methods take each edge pair from the edge pair collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edge pairs derived from the input edge pair.\n" + "The output edge pair collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that flips the edge pairs (swaps first and second edge):" + "\n" + "@code\n" + "class FlipEdgePairs < RBA::EdgePairOperator\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n" + " end\n" + " \n" + " # Flips the edge pair\n" + " def process(edge_pair)\n" + " return [ RBA::EdgePair::new(edge_pair.second, edge_pair.first) ]\n" + " end\n" + "\n" + "end\n" + "\n" + "edge_pairs = ... # some EdgePairs object\n" + "flipped = edge_pairs.processed(FlipEdgePairs::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgePairToPolygonProcessor ("db", "EdgePairToPolygonOperator", + shape_processor_impl::method_decls (false), + "@brief A generic edge-pair-to-polygon operator\n" + "\n" + "Edge pair processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\EdgePairs#processed method.\n" + "\n" + "Conceptually, these methods take each edge pair from the edge pair collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output polygons derived from the input edge pair.\n" + "The output region is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\EdgeToPolygonOperator class, with the exception that this incarnation receives edge pairs.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgePairToEdgeProcessor ("db", "EdgePairToEdgeOperator", + shape_processor_impl::method_decls (false), + "@brief A generic edge-pair-to-edge operator\n" + "\n" + "Edge processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own " + "operator class and pass an instance to \\EdgePairs#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edges derived from the input edge pair.\n" + "The output edge pair collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\EdgeToEdgePairOperator class, with the exception that this incarnation has to deliver edges and takes edge pairs.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// EdgePairs binding + +static db::EdgePairs *new_v () { return new db::EdgePairs (); } @@ -181,6 +356,40 @@ static size_t id (const db::EdgePairs *ep) return tl::id_of (ep->delegate ()); } +static db::EdgePairs filtered (const db::EdgePairs *r, const EdgePairFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f) +{ + r->filter (*f); +} + +static db::EdgePairs processed_epep (const db::EdgePairs *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static void process_epep (db::EdgePairs *r, const shape_processor_impl *f) +{ + r->process (*f); +} + +static db::Edges processed_epe (const db::EdgePairs *r, const shape_processor_impl *f) +{ + db::Edges out; + r->processed (out, *f); + return out; +} + +static db::Region processed_epp (const db::EdgePairs *r, const shape_processor_impl *f) +{ + db::Region out; + r->processed (out, *f); + return out; +} + static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { db::EdgePairFilterByDistance ef (length, length + 1, inverse); @@ -619,6 +828,42 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection)\n" + "See \\EdgePairFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\EdgePairFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("process", &process_epep, gsi::arg ("process"), + "@brief Applies a generic edge pair processor in place (replacing the edge pairs from the EdgePairs collection)\n" + "See \\EdgePairProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_epep, gsi::arg ("processed"), + "@brief Applies a generic edge pair processor and returns a processed copy\n" + "See \\EdgePairProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_epe, gsi::arg ("processed"), + "@brief Applies a generic edge-pair-to-edge processor and returns an edge collection with the results\n" + "See \\EdgePairToEdgeProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_epp, gsi::arg ("processed"), + "@brief Applies a generic edge-pair-to-polygon processor and returns an Region with the results\n" + "See \\EdgePairToPolygonProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filters the edge pairs by length of one of their edges\n" "Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index f74687b1b..d325dfa26 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -37,6 +37,196 @@ namespace gsi { +// --------------------------------------------------------------------------------- +// EdgeFilter binding + +class EdgeFilterImpl + : public shape_filter_impl +{ +public: + EdgeFilterImpl () { } + + bool issue_selected (const db::Edge &) const + { + return false; + } + + virtual bool selected (const db::Edge &edge) const + { + if (f_selected.can_issue ()) { + return f_selected.issue (&EdgeFilterImpl::issue_selected, edge); + } else { + return issue_selected (edge); + } + } + + // Returns true if all edges match the criterion + virtual bool selected (const std::unordered_set &edges) const + { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e)) { + return false; + } + } + return true; + } + + gsi::Callback f_selected; + +private: + // No copying + EdgeFilterImpl &operator= (const EdgeFilterImpl &); + EdgeFilterImpl (const EdgeFilterImpl &); +}; + +Class decl_EdgeFilterImpl ("db", "EdgeFilter", + EdgeFilterImpl::method_decls (true) + + callback ("selected", &EdgeFilterImpl::issue_selected, &EdgeFilterImpl::f_selected, gsi::arg ("edge"), + "@brief Selects an edge\n" + "This method is the actual payload. It needs to be reimplemented in a derived class.\n" + "It needs to analyze the edge and return 'true' if it should be kept and 'false' if it should be discarded." + ), + "@brief A generic edge filter adaptor\n" + "\n" + "Edge filters are an efficient way to filter edge from a Edges collection. To apply a filter, derive your own " + "filter class and pass an instance to the \\Edges#filter or \\Edges#filtered method.\n" + "\n" + "Conceptually, these methods take each edge from the collection and present it to the filter's 'selected' method.\n" + "Based on the result of this evaluation, the edge is kept or discarded.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the filter will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You " + "need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using the filter.\n" + "\n" + "You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that filters edges parallel to a given one:" + "\n" + "@code\n" + "class ParallelFilter < RBA::EdgeFilter\n" + "\n" + " # Constructor\n" + " def initialize(ref_edge)\n" + " self.is_scale_invariant # orientation matters, but scale does not\n" + " @ref_edge = ref_edge\n" + " end\n" + " \n" + " # Select only parallel ones\n" + " def selected(edge)\n" + " return edge.is_parallel?(@ref_edge)\n" + " end\n" + "\n" + "end\n" + "\n" + "edges = ... # some Edges object\n" + "ref_edge = ... # some Edge\n" + "parallel_only = edges.filtered(ParallelFilter::new(ref_edge))\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// EdgeProcessor binding + +Class > decl_EdgeProcessorBase ("db", "EdgeOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-polygon operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\Edges#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edges derived from the input edge.\n" + "The output edge collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that shrinks every edge to half of the size, but does not change the position.\n" + "In this example the 'position' is defined by the center of the edge:" + "\n" + "@code\n" + "class ShrinkToHalf < RBA::EdgeOperator\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # scale or orientation do not matter\n" + " end\n" + " \n" + " # Shrink to half size\n" + " def process(edge)\n" + " shift = edge.bbox.center - RBA::Point::new # shift vector\n" + " return [ (edge.moved(-shift) * 0.5).moved(shift) ]\n" + " end\n" + "\n" + "end\n" + "\n" + "edges = ... # some Edges collection\n" + "shrinked_to_half = edges.processed(ShrinkToHalf::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgeToPolygonProcessor ("db", "EdgeToPolygonOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-polygon operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\Edges#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output polygons derived from the input edge.\n" + "The output region is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\EdgeOperator class, with the exception that this incarnation has to deliver edges.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgeToEdgePairProcessor ("db", "EdgeToEdgePairOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-edge-pair operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\Edges#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edge pairs derived from the input edge.\n" + "The output edge pair collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\EdgeOperator class, with the exception that this incarnation has to deliver edge pairs.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// Edges binding + static inline std::vector as_2edges_vector (const std::pair &rp) { std::vector res; @@ -204,6 +394,38 @@ static db::Edges moved_xy (const db::Edges *r, db::Coord x, db::Coord y) return r->transformed (db::Disp (db::Vector (x, y))); } +static db::Edges filtered (const db::Edges *r, const EdgeFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::Edges *r, const EdgeFilterImpl *f) +{ + r->filter (*f); +} + +static db::Edges processed_ee (const db::Edges *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static void process_ee (db::Edges *r, const shape_processor_impl *f) +{ + r->process (*f); +} + +static db::EdgePairs processed_eep (const db::Edges *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static db::Region processed_ep (const db::Edges *r, const shape_processor_impl *f) +{ + db::Region out; + r->processed (out, *f); + return out; +} + static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type length, bool inverse) { db::EdgeLengthFilter f (length, length + 1, inverse); @@ -434,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)); } @@ -627,6 +849,42 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been introduced in version 0.26." ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the edges from the Edges collection)\n" + "See \\EdgeFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\EdgeFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("process", &process_ee, gsi::arg ("process"), + "@brief Applies a generic edge processor in place (replacing the edges from the Edges collection)\n" + "See \\EdgeProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_ee, gsi::arg ("processed"), + "@brief Applies a generic edge processor and returns a processed copy\n" + "See \\EdgeProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_eep, gsi::arg ("processed"), + "@brief Applies a generic edge-to-edge-pair processor and returns an edge pair collection with the results\n" + "See \\EdgeToEdgePairProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_ep, gsi::arg ("processed"), + "@brief Applies a generic edge-to-polygon processor and returns an edge collection with the results\n" + "See \\EdgeToPolygonProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " @@ -975,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" 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 417351c16..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 @@ -61,7 +94,7 @@ struct simple_polygon_defs } } - static point_type point (C *c, size_t p) + static point_type point (const C *c, size_t p) { if (c->hull ().size () > p) { return c->hull ()[p]; @@ -70,12 +103,12 @@ struct simple_polygon_defs } } - static size_t num_points (C *c) + static size_t num_points (const C *c) { return c->hull ().size (); } - static bool is_empty (C *c) + static bool is_empty (const C *c) { return c->hull ().size () == 0; } @@ -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." @@ -868,17 +908,17 @@ struct polygon_defs } } - static size_t num_points (C *c) + static size_t num_points (const C *c) { return c->vertices (); } - static bool is_empty (C *c) + static bool is_empty (const C *c) { return c->vertices () == 0; } - static point_type point_hull (C *c, size_t p) + static point_type point_hull (const C *c, size_t p) { if (c->hull ().size () > p) { return c->hull ()[p]; @@ -887,7 +927,7 @@ struct polygon_defs } } - static point_type point_hole (C *c, unsigned int n, size_t p) + static point_type point_hole (const C *c, unsigned int n, size_t p) { if (c->holes () > n && c->contour (n + 1).size () > p) { return c->contour (n + 1)[p]; @@ -896,12 +936,12 @@ struct polygon_defs } } - static size_t num_points_hull (C *c) + static size_t num_points_hull (const C *c) { return c->hull ().size (); } - static size_t num_points_hole (C *c, unsigned int n) + static size_t num_points_hole (const C *c, unsigned int n) { return c->contour (n + 1).size (); } @@ -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/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 7bac67ea8..f978a7c4f 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -47,6 +47,190 @@ namespace gsi { +// --------------------------------------------------------------------------------- +// PolygonFilter binding + +class PolygonFilterImpl + : public shape_filter_impl +{ +public: + PolygonFilterImpl () { } + + bool issue_selected (const db::Polygon &) const + { + return false; + } + + virtual bool selected (const db::Polygon &polygon) const + { + if (f_selected.can_issue ()) { + return f_selected.issue (&PolygonFilterImpl::issue_selected, polygon); + } else { + return issue_selected (polygon); + } + } + + virtual bool selected (const db::PolygonRef &polygon) const + { + db::Polygon p; + polygon.instantiate (p); + return selected (p); + } + + gsi::Callback f_selected; + +private: + // No copying + PolygonFilterImpl &operator= (const PolygonFilterImpl &); + PolygonFilterImpl (const PolygonFilterImpl &); +}; + +Class decl_PolygonFilterImpl ("db", "PolygonFilter", + PolygonFilterImpl::method_decls (true) + + callback ("selected", &PolygonFilterImpl::issue_selected, &PolygonFilterImpl::f_selected, gsi::arg ("polygon"), + "@brief Selects a polygon\n" + "This method is the actual payload. It needs to be reimplemented in a derived class.\n" + "It needs to analyze the polygon and return 'true' if it should be kept and 'false' if it should be discarded." + ), + "@brief A generic polygon filter adaptor\n" + "\n" + "Polygon filters are an efficient way to filter polygons from a Region. To apply a filter, derive your own " + "filter class and pass an instance to the \\Region#filter or \\Region#filtered method.\n" + "\n" + "Conceptually, these methods take each polygon from the region and present it to the filter's 'selected' method.\n" + "Based on the result of this evaluation, the polygon is kept or discarded.\n" + "\n" + "The magic happens when deep mode regions are involved. In that case, the filter will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You " + "need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using the filter.\n" + "\n" + "You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that filters triangles:" + "\n" + "@code\n" + "class TriangleFilter < RBA::PolygonFilter\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # the triangle nature is not dependent on the scale or orientation\n" + " end\n" + " \n" + " # Select only triangles\n" + " def selected(polygon)\n" + " return polygon.holes == 0 && polygon.num_points == 3\n" + " end\n" + "\n" + "end\n" + "\n" + "region = ... # some Region\n" + "triangles_only = region.filtered(TriangleFilter::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// PolygonProcessor binding + +Class > decl_PolygonOperator ("db", "PolygonOperator", + shape_processor_impl::method_decls (true), + "@brief A generic polygon operator\n" + "\n" + "Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own " + "operator class and pass an instance to the \\Region#process or \\Region#processed method.\n" + "\n" + "Conceptually, these methods take each polygon from the region and present it to the operators' 'process' method.\n" + "The result of this call is a list of zero to many output polygons derived from the input polygon.\n" + "The output region is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that shrinks every polygon to half of the size but does not change the position.\n" + "In this example the 'position' is defined by the center of the bounding box:" + "\n" + "@code\n" + "class ShrinkToHalf < RBA::PolygonOperator\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # scale or orientation do not matter\n" + " end\n" + " \n" + " # Shrink to half size\n" + " def process(polygon)\n" + " shift = polygon.bbox.center - RBA::Point::new # shift vector\n" + " return [ (polygon.moved(-shift) * 0.5).moved(shift) ]\n" + " end\n" + "\n" + "end\n" + "\n" + "region = ... # some Region\n" + "shrinked_to_half = region.processed(ShrinkToHalf::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeOperator", + shape_processor_impl::method_decls (true), + "@brief A generic polygon-to-edge operator\n" + "\n" + "Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own " + "operator class and pass an instance to the \\Region#processed method.\n" + "\n" + "Conceptually, these methods take each polygon from the region and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edges derived from the input polygon.\n" + "The output edge collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\PolygonOperator class, with the exception that this incarnation has to deliver edges.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_PolygonToEdgePairProcessor ("db", "PolygonToEdgePairOperator", + shape_processor_impl::method_decls (true), + "@brief A generic polygon-to-edge-pair operator\n" + "\n" + "Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own " + "operator class and pass an instance to the \\Region#processed method.\n" + "\n" + "Conceptually, these methods take each polygon from the region and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edge pairs derived from the input polygon.\n" + "The output edge pair collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\PolygonOperator class, with the exception that this incarnation has to deliver edge pairs.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// Region binding + static inline std::vector as_2region_vector (const std::pair &rp) { std::vector res; @@ -318,6 +502,36 @@ static db::Edges extent_refs_edges (const db::Region *r, double fx1, double fy1, return r->processed (db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2)); } +static db::Region filtered (const db::Region *r, const PolygonFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::Region *r, const PolygonFilterImpl *f) +{ + r->filter (*f); +} + +static db::Region processed_pp (const db::Region *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static void process_pp (db::Region *r, const shape_processor_impl *f) +{ + r->process (*f); +} + +static db::EdgePairs processed_pep (const db::Region *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static db::Edges processed_pe (const db::Region *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + static db::Region with_perimeter1 (const db::Region *r, db::Region::perimeter_type perimeter, bool inverse) { db::RegionPerimeterFilter f (perimeter, perimeter + 1, inverse); @@ -788,6 +1002,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) { @@ -2279,15 +2505,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" @@ -2363,6 +2594,42 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.28.\n" ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the polygons from the Region)\n" + "See \\PolygonFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\PolygonFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("process", &process_pp, gsi::arg ("process"), + "@brief Applies a generic polygon processor in place (replacing the polygons from the Region)\n" + "See \\PolygonProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_pp, gsi::arg ("processed"), + "@brief Applies a generic polygon processor and returns a processed copy\n" + "See \\PolygonProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_pep, gsi::arg ("processed"), + "@brief Applies a generic polygon-to-edge-pair processor and returns an edge pair collection with the results\n" + "See \\PolygonToEdgePairProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_pe, gsi::arg ("processed"), + "@brief Applies a generic polygon-to-edge processor and returns an edge collection with the results\n" + "See \\PolygonToEdgeProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("rectangles", &rectangles, "@brief Returns all polygons which are rectangles\n" "This method returns all polygons in self which are rectangles." @@ -2674,7 +2941,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" @@ -3327,6 +3594,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" @@ -3340,7 +3649,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" @@ -3359,7 +3668,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/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index a823cecc8..be37cf36e 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -98,17 +98,17 @@ struct text_defs t->font (db::Font (f)); } - static int get_font (C *t) + static int get_font (const C *t) { return t->font (); } - static point_type get_pos (C *t) + static point_type get_pos (const C *t) { return t->trans () * point_type (); } - static box_type get_bbox (C *t) + static box_type get_bbox (const C *t) { point_type p = get_pos (t); return box_type (p, p); @@ -124,7 +124,7 @@ struct text_defs t->halign (db::HAlign (f)); } - static db::HAlign get_halign (C *t) + static db::HAlign get_halign (const C *t) { return t->halign (); } @@ -139,12 +139,12 @@ struct text_defs t->valign (db::VAlign (f)); } - static db::VAlign get_valign (C *t) + static db::VAlign get_valign (const C *t) { return t->valign (); } - static C moved (C *c, const vector_type &p) + static C moved (const C *c, const vector_type &p) { return c->transformed (simple_trans_type (p)); } @@ -155,7 +155,7 @@ struct text_defs return *c; } - static C moved_xy (C *c, coord_type dx, coord_type dy) + static C moved_xy (const C *c, coord_type dx, coord_type dy) { return c->transformed (simple_trans_type (vector_type (dx, dy))); } diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index b105c5d82..8be9238b8 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -33,6 +33,160 @@ namespace gsi { +// --------------------------------------------------------------------------------- +// TextFilter binding + +class TextFilterImpl + : public shape_filter_impl +{ +public: + TextFilterImpl () { } + + bool issue_selected (const db::Text &) const + { + return false; + } + + virtual bool selected (const db::Text &text) const + { + if (f_selected.can_issue ()) { + return f_selected.issue (&TextFilterImpl::issue_selected, text); + } else { + return issue_selected (text); + } + } + + gsi::Callback f_selected; + +private: + // No copying + TextFilterImpl &operator= (const TextFilterImpl &); + TextFilterImpl (const TextFilterImpl &); +}; + +Class decl_TextFilterImpl ("db", "TextFilter", + TextFilterImpl::method_decls (false) + + callback ("selected", &TextFilterImpl::issue_selected, &TextFilterImpl::f_selected, gsi::arg ("text"), + "@brief Selects a text\n" + "This method is the actual payload. It needs to be reimplemented in a derived class.\n" + "It needs to analyze the text and return 'true' if it should be kept and 'false' if it should be discarded." + ), + "@brief A generic text filter adaptor\n" + "\n" + "Text filters are an efficient way to filter texts from a Texts collection. To apply a filter, derive your own " + "filter class and pass an instance to \\Texts#filter or \\Texts#filtered method.\n" + "\n" + "Conceptually, these methods take each text from the collection and present it to the filter's 'selected' method.\n" + "Based on the result of this evaluation, the text is kept or discarded.\n" + "\n" + "The magic happens when deep mode text collections are involved. In that case, the filter will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You " + "need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using the filter.\n" + "\n" + "You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that filters texts with a given string length:" + "\n" + "@code\n" + "class TextStringLengthFilter < RBA::TextFilter\n" + "\n" + " # Constructor\n" + " def initialize(string_length)\n" + " self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n" + " @string_length = string_length\n" + " end\n" + " \n" + " # Select texts with given string length\n" + " def selected(text)\n" + " return text.string.size == @string_length\n" + " end\n" + "\n" + "end\n" + "\n" + "texts = ... # some Texts object\n" + "with_length_3 = edges.filtered(TextStringLengthFilter::new(3))\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// TextProcessor binding + +Class > decl_TextProcessor ("db", "TextOperator", + shape_processor_impl::method_decls (false), + "@brief A generic text operator\n" + "\n" + "Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\Texts#processed or \\Texts#process method.\n" + "\n" + "Conceptually, these methods take each text from the edge pair collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output texts derived from the input text.\n" + "The output text collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that replaces the text string:" + "\n" + "@code\n" + "class ReplaceTextString < RBA::TextOperator\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n" + " end\n" + " \n" + " # Replaces the string by a number representing the string length\n" + " def process(text)\n" + " new_text = text.dup # need a copy as we cannot modify the text passed\n" + " new_text.string = text.string.size.to_s\n" + " return [ new_text ]\n" + " end\n" + "\n" + "end\n" + "\n" + "texts = ... # some Texts object\n" + "modified = texts.processed(ReplaceTextString::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_TextToPolygonProcessor ("db", "TextToPolygonOperator", + shape_processor_impl::method_decls (false), + "@brief A generic text-to-polygon operator\n" + "\n" + "Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own " + "operator class and pass an instance to the \\Texts#processed method.\n" + "\n" + "Conceptually, these methods take each text from the text collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output polygons derived from the input text.\n" + "The output region is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\TextOperator class, with the exception that this incarnation delivers polygons.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +// --------------------------------------------------------------------------------- +// Texts binding + static db::Texts *new_v () { return new db::Texts (); @@ -152,6 +306,33 @@ static size_t id (const db::Texts *t) return tl::id_of (t->delegate ()); } +static db::Texts filtered (const db::Texts *r, const TextFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::Texts *r, const TextFilterImpl *f) +{ + r->filter (*f); +} + +static db::Texts processed_tt (const db::Texts *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static void process_tt (db::Texts *r, const shape_processor_impl *f) +{ + r->process (*f); +} + +static db::Region processed_tp (const db::Texts *r, const shape_processor_impl *f) +{ + db::Region out; + r->processed (out, *f); + return out; +} + static db::Texts with_text (const db::Texts *r, const std::string &text, bool inverse) { db::TextStringFilter f (text, inverse); @@ -401,6 +582,36 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "@brief Converts the edge pairs to polygons\n" "This method creates polygons from the texts. This is equivalent to calling \\extents." ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the texts from the Texts collection)\n" + "See \\TextFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\TextFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("process", &process_tt, gsi::arg ("process"), + "@brief Applies a generic text processor in place (replacing the texts from the text collection)\n" + "See \\TextProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_tt, gsi::arg ("processed"), + "@brief Applies a generic text processor and returns a processed copy\n" + "See \\TextProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("processed", &processed_tp, gsi::arg ("processed"), + "@brief Applies a generic text-to-polygon processor and returns a region with the results\n" + "See \\TextToPolygonProcessor for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_text", with_text, gsi::arg ("text"), gsi::arg ("inverse"), "@brief Filter the text by text string\n" "If \"inverse\" is false, this method returns the texts with the given string.\n" diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 1d9f24594..7d992997e 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,152 @@ 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(deep_edges_and_cheats) { 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/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 6d75f7be3..edf6727dd 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -25,6 +25,7 @@ #include "dbNetlistCompare.h" #include "dbNetlistCrossReference.h" #include "dbNetlistSpiceReader.h" +#include "dbNetlistCompareUtils.h" class NetlistCompareTestLogger : public db::NetlistCompareLogger @@ -437,6 +438,101 @@ TEST(0_EqualDeviceParameters) EXPECT_EQ (dc.less (d2, d1), false); } +TEST(0_NetNameEquivalence) +{ + db::Netlist a, b; + a.set_case_sensitive (true); + b.set_case_sensitive (false); + + EXPECT_EQ (db::combined_case_sensitive (&a, &b), false); + + b.set_case_sensitive (true); + EXPECT_EQ (db::combined_case_sensitive (&a, &b), true); + + a.set_case_sensitive (false); + EXPECT_EQ (db::combined_case_sensitive (&a, &b), false); + + db::Circuit *ca = new db::Circuit (); + ca->set_name ("C"); + a.add_circuit (ca); + + db::Circuit *cb = new db::Circuit (); + cb->set_name ("C"); + b.add_circuit (cb); + + db::Net *na = new db::Net ("net1"); + ca->add_net (na); + + db::Net *nb = new db::Net ("net1"); + cb->add_net (nb); + + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("NET1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("NET2"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("NET11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net0abc"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + nb->set_name ("NET0"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + a.set_case_sensitive (true); + b.set_case_sensitive (true); + + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("net2"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net0"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + nb->set_name ("NET1"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + na->set_name ("NET1"); + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + b.set_case_sensitive (false); + + // colon terminates the net name, so that NET:I and NET and identical + + na->set_name ("NET1:I"); + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + na->set_name ("NET1:I"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + na->set_name ("NET1"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + na->set_name ("NET2"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + na->set_name ("NET1"); + nb->set_name ("net1abc:O"); + EXPECT_EQ (db::name_compare (na, nb), -1); +} + TEST(1_SimpleInverter) { const char *nls1 = 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/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.xml b/src/doc/doc/about/drc_ref.xml index 889c3fedc..203941a1d 100644 --- a/src/doc/doc/about/drc_ref.xml +++ b/src/doc/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 7ee678eca..6b080c952 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -1,7 +1,7 @@ - + @@ -358,7 +358,7 @@ The plain function is equivalent to "primary.bbox_width". This method acts on edge expressions and delivers a specific part of each edge. See layer#centers for details about this functionality.

-

"corners" - Applies smoothing

+

"corners" - Selects corners of polygons

Usage:

"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 @@ -794,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/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index f03149944..8fd41f6f5 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index a9ab3eaab..74d119bcb 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml index 4a38dd6db..58168f012 100644 --- a/src/doc/doc/about/lvs_ref.xml +++ b/src/doc/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index c262d6815..c1d2ad914 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index f5f851203..9feef786f 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/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 14eb0c842..e5f2ffa26 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_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 1985cc67c..8c970483c 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -379,7 +379,8 @@ module DRC arg1.is_a?(String) || raise("The first argument has to be a string") @pre_extract_config << lambda { |l2n| l2n.join_nets(arg1, arg2) } else - arg1.is_a?(String) || raise("The argument has to be a string") + arg1.is_a?(Array) || raise("The argument has to be an array of strings") + arg1.find { |a| !a.is_a?(String) } && raise("The argument has to be an array of strings") @pre_extract_config << lambda { |l2n| l2n.join_nets(arg1) } end 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/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index d82d5d56d..a5603bd3f 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -289,6 +289,12 @@ TEST(31_MustConnect2) run_test (_this, "must_connect2", "must_connect2.gds"); } +// Intermediate cell propagates must-connect pins +TEST(32_MustConnect3) +{ + run_test (_this, "must_connect3", "must_connect3.gds"); +} + // issue 1609 TEST(40_DeviceExtractorErrors) { 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/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 a4df7f04b..8bd642f04 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -2358,7 +2358,7 @@ RubyInterpreter::load_file (const std::string &filename_utf8) { std::string fl (rb_cstring_from_utf8 (filename_utf8)); - rb_set_progname (rb_str_new (fl.c_str (), long (fl.size ()))); + ruby_script (fl.c_str ()); rb_set_errinfo (Qnil); int error = 0; diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index ab9383ba9..f11463965 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -530,10 +530,10 @@ VALUE rba_eval_string_in_context (const char *expr, const char *file, int line, rb_set_errinfo (Qnil); if (file) { - rb_set_progname (rb_str_new (file, long (strlen (file)))); + ruby_script (file); } else { const char *e = ""; - rb_set_progname (rb_str_new (e, long (strlen (e)))); + ruby_script (e); } int argc; diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index 3105f1dfc..1f9e6b5d8 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -147,14 +147,6 @@ inline std::string rb_cstring_from_utf8 (const std::string &utf8) return utf8; } -/** - * @brief A setter for $0 - */ -inline void rb_set_progname (VALUE pn) -{ - rb_gv_set ("PROGRAM_NAME", pn); -} - /** * @brief Sets up a block for protected evaluation * 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..ba2774439 --- /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 +{ + +extern 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..c30fa6679 --- /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 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/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/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/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index cb79afe2b..a1ff6a9d4 100644 --- a/testdata/lvs/double_height2.lvsdb +++ b/testdata/lvs/double_height2.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) - H(E B('Must-connect nets R of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb index 509b5b815..049cec097 100644 --- a/testdata/lvs/double_height2_texts.lvsdb +++ b/testdata/lvs/double_height2_texts.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) - H(E B('Must-connect nets R of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/must_connect2.lvsdb b/testdata/lvs/must_connect2.lvsdb index b3fd8d3a3..02ee5a88c 100644 --- a/testdata/lvs/must_connect2.lvsdb +++ b/testdata/lvs/must_connect2.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) H(W B('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) - H(E B('Must-connect nets VSS of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets VSS of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$2[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/must_connect3.cir b/testdata/lvs/must_connect3.cir new file mode 100644 index 000000000..f9a636701 --- /dev/null +++ b/testdata/lvs/must_connect3.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +.SUBCKT TOP +X$2 \$1 \$I2 \$I1 \$I1 \$1.Q \$2 INV2 +X$3 \$1.A \$I3 \$2 \$I3 \$I2 \$1 INVCHAIN +.ENDS TOP + +.SUBCKT INVCHAIN IN IN2 VSS|VSS2|VSS2B OUT OUT2 VDD +X$1 VDD IN2 \$1 \$1 OUT2 VSS|VSS2|VSS2B INV2 +X$2 VDD IN \$2 \$2 OUT VSS|VSS2|VSS2B INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$3 \$4 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/must_connect3.gds b/testdata/lvs/must_connect3.gds new file mode 100644 index 000000000..e0115df23 Binary files /dev/null and b/testdata/lvs/must_connect3.gds differ diff --git a/testdata/lvs/must_connect3.lvs b/testdata/lvs/must_connect3.lvs new file mode 100644 index 000000000..ac34742e7 --- /dev/null +++ b/testdata/lvs/must_connect3.lvs @@ -0,0 +1,142 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_lvsdb + report_lvs($lvs_test_target_lvsdb) +else + report_lvs +end + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") +connect_implicit("TOP", "VSS*") +# Fix 1 +connect_explicit("INVCHAIN", ["VSS2", "VSS2B", "VSS"]) +connect_implicit("INVCHAIN", "VDD") +connect_explicit("TOP", ["VDD"]) + +ignore_extraction_errors(true) + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic(File.basename(source.path, ".*") + ".sch", reader) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Compare section + +netlist.simplify +align + +# Skip as we have errors .. +compare + diff --git a/testdata/lvs/must_connect3.lvsdb b/testdata/lvs/must_connect3.lvsdb new file mode 100644 index 000000000..83f89832d --- /dev/null +++ b/testdata/lvs/must_connect3.lvsdb @@ -0,0 +1,482 @@ +#%lvsdb-klayout +J( + W(TOP) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1890 -1450) (0 0)) + ) + N(2 I(A1) + R(l11 (1480 7110) (0 0)) + ) + N(3 I(A2) + R(l11 (1520 1950) (0 0)) + ) + N(4 I(Q1) + R(l12 (1920 7070) (0 0)) + ) + N(5 I(Q2) + R(l12 (1940 1950) (0 0)) + ) + N(6 I(VSS) + R(l13 (2680 8390) (0 0)) + R(l13 (-30 -7640) (0 0)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-915 -15) (10415 9215)) + N(1 + R(l3 (7340 1650) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(3 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(4 I(IN2) + R(l3 (5665 6790) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(5 I('VSS,VSS2,VSS2B') + R(l10 (-915 5290) (250 2960)) + R(l10 (-250 0) (915 250)) + R(l10 (-915 -7825) (915 250)) + R(l10 (-915 0) (250 3145)) + R(l13 (155 4305) (0 0)) + R(l13 (8990 -255) (0 0)) + R(l13 (25 -7115) (0 0)) + ) + N(6 I(OUT) + R(l12 (1890 2105) (0 0)) + ) + N(7 I(OUT2) + R(l12 (7730 2155) (0 0)) + ) + N(8 I(VDD) + R(l13 (8035 4540) (0 0)) + R(l13 (-5735 60) (0 0)) + ) + P(3 I(IN)) + P(4 I(IN2)) + P(5 I('VSS,VSS2,VSS2B')) + P(6 I(OUT)) + P(7 I(OUT2)) + P(8 I(VDD)) + X(1 INV2 Y(5780 -15) + P(0 8) + P(1 4) + P(2 1) + P(3 1) + P(4 7) + P(5 5) + ) + X(2 INV2 Y(0 0) + P(0 8) + P(1 3) + P(2 2) + P(3 2) + P(4 6) + P(5 5) + ) + ) + X(TOP + R((-305 350) (15415 9225)) + N(1 + R(l10 (3200 4800) (8800 400)) + R(l10 (-5110 -425) (0 0)) + R(l10 (-4295 30) (0 0)) + R(l10 (9270 -80) (0 0)) + ) + N(2 + R(l10 (-305 4435) (250 1220)) + R(l10 (3665 -4655) (2780 400)) + R(l10 (-2780 6900) (2815 440)) + R(l10 (-710 -250) (0 0)) + R(l10 (3675 -165) (1975 565)) + R(l10 (-1975 -8190) (1975 575)) + R(l10 (-1005 -255) (0 0)) + ) + N(3 + R(l3 (12950 2130) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(4 + R(l3 (12100 7300) (640 530)) + R(l7 (-540 -415) (270 250)) + R(l8 (-1695 -250) (1695 250)) + R(l8 (-4075 -5650) (2630 250)) + R(l8 (-250 0) (250 5150)) + ) + N(5 + R(l7 (6465 7325) (220 240)) + R(l8 (-4100 -5365) (3125 250)) + R(l8 (-250 0) (250 4860)) + R(l8 (-250 0) (1225 250)) + ) + N(6 I($1.A) + R(l11 (975 7530) (0 0)) + ) + N(7 I($1.Q) + R(l12 (13260 2010) (0 0)) + ) + X(2 INV2 Y(11365 375) + P(0 1) + P(1 4) + P(2 3) + P(3 3) + P(4 7) + P(5 2) + ) + X(3 INVCHAIN Y(610 365) + P(0 6) + P(1 5) + P(2 2) + P(3 5) + P(4 4) + P(5 1) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + N(7 I('1')) + N(8 I('2')) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 7) + P(4 7) + P(5 4) + ) + X(2 INV2 I($3) + P(0 1) + P(1 2) + P(2 5) + P(3 8) + P(4 8) + P(5 6) + ) + ) + X(TOP + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I('1')) + N(5 I('3')) + N(6 I('2')) + N(7 I(Q)) + X(1 INVCHAIN I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + X(2 INV2 I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + P(4 6) + P(5 7) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 1) + N(5 6 1) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + L( + M(W B('Matching nets OUT vs. Q1 from an ambiguous group of nets')) + M(W B('Matching nets OUT2 vs. Q2 from an ambiguous group of nets')) + M(I B('Matching nets $2 vs. 1 following an ambiguous match')) + M(I B('Matching nets IN vs. A1 following an ambiguous match')) + M(I B('Matching nets $1 vs. 2 following an ambiguous match')) + M(I B('Matching nets IN2 vs. A2 following an ambiguous match')) + ) + Z( + N(2 7 1) + N(1 8 1) + N(3 3 1) + N(4 5 1) + N(6 4 W) + N(7 6 W) + N(8 1 1) + N(5 2 1) + P(0 2 1) + P(1 4 1) + P(3 3 1) + P(4 5 1) + P(5 0 1) + P(2 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(TOP TOP 1 + Z( + N(5 4 1) + N(3 6 1) + N(4 5 1) + N(1 1 1) + N(2 2 1) + N(6 3 1) + N(7 7 1) + X(2 2 1) + X(3 1 1) + ) + ) +) diff --git a/testdata/lvs/must_connect3.sch b/testdata/lvs/must_connect3.sch new file mode 100644 index 000000000..9114c6de9 --- /dev/null +++ b/testdata/lvs/must_connect3.sch @@ -0,0 +1,24 @@ + +.SUBCKT TOP +X$1 VDD VSS A 1 1 3 INVCHAIN +X$2 VDD VSS 3 2 2 Q INV2 +.ENDS TOP + +* cell INVCHAIN +.SUBCKT INVCHAIN VDD VSS A1 Q1 A2 Q2 +X$2 VDD VSS A1 1 1 Q1 INV2 +X$3 VDD VSS A2 2 2 Q2 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV + diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index fa1ccad3c..f9b3289be 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -30,6 +30,60 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class PerpendicularEdgesFilter < RBA::EdgePairFilter + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + end + + # Select edge pairs where the edges are perpendicular + def selected(edge_pair) + return edge_pair.first.d.sprod_sign(edge_pair.second.d) == 0 + end + +end + +class FlipEdgePair < RBA::EdgePairOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + end + + # Flips the edge pair + def process(edge_pair) + return [ RBA::EdgePair::new(edge_pair.second, edge_pair.first) ] + end + +end + +class SomeEdgePairToEdgeOperator < RBA::EdgePairToEdgeOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(ep) + return [ RBA::Edge::new(ep.first.p1, ep.second.p2) ] + end + +end + +class SomeEdgePairToPolygonOperator < RBA::EdgePairToPolygonOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(ep) + return [ RBA::Polygon::new(ep.bbox) ] + end + +end + class DBEdgePairs_TestClass < TestBase # Basics @@ -331,6 +385,90 @@ class DBEdgePairs_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = PerpendicularEdgesFilter::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = PerpendicularEdgesFilter::new + + edge_pairs = RBA::EdgePairs::new + edge_pairs.insert(RBA::EdgePair::new([0, 0, 100, 0], [0, 100, 0, 300 ])) + edge_pairs.insert(RBA::EdgePair::new([200, 0, 300, 0], [200, 100, 220, 300 ])) + + assert_equal(edge_pairs.filtered(f).to_s, "(0,0;100,0)/(0,100;0,300)") + assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300);(200,0;300,0)/(200,100;220,300)") + edge_pairs.filter(f) + assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300)") + + end + + # Generic processors + def test_generic_processors_epep + + # Some basic tests for the processor class + + f = FlipEdgePair::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + edge_pairs = RBA::EdgePairs::new + + edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500))) + + assert_equal(edge_pairs.processed(FlipEdgePair::new).to_s, "(200,300;200,500)/(0,0;100,100)") + assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)") + edge_pairs.process(FlipEdgePair::new) + assert_equal(edge_pairs.to_s, "(200,300;200,500)/(0,0;100,100)") + + end + + # Generic processors + def test_generic_processors_epe + + p = SomeEdgePairToEdgeOperator::new + + edge_pairs = RBA::EdgePairs::new + + edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500))) + + assert_equal(edge_pairs.processed(p).to_s, "(0,0;200,500)") + assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)") + + end + + # Generic processors + def test_generic_processors_epp + + p = SomeEdgePairToPolygonOperator::new + + edge_pairs = RBA::EdgePairs::new + + edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500))) + + assert_equal(edge_pairs.processed(p).to_s, "(0,0;0,500;200,500;200,0)") + assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)") + + end + end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index f64dc910e..dc4ae88bb 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -30,6 +30,64 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class ParallelFilter < RBA::EdgeFilter + + # Constructor + def initialize(ref_edge) + self.is_scale_invariant # orientation matters, but scale does not + @ref_edge = ref_edge + end + + # Select only parallel ones + def selected(edge) + return edge.is_parallel?(@ref_edge) + end + +end + +class ShrinkToHalfEdgeOperator < RBA::EdgeOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + # Shrink to half size + def process(edge) + shift = edge.bbox.center - RBA::Point::new # shift vector + return [ (edge.moved(-shift) * 0.5).moved(shift) ] + end + +end + +class SomeEdgeToEdgePairOperator < RBA::EdgeToEdgePairOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(edge) + box = edge.bbox + return [ RBA::EdgePair::new([ box.left, box.bottom, box.left, box.top ], [ box.right, box.bottom, box.right, box.top ]) ] + end + +end + +class SomeEdgeToPolygonOperator < RBA::EdgeToPolygonOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(edge) + box = edge.bbox + return [ RBA::Polygon::new(box) ] + end + +end + class DBEdges_TestClass < TestBase # Basics @@ -791,6 +849,105 @@ class DBEdges_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100)) + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = false + assert_equal(f.requires_raw_input, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100)) + + edges = RBA::Edges::new + edges.insert(RBA::Edge::new(100, 0, 200, 100)) + edges.insert(RBA::Edge::new(100, 100, 100, 200)) + + assert_equal(edges.filtered(f).to_s, "(100,0;200,100)") + assert_equal(edges.to_s, "(100,0;200,100);(100,100;100,200)") + edges.filter(f) + assert_equal(edges.to_s, "(100,0;200,100)") + + end + + # Generic processors + def test_generic_processors_ee + + # Some basic tests for the processor class + + f = ShrinkToHalfEdgeOperator::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = true + assert_equal(f.requires_raw_input, true) + assert_equal(f.result_is_merged, false) + f.result_is_merged = true + assert_equal(f.result_is_merged, true) + assert_equal(f.result_must_not_be_merged, false) + f.result_must_not_be_merged = true + assert_equal(f.result_must_not_be_merged, true) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(ShrinkToHalfEdgeOperator::new).to_s, "(25,25;75,75);(200,350;200,450)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + edges.process(ShrinkToHalfEdgeOperator::new) + assert_equal(edges.to_s, "(25,25;75,75);(200,350;200,450)") + + end + + # Generic processors + def test_generic_processors_eep + + p = SomeEdgeToEdgePairOperator::new + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(p).to_s, "(0,0;0,100)/(100,0;100,100);(200,300;200,500)/(200,300;200,500)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + + end + + # Generic processors + def test_generic_processors_ep + + p = SomeEdgeToPolygonOperator::new + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(p).to_s, "(0,0;0,100;100,100;100,0);(200,300;200,300;200,500;200,500)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + + end + end load("test_epilogue.rb") 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..cb1910017 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -821,6 +821,54 @@ class DBPolygon_TestClass < TestBase end + def test_breakPolygon + + pts = [] + pts << RBA::Point::new(0, 0) + pts << RBA::Point::new(0, 1000) + pts << RBA::Point::new(100, 1000) + pts << RBA::Point::new(100, 100) + pts << RBA::Point::new(1000, 100) + pts << RBA::Point::new(1000, 0) + + split = RBA::Polygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::SimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + pts = [] + pts << RBA::DPoint::new(0, 0) + pts << RBA::DPoint::new(0, 1000) + pts << RBA::DPoint::new(100, 1000) + pts << RBA::DPoint::new(100, 100) + pts << RBA::DPoint::new(1000, 100) + pts << RBA::DPoint::new(1000, 0) + + split = RBA::DPolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::DSimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + end + def test_voidMethodsReturnSelf hull = [ RBA::Point::new(0, 0), RBA::Point::new(6000, 0), diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index d08f161fa..0d81b5efd 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -30,6 +30,63 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class TriangleFilter < RBA::PolygonFilter + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # the triangle nature is not dependent on the scale or orientation + end + + # Select only triangles + def selected(polygon) + return polygon.holes == 0 && polygon.num_points == 3 + end + +end + +class ShrinkToHalfOperator < RBA::PolygonOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + # Shrink to half size + def process(polygon) + shift = polygon.bbox.center - RBA::Point::new # shift vector + return [ (polygon.moved(-shift) * 0.5).moved(shift) ] + end + +end + +class SomePolygonToEdgePairOperator < RBA::PolygonToEdgePairOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(polygon) + box = polygon.bbox + return [ RBA::EdgePair::new([ box.left, box.bottom, box.left, box.top ], [ box.right, box.bottom, box.right, box.top ]) ] + end + +end + +class SomePolygonToEdgeOperator < RBA::PolygonToEdgeOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(polygon) + box = polygon.bbox + return [ RBA::Edge::new(box.p1, box.p2) ] + end + +end + class DBRegion_TestClass < TestBase # Basics @@ -1226,6 +1283,104 @@ class DBRegion_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = TriangleFilter::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = true + assert_equal(f.requires_raw_input, true) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + region = RBA::Region::new + + region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]])) + region.insert(RBA::Box::new(200, 0, 300, 100)) + + assert_equal(region.filtered(TriangleFilter::new).to_s, "(0,0;100,100;100,0)") + assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") + region.filter(TriangleFilter::new) + assert_equal(region.to_s, "(0,0;100,100;100,0)") + + end + + # Generic processors + def test_generic_processors_pp + + # Some basic tests for the processor class + + f = ShrinkToHalfOperator::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = true + assert_equal(f.requires_raw_input, true) + assert_equal(f.result_is_merged, false) + f.result_is_merged = true + assert_equal(f.result_is_merged, true) + assert_equal(f.result_must_not_be_merged, false) + f.result_must_not_be_merged = true + assert_equal(f.result_must_not_be_merged, true) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + region = RBA::Region::new + + region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]])) + region.insert(RBA::Box::new(200, 0, 300, 100)) + + assert_equal(region.processed(ShrinkToHalfOperator::new).to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)") + assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") + region.process(ShrinkToHalfOperator::new) + assert_equal(region.to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)") + + end + + # Generic processors + def test_generic_processors_pep + + p = SomePolygonToEdgePairOperator::new + + region = RBA::Region::new + + region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]])) + region.insert(RBA::Box::new(200, 0, 300, 100)) + + assert_equal(region.processed(p).to_s, "(0,0;0,100)/(100,0;100,100);(200,0;200,100)/(300,0;300,100)") + assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") + + end + + # Generic processors + def test_generic_processors_pe + + p = SomePolygonToEdgeOperator::new + + region = RBA::Region::new + + region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]])) + region.insert(RBA::Box::new(200, 0, 300, 100)) + + assert_equal(region.processed(p).to_s, "(0,0;100,100);(200,0;300,100)") + assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") + + end + # rasterize def test_rasterize diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 7b9a65288..70429a1a0 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -29,6 +29,52 @@ def csort(s) # splits at ");(" without consuming the brackets s.split(/(?<=\));(?=\()/).sort.join(";") end + +class TextStringLengthFilter < RBA::TextFilter + + # Constructor + def initialize(string_length) + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + @string_length = string_length + end + + # Select texts with given string length + def selected(text) + return text.string.size == @string_length + end + +end + +class ReplaceTextString < RBA::TextOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + end + + # Replaces the string by a number representing the string length + def process(text) + new_text = text.dup # need a copy as we cannot modify the text passed + new_text.string = text.string.size.to_s + return [ new_text ] + end + +end + +class SomeTextToPolygonOperator < RBA::TextToPolygonOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + end + + # Replaces the string by a number representing the string length + def process(text) + s = text.string.size * 10 + return [ RBA::Polygon::new(text.bbox.enlarged(s)) ] + end + +end class DBTexts_TestClass < TestBase @@ -324,6 +370,79 @@ class DBTexts_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = TextStringLengthFilter::new(3) + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = TextStringLengthFilter::new(3) + + texts = RBA::Texts::new + texts.insert(RBA::Text::new("long", [ RBA::Trans::M45, 10, 0 ])) + texts.insert(RBA::Text::new("tla", [ RBA::Trans::R0, 0, 0 ])) + texts.insert(RBA::Text::new("00", [ RBA::Trans::R90, 0, 20 ])) + + assert_equal(texts.filtered(f).to_s, "('tla',r0 0,0)") + assert_equal(texts.to_s, "('long',m45 10,0);('tla',r0 0,0);('00',r90 0,20)") + texts.filter(f) + assert_equal(texts.to_s, "('tla',r0 0,0)") + + end + + # Generic processors + def test_generic_processors_tt + + # Some basic tests for the processor class + + f = ReplaceTextString::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + texts = RBA::Texts::new + + texts.insert(RBA::Text::new("abc", RBA::Trans::new)) + texts.insert(RBA::Text::new("a long text", RBA::Trans::M45)) + + assert_equal(texts.processed(ReplaceTextString::new).to_s, "('3',r0 0,0);('11',m45 0,0)") + assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)") + texts.process(ReplaceTextString::new) + assert_equal(texts.to_s, "('3',r0 0,0);('11',m45 0,0)") + + end + + # Generic processors + def test_generic_processors_tp + + p = SomeTextToPolygonOperator::new + + texts = RBA::Texts::new + + texts.insert(RBA::Text::new("abc", RBA::Trans::new)) + texts.insert(RBA::Text::new("a long text", RBA::Trans::M45)) + + assert_equal(texts.processed(p).to_s, "(-30,-30;-30,30;30,30;30,-30);(-110,-110;-110,110;110,110;110,-110)") + assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)") + + end + end 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")