DRC: added collect*, select and each methods.

This commit is contained in:
Matthias Koefferlein 2017-07-17 21:36:29 +02:00
parent 703a0ee85d
commit 7d31825b11
5 changed files with 294 additions and 2 deletions

View File

@ -220,6 +220,7 @@ HEAD
doc += "<doc>\n"
doc += "<title>#{escape($title)}</title>\n"
doc += "<keyword name=\"#{escape($title)}\"/>\n"
doc += "<topics>\n"

View File

@ -262,7 +262,7 @@ module DRC
def data
@data
end
# %DRC%
# @name insert
# @brief Inserts one or many objects into the layer
@ -908,7 +908,7 @@ CODE
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
# @/ul
#
# The following image shows the effect of this method
# The following images show the effect of this method:
#
# @table
# @tr
@ -1135,6 +1135,136 @@ CODE
end
CODE
end
# %DRC%
# @name select
# @brief Selects edges, edge pairs or polygons based on evaluation of a block
# @synopsis layer.select { |object| ... }
# This method evaluates the block and returns a new container with those objects for which
# the block evaluates to true. It is available for edge, polygon and edge pair layers.
# The corresponding objects are RBA::DPolygon, RBA::DEdge or RBA::DEdgePair.
#
# Because this method executes inside the interpreter, it's inherently slow. Tiling does not
# apply to this method.
#
# Here is a (slow) equivalent of the area selection method:
#
# @code
# new_layer = layer.select { |polygon| polygon.area >= 10.0 }
# @/code
def select(&amp;block)
new_data = @data.class.new
t = RBA::CplxTrans::new(@engine.dbu)
@engine.run_timed("\"select\" in: #{@engine.src_line}", @data) do
@data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
block.call(object.transformed(t)) &amp;&amp; new_data.insert(object)
end
end
DRCLayer::new(@engine, new_data)
end
# %DRC%
# @name each
# @brief Iterates over the objects from the layer
# @synopsis layer.each { |object| ... }
# This method evaluates the block on each object of the layer. Depending on the
# layer type, these objects are of RBA::DPolygon, RBA::DEdge or RBA::DEdgePair type.
#
# Because this method executes inside the interpreter, it's inherently slow. Tiling does not
# apply to this method.
def each(&amp;block)
t = RBA::CplxTrans::new(@engine.dbu)
@engine.run_timed("\"select\" in: #{@engine.src_line}", @data) do
@data.send(@data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
block.call(object.transformed(t))
end
end
end
# %DRC%
# @name collect
# @brief Transforms a layer
# @synopsis layer.collect { |object| ... }
# This method evaluates the block for each object in the layer and returns a new layer with the objects
# returned from the block. It is available for edge, polygon and edge pair layers.
# The corresponding objects are RBA::DPolygon, RBA::DEdge or RBA::DEdgePair.
#
# If the block evaluates to nil, no object is added to the output layer. If it returns an array, each of
# the objects in the array is added.
# The returned layer is of the original type and will only accept objects of the respective type. Hence,
# for polygon layers, RBA::DPolygon objects need to be returned. For edge layers those need to be RBA::DEdge
# and for edge pair layers, they need to be RBA::DEdgePair objects. For convenience, RBA::Polygon, RBA::Edge
# and RBA::EdgePair objects are accepted too and are scaled by the database unit to render micrometer-unit
# objects. RBA::Region, RBA::Edges and RBA::EdgePair objects are accepted as well and the corresponding
# content of that collections is inserted into the output layer.
#
# Other versions are available that allow translation of objects into other types (\collect_to_polygons,
# \collect_to_edges and \collect_to_edge_pairs).
#
# Because this method executes inside the interpreter, it's inherently slow. Tiling does not
# apply to this method.
#
# Here is a slow equivalent of the rotated method
#
# @code
# # Rotates by 45 degree
# t = RBA::DCplxTrans(1.0, 45.0, false, RBA::DVector::new)
# new_layer = layer.collect { |polygon| polygon.transformed(t) }
# @/code
# %DRC%
# @name collect_to_region
# @brief Transforms a layer into polygon objects
# @synopsis layer.collect { |object| ... }
# This method is similar to \collect, but creates a polygon layer. It expects the block to
# deliver objects that can be converted into polygons. Such objects are of class RBA::DPolygon,
# RBA::DBox, RBA::DPath, RBA::Polygon, RBA::Path, RBA::Box and RBA::Region.
# %DRC%
# @name collect_to_edges
# @brief Transforms a layer into edge objects
# @synopsis layer.collect { |object| ... }
# This method is similar to \collect, but creates an edge layer. It expects the block to
# deliver objects that can be converted into edges. If polygon-like objects are returned, their
# contours will be turned into edge sequences.
# %DRC%
# @name collect_to_edge_pairs
# @brief Transforms a layer into edge pair objects
# @synopsis layer.collect { |object| ... }
# This method is similar to \collect, but creates an edge pair layer. It expects the block to
# deliver RBA::EdgePair, RBA::DEdgePair or RBA::EdgePairs objects.
%w(collect collect_to_region collect_to_edges collect_to_edge_pairs).each do |f|
eval &lt;&lt;"CODE"
def #{f}(&amp;block)
if :#{f} == :collect
new_data = @data.class.new
elsif :#{f} == :collect_to_region
new_data = RBA::Region.new
elsif :#{f} == :collect_to_edges
new_data = RBA::Edges.new
elsif :#{f} == :collect_to_edge_pairs
new_data = RBA::EdgePairs.new
end
t = RBA::CplxTrans::new(@engine.dbu)
dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu)
@engine.run_timed("\\"select\\" in: " + @engine.src_line, @data) do
@data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
insert_object_into(new_data, block.call(object.transformed(t)), dbu_trans)
end
end
DRCLayer::new(@engine, new_data)
end
CODE
end
# %DRC%
# @name odd_polygons
@ -3008,6 +3138,30 @@ CODE
private
def insert_object_into(container, object, dbu_trans)
if object.is_a?(Array)
object.each { |o| insert_object_into(container, o, dbu_trans) }
elsif container.is_a?(RBA::Region)
if object.is_a?(RBA::Region) || object.is_a?(RBA::Polygon) || object.is_a?(RBA::SimplePolygon) || object.is_a?(RBA::Box) || object.is_a?(RBA::Path)
container.insert(object)
elsif object.is_a?(RBA::DPolygon) || object.is_a?(RBA::DSimplePolygon) || object.is_a?(RBA::DBox) || object.is_a?(RBA::DPath)
container.insert(object.transformed(dbu_trans))
end
elsif container.is_a?(RBA::Edges)
if object.is_a?(RBA::Region) || object.is_a?(RBA::Edges) || object.is_a?(RBA::Edge) || object.is_a?(RBA::Polygon) || object.is_a?(RBA::SimplePolygon) || object.is_a?(RBA::Box) || object.is_a?(RBA::Path)
container.insert(object)
elsif object.is_a?(RBA::DPolygon) || object.is_a?(RBA::DSimplePolygon) || object.is_a?(RBA::DBox) || object.is_a?(RBA::DPath) || object.is_a?(RBA::DEdge)
container.insert(object.transformed(dbu_trans))
end
elsif container.is_a?(RBA::EdgePairs)
if object.is_a?(RBA::EdgePairs) || object.is_a?(RBA::EdgePair)
container.insert(object)
elsif object.is_a?(RBA::DEdgePair)
container.insert(object.transformed(dbu_trans))
end
end
end
def prep_value(a)
if a.is_a?(RBA::DPoint)
RBA::Point::from_dpoint(a * (1.0 / @engine.dbu.to_f))
@ -4252,6 +4406,7 @@ CODE
t = RBA::Timer::new
t.start
GC.start # force a garbage collection before the operation to free unused memory
res = yield
t.stop

