mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into xor-performance
This commit is contained in:
commit
bc3895ac9f
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
0.29.0 (2024-04-01):
|
||||
* TODO
|
||||
|
||||
0.28.17 (2024-02-16):
|
||||
* Enhancement: %GITHUB%/issues/1626 Technology specific grids
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
klayout (0.29.0-1) unstable; urgency=low
|
||||
|
||||
* New features and bugfixes
|
||||
- See changelog
|
||||
|
||||
-- Matthias Köfferlein <matthias@koefferlein.de> Fri, 01 Apr 2024 12:00:00 +0100
|
||||
|
||||
klayout (0.28.17-1) unstable; urgency=low
|
||||
|
||||
* New features and bugfixes
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -147,6 +147,24 @@ void AsIfFlatEdgePairs::invalidate_bbox ()
|
|||
m_bbox_valid = false;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdgePairs::processed (const EdgePairProcessorBase &filter) const
|
||||
{
|
||||
std::unique_ptr<FlatEdgePairs> edge_pairs (new FlatEdgePairs ());
|
||||
|
||||
std::vector<db::EdgePair> res_edge_pairs;
|
||||
|
||||
for (EdgePairsIterator e = begin (); ! e.at_end (); ++e) {
|
||||
res_edge_pairs.clear ();
|
||||
filter.process (*e, res_edge_pairs);
|
||||
for (std::vector<db::EdgePair>::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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 <sstream>
|
||||
|
||||
|
|
@ -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<db::Edge, size_t, db::Polygon, size_t> 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<FlatEdges> output (new FlatEdges (true));
|
||||
|
||||
if (! inverse) {
|
||||
|
||||
edge_to_region_interaction_filter<FlatEdges> filter (output.get (), mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
} else {
|
||||
|
||||
std::set<db::Edge> result;
|
||||
edge_to_region_interaction_filter<std::set<db::Edge> > filter (&result, mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
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<EdgesDelegate *, EdgesDelegate *> region_pair ()
|
||||
{
|
||||
return std::make_pair (m_e1.release (), m_e2.release ());
|
||||
}
|
||||
|
||||
const std::vector<db::Shapes *> &results () { return m_results; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<FlatEdges> m_e1, m_e2;
|
||||
std::vector<db::Shapes *> 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<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
bool counting = !(min_count == 1 && max_count == std::numeric_limits<size_t>::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<db::Polygon> op (mode, inverse ? db::edge_to_polygon_interacting_local_operation<db::Polygon>::Inverse : db::edge_to_polygon_interacting_local_operation<db::Polygon>::Normal, min_count, max_count);
|
||||
|
||||
db::local_processor<db::Edge, db::Polygon, db::Edge> proc;
|
||||
proc.set_base_verbosity (base_verbosity ());
|
||||
proc.set_description (progress_desc ());
|
||||
proc.set_report_progress (report_progress ());
|
||||
|
||||
std::vector<generic_shape_iterator<db::Polygon> > 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<bool> (), &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<size_t>::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<FlatEdges> output (new FlatEdges (true));
|
||||
db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count);
|
||||
|
||||
if (! inverse) {
|
||||
db::local_processor<db::Edge, db::Edge, db::Edge> proc;
|
||||
proc.set_base_verbosity (base_verbosity ());
|
||||
proc.set_description (progress_desc ());
|
||||
proc.set_report_progress (report_progress ());
|
||||
|
||||
edge_interaction_filter<FlatEdges> filter (*output, mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
std::vector<generic_shape_iterator<db::Edge> > 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<bool> (), &op, oph.results ());
|
||||
|
||||
std::set<db::Edge> result;
|
||||
edge_interaction_filter<std::set<db::Edge> > filter (result, mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<db::Edge, size_t, db::Polygon, size_t> scanner (report_progress (), progress_desc ());
|
||||
bool counting = !(min_count == 1 && max_count == std::numeric_limits<size_t>::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<db::Polygon> op (mode, db::edge_to_polygon_interacting_local_operation<db::Polygon>::Both, min_count, max_count);
|
||||
|
||||
AddressablePolygonDelivery p = region.addressable_merged_polygons ();
|
||||
db::local_processor<db::Edge, db::Polygon, db::Edge> 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<generic_shape_iterator<db::Polygon> > 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<FlatEdges> output (new FlatEdges (true));
|
||||
std::unique_ptr<FlatEdges> output2 (new FlatEdges (true));
|
||||
proc.run_flat (edges, others, std::vector<bool> (), &op, oph.results ());
|
||||
|
||||
std::set<db::Edge> result;
|
||||
edge_to_region_interaction_filter<std::set<db::Edge> > filter (&result, mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
bool counting = !(min_count == 1 && max_count == std::numeric_limits<size_t>::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<db::Edge, db::Edge, db::Edge> 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<generic_shape_iterator<db::Edge> > 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<FlatEdges> output (new FlatEdges (true));
|
||||
std::unique_ptr<FlatEdges> output2 (new FlatEdges (true));
|
||||
proc.run_flat (edges, others, std::vector<bool> (), &op, oph.results ());
|
||||
|
||||
std::set<db::Edge> results;
|
||||
edge_interaction_filter<std::set<db::Edge> > filter (results, mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
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<FlatEdges> output (new FlatEdges (true));
|
||||
edge_interaction_filter<FlatEdges> filter (*output, EdgesInteract);
|
||||
edge_interaction_filter<FlatEdges> filter (*output, EdgesInteract, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
return output.release ();
|
||||
|
|
@ -324,7 +307,7 @@ AsIfFlatEdges::pull_generic (const Region &other) const
|
|||
|
||||
std::unique_ptr<FlatRegion> output (new FlatRegion (true));
|
||||
|
||||
edge_to_region_interaction_filter<FlatRegion> filter (output.get (), EdgesInteract);
|
||||
edge_to_polygon_interaction_filter<FlatRegion> filter (output.get (), EdgesInteract, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<size_t>::max ());
|
||||
}
|
||||
|
||||
std::pair<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<size_t>::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<size_t>::max ());
|
||||
}
|
||||
|
||||
std::pair<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<size_t>::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<size_t>::max ());
|
||||
}
|
||||
|
||||
std::pair<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<size_t>::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<size_t>::max ());
|
||||
}
|
||||
|
||||
std::pair<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<FlatEdges> 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<FlatEdges> dots (new FlatEdges (false));
|
||||
for (EdgesIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
if (e->is_degenerate ()) {
|
||||
dots->insert (*e);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<EdgesDelegate *, EdgesDelegate *> res (0, 0);
|
||||
|
||||
if (mode == EdgePolygonOp::Both) {
|
||||
res = dots->selected_interacting_pair_generic (other, include_borders ? EdgesInteract : EdgesInside, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
} else if (mode == EdgePolygonOp::Inside) {
|
||||
res.first = dots->selected_interacting_generic (other, include_borders ? EdgesInteract : EdgesInside, false, size_t (1), std::numeric_limits<size_t>::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<size_t>::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 ());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<EdgesDelegate *, EdgesDelegate *> andnot_with (const Edges &other) const
|
||||
{
|
||||
return boolean_andnot (&other);
|
||||
}
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> andnot_with (const Region &other) const
|
||||
{
|
||||
return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/);
|
||||
}
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other) const;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<FlatEdges> 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<db::Edge> 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 ();
|
||||
|
|
|
|||
|
|
@ -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<size_t>::max ()).first;
|
||||
return selected_interacting_generic (other, 1, false, Positive, size_t (1), std::numeric_limits<size_t>::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<size_t>::max ()).first;
|
||||
return selected_interacting_generic (other, 1, false, Negative, size_t (1), std::numeric_limits<size_t>::max ()).first;
|
||||
}
|
||||
|
||||
virtual std::pair<RegionDelegate *, RegionDelegate *> selected_outside_pair (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (0), std::numeric_limits<size_t>::max ());
|
||||
return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_inside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, Positive, size_t (0), std::numeric_limits<size_t>::max ()).first;
|
||||
return selected_interacting_generic (other, -1, true, Positive, size_t (1), std::numeric_limits<size_t>::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<size_t>::max ()).first;
|
||||
return selected_interacting_generic (other, -1, true, Negative, size_t (1), std::numeric_limits<size_t>::max ()).first;
|
||||
}
|
||||
|
||||
virtual std::pair<RegionDelegate *, RegionDelegate *> selected_inside_pair (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (0), std::numeric_limits<size_t>::max ());
|
||||
return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_enclosing (const Region &other, size_t min_count, size_t max_count) const
|
||||
|
|
|
|||
|
|
@ -164,6 +164,24 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const
|
|||
return new_texts.release ();
|
||||
}
|
||||
|
||||
TextsDelegate *
|
||||
AsIfFlatTexts::processed (const TextProcessorBase &filter) const
|
||||
{
|
||||
std::unique_ptr<FlatTexts> texts (new FlatTexts ());
|
||||
|
||||
std::vector<db::Text> res_texts;
|
||||
|
||||
for (TextsIterator e = begin (); ! e.at_end (); ++e) {
|
||||
res_texts.clear ();
|
||||
filter.process (*e, res_texts);
|
||||
for (std::vector<db::Text>::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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<const Obj *> m_ignore_single;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<db::EdgePair, db::EdgePair, db::DeepEdgePairs> (deep_layer (), filter);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
DeepEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -914,10 +914,19 @@ EdgesDelegate *DeepEdges::merged () const
|
|||
return res.release ();
|
||||
}
|
||||
|
||||
DeepLayer
|
||||
std::pair<DeepLayer, DeepLayer>
|
||||
DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const
|
||||
{
|
||||
std::vector<unsigned int> 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<DeepLayer, DeepLayer>
|
||||
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<db::Layout &> (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<unsigned int> 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<EdgesDelegate *, EdgesDelegate *> 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<size_t>::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<size_t>::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<size_t>::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 <const DeepEdges *> (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<EdgesDelegate *, EdgesDelegate *>
|
||||
DeepEdges::andnot_with (const Edges &other) const
|
||||
{
|
||||
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
|
||||
const DeepEdges *other_deep = dynamic_cast <const DeepEdges *> (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<db::Edge, db::Edge, db::Edge>
|
||||
{
|
||||
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<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == (m_output_mode == Both ? 2 : 1));
|
||||
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
std::unordered_set<db::Edge> *result2 = 0;
|
||||
if (m_output_mode == Both) {
|
||||
result2 = &results[1];
|
||||
}
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
|
||||
std::set<db::Edge> others;
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (&subject, 0);
|
||||
}
|
||||
|
||||
for (std::set<db::Edge>::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<db::Edge> interacting;
|
||||
edge_interaction_filter<std::unordered_set<db::Edge> > filter (interacting, m_mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::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<std::unordered_set<db::Edge> > filter (result, m_mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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<db::Edge, db::Edge, db::Edge>
|
||||
{
|
||||
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<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == 1);
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
|
||||
std::set<db::Edge> others;
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (&subject, 1);
|
||||
}
|
||||
|
||||
for (std::set<db::Edge>::const_iterator o = others.begin (); o != others.end (); ++o) {
|
||||
scanner.insert (o.operator-> (), 0);
|
||||
}
|
||||
|
||||
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result, EdgesInteract);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
|
||||
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<db::Edge, db::PolygonRef, db::Edge>
|
||||
{
|
||||
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<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1));
|
||||
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
std::unordered_set<db::Edge> *result2 = 0;
|
||||
if (m_output_mode == Both) {
|
||||
result2 = &results[1];
|
||||
}
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner;
|
||||
|
||||
std::set<db::PolygonRef> others;
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert1 (&subject, 0);
|
||||
}
|
||||
|
||||
std::list<db::Polygon> heap;
|
||||
for (std::set<db::PolygonRef>::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<db::Edge> interacting;
|
||||
edge_to_region_interaction_filter<std::unordered_set<db::Edge> > filter (&interacting, m_mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::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<std::unordered_set<db::Edge> > filter (&result, m_mode);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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<db::PolygonRef> &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<db::PolygonRef> *mp_result;
|
||||
};
|
||||
|
||||
class Edge2PolygonPullLocalOperation
|
||||
: public local_operation<db::Edge, db::PolygonRef, db::PolygonRef>
|
||||
{
|
||||
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<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::PolygonRef> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == 1);
|
||||
std::unordered_set<db::PolygonRef> &result = results.front ();
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner;
|
||||
|
||||
std::set<db::PolygonRef> others;
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert1 (&subject, 1);
|
||||
}
|
||||
|
||||
std::list<db::Polygon> heap;
|
||||
for (std::set<db::PolygonRef>::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<ResultInserter> filter (&inserter, EdgesInteract);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
}
|
||||
|
||||
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<db::DeepRegion> dr_holder;
|
||||
const db::DeepRegion *other_deep = dynamic_cast<const db::DeepRegion *> (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<size_t>::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<db::PolygonRef> op (mode, inverse ? db::edge_to_polygon_interacting_local_operation<db::PolygonRef>::Inverse : db::edge_to_polygon_interacting_local_operation<db::PolygonRef>::Normal, min_count, max_count);
|
||||
|
||||
db::local_processor<db::Edge, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<db::DeepRegion> dr_holder;
|
||||
const db::DeepRegion *other_deep = dynamic_cast<const db::DeepRegion *> (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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::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<db::PolygonRef> op (mode, db::edge_to_polygon_interacting_local_operation<db::PolygonRef>::Both, min_count, max_count);
|
||||
|
||||
db::local_processor<db::Edge, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&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<size_t>::max ());
|
||||
|
||||
std::unique_ptr<db::DeepEdges> dr_holder;
|
||||
const db::DeepEdges *other_deep = dynamic_cast<const db::DeepEdges *> (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<db::Edge, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&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<EdgesDelegate *, EdgesDelegate *>
|
||||
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<size_t>::max ());
|
||||
|
||||
std::unique_ptr<db::DeepEdges> dr_holder;
|
||||
const db::DeepEdges *other_deep = dynamic_cast<const db::DeepEdges *> (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<db::Edge, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&edges.layout ()), const_cast<db::Cell *> (&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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<DeepLayer, DeepLayer> and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const;
|
||||
std::pair<DeepLayer, DeepLayer> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> 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 <class Result, class OutputContainer> OutputContainer *processed_impl (const edge_processor<Result> &filter) const;
|
||||
|
|
|
|||
|
|
@ -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<db::DeepEdges> 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<db::Edge> 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 ())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const;
|
||||
DeepRegion *apply_filter (const PolygonFilterBase &filter) const;
|
||||
|
||||
template <class Result, class OutputContainer> OutputContainer *processed_impl (const polygon_processor<Result> &filter) const;
|
||||
|
||||
template <class Proc>
|
||||
void configure_proc (Proc &proc) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<db::Text, db::Text, db::DeepTexts> (deep_layer (), filter);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<db::Point> *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<db::Point> *dots, std::set<db::Point> *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<db::Point> *mp_dots, *mp_dots2;
|
||||
db::EdgeBoolOp m_op;
|
||||
};
|
||||
|
||||
|
|
@ -253,15 +285,8 @@ struct EdgeBooleanClusterCollector
|
|||
: public db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> >
|
||||
{
|
||||
EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op, OutputContainer *output2 = 0)
|
||||
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (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<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, op), op != EdgeAnd /*report single*/),
|
||||
mp_output (output), mp_intersections (intersections)
|
||||
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (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<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> >::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<bool, db::Point> 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<bool, db::Point> 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<bool, db::Point> 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<bool, db::Point> 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<bool, db::Point> 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<db::Point> m_dots, m_dots2;
|
||||
|
||||
static void add_orphan_dots (const std::set<db::Point> &dots, OutputContainer *output)
|
||||
{
|
||||
if (dots.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Point, size_t> 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<db::Edge, size_t, db::Point, size_t> 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<db::Point>::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) {
|
||||
intersections_to_edge_scanner.insert2 (p.operator-> (), 0);
|
||||
for (std::set<db::Point>::const_iterator p = dots.begin (); p != dots.end (); ++p) {
|
||||
dots_to_edge_scanner.insert2 (p.operator-> (), 0);
|
||||
}
|
||||
|
||||
std::set<db::Point> points_to_remove;
|
||||
RemovePointsOnEdges rpoe (points_to_remove);
|
||||
intersections_to_edge_scanner.process (rpoe, 1, db::box_convert<db::Edge> (), db::box_convert<db::Point> ());
|
||||
dots_to_edge_scanner.process (rpoe, 1, db::box_convert<db::Edge> (), db::box_convert<db::Point> ());
|
||||
|
||||
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<db::Point> m_intersections;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class RegionDelegate;
|
|||
class EdgesDelegate;
|
||||
class Layout;
|
||||
|
||||
typedef shape_collection_processor<db::EdgePair, db::EdgePair> EdgePairProcessorBase;
|
||||
typedef shape_collection_processor<db::EdgePair, db::Polygon> EdgePairToPolygonProcessorBase;
|
||||
typedef shape_collection_processor<db::EdgePair, db::Edge> 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;
|
||||
|
|
|
|||
|
|
@ -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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<Edges, Edges> selected_interacting_differential (const Region &other) const
|
||||
std::pair<Edges, Edges> selected_interacting_differential (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits<size_t>::max ()) const
|
||||
{
|
||||
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other);
|
||||
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other, min_count, max_count);
|
||||
return std::pair<Edges, Edges> (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<size_t>::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<size_t>::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<Edges, Edges> selected_interacting_differential (const Edges &other) const
|
||||
std::pair<Edges, Edges> selected_interacting_differential (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits<size_t>::max ()) const
|
||||
{
|
||||
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other);
|
||||
std::pair<db::EdgesDelegate *, db::EdgesDelegate *> p = mp_delegate->selected_interacting_pair (other, min_count, max_count);
|
||||
return std::pair<Edges, Edges> (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<size_t>::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<size_t>::max ()) const
|
||||
{
|
||||
return Edges (mp_delegate->selected_not_interacting (other));
|
||||
return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -255,12 +255,12 @@ public:
|
|||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other) const = 0;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const = 0;
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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;
|
||||
|
|
|
|||
|
|
@ -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<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == (m_output_mode == Both ? 2 : 1));
|
||||
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
std::unordered_set<db::Edge> *result2 = 0;
|
||||
if (m_output_mode == Both) {
|
||||
result2 = &results[1];
|
||||
}
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
|
||||
std::set<db::Edge> others;
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (&subject, 0);
|
||||
}
|
||||
|
||||
for (std::set<db::Edge>::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<db::Edge> interacting;
|
||||
edge_interaction_filter<std::unordered_set<db::Edge> > filter (interacting, m_mode, m_min_count, m_max_count);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::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<std::unordered_set<db::Edge> > filter (result, m_mode, m_min_count, m_max_count);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == 1);
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
|
||||
std::set<db::Edge> others;
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (&subject, 1);
|
||||
}
|
||||
|
||||
for (std::set<db::Edge>::const_iterator o = others.begin (); o != others.end (); ++o) {
|
||||
scanner.insert (o.operator-> (), 0);
|
||||
}
|
||||
|
||||
edge_interaction_filter<std::unordered_set<db::Edge> > filter (result, EdgesInteract, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
|
||||
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 <class TI>
|
||||
edge_to_polygon_interacting_local_operation<TI>::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 <class TI>
|
||||
db::Coord edge_to_polygon_interacting_local_operation<TI>::dist () const
|
||||
{
|
||||
// touching is sufficient
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const db::Polygon *deref (const db::Polygon &poly, std::list<db::Polygon> &)
|
||||
{
|
||||
return &poly;
|
||||
}
|
||||
|
||||
static const db::Polygon *deref (const db::PolygonRef &pref, std::list<db::Polygon> &heap)
|
||||
{
|
||||
heap.push_back (pref.obj ().transformed (pref.trans ()));
|
||||
return & heap.back ();
|
||||
}
|
||||
|
||||
template <class TI>
|
||||
void edge_to_polygon_interacting_local_operation<TI>::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::Edge, TI> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1));
|
||||
|
||||
std::unordered_set<db::Edge> &result = results.front ();
|
||||
|
||||
std::unordered_set<db::Edge> *result2 = 0;
|
||||
if (m_output_mode == Both) {
|
||||
result2 = &results[1];
|
||||
}
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner;
|
||||
|
||||
std::set<TI> others;
|
||||
for (typename shape_interactions<db::Edge, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (typename shape_interactions<db::Edge, TI>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (typename shape_interactions<db::Edge, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert1 (&subject, 0);
|
||||
}
|
||||
|
||||
std::list<db::Polygon> heap;
|
||||
for (typename std::set<TI>::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<db::Edge> interacting;
|
||||
edge_to_polygon_interaction_filter<std::unordered_set<db::Edge> > filter (&interacting, m_mode, m_min_count, m_max_count);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
for (typename shape_interactions<db::Edge, TI>::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<std::unordered_set<db::Edge> > filter (&result, m_mode, m_min_count, m_max_count);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class TI>
|
||||
OnEmptyIntruderHint edge_to_polygon_interacting_local_operation<TI>::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 <class TI>
|
||||
std::string edge_to_polygon_interacting_local_operation<TI>::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<db::Polygon>;
|
||||
template class edge_to_polygon_interacting_local_operation<db::PolygonRef>;
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Edge2EdgePullLocalOperation implementation
|
||||
|
||||
namespace {
|
||||
|
||||
struct ResultInserter
|
||||
{
|
||||
typedef db::Polygon value_type;
|
||||
|
||||
ResultInserter (db::Layout *layout, std::unordered_set<db::PolygonRef> &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<db::PolygonRef> *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<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::PolygonRef> > &results, const db::LocalProcessorBase * /*proc*/) const
|
||||
{
|
||||
tl_assert (results.size () == 1);
|
||||
std::unordered_set<db::PolygonRef> &result = results.front ();
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner;
|
||||
|
||||
std::set<db::PolygonRef> others;
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j).second);
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert1 (&subject, 1);
|
||||
}
|
||||
|
||||
std::list<db::Polygon> heap;
|
||||
for (std::set<db::PolygonRef>::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<ResultInserter> filter (&inserter, EdgesInteract, size_t (1), std::numeric_limits<size_t>::max ());
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
}
|
||||
|
||||
OnEmptyIntruderHint Edge2PolygonPullLocalOperation::on_empty_intruder_hint () const
|
||||
{
|
||||
return Drop;
|
||||
}
|
||||
|
||||
std::string Edge2PolygonPullLocalOperation::description () const
|
||||
{
|
||||
return tl::to_string (tr ("Select interacting regions"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::Edge, db::Edge, db::Edge>
|
||||
{
|
||||
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<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &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<db::Edge, db::Edge, db::Edge>
|
||||
{
|
||||
public:
|
||||
Edge2EdgePullLocalOperation ();
|
||||
|
||||
virtual db::Coord dist () const;
|
||||
virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::Edge, db::Edge> &interactions, std::vector<std::unordered_set<db::Edge> > &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 TI>
|
||||
class DB_PUBLIC edge_to_polygon_interacting_local_operation
|
||||
: public local_operation<db::Edge, TI, db::Edge>
|
||||
{
|
||||
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<db::Edge, TI> &interactions, std::vector<std::unordered_set<db::Edge> > &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<db::Edge, db::PolygonRef, db::PolygonRef>
|
||||
{
|
||||
public:
|
||||
Edge2PolygonPullLocalOperation ();
|
||||
|
||||
virtual db::Coord dist () const;
|
||||
virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::PolygonRef> > &results, const db::LocalProcessorBase * /*proc*/) const;
|
||||
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
|
||||
virtual std::string description () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -189,17 +189,22 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector<db::Edge> &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)
|
||||
|
|
|
|||
|
|
@ -339,16 +339,39 @@ class edge_interaction_filter
|
|||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
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<size_t>::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<const db::Edge *> m_seen;
|
||||
std::map<const db::Edge *, size_t> 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 OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class edge_to_region_interaction_filter
|
||||
class edge_to_polygon_interaction_filter
|
||||
: public db::box_scanner_receiver2<db::Edge, size_t, db::Polygon, size_t>
|
||||
{
|
||||
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<size_t>::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<const OutputType *, size_t> m_counts;
|
||||
std::set<const OutputType *> m_seen;
|
||||
EdgeInteractionMode m_mode;
|
||||
size_t m_min_count, m_max_count;
|
||||
bool m_counting;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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<EdgesDelegate *, EdgesDelegate *> selected_interacting_pair (const Region &, size_t, size_t) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); }
|
||||
virtual std::pair<EdgesDelegate *, EdgesDelegate *> 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 (); }
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<db::Edge> ());
|
||||
|
|
@ -145,9 +143,7 @@ FlatEdges::ensure_merged_edges_valid () const
|
|||
std::map<db::properties_id_type, std::vector<const db::Edge *> > 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) {
|
||||
|
|
|
|||
|
|
@ -2575,6 +2575,7 @@ template class DB_PUBLIC local_processor_cell_contexts<db::PolygonRef, db::Edge,
|
|||
template class DB_PUBLIC local_processor_cell_contexts<db::PolygonRef, db::PolygonRef, db::EdgePair>;
|
||||
template class DB_PUBLIC local_processor_cell_contexts<db::Polygon, db::Polygon, db::EdgePair>;
|
||||
template class DB_PUBLIC local_processor_cell_contexts<db::Edge, db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_cell_contexts<db::Edge, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_cell_contexts<db::Edge, db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_cell_contexts<db::Edge, db::Edge, db::EdgePair>;
|
||||
|
||||
|
|
@ -2591,6 +2592,7 @@ template class DB_PUBLIC shape_interactions<db::PolygonRef, db::Text>;
|
|||
template class DB_PUBLIC shape_interactions<db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC shape_interactions<db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC shape_interactions<db::Edge, db::PolygonRef>;
|
||||
template class DB_PUBLIC shape_interactions<db::Edge, db::Polygon>;
|
||||
template class DB_PUBLIC shape_interactions<db::TextRef, db::TextRef>;
|
||||
template class DB_PUBLIC shape_interactions<db::TextRef, db::PolygonRef>;
|
||||
|
||||
|
|
@ -2621,6 +2623,7 @@ template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, d
|
|||
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Edge, db::EdgePair>;
|
||||
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::PolygonRef, db::PolygonRef>;
|
||||
|
||||
|
|
@ -2644,6 +2647,7 @@ template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRef,
|
|||
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Polygon, db::EdgePair>;
|
||||
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Edge, db::EdgePair>;
|
||||
|
||||
// explicit instantiations
|
||||
|
|
@ -2671,6 +2675,7 @@ template class DB_PUBLIC local_processor<db::Polygon, db::Polygon, db::EdgePair>
|
|||
template class DB_PUBLIC local_processor<db::Polygon, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor<db::Edge, db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC local_processor<db::Edge, db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC local_processor<db::Edge, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_processor<db::Edge, db::PolygonRef, db::PolygonRef>;
|
||||
template class DB_PUBLIC local_processor<db::Edge, db::Edge, db::EdgePair>;
|
||||
template class DB_PUBLIC local_processor<db::TextRef, db::PolygonRef, db::TextRef>;
|
||||
|
|
|
|||
|
|
@ -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<const db::SubCircuit *> path;
|
||||
check_must_connect_impl (c, a, b, c, a, b, path);
|
||||
}
|
||||
|
||||
static std::string path_msg (const std::vector<const db::SubCircuit *> &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<const db::SubCircuit *> &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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<std::string> &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<const db::SubCircuit *> &path);
|
||||
|
||||
// implementation of NetlistManipulationCallbacks
|
||||
virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ template class DB_PUBLIC local_operation<db::Polygon, db::Polygon, db::EdgePair>
|
|||
template class DB_PUBLIC local_operation<db::Polygon, db::TextRef, db::TextRef>;
|
||||
template class DB_PUBLIC local_operation<db::Edge, db::Edge, db::Edge>;
|
||||
template class DB_PUBLIC local_operation<db::Edge, db::PolygonRef, db::Edge>;
|
||||
template class DB_PUBLIC local_operation<db::Edge, db::Polygon, db::Edge>;
|
||||
template class DB_PUBLIC local_operation<db::Edge, db::PolygonRef, db::PolygonRef>;
|
||||
template class DB_PUBLIC local_operation<db::Edge, db::Edge, db::EdgePair>;
|
||||
template class DB_PUBLIC local_operation<db::TextRef, db::PolygonRef, db::PolygonRef>;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<size_t, const NetGraphNode *> &nodes_by_edges1, const std::multimap<size_t, const NetGraphNode *> &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<size_t, const NetGraphNode
|
|||
msg = tl::to_string (tr ("Connecting nets %s and %s is making a better match to net %s from reference netlist (fuzziness %d nodes)"));
|
||||
}
|
||||
|
||||
for (auto i = nodes_by_edges1.begin (); i != nodes_by_edges1.end (); ++i) {
|
||||
size_t tries = max_search;
|
||||
|
||||
for (auto i = nodes_by_edges1.begin (); i != nodes_by_edges1.end () && tries > 0; ++i) {
|
||||
|
||||
if (i->first < min_edges) {
|
||||
continue;
|
||||
|
|
@ -1376,7 +1378,7 @@ analyze_nodes_for_close_matches (const std::multimap<size_t, const NetGraphNode
|
|||
|
||||
std::set<const db::NetGraphNode *> 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::multimap<size_t, const NetGraphNode
|
|||
|
||||
auto k = nodes_by_edges2.lower_bound (ne);
|
||||
|
||||
size_t tries = max_search;
|
||||
for ( ; k != nodes_by_edges2.end () && j->first + 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<size_t, const NetGraphNode
|
|||
void
|
||||
NetlistCompareCore::analyze_failed_matches () const
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Analyzing failed matches");
|
||||
|
||||
// Determine the range of nodes with same identity
|
||||
|
||||
std::vector<NetGraphNode::edge_type> no_edges;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -92,30 +92,30 @@ std::string nets2string (const std::pair<const db::Net *, const db::Net *> &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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -556,16 +556,23 @@ Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore
|
|||
|
||||
fill_texts (si.first, pat, pattern, dot_delivery<db::FlatEdges> (), 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<dot_delivery<db::Shapes> > pipe = text_shape_receiver<dot_delivery<db::Shapes> > (dot_delivery<db::Shapes> (), 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
|
|||
|
||||
std::set<db::Edge> partial_edges;
|
||||
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, 0, db::EdgeNot);
|
||||
ec.add (o, 0);
|
||||
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
|
||||
|
|
|
|||
|
|
@ -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 Result>
|
||||
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<Result> &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<db::Polygon, db::Polygon> PolygonProcessorBase;
|
||||
typedef shape_collection_processor<db::Polygon, db::Edge> PolygonToEdgeProcessorBase;
|
||||
typedef shape_collection_processor<db::Polygon, db::EdgePair> 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;
|
||||
|
|
|
|||
|
|
@ -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<db::Edge> &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<db::Edge> &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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::Edge> &result) const;
|
||||
|
||||
private:
|
||||
EdgeMode m_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class RegionDelegate;
|
|||
class EdgesDelegate;
|
||||
class Layout;
|
||||
|
||||
typedef shape_collection_processor<db::Text, db::Text> TextProcessorBase;
|
||||
typedef shape_collection_processor<db::Text, db::Polygon> TextToPolygonProcessorBase;
|
||||
|
||||
typedef db::generic_shape_iterator_delegate_base <db::Text> 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;
|
||||
|
|
|
|||
|
|
@ -316,13 +316,13 @@ static db::CompoundRegionOperationNode *new_minkowski_sum_node4 (db::CompoundReg
|
|||
return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation<std::vector<db::Point> > (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<db::CompoundRegionOperationNode> 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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<db::CompoundRegionOperationNode> 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<db::Edge::distance_type>::max (), "max"),
|
||||
"@brief Creates a node filtering edges by their length.\n"
|
||||
|
|
|
|||
|
|
@ -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 FilterBase>
|
||||
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 ProcessorBase>
|
||||
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<result_type> &res) const
|
||||
{
|
||||
res = do_process (shape);
|
||||
}
|
||||
|
||||
std::vector<result_type> issue_do_process (const shape_type &) const
|
||||
{
|
||||
return std::vector<result_type> ();
|
||||
}
|
||||
|
||||
std::vector<result_type> do_process (const shape_type &shape) const
|
||||
{
|
||||
if (f_process.can_issue ()) {
|
||||
return f_process.issue<shape_processor_impl, std::vector<result_type>, 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
|
||||
|
|
|
|||
|
|
@ -36,7 +36,182 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
static db::EdgePairs *new_v ()
|
||||
// ---------------------------------------------------------------------------------
|
||||
// EdgePairFilter binding
|
||||
|
||||
class EdgePairFilterImpl
|
||||
: public shape_filter_impl<db::EdgePairFilterBase>
|
||||
{
|
||||
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, bool, const db::EdgePair &> (&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<gsi::EdgePairFilterImpl> 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<shape_processor_impl<db::EdgePairProcessorBase> > decl_EdgePairProcessor ("db", "EdgePairOperator",
|
||||
shape_processor_impl<db::EdgePairProcessorBase>::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<shape_processor_impl<db::EdgePairToPolygonProcessorBase> > decl_EdgePairToPolygonProcessor ("db", "EdgePairToPolygonOperator",
|
||||
shape_processor_impl<db::EdgePairToPolygonProcessorBase>::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<shape_processor_impl<db::EdgePairToEdgeProcessorBase> > decl_EdgePairToEdgeProcessor ("db", "EdgePairToEdgeOperator",
|
||||
shape_processor_impl<db::EdgePairToEdgeProcessorBase>::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<db::EdgePairProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static void process_epep (db::EdgePairs *r, const shape_processor_impl<db::EdgePairProcessorBase> *f)
|
||||
{
|
||||
r->process (*f);
|
||||
}
|
||||
|
||||
static db::Edges processed_epe (const db::EdgePairs *r, const shape_processor_impl<db::EdgePairToEdgeProcessorBase> *f)
|
||||
{
|
||||
db::Edges out;
|
||||
r->processed (out, *f);
|
||||
return out;
|
||||
}
|
||||
|
||||
static db::Region processed_epp (const db::EdgePairs *r, const shape_processor_impl<db::EdgePairToPolygonProcessorBase> *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<db::EdgePairs> 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 "
|
||||
|
|
|
|||
|
|
@ -37,6 +37,196 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// EdgeFilter binding
|
||||
|
||||
class EdgeFilterImpl
|
||||
: public shape_filter_impl<db::EdgeFilterBase>
|
||||
{
|
||||
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, bool, const db::Edge &> (&EdgeFilterImpl::issue_selected, edge);
|
||||
} else {
|
||||
return issue_selected (edge);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if all edges match the criterion
|
||||
virtual bool selected (const std::unordered_set<db::Edge> &edges) const
|
||||
{
|
||||
for (std::unordered_set<db::Edge>::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<gsi::EdgeFilterImpl> 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<shape_processor_impl<db::EdgeProcessorBase> > decl_EdgeProcessorBase ("db", "EdgeOperator",
|
||||
shape_processor_impl<db::EdgeProcessorBase>::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<shape_processor_impl<db::EdgeToPolygonProcessorBase> > decl_EdgeToPolygonProcessor ("db", "EdgeToPolygonOperator",
|
||||
shape_processor_impl<db::EdgeToPolygonProcessorBase>::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<shape_processor_impl<db::EdgeToEdgePairProcessorBase> > decl_EdgeToEdgePairProcessor ("db", "EdgeToEdgePairOperator",
|
||||
shape_processor_impl<db::EdgeToEdgePairProcessorBase>::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<db::Edges> as_2edges_vector (const std::pair<db::Edges, db::Edges> &rp)
|
||||
{
|
||||
std::vector<db::Edges> 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<db::EdgeProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static void process_ee (db::Edges *r, const shape_processor_impl<db::EdgeProcessorBase> *f)
|
||||
{
|
||||
r->process (*f);
|
||||
}
|
||||
|
||||
static db::EdgePairs processed_eep (const db::Edges *r, const shape_processor_impl<db::EdgeToEdgePairProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static db::Region processed_ep (const db::Edges *r, const shape_processor_impl<db::EdgeToPolygonProcessorBase> *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<db::Edges> split_outside_with_region (const db::Edges *r, con
|
|||
return as_2edges_vector (r->selected_outside_differential (other));
|
||||
}
|
||||
|
||||
static std::vector<db::Edges> split_interacting_with_edges (const db::Edges *r, const db::Edges &other)
|
||||
static std::vector<db::Edges> 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<db::Edges> split_interacting_with_region (const db::Edges *r, const db::Region &other)
|
||||
static std::vector<db::Edges> 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<db::Edges> 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<db::Edges> 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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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<size_t>::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"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbPCellDeclaration.h"
|
||||
#include "dbLibrary.h"
|
||||
#include "dbLibraryManager.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
|
@ -701,23 +702,23 @@ Class<PCellDeclarationImpl> 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<tl::Variant> ());
|
||||
pd->set_choice_descriptions (std::vector<std::string> ());
|
||||
}
|
||||
|
||||
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<tl::Variant> vv = pd->get_choices ();
|
||||
std::vector<std::string> 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<db::PCellParameterDeclaration> 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<db::PCellParameterDeclaration> 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<db::PCellParameterDeclaration> 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"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -31,6 +31,39 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
template <class C>
|
||||
static std::vector<C> split_poly (const C *p)
|
||||
{
|
||||
std::vector<C> parts;
|
||||
db::split_polygon (*p, parts);
|
||||
return parts;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static void break_polygon (const C &poly, size_t max_vertex_count, double max_area_ratio, std::vector<C> &result)
|
||||
{
|
||||
if ((max_vertex_count > 0 && poly.vertices () > max_vertex_count) ||
|
||||
(max_area_ratio > 0 && poly.area_ratio () > max_area_ratio)) {
|
||||
|
||||
std::vector<C> 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 <class C>
|
||||
static std::vector<C> break_poly (const C *p, size_t max_vertex_count, double max_area_ratio)
|
||||
{
|
||||
std::vector<C> 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<C> split_poly (const C *p)
|
||||
{
|
||||
std::vector<C> 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<C>,
|
||||
"@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<C>, 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<C> split_spoly (const C *p)
|
||||
{
|
||||
std::vector<C> 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<C>,
|
||||
"@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<C>, 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."
|
||||
|
|
|
|||
|
|
@ -47,6 +47,190 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// PolygonFilter binding
|
||||
|
||||
class PolygonFilterImpl
|
||||
: public shape_filter_impl<db::AllMustMatchFilter>
|
||||
{
|
||||
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, bool, const db::Polygon &> (&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<gsi::PolygonFilterImpl> 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<shape_processor_impl<db::PolygonProcessorBase> > decl_PolygonOperator ("db", "PolygonOperator",
|
||||
shape_processor_impl<db::PolygonProcessorBase>::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<shape_processor_impl<db::PolygonToEdgeProcessorBase> > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeOperator",
|
||||
shape_processor_impl<db::PolygonToEdgeProcessorBase>::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<shape_processor_impl<db::PolygonToEdgePairProcessorBase> > decl_PolygonToEdgePairProcessor ("db", "PolygonToEdgePairOperator",
|
||||
shape_processor_impl<db::PolygonToEdgePairProcessorBase>::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<db::Region> as_2region_vector (const std::pair<db::Region, db::Region> &rp)
|
||||
{
|
||||
std::vector<db::Region> 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<db::PolygonProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static void process_pp (db::Region *r, const shape_processor_impl<db::PolygonProcessorBase> *f)
|
||||
{
|
||||
r->process (*f);
|
||||
}
|
||||
|
||||
static db::EdgePairs processed_pep (const db::Region *r, const shape_processor_impl<db::PolygonToEdgePairProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static db::Edges processed_pe (const db::Region *r, const shape_processor_impl<db::PolygonToEdgeProcessorBase> *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<std::vector<double> >
|
||||
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<db::Region> 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<db::Shapes>, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
|
||||
"@brief Decomposes the region into convex pieces.\n"
|
||||
|
|
@ -2363,6 +2594,42 @@ Class<db::Region> 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<db::Region> 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<db::metrics_type> decl_Metrics ("db", "Metrics",
|
|||
gsi::ClassExt<db::Region> inject_Metrics_in_Region (decl_Metrics.defs ());
|
||||
gsi::ClassExt<db::Edges> inject_Metrics_in_Edges (decl_Metrics.defs ());
|
||||
|
||||
gsi::Enum<db::PolygonToEdgeProcessor::EdgeMode> 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<db::Region> inject_EdgeMode_in_Region (decl_EdgeMode.defs ());
|
||||
|
||||
gsi::Enum<db::zero_distance_mode> 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<db::zero_distance_mode> 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<db::zero_distance_mode> 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:
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,160 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// TextFilter binding
|
||||
|
||||
class TextFilterImpl
|
||||
: public shape_filter_impl<db::TextFilterBase>
|
||||
{
|
||||
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, bool, const db::Text &> (&TextFilterImpl::issue_selected, text);
|
||||
} else {
|
||||
return issue_selected (text);
|
||||
}
|
||||
}
|
||||
|
||||
gsi::Callback f_selected;
|
||||
|
||||
private:
|
||||
// No copying
|
||||
TextFilterImpl &operator= (const TextFilterImpl &);
|
||||
TextFilterImpl (const TextFilterImpl &);
|
||||
};
|
||||
|
||||
Class<gsi::TextFilterImpl> 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<shape_processor_impl<db::TextProcessorBase> > decl_TextProcessor ("db", "TextOperator",
|
||||
shape_processor_impl<db::TextProcessorBase>::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<shape_processor_impl<db::TextToPolygonProcessorBase> > decl_TextToPolygonProcessor ("db", "TextToPolygonOperator",
|
||||
shape_processor_impl<db::TextToPolygonProcessorBase>::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<db::TextProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static void process_tt (db::Texts *r, const shape_processor_impl<db::TextProcessorBase> *f)
|
||||
{
|
||||
r->process (*f);
|
||||
}
|
||||
|
||||
static db::Region processed_tp (const db::Texts *r, const shape_processor_impl<db::TextToPolygonProcessorBase> *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<db::Texts> 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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <cstdlib>
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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<db::Edge> 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)");
|
||||
}
|
||||
|
|
@ -87,6 +87,7 @@ SOURCES = \
|
|||
dbDeepTextsTests.cc \
|
||||
dbNetShapeTests.cc \
|
||||
dbHierNetsProcessorTests.cc \
|
||||
dbRegionProcessorTests.cc \
|
||||
dbAsIfFlatRegionTests.cc
|
||||
|
||||
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
@ -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 <a href="/about/drc_ref_layer.xml#centers">layer#centers</a> for details about this functionality.
|
||||
</p>
|
||||
<a name="corners"/><h2>"corners" - Applies smoothing</h2>
|
||||
<a name="corners"/><h2>"corners" - Selects corners of polygons</h2>
|
||||
<keyword name="corners"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
|
|
@ -369,8 +369,10 @@ See <a href="/about/drc_ref_layer.xml#centers">layer#centers</a> for details abo
|
|||
<p>
|
||||
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.
|
||||
</p><p>
|
||||
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
|
||||
|
|
@ -386,8 +388,8 @@ out = in.drc(primary.corners) # equivalent
|
|||
The following example selects all inner corners:
|
||||
</p><p>
|
||||
<pre>
|
||||
out = in.drc(corners < 0)
|
||||
out = in.drc(primary.corners < 0) # equivalent
|
||||
out = in.drc(corners > 0)
|
||||
out = in.drc(primary.corners > 0) # equivalent
|
||||
</pre>
|
||||
</p><p>
|
||||
The "corners" method is available as a plain function or as a method on <a href="/about/drc_ref_drc.xml">DRC</a> expressions.
|
||||
|
|
@ -453,6 +455,7 @@ out = in.drc(covering(other) > 2)
|
|||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>expression.edges</tt></li>
|
||||
<li><tt>expression.edges(mode)</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
Polygons will be separated into edges forming their contours. Edge pairs will be
|
||||
|
|
@ -466,6 +469,20 @@ for the edges:
|
|||
<pre>
|
||||
out = in.drc(primary.edges)
|
||||
</pre>
|
||||
</p><p>
|
||||
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:
|
||||
</p><p>
|
||||
<pre>
|
||||
out = in.drc(primary.edges(convex))
|
||||
</pre>
|
||||
</p><p>
|
||||
The mode argument is ignored when translating other objects than
|
||||
polygons.
|
||||
</p>
|
||||
<a name="end_segments"/><h2>"end_segments" - Returns the part at the end of each edge of the input</h2>
|
||||
<keyword name="end_segments"/>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
<title>DRC Reference: Global Functions</title>
|
||||
<keyword name="global"/>
|
||||
<p>
|
||||
Some functions are available on global level and can be used without any object.
|
||||
Most of them are convenience functions that basically act on some default object
|
||||
or provide function-like alternatives for the methods.
|
||||
</p>
|
||||
<h2-index/>
|
||||
<a name="angle"/><h2>"angle" - In universal DRC context: selects edges based on their orientation</h2>
|
||||
<keyword name="angle"/>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
<title>DRC Reference: Layer Object</title>
|
||||
<keyword name="Layer"/>
|
||||
<p>
|
||||
The layer object represents a collection of polygons, edges or edge pairs.
|
||||
</p>
|
||||
<h2-index/>
|
||||
<a name="&"/><h2>"&" - Boolean AND operation</h2>
|
||||
<keyword name="&"/>
|
||||
|
|
@ -268,7 +265,7 @@ deliver objects that can be converted into polygons. Such objects are of class <
|
|||
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
|
||||
</p><p>
|
||||
The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
|
||||
</p><p>
|
||||
|
|
@ -786,6 +783,11 @@ apply to this method.
|
|||
</ul>
|
||||
<a name="edges"/><h2>"edges" - Decomposes the layer into single edges</h2>
|
||||
<keyword name="edges"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.edges</tt></li>
|
||||
<li><tt>layer.edges(mode)</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
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.
|
|||
</p><p>
|
||||
Merged semantics applies, i.e. the result reflects merged polygons rather than
|
||||
individual ones unless raw mode is chosen.
|
||||
</p><p>
|
||||
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:
|
||||
</p><p>
|
||||
<pre>
|
||||
out = in.edges(convex)
|
||||
</pre>
|
||||
</p><p>
|
||||
This feature is only available for polygon layers.
|
||||
</p><p>
|
||||
The following images show the effect of the mode argument:
|
||||
</p><p>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="/images/drc_edge_modes1.png"/></td>
|
||||
<td><img src="/images/drc_edge_modes2.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="/images/drc_edge_modes3.png"/></td>
|
||||
<td><img src="/images/drc_edge_modes4.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="/images/drc_edge_modes5.png"/></td>
|
||||
<td><img src="/images/drc_edge_modes6.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<a name="edges?"/><h2>"edges?" - Returns true, if the layer is an edge layer</h2>
|
||||
<keyword name="edges?"/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
|
||||
|
||||
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
|
||||
<!-- DO NOT EDIT! -->
|
||||
|
||||
<doc>
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
|
|
@ -59,6 +59,12 @@
|
|||
<file alias="drc_extended2.png">doc/images/drc_extended2.png</file>
|
||||
<file alias="drc_extended3.png">doc/images/drc_extended3.png</file>
|
||||
<file alias="drc_extended4.png">doc/images/drc_extended4.png</file>
|
||||
<file alias="drc_edge_modes1.png">doc/images/drc_edge_modes1.png</file>
|
||||
<file alias="drc_edge_modes2.png">doc/images/drc_edge_modes2.png</file>
|
||||
<file alias="drc_edge_modes3.png">doc/images/drc_edge_modes3.png</file>
|
||||
<file alias="drc_edge_modes4.png">doc/images/drc_edge_modes4.png</file>
|
||||
<file alias="drc_edge_modes5.png">doc/images/drc_edge_modes5.png</file>
|
||||
<file alias="drc_edge_modes6.png">doc/images/drc_edge_modes6.png</file>
|
||||
<file alias="drc_extents1.png">doc/images/drc_extents1.png</file>
|
||||
<file alias="drc_extents2.png">doc/images/drc_extents2.png</file>
|
||||
<file alias="drc_inside.png">doc/images/drc_inside.png</file>
|
||||
|
|
|
|||
|
|
@ -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%
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ private:
|
|||
void get_parameters_internal (db::ParameterStates &states, bool &edit_error);
|
||||
std::vector<tl::Variant> parameter_from_states (const db::ParameterStates &states) const;
|
||||
void states_from_parameters (db::ParameterStates &states, const std::vector<tl::Variant> ¶meters);
|
||||
void check_range (const tl::Variant& value, const db::PCellParameterDeclaration &decl);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue