diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 8640749e4..881644217 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -123,6 +123,102 @@ Class decl_EdgeFilterImpl ("db", "EdgeFilter", "This class has been introduced in version 0.29.\n" ); +// --------------------------------------------------------------------------------- +// EdgeProcessor binding + +Class > decl_EdgeProcessorBase ("db", "EdgeOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-polygon operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to \\Edges#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output edges derived from the input edge.\n" + "The output edge collection is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "Here is some example that shrinks every edge to half of the size, but does not change the position.\n" + "In this example the 'position' is defined by the center of the edge:" + "\n" + "@code\n" + "class ShrinkToHalf < RBA::EdgeOperator\n" + "\n" + " # Constructor\n" + " def initialize\n" + " self.is_isotropic_and_scale_invariant # scale or orientation do not matter\n" + " end\n" + " \n" + " # Shrink to half size\n" + " def process(edge)\n" + " shift = edge.bbox.center - RBA::Point::new # shift vector\n" + " return [ (edge.moved(-shift) * 0.5).moved(shift) ]\n" + " end\n" + "\n" + "end\n" + "\n" + "edges = ... # some Edges collection\n" + "shrinked_to_half = edges.processed(ShrinkToHalf::new)\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgeToPolygonProcessor ("db", "EdgeToPolygonOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-polygon operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to \\Edges#processed method.\n" + "\n" + "Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n" + "The result of this call is a list of zero to many output polygons derived from the input edge.\n" + "The output region is the sum over all these individual results.\n" + "\n" + "The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible " + "and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You " + "need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "before using it.\n" + "\n" + "You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant " + "formation which is not always desired and blows up the hierarchy.\n" + "\n" + "For a basic example see the \\EdgeOperator class, with the exception that this incarnation has to deliver edges.\n" + "\n" + "This class has been introduced in version 0.29.\n" +); + +Class > decl_EdgeToEdgePairProcessor ("db", "EdgeToEdgePairOperator", + shape_processor_impl::method_decls (true), + "@brief A generic edge-to-edge-pair operator\n" + "\n" + "Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own " + "operator class and pass an instance to \\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 @@ -303,6 +399,28 @@ static void filter (db::Edges *r, const EdgeFilterImpl *f) r->filter (*f); } +static db::Edges processed_ee (const db::Edges *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static void process_ee (db::Edges *r, const shape_processor_impl *f) +{ + r->process (*f); +} + +static db::EdgePairs processed_eep (const db::Edges *r, const shape_processor_impl *f) +{ + return r->processed (*f); +} + +static db::Region processed_ep (const db::Edges *r, const shape_processor_impl *f) +{ + db::Region out; + r->processed (out, *f); + return out; +} + static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type length, bool inverse) { db::EdgeLengthFilter f (length, length + 1, inverse); @@ -732,6 +850,30 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\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 " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 83a9b5a62..a5d871e39 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -130,20 +130,20 @@ Class decl_PolygonFilterImpl ("db", "PolygonFilter", // --------------------------------------------------------------------------------- // PolygonProcessor binding -Class > decl_PolygonProcessor ("db", "PolygonProcessor", +Class > decl_PolygonOperator ("db", "PolygonOperator", shape_processor_impl::method_decls (true), - "@brief A generic polygon processor adaptor\n" + "@brief A generic polygon operator\n" "\n" - "Polygon processors are an efficient way to process polygons from a Region. To apply a processors, derive your own " - "processors class and pass an instance to \\Region#process or \\Region#processed method.\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" "\n" - "Conceptually, these methods take each polygon from the region and present it to the processor's 'process' method.\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 processor behaves. You " - "need to configure the processor by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "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 " @@ -153,7 +153,7 @@ Class > decl_PolygonProcessor ("d "In this example the 'position' is defined by the center of the bounding box:" "\n" "@code\n" - "class ShrinkToHalfProcessor < RBA::PolygonProcessor\n" + "class ShrinkToHalf < RBA::PolygonOperator\n" "\n" " # Constructor\n" " def initialize\n" @@ -175,50 +175,50 @@ Class > decl_PolygonProcessor ("d "This class has been introduced in version 0.29.\n" ); -Class > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeProcessor", +Class > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeOperator", shape_processor_impl::method_decls (true), - "@brief A generic polygon-to-edge processor adaptor\n" + "@brief A generic polygon-to-edge operator\n" "\n" - "Polygon processors are an efficient way to process polygons from a Region. To apply a processors, derive your own " - "processors class and pass an instance to \\Region#processed method.\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" "\n" - "Conceptually, these methods take each polygon from the region and present it to the processor's 'process' method.\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 processor behaves. You " - "need to configure the processor by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "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 \\PolygonProcessor class, with the exception that this incarnation has to deliver edges.\n" + "For a basic example see the \\PolygonOperator class, with the exception that this incarnation has to deliver edges.\n" "\n" "This class has been introduced in version 0.29.\n" ); -Class > decl_PolygonToEdgePairProcessor ("db", "PolygonToEdgePairProcessor", +Class > decl_PolygonToEdgePairProcessor ("db", "PolygonToEdgePairOperator", shape_processor_impl::method_decls (true), - "@brief A generic polygon-to-edge-pair processor adaptor\n" + "@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 processors, derive your own " - "processors class and pass an instance to \\Region#processed method.\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" "\n" - "Conceptually, these methods take each polygon from the region and present it to the processor's 'process' method.\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 processor behaves. You " - "need to configure the processor by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant " + "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 \\PolygonProcessor class, with the exception that this incarnation has to deliver edge pairs.\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" ); diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 5d3eb0660..6d6f8df37 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -45,6 +45,49 @@ class ParallelFilter < RBA::EdgeFilter end +class ShrinkToHalfEdgeOperator < RBA::EdgeOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + # Shrink to half size + def process(edge) + shift = edge.bbox.center - RBA::Point::new # shift vector + return [ (edge.moved(-shift) * 0.5).moved(shift) ] + end + +end + +class SomeEdgeToEdgePairOperator < RBA::EdgeToEdgePairOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(edge) + box = edge.bbox + return [ RBA::EdgePair::new([ box.left, box.bottom, box.left, box.top ], [ box.right, box.bottom, box.right, box.top ]) ] + end + +end + +class SomeEdgeToPolygonOperator < RBA::EdgeToPolygonOperator + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # scale or orientation do not matter + end + + def process(edge) + box = edge.bbox + return [ RBA::Polygon::new(box) ] + end + +end + class DBEdges_TestClass < TestBase # Basics @@ -796,6 +839,73 @@ class DBEdges_TestClass < TestBase end + # Generic processors + def test_generic_processors_ee + + # Some basic tests for the processor class + + f = ShrinkToHalfEdgeOperator::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = true + assert_equal(f.requires_raw_input, true) + assert_equal(f.result_is_merged, false) + f.result_is_merged = true + assert_equal(f.result_is_merged, true) + assert_equal(f.result_must_not_be_merged, false) + f.result_must_not_be_merged = true + assert_equal(f.result_must_not_be_merged, true) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(ShrinkToHalfEdgeOperator::new).to_s, "(25,25;75,75);(200,350;200,450)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + edges.process(ShrinkToHalfEdgeOperator::new) + assert_equal(edges.to_s, "(25,25;75,75);(200,350;200,450)") + + end + + # Generic processors + def test_generic_processors_eep + + p = SomeEdgeToEdgePairOperator::new + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(p).to_s, "(0,0;0,100)/(100,0;100,100);(200,300;200,500)/(200,300;200,500)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + + end + + # Generic processors + def test_generic_processors_ep + + p = SomeEdgeToPolygonOperator::new + + edges = RBA::Edges::new + + edges.insert(RBA::Edge::new(0, 0, 100, 100)) + edges.insert(RBA::Edge::new(200, 300, 200, 500)) + + assert_equal(edges.processed(p).to_s, "(0,0;0,100;100,100;100,0);(200,300;200,300;200,500;200,500)") + assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 87da79aa1..0297e36a0 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -44,7 +44,7 @@ class TriangleFilter < RBA::PolygonFilter end -class ShrinkToHalfProcessor < RBA::PolygonProcessor +class ShrinkToHalfOperator < RBA::PolygonOperator # Constructor def initialize @@ -59,7 +59,7 @@ class ShrinkToHalfProcessor < RBA::PolygonProcessor end -class SomePolygonToEdgePairProcessor < RBA::PolygonToEdgePairProcessor +class SomePolygonToEdgePairOperator < RBA::PolygonToEdgePairOperator # Constructor def initialize @@ -73,7 +73,7 @@ class SomePolygonToEdgePairProcessor < RBA::PolygonToEdgePairProcessor end -class SomePolygonToEdgeProcessor < RBA::PolygonToEdgeProcessor +class SomePolygonToEdgeOperator < RBA::PolygonToEdgeOperator # Constructor def initialize @@ -1281,7 +1281,7 @@ class DBRegion_TestClass < TestBase # Some basic tests for the processor class - f = ShrinkToHalfProcessor::new + f = ShrinkToHalfOperator::new assert_equal(f.wants_variants?, true) f.wants_variants = false assert_equal(f.wants_variants?, false) @@ -1306,9 +1306,9 @@ class DBRegion_TestClass < TestBase region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]])) region.insert(RBA::Box::new(200, 0, 300, 100)) - assert_equal(region.processed(ShrinkToHalfProcessor::new).to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)") + assert_equal(region.processed(ShrinkToHalfOperator::new).to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)") assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") - region.process(ShrinkToHalfProcessor::new) + region.process(ShrinkToHalfOperator::new) assert_equal(region.to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)") end @@ -1316,7 +1316,7 @@ class DBRegion_TestClass < TestBase # Generic processors def test_generic_processors_pep - p = SomePolygonToEdgePairProcessor::new + p = SomePolygonToEdgePairOperator::new region = RBA::Region::new @@ -1331,7 +1331,7 @@ class DBRegion_TestClass < TestBase # Generic processors def test_generic_processors_pe - p = SomePolygonToEdgeProcessor::new + p = SomePolygonToEdgeOperator::new region = RBA::Region::new