Polygon neighborhood compound operation

This commit is contained in:
Matthias Koefferlein 2025-02-03 20:47:31 +01:00
parent aa67448401
commit 1f44a951f4
12 changed files with 313 additions and 8 deletions

View File

@ -231,11 +231,11 @@ static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector
return new db::EdgeNeighborhoodCompoundOperationNode (children, visitor, bext, eext, din, dout);
}
gsi::ClassExt<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode_ext (
gsi::ClassExt<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode_ext_EdgeNeighborhood (
gsi::constructor ("new_edge_neighborhood", &new_edge_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("bext", 0), gsi::arg ("eext", 0), gsi::arg ("din", 0), gsi::arg ("dout", 0),
"@brief Creates a new edge neighborhood collector\n"
"\n"
"@param children The inputs to use. The inputs are enumrated by base zero indexes in the visitor callback.\n"
"@param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback.\n"
"@param visitor The visitor object (see \\EdgeNeighborhoodVisitor) receiving the edge events.\n"
"@param bext The search window extension to use at the edge beginning.\n"
"@param eext The search window extension to use at the edge end.\n"

View File

@ -123,17 +123,17 @@ Class<gsi::PolygonNeighborhoodVisitorImpl> decl_PolygonNeighborhoodVisitorImpl (
// ---------------------------------------------------------------------------------
// PolygonNeighborhoodCompoundOperationNode binding
static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector<db::CompoundRegionOperationNode *> &children, db::PolygonNeighborhoodVisitor *visitor, const db::Coord dist)
static db::CompoundRegionOperationNode *new_polygon_neighborhood (const std::vector<db::CompoundRegionOperationNode *> &children, db::PolygonNeighborhoodVisitor *visitor, const db::Coord dist)
{
return new db::PolygonNeighborhoodCompoundOperationNode (children, visitor, dist);
}
gsi::ClassExt<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode_ext (
gsi::constructor ("new_polygon_neighborhood", &new_edge_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("dist", 0),
"@brief Creates a new edge neighborhood collector\n"
gsi::ClassExt<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode_ext_PolygonNeighborhood (
gsi::constructor ("new_polygon_neighborhood", &new_polygon_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("dist", 0),
"@brief Creates a new polygon neighborhood collector\n"
"\n"
"@param children The inputs to use. The inputs are enumrated by base zero indexes in the visitor callback.\n"
"@param visitor The visitor object (see \\PolygonNeighborhoodVisitor) receiving the edge events.\n"
"@param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback.\n"
"@param visitor The visitor object (see \\PolygonNeighborhoodVisitor) receiving the polygon events.\n"
"@param dist The search distance in which to look up neighbors.\n"
"\n"
"This constructor has been introduced in version 0.30.0.\n"

View File

@ -0,0 +1,161 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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 "dbRegion.h"
#include "dbPolygonNeighborhood.h"
#include "dbReader.h"
#include "dbTestSupport.h"
#include "tlStream.h"
#include <cstdio>
namespace
{
class PNPrimaryCopyVisitor
: public db::PolygonNeighborhoodVisitor
{
public:
PNPrimaryCopyVisitor ()
{
set_result_type (db::CompoundRegionOperationNode::ResultType::Region);
}
void neighbors (const db::Layout *, const db::Cell *, const db::PolygonWithProperties &polygon, const neighbors_type &)
{
output_polygon (polygon);
}
};
class PNPrimaryCopyIntruderVisitor
: public db::PolygonNeighborhoodVisitor
{
public:
PNPrimaryCopyIntruderVisitor (unsigned int input)
{
set_result_type (db::CompoundRegionOperationNode::ResultType::Edges);
m_input = input;
}
void neighbors (const db::Layout *, const db::Cell *, const db::PolygonWithProperties &polygon, const neighbors_type &neighbors)
{
for (auto n = neighbors.begin (); n != neighbors.end (); ++n) {
if (n->first == m_input) {
for (auto p = n->second.begin (); p != n->second.end (); ++p) {
output_edge (db::Edge (polygon.box ().center (), p->box ().center ()));
}
}
}
}
private:
unsigned int m_input;
};
}
static void prep_layer (db::Layout &ly, int gds_layer, db::Region &r, db::DeepShapeStore &dss, bool deep)
{
unsigned int li = ly.get_layer (db::LayerProperties (gds_layer, 0));
if (deep) {
r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li), dss);
} else {
r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li));
}
}
static void run_test (tl::TestBase *_this, db::PolygonNeighborhoodVisitor &visitor, const std::string &au_name, bool deep = true, db::Coord dist = 0)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/polygon_neighborhood.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::DeepShapeStore dss;
db::Region r1, r2, r3;
prep_layer (ly, 1, r1, dss, deep);
prep_layer (ly, 2, r2, dss, deep);
prep_layer (ly, 3, r3, dss, deep);
std::vector<db::CompoundRegionOperationNode *> children;
children.push_back (new db::CompoundRegionOperationPrimaryNode ());
children.push_back (new db::CompoundRegionOperationForeignNode ());
children.push_back (new db::CompoundRegionOperationSecondaryNode (&r2));
children.push_back (new db::CompoundRegionOperationSecondaryNode (&r3));
db::PolygonNeighborhoodCompoundOperationNode en_node (children, &visitor, dist);
unsigned int l100 = ly.get_layer (db::LayerProperties (100, 0));
if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Region) {
auto res = r1.cop_to_region (en_node);
res.insert_into (&ly, *ly.begin_top_down (), l100);
} else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Edges) {
auto res = r1.cop_to_edges (en_node);
res.insert_into (&ly, *ly.begin_top_down (), l100);
} else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::EdgePairs) {
auto res = r1.cop_to_edge_pairs (en_node);
res.insert_into (&ly, *ly.begin_top_down (), l100);
}
db::compare_layouts (_this, ly, tl::testdata () + au_name);
}
TEST(1)
{
PNPrimaryCopyVisitor visitor;
run_test (_this, visitor, "/algo/polygon_neighborhood_au1.gds");
}
TEST(2)
{
PNPrimaryCopyIntruderVisitor visitor (0);
run_test (_this, visitor, "/algo/polygon_neighborhood_au2.gds", true, 2000);
}
TEST(3)
{
PNPrimaryCopyIntruderVisitor visitor (1);
run_test (_this, visitor, "/algo/polygon_neighborhood_au3.gds", true, 2000);
}
TEST(4)
{
PNPrimaryCopyIntruderVisitor visitor (2);
run_test (_this, visitor, "/algo/polygon_neighborhood_au4.gds", true, 2000);
}
TEST(5)
{
PNPrimaryCopyIntruderVisitor visitor (3);
run_test (_this, visitor, "/algo/polygon_neighborhood_au5.gds", true, 2000);
}