View File

@ -6,6 +6,7 @@
<doc>
<title>DRC Reference</title>
<keyword name="DRC Reference"/>
<topics>
<topic href="/about/drc_ref_layer.xml"/>
<topic href="/about/drc_ref_source.xml"/>

View File

@ -148,6 +148,74 @@ Clean state is the default.
See <a href="#raw">raw</a> for some remarks about how this state is
propagated.
</p>
<h2>"collect" - Transforms a layer</h2>
<keyword name="collect"/>
<a name="collect"/><p>Usage:</p>
<ul>
<li><tt>layer.collect { |object| ... }</tt></li>
</ul>
<p>
This method evaluates the block for each object in the layer and returns a new layer with the objects
returned from the block. It is available for edge, polygon and edge pair layers.
The corresponding objects are <class_doc href="DPolygon">DPolygon</class_doc>, <class_doc href="DEdge">DEdge</class_doc> or <class_doc href="DEdgePair">DEdgePair</class_doc>.
</p><p>
If the block evaluates to nil, no object is added to the output layer. If it returns an array, each of
the objects in the array is added.
The returned layer is of the original type and will only accept objects of the respective type. Hence,
for polygon layers, <class_doc href="DPolygon">DPolygon</class_doc> objects need to be returned. For edge layers those need to be <class_doc href="DEdge">DEdge</class_doc>
and for edge pair layers, they need to be <class_doc href="DEdgePair">DEdgePair</class_doc> objects. For convenience, <class_doc href="Polygon">Polygon</class_doc>, <class_doc href="Edge">Edge</class_doc>
and <class_doc href="EdgePair">EdgePair</class_doc> objects are accepted too and are scaled by the database unit to render micrometer-unit
objects. <class_doc href="Region">Region</class_doc>, <class_doc href="Edges">Edges</class_doc> and <class_doc href="EdgePair">EdgePair</class_doc> objects are accepted as well and the corresponding
content of that collections is inserted into the output layer.
</p><p>
Other versions are available that allow translation of objects into other types (<a href="#collect_to_polygons">collect_to_polygons</a>,
<a href="#collect_to_edges">collect_to_edges</a> and <a href="#collect_to_edge_pairs">collect_to_edge_pairs</a>).
</p><p>
Because this method executes inside the interpreter, it's inherently slow. Tiling does not
apply to this method.
</p><p>
Here is a slow equivalent of the rotated method
</p><p>
<pre>
# Rotates by 45 degree
t = <class_doc href="DCplxTrans">DCplxTrans</class_doc>(1.0, 45.0, false, <class_doc href="DVector">DVector</class_doc>::new)
new_layer = layer.collect do |polygon|
polygon.transformed(t)
end
</pre>
</p>
<h2>"collect_to_edge_pairs" - Transforms a layer into edge pair objects</h2>
<keyword name="collect_to_edge_pairs"/>
<a name="collect_to_edge_pairs"/><p>Usage:</p>
<ul>
<li><tt>layer.collect { |object| ... }</tt></li>
</ul>
<p>
This method is similar to <a href="#collect">collect</a>, but creates an edge pair layer. It expects the block to
deliver <class_doc href="EdgePair">EdgePair</class_doc>, <class_doc href="DEdgePair">DEdgePair</class_doc> or <class_doc href="EdgePairs">EdgePairs</class_doc> objects.
</p>
<h2>"collect_to_edges" - Transforms a layer into edge objects</h2>
<keyword name="collect_to_edges"/>
<a name="collect_to_edges"/><p>Usage:</p>
<ul>
<li><tt>layer.collect { |object| ... }</tt></li>
</ul>
<p>
This method is similar to <a href="#collect">collect</a>, but creates an edge layer. It expects the block to
deliver objects that can be converted into edges. If polygon-like objects are returned, their
contours will be turned into edge sequences.
</p>
<h2>"collect_to_region" - Transforms a layer into polygon objects</h2>
<keyword name="collect_to_region"/>
<a name="collect_to_region"/><p>Usage:</p>
<ul>
<li><tt>layer.collect { |object| ... }</tt></li>
</ul>
<p>
This method is similar to <a href="#collect">collect</a>, but creates a polygon layer. It expects the block to
deliver objects that can be converted into polygons. Such objects are of class <class_doc href="DPolygon">DPolygon</class_doc>,
<class_doc href="DBox">DBox</class_doc>, <class_doc href="DPath">DPath</class_doc>, <class_doc href="Polygon">Polygon</class_doc>, <class_doc href="Path">Path</class_doc>, <class_doc href="Box">Box</class_doc> and <class_doc href="Region">Region</class_doc>.
</p>
<h2>"corners" - Selects corners of polygons</h2>
<keyword name="corners"/>
<a name="corners"/><p>Usage:</p>
@ -177,6 +245,7 @@ The following image shows the effect of this method
<tr>
<td><img src="/images/drc_corners1.png"/></td>
<td><img src="/images/drc_corners2.png"/></td>
<td><img src="/images/drc_corners3.png"/></td>
</tr>
</table>
</p>
@ -209,6 +278,19 @@ layer. Using the dup method will avoid that.
However, dup will double the memory required to hold the data
and performing the deep copy may be expensive in terms of CPU time.
</p>
<h2>"each" - Iterates over the objects from the layer</h2>
<keyword name="each"/>
<a name="each"/><p>Usage:</p>
<ul>
<li><tt>layer.each { |object| ... }</tt></li>
</ul>
<p>
This method evaluates the block on each object of the layer. Depending on the
layer type, these objects are of <class_doc href="DPolygon">DPolygon</class_doc>, <class_doc href="DEdge">DEdge</class_doc> or <class_doc href="DEdgePair">DEdgePair</class_doc> type.
</p><p>
Because this method executes inside the interpreter, it's inherently slow. Tiling does not
apply to this method.
</p>
<h2>"edge_pairs?" - Returns true, if the layer is an edge pair collection</h2>
<keyword name="edge_pairs?"/>
<a name="edge_pairs?"/><p>Usage:</p>
@ -1419,6 +1501,28 @@ The following images shows the effect of the "scaled" method:
Applies to edge pair collections only.
Returns the second edges of the edge pairs in the collection.
</p>
<h2>"select" - Selects edges, edge pairs or polygons based on evaluation of a block</h2>
<keyword name="select"/>
<a name="select"/><p>Usage:</p>
<ul>
<li><tt>layer.select { |object| ... }</tt></li>
</ul>
<p>
This method evaluates the block and returns a new container with those objects for which
the block evaluates to true. It is available for edge, polygon and edge pair layers.
The corresponding objects are <class_doc href="DPolygon">DPolygon</class_doc>, <class_doc href="DEdge">DEdge</class_doc> or <class_doc href="DEdgePair">DEdgePair</class_doc>.
</p><p>
Because this method executes inside the interpreter, it's inherently slow. Tiling does not
apply to this method.
</p><p>
Here is a (slow) equivalent of the area selection method:
</p><p>
<pre>
new_layer = layer.select do |polygon|
polygon.area &gt;= 10.0
end
</pre>
</p>
<h2>"select_inside" - Selects shapes or regions of self which are inside the other region</h2>
<keyword name="select_inside"/>
<a name="select_inside"/><p>Usage:</p>

View File

@ -546,6 +546,37 @@ a1.corners.sized(0.05).output(1060, 0)
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
a1.collect_to_region { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1102, 0)
a1.collect_to_edges { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1103, 0)
a1.collect { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1104, 0)
a1.collect { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1105, 0)
lx = polygon_layer
a1.each { |p| p.is_box? && lx.insert(p) }
lx.output(1106, 0)
a1.collect { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1107, 0)
a1.collect { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1108, 0)
a1.collect { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1109, 0)
a1.collect { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1110, 0)
a1.collect { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1111, 0)
a1.collect_to_edges { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1112, 0)
a1.collect_to_edges { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1113, 0)
a1.collect_to_edges { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1114, 0)
a1.collect_to_edges { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1115, 0)
a1.collect_to_edges { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1116, 0)
a1.collect_to_edges { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1117, 0)
a1.collect_to_edges { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1118, 0)
a1.edges.select { |p| p.length < 0.8 }.output(1120, 0)
a1.edges.collect { |p| p.length < 0.8 && p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.enlarged(0.1, 0.1) }.output(1122, 0)
a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(100, 100) }.output(1123, 0)
# edge pair collect
a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0)
a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
puts "=== Single-cell testsuite ==="
run_testsuite(0, 1)