EdgePairs generic processor

This commit is contained in:
Matthias Koefferlein 2024-01-26 16:09:01 +01:00
parent b7277631c3
commit ce88affa67
12 changed files with 325 additions and 18 deletions

View File

@ -147,6 +147,28 @@ void AsIfFlatEdgePairs::invalidate_bbox ()
m_bbox_valid = false;
}
EdgePairsDelegate *
AsIfFlatEdgePairs::processed (const EdgePairProcessorBase &filter) const
{
std::unique_ptr<FlatEdgePairs> edge_pairs (new FlatEdgePairs ());
if (filter.result_must_not_be_merged ()) {
edge_pairs->set_merged_semantics (false);
}
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
{

View File

@ -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)
{

View File

@ -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
{

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -109,6 +109,100 @@ Class<gsi::EdgePairFilterImpl> decl_EdgePairFilterImpl ("db", "EdgePairFilter",
"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
@ -267,6 +361,30 @@ 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);
@ -717,6 +835,30 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\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 "

View File

@ -84,7 +84,7 @@ Class<gsi::EdgeFilterImpl> decl_EdgeFilterImpl ("db", "EdgeFilter",
"@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 \\Edges#filter or \\Edges#filtered method.\n"
"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"
@ -131,7 +131,7 @@ Class<shape_processor_impl<db::EdgeProcessorBase> > decl_EdgeProcessorBase ("db"
"@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 \\Edges#processed method.\n"
"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"
@ -176,7 +176,7 @@ Class<shape_processor_impl<db::EdgeToPolygonProcessorBase> > decl_EdgeToPolygonP
"@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 \\Edges#processed method.\n"
"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"
@ -200,7 +200,7 @@ Class<shape_processor_impl<db::EdgeToEdgePairProcessorBase> > decl_EdgeToEdgePai
"@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 \\Edges#processed method.\n"
"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"

View File

@ -90,7 +90,7 @@ Class<gsi::PolygonFilterImpl> decl_PolygonFilterImpl ("db", "PolygonFilter",
"@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 \\Region#filter or \\Region#filtered method.\n"
"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"
@ -135,7 +135,7 @@ Class<shape_processor_impl<db::PolygonProcessorBase> > decl_PolygonOperator ("db
"@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 \\Region#process or \\Region#processed method.\n"
"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"
@ -180,7 +180,7 @@ Class<shape_processor_impl<db::PolygonToEdgeProcessorBase> > decl_PolygonToEdgeP
"@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 \\Region#processed method.\n"
"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"
@ -204,7 +204,7 @@ Class<shape_processor_impl<db::PolygonToEdgePairProcessorBase> > decl_PolygonToE
"@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 \\Region#processed method.\n"
"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"

View File

@ -44,6 +44,46 @@ class PerpendicularEdgesFilter < RBA::EdgePairFilter
end
class FlipEdgePair < RBA::EdgePairOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
end
# Flips the edge pair
def process(edge_pair)
return [ RBA::EdgePair::new(edge_pair.second, edge_pair.first) ]
end
end
class SomeEdgePairToEdgeOperator < RBA::EdgePairToEdgeOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(ep)
return [ RBA::Edge::new(ep.first.p1, ep.second.p2) ]
end
end
class SomeEdgePairToPolygonOperator < RBA::EdgePairToPolygonOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(ep)
return [ RBA::Polygon::new(ep.bbox) ]
end
end
class DBEdgePairs_TestClass < TestBase
# Basics
@ -374,6 +414,61 @@ class DBEdgePairs_TestClass < TestBase
end
# Generic processors
def test_generic_processors_epep
# Some basic tests for the processor class
f = FlipEdgePair::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(FlipEdgePair::new).to_s, "(200,300;200,500)/(0,0;100,100)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
edge_pairs.process(FlipEdgePair::new)
assert_equal(edge_pairs.to_s, "(200,300;200,500)/(0,0;100,100)")
end
# Generic processors
def test_generic_processors_epe
p = SomeEdgePairToEdgeOperator::new
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(p).to_s, "(0,0;200,500)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
end
# Generic processors
def test_generic_processors_epp
p = SomeEdgePairToPolygonOperator::new
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(p).to_s, "(0,0;0,500;200,500;200,0)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
end
end