View File

@ -12,6 +12,7 @@ SOURCES = \
dbFillToolTests.cc \
dbLogTests.cc \
dbObjectWithPropertiesTests.cc \
dbPolygonNeighborhoodTests.cc \
dbRecursiveInstanceIteratorTests.cc \
dbRegionCheckUtilsTests.cc \
dbTriangleTests.cc \

View File

@ -102,6 +102,7 @@ RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb")
RUBYTEST (dbEdgesTest, "dbEdgesTest.rb")
RUBYTEST (dbEdgeTest, "dbEdgeTest.rb")
RUBYTEST (dbEdgeNeighborhood, "dbEdgeNeighborhood.rb")
RUBYTEST (dbPolygonNeighborhood, "dbPolygonNeighborhood.rb")
RUBYTEST (dbGlyphs, "dbGlyphs.rb")
RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb")
RUBYTEST (dbInstanceTest, "dbInstanceTest.rb")

BIN
testdata/algo/polygon_neighborhood.gds vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

142
testdata/ruby/dbPolygonNeighborhood.rb vendored Normal file
View File

@ -0,0 +1,142 @@
# encoding: UTF-8
# KLayout Layout Viewer
# Copyright (C) 2006-2025 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
if !$:.member?(File::dirname($0))
$:.push(File::dirname($0))
end
load("test_prologue.rb")
class MyVisitor < RBA::PolygonNeighborhoodVisitor
def initialize
@log = {}
@current_log = nil
end
def log
@log.keys.sort { |a,b| a < b ? -1 : (a == b ? 0 : 1) }.collect { |k| @log[k].join("") }.join("")
end
def neighbors(layout, cell, polygon, neighborhood)
polygon = polygon.dup
output(polygon)
@log[polygon] ||= []
@current_log = @log[polygon]
@current_log << "Polygon: #{polygon}\n"
neighborhood.each do |inp, poly|
poly_str = poly.collect { |p| p.to_s }.join("/")
@current_log << " #{inp}: #{poly_str}\n"
end
end
end
class MyVisitor2 < RBA::PolygonNeighborhoodVisitor
def initialize
self.result_type = RBA::CompoundRegionOperationNode::ResultType::Edges
end
def neighbors(layout, cell, polygon, neighborhood)
neighborhood.each do |inp, poly|
poly.each do |p|
e = RBA::Edge::new(polygon.bbox.center, p.bbox.center)
output(e)
end
end
end
end
class DBPolygonNeighborhood_TestClass < TestBase
# basic events
def test_1
ly = RBA::Layout::new
l1 = ly.layer(1, 0)
cell = ly.create_cell("TOP")
pid1 = RBA::Layout::properties_id({ 1 => "one" })
cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 1000), pid1)
cell.shapes(l1).insert(RBA::Box::new(-1100, 0, -100, 1000))
prim = RBA::Region::new(cell.begin_shapes_rec(l1))
prim.enable_properties
visitor = MyVisitor::new
visitor.result_type = RBA::CompoundRegionOperationNode::ResultType::Region
assert_equal(visitor.result_type, RBA::CompoundRegionOperationNode::ResultType::Region)
dist = 101
children = [
RBA::CompoundRegionOperationNode::new_foreign
]
node = RBA::CompoundRegionOperationNode::new_polygon_neighborhood(children, visitor, dist)
res = prim.complex_op(node)
assert_equal(visitor.log,
"Polygon: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" +
" 0: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" +
"Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" +
" 0: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n"
)
assert_equal(res.to_s, "(-1100,0;-1100,1000;-100,1000;-100,0);(0,0;0,1000;1000,1000;1000,0){1=>one}")
end
# edge pair output, to_original_trans
def test_2
ly = RBA::Layout::new
l1 = ly.layer(1, 0)
cell = ly.create_cell("TOP")
cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 1000))
cell.shapes(l1).insert(RBA::Box::new(-1100, 0, -100, 1000))
prim = RBA::Region::new(cell.begin_shapes_rec(l1))
visitor = MyVisitor2::new
dist = 101
children = [
RBA::CompoundRegionOperationNode::new_foreign
]
node = RBA::CompoundRegionOperationNode::new_polygon_neighborhood(children, visitor, dist)
res = prim.complex_op(node)
assert_equal(res.to_s, "(-600,500;500,500);(500,500;-600,500)")
end
end
load("test_epilogue.rb")