mirror of https://github.com/KLayout/klayout.git
Merge pull request #1916 from KLayout/maybe-pex-support
Some add-ons for potential application in the PEX context
This commit is contained in:
commit
ec21809cc3
|
|
@ -23,6 +23,7 @@ SOURCES = \
|
||||||
dbCommonReader.cc \
|
dbCommonReader.cc \
|
||||||
dbCompoundOperation.cc \
|
dbCompoundOperation.cc \
|
||||||
dbEdge.cc \
|
dbEdge.cc \
|
||||||
|
dbEdgeNeighborhood.cc \
|
||||||
dbEdgePair.cc \
|
dbEdgePair.cc \
|
||||||
dbEdgePairFilters.cc \
|
dbEdgePairFilters.cc \
|
||||||
dbEdgePairRelations.cc \
|
dbEdgePairRelations.cc \
|
||||||
|
|
@ -119,6 +120,7 @@ SOURCES = \
|
||||||
gsiDeclDbCommonStreamOptions.cc \
|
gsiDeclDbCommonStreamOptions.cc \
|
||||||
gsiDeclDbCompoundOperation.cc \
|
gsiDeclDbCompoundOperation.cc \
|
||||||
gsiDeclDbEdge.cc \
|
gsiDeclDbEdge.cc \
|
||||||
|
gsiDeclDbEdgeNeighborhood.cc \
|
||||||
gsiDeclDbEdgePair.cc \
|
gsiDeclDbEdgePair.cc \
|
||||||
gsiDeclDbEdgePairs.cc \
|
gsiDeclDbEdgePairs.cc \
|
||||||
gsiDeclDbEdgeProcessor.cc \
|
gsiDeclDbEdgeProcessor.cc \
|
||||||
|
|
@ -249,6 +251,7 @@ HEADERS = \
|
||||||
dbCommonReader.h \
|
dbCommonReader.h \
|
||||||
dbCompoundOperation.h \
|
dbCompoundOperation.h \
|
||||||
dbEdge.h \
|
dbEdge.h \
|
||||||
|
dbEdgeNeighborhood.h \
|
||||||
dbEdgePair.h \
|
dbEdgePair.h \
|
||||||
dbEdgePairFilters.h \
|
dbEdgePairFilters.h \
|
||||||
dbEdgePairRelations.h \
|
dbEdgePairRelations.h \
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ protected:
|
||||||
virtual db::Coord computed_dist () const = 0;
|
virtual db::Coord computed_dist () const = 0;
|
||||||
|
|
||||||
virtual std::string generated_description () const;
|
virtual std::string generated_description () const;
|
||||||
|
virtual bool wants_caching () const { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_description;
|
std::string m_description;
|
||||||
|
|
@ -276,23 +277,32 @@ private:
|
||||||
{
|
{
|
||||||
// TODO: confine caching to those nodes which need it.
|
// TODO: confine caching to those nodes which need it.
|
||||||
|
|
||||||
std::pair<bool, std::vector<std::unordered_set<TR> > *> cp = cache->get<TR> (this);
|
if (wants_caching ()) {
|
||||||
|
|
||||||
if (! cp.first) {
|
std::pair<bool, std::vector<std::unordered_set<TR> > *> cp = cache->get<TR> (this);
|
||||||
|
|
||||||
std::vector<std::unordered_set<TR> > uncached_results;
|
if (! cp.first) {
|
||||||
uncached_results.resize (results.size ());
|
|
||||||
|
|
||||||
do_compute_local (cache, layout, cell, interactions, uncached_results, proc);
|
std::vector<std::unordered_set<TR> > uncached_results;
|
||||||
|
uncached_results.resize (results.size ());
|
||||||
|
|
||||||
cp.second->swap (uncached_results);
|
do_compute_local (cache, layout, cell, interactions, uncached_results, proc);
|
||||||
|
|
||||||
|
cp.second->swap (uncached_results);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tl_assert (results.size () == cp.second->size ());
|
||||||
|
for (size_t r = 0; r < results.size (); ++r) {
|
||||||
|
results[r].insert ((*cp.second)[r].begin (), (*cp.second)[r].end ());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
do_compute_local (cache, layout, cell, interactions, results, proc);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tl_assert (results.size () == cp.second->size ());
|
|
||||||
for (size_t r = 0; r < results.size (); ++r) {
|
|
||||||
results[r].insert ((*cp.second)[r].begin (), (*cp.second)[r].end ());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,399 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "dbEdgeNeighborhood.h"
|
||||||
|
#include "dbBoxScanner.h"
|
||||||
|
#include "dbClip.h"
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
static db::IMatrix3d to_original_trans (const db::Edge &edge)
|
||||||
|
{
|
||||||
|
// compute normal and unit vector along edge
|
||||||
|
db::DVector e = db::DVector (edge.d ());
|
||||||
|
e = e * (1.0 / e.double_length ());
|
||||||
|
db::DVector ne (-e.y (), e.x ());
|
||||||
|
|
||||||
|
// transform on the edge
|
||||||
|
return db::IMatrix3d (e.x (), ne.x (), e.y (), ne.y (), edge.p1 ().x (), edge.p1 ().y (), 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
EdgeNeighborhoodVisitor::EdgeNeighborhoodVisitor ()
|
||||||
|
: m_result_type (db::CompoundRegionOperationNode::ResultType::Edges)
|
||||||
|
{
|
||||||
|
disconnect_outputs ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set<db::Polygon> *polygons) const
|
||||||
|
{
|
||||||
|
disconnect_outputs ();
|
||||||
|
mp_polygons = polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::connect_output (db::Layout *layout, std::unordered_set<db::PolygonRef> *polygons) const
|
||||||
|
{
|
||||||
|
disconnect_outputs ();
|
||||||
|
mp_layout = layout;
|
||||||
|
mp_polygon_refs = polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::connect_output (db::Layout * /*layout*/, std::unordered_set<db::Edge> *edges) const
|
||||||
|
{
|
||||||
|
disconnect_outputs ();
|
||||||
|
mp_edges = edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set<db::EdgePair> *edge_pairs) const
|
||||||
|
{
|
||||||
|
disconnect_outputs ();
|
||||||
|
mp_edge_pairs = edge_pairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::disconnect_outputs () const
|
||||||
|
{
|
||||||
|
mp_layout = 0;
|
||||||
|
mp_polygons = 0;
|
||||||
|
mp_polygon_refs = 0;
|
||||||
|
mp_edges = 0;
|
||||||
|
mp_edge_pairs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::output_polygon (const db::Polygon &poly)
|
||||||
|
{
|
||||||
|
if (mp_polygons) {
|
||||||
|
mp_polygons->insert (poly);
|
||||||
|
} else if (mp_polygon_refs) {
|
||||||
|
tl_assert (mp_layout != 0);
|
||||||
|
mp_polygon_refs->insert (db::PolygonRef (poly, mp_layout->shape_repository ()));
|
||||||
|
} else {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("EdgeNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::output_edge (const db::Edge &edge)
|
||||||
|
{
|
||||||
|
if (mp_edges == 0) {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("EdgeNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')")));
|
||||||
|
}
|
||||||
|
mp_edges->insert (edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodVisitor::output_edge_pair (const db::EdgePair &edge_pair)
|
||||||
|
{
|
||||||
|
if (mp_edge_pairs == 0) {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("EdgeNeighborhoodVisitor is not configured for edge pair output (use 'result_type=EdgePairs')")));
|
||||||
|
}
|
||||||
|
mp_edge_pairs->insert (edge_pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
db::IMatrix3d
|
||||||
|
EdgeNeighborhoodVisitor::to_original_trans (const db::Edge &edge)
|
||||||
|
{
|
||||||
|
return db::to_original_trans (edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
db::IMatrix3d
|
||||||
|
EdgeNeighborhoodVisitor::to_edge_local_trans (const db::Edge &edge)
|
||||||
|
{
|
||||||
|
return db::to_original_trans (edge).inverted ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::EdgeNeighborhoodCompoundOperationNode (const std::vector<CompoundRegionOperationNode *> &children, EdgeNeighborhoodVisitor *visitor, db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout)
|
||||||
|
: CompoundRegionMultiInputOperationNode (children), m_bext (bext), m_eext (eext), m_din (din), m_dout (dout), mp_visitor (visitor)
|
||||||
|
{
|
||||||
|
tl_assert (visitor != 0);
|
||||||
|
visitor->keep ();
|
||||||
|
}
|
||||||
|
|
||||||
|
db::Coord
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::computed_dist () const
|
||||||
|
{
|
||||||
|
return std::max (std::max (m_bext, m_eext), std::max (m_din, m_dout)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::generated_description () const
|
||||||
|
{
|
||||||
|
return tl::to_string (tr ("Neighborhood collector"));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class EdgeCollectorReceiver
|
||||||
|
: public db::box_scanner_receiver2<db::Edge, unsigned int, db::Polygon, unsigned int>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EdgeCollectorReceiver (db::EdgeNeighborhoodVisitor *visitor, const db::Layout *layout, const db::Cell *cell,
|
||||||
|
db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout)
|
||||||
|
: mp_visitor (visitor), mp_layout (layout), mp_cell (cell),
|
||||||
|
m_bext (bext), m_eext (eext), m_din (din), m_dout (dout)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
void add (const db::Edge *o1, const unsigned int &p1, const db::Polygon *o2, const unsigned int &p2)
|
||||||
|
{
|
||||||
|
m_edge_neighbors[p1][p2].push_back (o2);
|
||||||
|
enter_edge (o1, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish1 (const db::Edge *o1, const unsigned int &p1)
|
||||||
|
{
|
||||||
|
m_edge_neighbors[p1];
|
||||||
|
enter_edge (o1, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize (bool)
|
||||||
|
{
|
||||||
|
for (auto en = m_edge_neighbors.begin (); en != m_edge_neighbors.end (); ++en) {
|
||||||
|
commit_edge (m_edges[en->first], en->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<unsigned int, std::map<unsigned int, std::vector<const db::Polygon *> > > m_edge_neighbors;
|
||||||
|
std::vector<db::Edge> m_edges;
|
||||||
|
db::EdgeNeighborhoodVisitor *mp_visitor;
|
||||||
|
const db::Layout *mp_layout;
|
||||||
|
const db::Cell *mp_cell;
|
||||||
|
db::Coord m_bext, m_eext, m_din, m_dout;
|
||||||
|
|
||||||
|
void enter_edge (const db::Edge *o1, const unsigned int &p1)
|
||||||
|
{
|
||||||
|
while (size_t (p1) >= m_edges.size ()) {
|
||||||
|
m_edges.push_back (db::Edge ());
|
||||||
|
}
|
||||||
|
m_edges[p1] = *o1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void commit_edge (const db::Edge &edge, const std::map<unsigned int, std::vector<const db::Polygon *> > &neighbors) const
|
||||||
|
{
|
||||||
|
if (edge.is_degenerate ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db::IMatrix3d from_original_trans = db::to_original_trans (edge).inverted ();
|
||||||
|
|
||||||
|
db::Edge ref_edge = from_original_trans * edge;
|
||||||
|
tl_assert (ref_edge.dy () == 0);
|
||||||
|
tl_assert (ref_edge.dx () > 0);
|
||||||
|
|
||||||
|
std::set<db::Coord> xpos;
|
||||||
|
db::Coord xmin = -m_bext - 1;
|
||||||
|
db::Coord xmax = ref_edge.dx () + m_eext + 1;
|
||||||
|
|
||||||
|
db::SimplePolygon per_edge_clip_box (db::Box (xmin, -m_din - 1, xmax, m_dout + 1));
|
||||||
|
|
||||||
|
// compute the merged neighbors
|
||||||
|
std::map<unsigned int, std::vector<db::Polygon> > merged_neighbors;
|
||||||
|
|
||||||
|
db::EdgeProcessor ep;
|
||||||
|
for (auto n = neighbors.begin (); n != neighbors.end (); ++n) {
|
||||||
|
|
||||||
|
ep.clear ();
|
||||||
|
|
||||||
|
size_t id = 0;
|
||||||
|
for (auto nn = n->second.begin (); nn != n->second.end (); ++nn) {
|
||||||
|
for (auto e = (*nn)->begin_edge (); ! e.at_end (); ++e) {
|
||||||
|
ep.insert (from_original_trans * *e, id);
|
||||||
|
}
|
||||||
|
id += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ep.insert (per_edge_clip_box, size_t (1));
|
||||||
|
|
||||||
|
db::BooleanOp and_op (db::BooleanOp::And);
|
||||||
|
db::PolygonContainer pc (merged_neighbors [n->first]);
|
||||||
|
db::PolygonGenerator pg (pc, false);
|
||||||
|
ep.process (pg, and_op);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto n = merged_neighbors.begin (); n != merged_neighbors.end (); ++n) {
|
||||||
|
for (auto p = n->second.begin (); p != n->second.end (); ++p) {
|
||||||
|
for (auto pe = p->begin_edge (); ! pe.at_end (); ++pe) {
|
||||||
|
db::Edge e = *pe;
|
||||||
|
xpos.insert (std::max (xmin, std::min (xmax, e.p1 ().x ())));
|
||||||
|
xpos.insert (std::max (xmin, std::min (xmax, e.p2 ().x ())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeNeighborhoodVisitor::neighbors_type binned_neighbors;
|
||||||
|
|
||||||
|
for (auto i = xpos.begin (); i != xpos.end (); ) {
|
||||||
|
|
||||||
|
db::Coord xfrom = *i;
|
||||||
|
++i;
|
||||||
|
if (i == xpos.end ()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
db::Coord xto = *i;
|
||||||
|
|
||||||
|
bool first_per_interval = true;
|
||||||
|
|
||||||
|
db::Box clip_box (xfrom, -m_din - 1, xto, m_dout + 1);
|
||||||
|
|
||||||
|
// NOTE: this could be more efficient if we had a multi-layer capable trapezoid decomposition tool
|
||||||
|
for (auto n = merged_neighbors.begin (); n != merged_neighbors.end (); ++n) {
|
||||||
|
|
||||||
|
EdgeNeighborhoodVisitor::neighbor_shapes_type polygons;
|
||||||
|
|
||||||
|
for (auto p = n->second.begin (); p != n->second.end (); ++p) {
|
||||||
|
db::clip_poly (*p, clip_box, polygons, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!polygons.empty ()) {
|
||||||
|
|
||||||
|
if (first_per_interval) {
|
||||||
|
first_per_interval = false;
|
||||||
|
binned_neighbors.push_back (EdgeNeighborhoodVisitor::neighbors_type::value_type ());
|
||||||
|
binned_neighbors.back ().first.first = xfrom;
|
||||||
|
binned_neighbors.back ().first.second = xto;
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeNeighborhoodVisitor::neighbors_per_interval_type &i2n = binned_neighbors.back ().second;
|
||||||
|
i2n[n->first].swap (polygons);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_visitor->on_edge (mp_layout, mp_cell, edge, binned_neighbors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_collect_neighbors (db::box_scanner2<db::Edge, unsigned int, db::Polygon, unsigned int> &scanner, const db::Layout *layout, const db::Cell *cell) const
|
||||||
|
{
|
||||||
|
EdgeCollectorReceiver rec (const_cast<db::EdgeNeighborhoodVisitor *> (mp_visitor.get ()), layout, cell, m_bext, m_eext, m_din, m_dout);
|
||||||
|
scanner.process (rec, computed_dist (), db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::PolygonRef, db::Edge> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::Edge> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::Polygon, db::Edge> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::Polygon> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::Polygon, db::Polygon> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::EdgePair> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::Polygon, db::EdgePair> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::PolygonRef> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::PolygonRef, db::PolygonRef> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::EdgePair> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
compute_local_impl<db::PolygonRef, db::EdgePair> (cache, layout, cell, interactions, results, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class TR>
|
||||||
|
void
|
||||||
|
EdgeNeighborhoodCompoundOperationNode::compute_local_impl (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions<T, T> &interactions, std::vector<std::unordered_set<TR> > &results, const db::LocalProcessorBase *proc) const
|
||||||
|
{
|
||||||
|
if (! mp_visitor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tl_assert (interactions.num_subjects () == 1);
|
||||||
|
tl_assert (! results.empty ());
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
mp_visitor->connect_output (layout, &results.front ());
|
||||||
|
|
||||||
|
db::box_scanner2<db::Edge, unsigned int, db::Polygon, unsigned int> scanner;
|
||||||
|
|
||||||
|
std::list<db::Edge> edges;
|
||||||
|
std::list<db::Polygon> polygons;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < children (); ++i) {
|
||||||
|
|
||||||
|
std::vector<std::unordered_set<T> > others;
|
||||||
|
others.push_back (std::unordered_set<T> ());
|
||||||
|
|
||||||
|
shape_interactions<T, T> computed_interactions;
|
||||||
|
child (i)->compute_local (cache, layout, cell, interactions_for_child (interactions, i, computed_interactions), others, proc);
|
||||||
|
|
||||||
|
for (auto p = others.front ().begin (); p != others.front ().end (); ++p) {
|
||||||
|
polygons.push_back (p->instantiate ());
|
||||||
|
scanner.insert2 (&polygons.back (), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &pr = interactions.begin_subjects ()->second;
|
||||||
|
unsigned int ie = 0;
|
||||||
|
for (auto e = pr.begin_edge (); ! e.at_end (); ++e, ++ie) {
|
||||||
|
edges.push_back (*e);
|
||||||
|
scanner.insert1 (&edges.back (), ie);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_cast<db::EdgeNeighborhoodVisitor *> (mp_visitor.get ())->begin_polygon (layout, cell, pr.instantiate ());
|
||||||
|
do_collect_neighbors (scanner, layout, cell);
|
||||||
|
const_cast<db::EdgeNeighborhoodVisitor *> (mp_visitor.get ())->end_polygon ();
|
||||||
|
|
||||||
|
mp_visitor->disconnect_outputs ();
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
mp_visitor->disconnect_outputs ();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HDR_dbEdgeNeighborhood
|
||||||
|
#define HDR_dbEdgeNeighborhood
|
||||||
|
|
||||||
|
#include "dbCommon.h"
|
||||||
|
#include "dbCompoundOperation.h"
|
||||||
|
#include "dbBoxScanner.h"
|
||||||
|
#include "dbMatrix.h"
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A visitor for the neighbors of an edge
|
||||||
|
*/
|
||||||
|
class DB_PUBLIC EdgeNeighborhoodVisitor
|
||||||
|
: public gsi::ObjectBase, public tl::Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::pair<double, double> position_interval_type;
|
||||||
|
typedef unsigned int input_key_type;
|
||||||
|
typedef std::vector<db::Polygon> neighbor_shapes_type;
|
||||||
|
typedef std::map<input_key_type, neighbor_shapes_type> neighbors_per_interval_type;
|
||||||
|
typedef std::vector<std::pair<position_interval_type, neighbors_per_interval_type> > neighbors_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
EdgeNeighborhoodVisitor ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
virtual ~EdgeNeighborhoodVisitor () { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure the polygon output
|
||||||
|
*/
|
||||||
|
void connect_output (db::Layout * /*layout*/, std::unordered_set<db::Polygon> *polygons) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure the polygon ref output
|
||||||
|
*/
|
||||||
|
void connect_output (db::Layout *layout, std::unordered_set<db::PolygonRef> *polygons) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure the edge output
|
||||||
|
*/
|
||||||
|
void connect_output (db::Layout * /*layout*/, std::unordered_set<db::Edge> *edges) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure the edge pair output
|
||||||
|
*/
|
||||||
|
void connect_output (db::Layout * /*layout*/, std::unordered_set<db::EdgePair> *edge_pairs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disconnects output
|
||||||
|
*/
|
||||||
|
void disconnect_outputs () const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event handler called when a new polygon is encountered
|
||||||
|
* Following this event, the edges with their neighborhood are reported.
|
||||||
|
* After the edges are reported, "end_polygon" is called.
|
||||||
|
*/
|
||||||
|
virtual void begin_polygon (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::Polygon & /*polygon*/) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event handler called after the polygon was processed
|
||||||
|
*/
|
||||||
|
virtual void end_polygon () { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event handler for each edge plus it's neighborhood
|
||||||
|
*/
|
||||||
|
virtual void on_edge (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::Edge & /*edge*/, const neighbors_type & /*neighbors*/) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a transformation to transform from edge-local space to original space
|
||||||
|
*/
|
||||||
|
static db::IMatrix3d to_original_trans (const db::Edge &edge);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a transformation to transform from original space into edge-local space
|
||||||
|
*/
|
||||||
|
static db::IMatrix3d to_edge_local_trans (const db::Edge &edge);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the result type
|
||||||
|
*/
|
||||||
|
void set_result_type (db::CompoundRegionOperationNode::ResultType result_type)
|
||||||
|
{
|
||||||
|
m_result_type = result_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the result type
|
||||||
|
*/
|
||||||
|
db::CompoundRegionOperationNode::ResultType result_type () const
|
||||||
|
{
|
||||||
|
return m_result_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delivers a polygon
|
||||||
|
* This function is only permitted if the result type is Region.
|
||||||
|
*/
|
||||||
|
void output_polygon (const db::Polygon &poly);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delivers an edge
|
||||||
|
* This function is only permitted if the result type is Edges.
|
||||||
|
*/
|
||||||
|
void output_edge (const db::Edge &edge);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delivers an edge pair object
|
||||||
|
* This function is only permitted if the result type is EdgePairs.
|
||||||
|
*/
|
||||||
|
void output_edge_pair (const db::EdgePair &edge_pair);
|
||||||
|
|
||||||
|
private:
|
||||||
|
db::CompoundRegionOperationNode::ResultType m_result_type;
|
||||||
|
mutable std::unordered_set<db::Polygon> *mp_polygons;
|
||||||
|
mutable std::unordered_set<db::PolygonRef> *mp_polygon_refs;
|
||||||
|
mutable std::unordered_set<db::Edge> *mp_edges;
|
||||||
|
mutable std::unordered_set<db::EdgePair> *mp_edge_pairs;
|
||||||
|
mutable db::Layout *mp_layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A local operation for implementation of the neighborhood visitor
|
||||||
|
*/
|
||||||
|
class DB_PUBLIC EdgeNeighborhoodCompoundOperationNode
|
||||||
|
: public CompoundRegionMultiInputOperationNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EdgeNeighborhoodCompoundOperationNode (const std::vector<CompoundRegionOperationNode *> &children, EdgeNeighborhoodVisitor *visitor, const db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout);
|
||||||
|
|
||||||
|
virtual ResultType result_type () const
|
||||||
|
{
|
||||||
|
return mp_visitor ? mp_visitor->result_type () : CompoundRegionOperationNode::Edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool wants_caching () const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual db::Coord computed_dist () const;
|
||||||
|
virtual std::string generated_description () const;
|
||||||
|
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::Polygon, db::Polygon> & /*interactions*/, std::vector<std::unordered_set<db::Edge> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::PolygonRef, db::PolygonRef> & /*interactions*/, std::vector<std::unordered_set<db::Edge> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::Polygon, db::Polygon> & /*interactions*/, std::vector<std::unordered_set<db::Polygon> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::Polygon, db::Polygon> & /*interactions*/, std::vector<std::unordered_set<db::EdgePair> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::PolygonRef, db::PolygonRef> & /*interactions*/, std::vector<std::unordered_set<db::PolygonRef> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<db::PolygonRef, db::PolygonRef> & /*interactions*/, std::vector<std::unordered_set<db::EdgePair> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
db::Coord m_bext, m_eext, m_din, m_dout;
|
||||||
|
tl::weak_ptr<EdgeNeighborhoodVisitor> mp_visitor;
|
||||||
|
|
||||||
|
void do_collect_neighbors (db::box_scanner2<db::Edge, unsigned int, db::Polygon, unsigned int> &scanner, const db::Layout *layout, const db::Cell *cell) const;
|
||||||
|
|
||||||
|
template <class T, class TR>
|
||||||
|
void compute_local_impl (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions<T, T> & /*interactions*/, std::vector<std::unordered_set<TR> > & /*results*/, const db::LocalProcessorBase * /*proc*/) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "gsiDecl.h"
|
||||||
|
#include "gsiEnums.h"
|
||||||
|
|
||||||
|
#include "tlThreads.h"
|
||||||
|
#include "dbEdgeNeighborhood.h"
|
||||||
|
|
||||||
|
namespace gsi
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------
|
||||||
|
// EdgeFilter binding
|
||||||
|
|
||||||
|
class EdgeNeighborhoodVisitorImpl
|
||||||
|
: public db::EdgeNeighborhoodVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EdgeNeighborhoodVisitorImpl () { }
|
||||||
|
|
||||||
|
void issue_on_edge (const db::Layout *, const db::Cell *, const db::Edge &, const tl::Variant &)
|
||||||
|
{
|
||||||
|
// just for signature
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_edge (const db::Layout *layout, const db::Cell *cell, const db::Edge &edge, const db::EdgeNeighborhoodVisitor::neighbors_type &neighbors)
|
||||||
|
{
|
||||||
|
if (f_on_edge.can_issue ()) {
|
||||||
|
|
||||||
|
tl::Variant neighborhood = build_neighbors (neighbors);
|
||||||
|
|
||||||
|
// NOTE: as scripts are potentially thread unsafe, we lock here
|
||||||
|
tl::MutexLocker locker (&m_lock);
|
||||||
|
return f_on_edge.issue<EdgeNeighborhoodVisitorImpl, const db::Layout *, const db::Cell *, const db::Edge &, const tl::Variant &> (&EdgeNeighborhoodVisitorImpl::issue_on_edge, layout, cell, edge, neighborhood);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi::Callback f_on_edge;
|
||||||
|
|
||||||
|
void issue_begin_polygon (const db::Layout *, const db::Cell *, const db::Polygon &)
|
||||||
|
{
|
||||||
|
// just for signature
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_polygon (const db::Layout *layout, const db::Cell *cell, const db::Polygon &poly)
|
||||||
|
{
|
||||||
|
if (f_begin_polygon.can_issue ()) {
|
||||||
|
// NOTE: as scripts are potentially thread unsafe, we lock here
|
||||||
|
tl::MutexLocker locker (&m_lock);
|
||||||
|
return f_begin_polygon.issue<EdgeNeighborhoodVisitorImpl, const db::Layout *, const db::Cell *, const db::Polygon &> (&EdgeNeighborhoodVisitorImpl::begin_polygon, layout, cell, poly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi::Callback f_begin_polygon;
|
||||||
|
|
||||||
|
void issue_end_polygon ()
|
||||||
|
{
|
||||||
|
// just for signature
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_polygon ()
|
||||||
|
{
|
||||||
|
if (f_end_polygon.can_issue ()) {
|
||||||
|
// NOTE: as scripts are potentially thread unsafe, we lock here
|
||||||
|
tl::MutexLocker locker (&m_lock);
|
||||||
|
return f_end_polygon.issue<EdgeNeighborhoodVisitorImpl> (&EdgeNeighborhoodVisitorImpl::end_polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi::Callback f_end_polygon;
|
||||||
|
|
||||||
|
static tl::Variant build_neighbors (const db::EdgeNeighborhoodVisitor::neighbors_type &neighbors)
|
||||||
|
{
|
||||||
|
tl::Variant result;
|
||||||
|
result.set_list ();
|
||||||
|
|
||||||
|
for (auto n = neighbors.begin (); n != neighbors.end (); ++n) {
|
||||||
|
|
||||||
|
tl::Variant row;
|
||||||
|
row.set_list ();
|
||||||
|
|
||||||
|
tl::Variant interval;
|
||||||
|
interval.set_list ();
|
||||||
|
interval.push (n->first.first);
|
||||||
|
interval.push (n->first.second);
|
||||||
|
row.push (interval);
|
||||||
|
|
||||||
|
tl::Variant nmap;
|
||||||
|
nmap.set_array ();
|
||||||
|
|
||||||
|
for (auto nn = n->second.begin (); nn != n->second.end (); ++nn) {
|
||||||
|
nmap.insert (tl::Variant (nn->first), tl::Variant (nn->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
row.push (nmap);
|
||||||
|
|
||||||
|
result.push (row);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// No copying
|
||||||
|
EdgeNeighborhoodVisitorImpl &operator= (const EdgeNeighborhoodVisitorImpl &);
|
||||||
|
EdgeNeighborhoodVisitorImpl (const EdgeNeighborhoodVisitorImpl &);
|
||||||
|
|
||||||
|
tl::Mutex m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
Class<db::EdgeNeighborhoodVisitor> decl_EdgeNeighborhoodVisitor ("db", "EdgeNeighborhoodVisitorBase",
|
||||||
|
"@hide"
|
||||||
|
);
|
||||||
|
|
||||||
|
Class<gsi::EdgeNeighborhoodVisitorImpl> decl_EdgeNeighborhoodVisitorImpl (decl_EdgeNeighborhoodVisitor, "db", "EdgeNeighborhoodVisitor",
|
||||||
|
gsi::callback ("on_edge", &EdgeNeighborhoodVisitorImpl::issue_on_edge, &EdgeNeighborhoodVisitorImpl::f_on_edge, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("edge"), gsi::arg ("neighborhood"),
|
||||||
|
"@brief Is called for each edge with the edge neighbors\n"
|
||||||
|
"This method is called for every edge on the input region. It delivers the edge and the edge neighborhood. "
|
||||||
|
"The edge neighborhood is classified in intervals along the edge. The intervals are given by a range of "
|
||||||
|
"positions along the edge - 0.0 being the beginning of the edge and positive values towards the end of the edge. "
|
||||||
|
"For 'bext' and 'eext' larger than zero (see "
|
||||||
|
"\\EdgeNeighborhoodCompoundOperationNode), the position can be negative or larger than the edge length.\n"
|
||||||
|
"\n"
|
||||||
|
"The structure of the neighbors is:\n"
|
||||||
|
"\n"
|
||||||
|
"@code\n"
|
||||||
|
"[\n"
|
||||||
|
" [ [ from, to ], { input_index => polygons } ]\n"
|
||||||
|
"]\n"
|
||||||
|
"@/code\n"
|
||||||
|
"\n"
|
||||||
|
"'from' and 'to' are the positions of the interval, 'input_index' is the index of the input the neighbors are on "
|
||||||
|
"(see 'children' argument of \\EdgeNeighborhoodCompoundOperationNode constructor), 'prop_id' is the properties ID of "
|
||||||
|
"the neighbors and 'polygons' is a list of polygons describing the neighborhood.\n"
|
||||||
|
"The polygons are projected on the edge - i.e. they are in a coordinate system where the edge is horizonal and "
|
||||||
|
"goes from (0,0) to (length,0).\n"
|
||||||
|
"\n"
|
||||||
|
"The polygons are boxes for manhattan input and trapezoids in the general case.\n"
|
||||||
|
) +
|
||||||
|
gsi::callback ("begin_polygon", &EdgeNeighborhoodVisitorImpl::issue_begin_polygon, &EdgeNeighborhoodVisitorImpl::f_begin_polygon, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("polygon"),
|
||||||
|
"@brief Is called for each new polygon\n"
|
||||||
|
"This event announces a new primary polygon. After this event, the edges of the polygon are reported via \\on_edge, "
|
||||||
|
"followed by a call of \\end_polygon.\n"
|
||||||
|
"\n"
|
||||||
|
"Note, that the polygon object is a temporary reference to a C++ object and it is only valid during the execution of this "
|
||||||
|
"callback. If you like to keep the polygon object, create a copy of it using the 'dup' method."
|
||||||
|
) +
|
||||||
|
gsi::callback ("end_polygon", &EdgeNeighborhoodVisitorImpl::issue_end_polygon, &EdgeNeighborhoodVisitorImpl::f_end_polygon,
|
||||||
|
"@brief Is called after the polygon\n"
|
||||||
|
"See \\begin_polygon for a description of this protocol."
|
||||||
|
) +
|
||||||
|
gsi::method ("output", &EdgeNeighborhoodVisitorImpl::output_polygon, gsi::arg ("polygon"),
|
||||||
|
"@brief Outputs a polygon\n"
|
||||||
|
"Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. "
|
||||||
|
"Note that you have to configure the result type as 'Region' on construction of the visitor before being able to do so.\n"
|
||||||
|
"\n"
|
||||||
|
"'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable "
|
||||||
|
"transformation to bring objects from 'edge is horizontal' space into the original space."
|
||||||
|
) +
|
||||||
|
gsi::method ("output", &EdgeNeighborhoodVisitorImpl::output_edge, gsi::arg ("edge"),
|
||||||
|
"@brief Outputs an edge\n"
|
||||||
|
"Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. "
|
||||||
|
"Note that you have to configure the result type as 'Edges' on construction of the visitor before being able to do so."
|
||||||
|
"\n"
|
||||||
|
"'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable "
|
||||||
|
"transformation to bring objects from 'edge is horizontal' space into the original space."
|
||||||
|
) +
|
||||||
|
gsi::method ("output", &EdgeNeighborhoodVisitorImpl::output_edge_pair, gsi::arg ("edge_pair"),
|
||||||
|
"@brief Outputs an edge pair\n"
|
||||||
|
"Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. "
|
||||||
|
"Note that you have to configure the result type as 'EdgePairs' on construction of the visitor before being able to do so."
|
||||||
|
"\n"
|
||||||
|
"'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable "
|
||||||
|
"transformation to bring objects from 'edge is horizontal' space into the original space."
|
||||||
|
) +
|
||||||
|
gsi::method ("to_original_trans", &EdgeNeighborhoodVisitorImpl::to_original_trans, gsi::arg ("edge"),
|
||||||
|
"@brief For a given edge, computes the transformation that brings objects from the normalized space (edge is horizontal) to the original space of the edge.\n"
|
||||||
|
"Use this method to compute the objects suitable for 'output', after you derived them in edge-local space.\n"
|
||||||
|
) +
|
||||||
|
gsi::method ("to_edge_local_trans", &EdgeNeighborhoodVisitorImpl::to_edge_local_trans, gsi::arg ("edge"),
|
||||||
|
"@brief For a given edge, computes the transformation that brings objects from original space to the edge-local space where the edge is horizontal.\n"
|
||||||
|
"Technically, this transformation is the inverse of \\to_original_trans.\n"
|
||||||
|
) +
|
||||||
|
gsi::method ("result_type=", &EdgeNeighborhoodVisitorImpl::set_result_type, gsi::arg ("result_type"),
|
||||||
|
"@brief Configures the result type\n"
|
||||||
|
"Use this method to indicate what type of result you want to deliver. You can use the corresponding 'output' method then to "
|
||||||
|
"deliver result shapes from one the callbacks (\\on_edge, \\begin_polygon, \\end_polygon). Set this attribute when you create "
|
||||||
|
"the visitor object. This attribute does not need to be set if no output is indended to be delivered."
|
||||||
|
) +
|
||||||
|
gsi::method ("result_type", &EdgeNeighborhoodVisitorImpl::result_type,
|
||||||
|
"@brief Gets the result type\n"
|
||||||
|
),
|
||||||
|
"@brief A visitor for the neighborhood of edges in the input\n"
|
||||||
|
"\n"
|
||||||
|
"Objects of this class are passed to \\EdgeNeighborhoodCompoundOperationNode constructor to handle "
|
||||||
|
"events on each edge of the primary input along with the neighborhood taken from the additional inputs.\n"
|
||||||
|
"\n"
|
||||||
|
"See \\on_edge for the description of the events delivered."
|
||||||
|
"\n"
|
||||||
|
"This class has been introduced in version 0.29.9.\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------
|
||||||
|
// EdgeProcessor binding
|
||||||
|
|
||||||
|
static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector<db::CompoundRegionOperationNode *> &children, db::EdgeNeighborhoodVisitor *visitor, const db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout)
|
||||||
|
{
|
||||||
|
return new db::EdgeNeighborhoodCompoundOperationNode (children, visitor, bext, eext, din, dout);
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi::ClassExt<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode_ext (
|
||||||
|
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 first one in the primary input, the others are neighbors.\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"
|
||||||
|
"@param din The search window extension to the 'outside' of the edge.\n"
|
||||||
|
"@param dout The search window extension to the 'inside' of the edge.\n"
|
||||||
|
"\n"
|
||||||
|
"This constructor has been introduced in version 0.29.9.\n"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include "dbBox.h"
|
#include "dbBox.h"
|
||||||
#include "dbPolygon.h"
|
#include "dbPolygon.h"
|
||||||
#include "dbEdge.h"
|
#include "dbEdge.h"
|
||||||
|
#include "dbEdgePair.h"
|
||||||
|
|
||||||
namespace gsi
|
namespace gsi
|
||||||
{
|
{
|
||||||
|
|
@ -130,6 +131,12 @@ static db::edge<C> trans_edge (const db::matrix_2d<C> *m, const db::edge<C> &e)
|
||||||
return e.transformed (*m);
|
return e.transformed (*m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
static db::edge_pair<C> trans_edge_pair (const db::matrix_2d<C> *m, const db::edge_pair<C> &e)
|
||||||
|
{
|
||||||
|
return e.transformed (*m);
|
||||||
|
}
|
||||||
|
|
||||||
template <class C>
|
template <class C>
|
||||||
static double coeff_m (const db::matrix_2d<C> *m, int i, int j)
|
static double coeff_m (const db::matrix_2d<C> *m, int i, int j)
|
||||||
{
|
{
|
||||||
|
|
@ -234,6 +241,12 @@ matrix2d_methods ()
|
||||||
"@param e The edge to transform.\n"
|
"@param e The edge to transform.\n"
|
||||||
"@return The transformed edge\n"
|
"@return The transformed edge\n"
|
||||||
) +
|
) +
|
||||||
|
gsi::method_ext ("*", &trans_edge_pair<C>, gsi::arg ("ep"),
|
||||||
|
"@brief Transforms an edge pair with this matrix.\n"
|
||||||
|
"@param ep The edge pair to transform.\n"
|
||||||
|
"@return The transformed edge\n"
|
||||||
|
"This variant has been added in version 0.29.9."
|
||||||
|
) +
|
||||||
gsi::method_ext ("*", &trans_box<C>, gsi::arg ("box"),
|
gsi::method_ext ("*", &trans_box<C>, gsi::arg ("box"),
|
||||||
"@brief Transforms a box with this matrix.\n"
|
"@brief Transforms a box with this matrix.\n"
|
||||||
"@param box The box to transform.\n"
|
"@param box The box to transform.\n"
|
||||||
|
|
@ -445,6 +458,12 @@ static db::edge<C> trans_edge3 (const db::matrix_3d<C> *m, const db::edge<C> &e)
|
||||||
return e.transformed (*m);
|
return e.transformed (*m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
static db::edge_pair<C> trans_edge_pair3 (const db::matrix_3d<C> *m, const db::edge_pair<C> &e)
|
||||||
|
{
|
||||||
|
return e.transformed (*m);
|
||||||
|
}
|
||||||
|
|
||||||
template <class C>
|
template <class C>
|
||||||
static double coeff_m3 (const db::matrix_3d<C> *m, int i, int j)
|
static double coeff_m3 (const db::matrix_3d<C> *m, int i, int j)
|
||||||
{
|
{
|
||||||
|
|
@ -617,6 +636,12 @@ matrix3d_methods ()
|
||||||
"@param e The edge to transform.\n"
|
"@param e The edge to transform.\n"
|
||||||
"@return The transformed edge\n"
|
"@return The transformed edge\n"
|
||||||
) +
|
) +
|
||||||
|
gsi::method_ext ("*", &trans_edge_pair3<C>, gsi::arg ("ep"),
|
||||||
|
"@brief Transforms an edge pair with this matrix.\n"
|
||||||
|
"@param ep The edge pair to transform.\n"
|
||||||
|
"@return The transformed edge pair\n"
|
||||||
|
"This variant has been added in version 0.29.9."
|
||||||
|
) +
|
||||||
gsi::method_ext ("*", &trans_box3<C>, gsi::arg ("box"),
|
gsi::method_ext ("*", &trans_box3<C>, gsi::arg ("box"),
|
||||||
"@brief Transforms a box with this matrix.\n"
|
"@brief Transforms a box with this matrix.\n"
|
||||||
"@param box The box to transform.\n"
|
"@param box The box to transform.\n"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "dbRegion.h"
|
||||||
|
#include "dbEdgeNeighborhood.h"
|
||||||
|
#include "dbReader.h"
|
||||||
|
#include "dbTestSupport.h"
|
||||||
|
|
||||||
|
#include "tlStream.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class ENPrimaryCopyVisitor
|
||||||
|
: public db::EdgeNeighborhoodVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ENPrimaryCopyVisitor ()
|
||||||
|
{
|
||||||
|
set_result_type (db::CompoundRegionOperationNode::ResultType::Region);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_polygon (const db::Layout *, const db::Cell *, const db::Polygon &polygon)
|
||||||
|
{
|
||||||
|
output_polygon (polygon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ENPrimaryCopyIntruderVisitor
|
||||||
|
: public db::EdgeNeighborhoodVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ENPrimaryCopyIntruderVisitor (unsigned int input)
|
||||||
|
{
|
||||||
|
set_result_type (db::CompoundRegionOperationNode::ResultType::Region);
|
||||||
|
m_input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_edge (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::Edge &edge, const neighbors_type &neighbors)
|
||||||
|
{
|
||||||
|
db::IMatrix3d trans = to_original_trans (edge);
|
||||||
|
|
||||||
|
for (auto n = neighbors.begin (); n != neighbors.end (); ++n) {
|
||||||
|
for (auto nn = n->second.begin (); nn != n->second.end (); ++nn) {
|
||||||
|
if (nn->first == m_input) {
|
||||||
|
for (auto p = nn->second.begin (); p != nn->second.end (); ++p) {
|
||||||
|
output_polygon (trans * *p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::EdgeNeighborhoodVisitor &visitor, const std::string &au_name, bool deep = true, db::Coord bext = 0, db::Coord eext = 0, db::Coord din = 0, db::Coord dout = 0)
|
||||||
|
{
|
||||||
|
db::Layout ly;
|
||||||
|
{
|
||||||
|
std::string fn (tl::testdata ());
|
||||||
|
fn += "/algo/edge_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::EdgeNeighborhoodCompoundOperationNode en_node (children, &visitor, bext, eext, din, dout);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
ENPrimaryCopyVisitor visitor;
|
||||||
|
run_test (_this, visitor, "/algo/edge_neighborhood_au1.gds");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(2)
|
||||||
|
{
|
||||||
|
ENPrimaryCopyIntruderVisitor visitor (0);
|
||||||
|
run_test (_this, visitor, "/algo/edge_neighborhood_au2.gds", true, 100, 100, 100, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(3)
|
||||||
|
{
|
||||||
|
ENPrimaryCopyIntruderVisitor visitor (1);
|
||||||
|
run_test (_this, visitor, "/algo/edge_neighborhood_au3.gds", true, 100, 100, 100, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(4)
|
||||||
|
{
|
||||||
|
ENPrimaryCopyIntruderVisitor visitor (2);
|
||||||
|
run_test (_this, visitor, "/algo/edge_neighborhood_au4.gds", true, 100, 100, 100, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(5)
|
||||||
|
{
|
||||||
|
ENPrimaryCopyIntruderVisitor visitor (3);
|
||||||
|
run_test (_this, visitor, "/algo/edge_neighborhood_au5.gds", true, 100, 100, 100, 2000);
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri)
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
dbCompoundOperationTests.cc \
|
dbCompoundOperationTests.cc \
|
||||||
|
dbEdgeNeighborhoodTests.cc \
|
||||||
dbFillToolTests.cc \
|
dbFillToolTests.cc \
|
||||||
dbLogTests.cc \
|
dbLogTests.cc \
|
||||||
dbRecursiveInstanceIteratorTests.cc \
|
dbRecursiveInstanceIteratorTests.cc \
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ PYTHONTEST (dbTransTest, "dbTransTest.py")
|
||||||
PYTHONTEST (dbLayoutToNetlist, "dbLayoutToNetlist.py")
|
PYTHONTEST (dbLayoutToNetlist, "dbLayoutToNetlist.py")
|
||||||
PYTHONTEST (dbLayoutVsSchematic, "dbLayoutVsSchematic.py")
|
PYTHONTEST (dbLayoutVsSchematic, "dbLayoutVsSchematic.py")
|
||||||
PYTHONTEST (dbNetlistCrossReference, "dbNetlistCrossReference.py")
|
PYTHONTEST (dbNetlistCrossReference, "dbNetlistCrossReference.py")
|
||||||
|
PYTHONTEST (rdbTest, "rdbTest.py")
|
||||||
PYTHONTEST (layLayers, "layLayers.py")
|
PYTHONTEST (layLayers, "layLayers.py")
|
||||||
PYTHONTEST (layObjects, "layObjects.py")
|
PYTHONTEST (layObjects, "layObjects.py")
|
||||||
PYTHONTEST (layPixelBuffer, "layPixelBuffer.py")
|
PYTHONTEST (layPixelBuffer, "layPixelBuffer.py")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ RUBYTEST (dbEdgePairsTest, "dbEdgePairsTest.rb")
|
||||||
RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb")
|
RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb")
|
||||||
RUBYTEST (dbEdgesTest, "dbEdgesTest.rb")
|
RUBYTEST (dbEdgesTest, "dbEdgesTest.rb")
|
||||||
RUBYTEST (dbEdgeTest, "dbEdgeTest.rb")
|
RUBYTEST (dbEdgeTest, "dbEdgeTest.rb")
|
||||||
|
RUBYTEST (dbEdgeNeighborhood, "dbEdgeNeighborhood.rb")
|
||||||
RUBYTEST (dbGlyphs, "dbGlyphs.rb")
|
RUBYTEST (dbGlyphs, "dbGlyphs.rb")
|
||||||
RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb")
|
RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb")
|
||||||
RUBYTEST (dbInstanceTest, "dbInstanceTest.rb")
|
RUBYTEST (dbInstanceTest, "dbInstanceTest.rb")
|
||||||
|
|
|
||||||
|
|
@ -881,6 +881,13 @@ Categories::category_by_name (const char *path)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Category *
|
||||||
|
Categories::category_by_raw_name (const std::string &name)
|
||||||
|
{
|
||||||
|
auto c = m_categories_by_name.find (name);
|
||||||
|
return c == m_categories_by_name.end () ? 0 : c->second;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Categories::import_category (Category *category)
|
Categories::import_category (Category *category)
|
||||||
{
|
{
|
||||||
|
|
@ -1353,6 +1360,11 @@ Database::create_category (const std::string &name)
|
||||||
Category *
|
Category *
|
||||||
Database::create_category (Categories *container, const std::string &name)
|
Database::create_category (Categories *container, const std::string &name)
|
||||||
{
|
{
|
||||||
|
Category *existing = container->category_by_raw_name (name);
|
||||||
|
if (existing) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
set_modified ();
|
set_modified ();
|
||||||
|
|
||||||
Category *cat = new Category (name);
|
Category *cat = new Category (name);
|
||||||
|
|
|
||||||
|
|
@ -413,8 +413,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_category (Category *cath);
|
void add_category (Category *cath);
|
||||||
|
|
||||||
void set_database (Database *database);
|
void set_database (Database *database);
|
||||||
|
Category *category_by_raw_name (const std::string &name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,981 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
import pya
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def to_s_test(self):
|
||||||
|
r = self.name()
|
||||||
|
r += "["
|
||||||
|
refs = [ self.database().cell_by_id(i.parent_cell_id).name() + "->" + str(i.trans) for i in self.each_reference() ]
|
||||||
|
r += ",".join(refs)
|
||||||
|
r += "]"
|
||||||
|
return r
|
||||||
|
|
||||||
|
def to_s_items(self):
|
||||||
|
r = self.name()
|
||||||
|
r += "["
|
||||||
|
ai = []
|
||||||
|
for i in self.each_item():
|
||||||
|
vv = [ str(v) for v in i.each_value() ]
|
||||||
|
ai.append("/".join(vv))
|
||||||
|
r += ",".join(ai)
|
||||||
|
r += "]"
|
||||||
|
return r
|
||||||
|
|
||||||
|
pya.RdbCell.to_s_items = to_s_items
|
||||||
|
pya.RdbCell.to_s_test = to_s_test
|
||||||
|
|
||||||
|
class RDB_TestClass(unittest.TestCase):
|
||||||
|
|
||||||
|
# RdbReference
|
||||||
|
def test_1(self):
|
||||||
|
|
||||||
|
ref = pya.RdbReference(pya.DCplxTrans(), 0)
|
||||||
|
self.assertEqual(ref.trans.__str__(), "r0 *1 0,0")
|
||||||
|
ref.trans = pya.DCplxTrans(5.0)
|
||||||
|
self.assertEqual(ref.trans.__str__(), "r0 *5 0,0")
|
||||||
|
|
||||||
|
self.assertEqual(ref.parent_cell_id, 0)
|
||||||
|
ref.parent_cell_id = 177
|
||||||
|
self.assertEqual(ref.parent_cell_id, 177)
|
||||||
|
|
||||||
|
# RdbCell, RdbCategory
|
||||||
|
def test_2(self):
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
self.assertEqual(cell.database().__repr__(), db.__repr__())
|
||||||
|
self.assertEqual(cell.name(), "cell_name")
|
||||||
|
self.assertEqual(cell.rdb_id(), 1)
|
||||||
|
|
||||||
|
cell2 = db.create_cell("new_cell", "var1")
|
||||||
|
self.assertEqual(cell2.name(), "new_cell")
|
||||||
|
self.assertEqual(cell2.layout_name(), "")
|
||||||
|
self.assertEqual(cell2.qname(), "new_cell:var1")
|
||||||
|
|
||||||
|
cell2 = db.create_cell("cell_name", "var1", "cell_name$1")
|
||||||
|
self.assertEqual(cell.name(), "cell_name")
|
||||||
|
self.assertEqual(cell.qname(), "cell_name:1")
|
||||||
|
self.assertEqual(db.cell_by_qname("cell_name:1").rdb_id(), cell.rdb_id())
|
||||||
|
self.assertEqual(db.cell_by_id(cell.rdb_id()).rdb_id(), cell.rdb_id())
|
||||||
|
self.assertEqual(cell2.name(), "cell_name")
|
||||||
|
self.assertEqual(cell2.layout_name(), "cell_name$1")
|
||||||
|
self.assertEqual(cell2.qname(), "cell_name:var1")
|
||||||
|
self.assertEqual(db.cell_by_qname("cell_name:var1").rdb_id(), cell2.rdb_id())
|
||||||
|
self.assertEqual(db.cell_by_id(cell2.rdb_id()).rdb_id(), cell2.rdb_id())
|
||||||
|
self.assertEqual(cell2.rdb_id(), 3)
|
||||||
|
self.assertEqual(cell.num_items(), 0)
|
||||||
|
self.assertEqual(cell2.num_items(), 0)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 0)
|
||||||
|
|
||||||
|
cc = db.variants("cell_name")
|
||||||
|
self.assertEqual(len(cc), 2)
|
||||||
|
self.assertEqual(cc[0], cell.rdb_id())
|
||||||
|
self.assertEqual(cc[1], cell2.rdb_id())
|
||||||
|
|
||||||
|
cc = [ c for c in db.each_cell() ]
|
||||||
|
self.assertEqual(len(cc), 3)
|
||||||
|
self.assertEqual(cc[0].rdb_id(), cell.rdb_id())
|
||||||
|
self.assertEqual(cc[2].rdb_id(), cell2.rdb_id())
|
||||||
|
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
self.assertEqual(cat.database().__repr__(), db.__repr__())
|
||||||
|
self.assertEqual(cat.name(), "cat")
|
||||||
|
self.assertEqual(cat.rdb_id(), 4)
|
||||||
|
self.assertEqual(cat.path(), "cat")
|
||||||
|
|
||||||
|
cats = db.create_category(cat, "subcat")
|
||||||
|
self.assertEqual(cats.name(), "subcat")
|
||||||
|
self.assertEqual(cats.rdb_id(), 5)
|
||||||
|
self.assertEqual(cats.path(), "cat.subcat")
|
||||||
|
self.assertEqual(cats.parent().rdb_id(), cat.rdb_id())
|
||||||
|
|
||||||
|
cat.description = "cat_desc"
|
||||||
|
self.assertEqual(cat.description, "cat_desc")
|
||||||
|
|
||||||
|
x = [ c for c in cat.each_sub_category() ]
|
||||||
|
self.assertEqual(len(x), 1)
|
||||||
|
self.assertEqual(x[0].rdb_id(), cats.rdb_id())
|
||||||
|
|
||||||
|
item = db.create_item(cell.rdb_id(), cat.rdb_id())
|
||||||
|
self.assertEqual(db.num_items(), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(), 0)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cat.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(cell.num_items(), 1)
|
||||||
|
self.assertEqual(cell2.num_items(), 0)
|
||||||
|
self.assertEqual(cat.num_items(), 1)
|
||||||
|
self.assertEqual(cats.num_items(), 0)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cat.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cats.num_items_visited(), 0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = db.create_item(1000, cat.rdb_id())
|
||||||
|
self.assertEqual(False, True)
|
||||||
|
except Exception as ex:
|
||||||
|
self.assertEqual(ex.__str__(), "Not a valid cell ID: 1000 in ReportDatabase.create_item")
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = db.create_item(cell.rdb_id(), 1001)
|
||||||
|
self.assertEqual(False, True)
|
||||||
|
except Exception as ex:
|
||||||
|
self.assertEqual(ex.__str__(), "Not a valid category ID: 1001 in ReportDatabase.create_item")
|
||||||
|
|
||||||
|
item2 = db.create_item(cell2, cats)
|
||||||
|
self.assertEqual(db.num_items(), 2)
|
||||||
|
self.assertEqual(db.num_items_visited(), 0)
|
||||||
|
self.assertEqual(db.num_items(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell2.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items(cell2.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell2.rdb_id(), cat.rdb_id()), 0)
|
||||||
|
self.assertEqual(cell.num_items(), 1)
|
||||||
|
self.assertEqual(cell2.num_items(), 1)
|
||||||
|
self.assertEqual(cat.num_items(), 2)
|
||||||
|
self.assertEqual(cats.num_items(), 1)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cat.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cats.num_items_visited(), 0)
|
||||||
|
|
||||||
|
db.set_item_visited(item, True)
|
||||||
|
self.assertEqual(item.is_visited(), True)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cat.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cats.num_items_visited(), 0)
|
||||||
|
self.assertEqual(db.num_items(), 2)
|
||||||
|
self.assertEqual(db.num_items_visited(), 1)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell2.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
|
||||||
|
db.set_item_visited(item2, True)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cat.num_items_visited(), 2)
|
||||||
|
self.assertEqual(cats.num_items_visited(), 1)
|
||||||
|
self.assertEqual(db.num_items(), 2)
|
||||||
|
self.assertEqual(db.num_items_visited(), 2)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
|
||||||
|
db.set_item_visited(item, False)
|
||||||
|
self.assertEqual(item.is_visited(), False)
|
||||||
|
self.assertEqual(cell.num_items_visited(), 0)
|
||||||
|
self.assertEqual(cell2.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cat.num_items_visited(), 1)
|
||||||
|
self.assertEqual(cats.num_items_visited(), 1)
|
||||||
|
self.assertEqual(db.num_items(), 2)
|
||||||
|
self.assertEqual(db.num_items_visited(), 1)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cat.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cat.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items_visited(cell2.rdb_id(), cats.rdb_id()), 1)
|
||||||
|
self.assertEqual(db.num_items(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
self.assertEqual(db.num_items_visited(cell.rdb_id(), cats.rdb_id()), 0)
|
||||||
|
|
||||||
|
ii = [ i for i in db.each_item() ]
|
||||||
|
self.assertEqual(len(ii), 2)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item.category_id())
|
||||||
|
self.assertEqual(ii[1].cell_id(), item2.cell_id())
|
||||||
|
self.assertEqual(ii[1].category_id(), item2.category_id())
|
||||||
|
|
||||||
|
ii = [ i for i in db.each_item_per_cell(cell.rdb_id()) ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item.category_id())
|
||||||
|
|
||||||
|
ii = [ i for i in cell.each_item() ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item.category_id())
|
||||||
|
|
||||||
|
ii = [ i for i in db.each_item_per_category(cats.rdb_id()) ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item2.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item2.category_id())
|
||||||
|
|
||||||
|
ii = [ i for i in cats.each_item() ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item2.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item2.category_id())
|
||||||
|
|
||||||
|
ii = [ i for i in db.each_item_per_cell_and_category(cell.rdb_id(), cats.rdb_id()) ]
|
||||||
|
self.assertEqual(len(ii), 0)
|
||||||
|
|
||||||
|
ii = [ i for i in db.each_item_per_cell_and_category(cell2.rdb_id(), cats.rdb_id()) ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].cell_id(), item2.cell_id())
|
||||||
|
self.assertEqual(ii[0].category_id(), item2.category_id())
|
||||||
|
|
||||||
|
refs = [ r for r in cell.each_reference() ]
|
||||||
|
self.assertEqual(len(refs), 0)
|
||||||
|
|
||||||
|
cell.add_reference(pya.RdbReference(pya.DCplxTrans(2.5), 178))
|
||||||
|
refs = [ r for r in cell.each_reference() ]
|
||||||
|
self.assertEqual(len(refs), 1)
|
||||||
|
self.assertEqual(refs[0].parent_cell_id, 178)
|
||||||
|
self.assertEqual(refs[0].database().__repr__(), db.__repr__())
|
||||||
|
|
||||||
|
cell.clear_references()
|
||||||
|
refs = [ r for r in cell.each_reference() ]
|
||||||
|
self.assertEqual(len(refs), 0)
|
||||||
|
|
||||||
|
# RdbItemValue
|
||||||
|
def test_3(self):
|
||||||
|
|
||||||
|
v = pya.RdbItemValue(1.0)
|
||||||
|
self.assertEqual(v.tag_id, 0)
|
||||||
|
v.tag_id = 15
|
||||||
|
self.assertEqual(v.tag_id, 15)
|
||||||
|
|
||||||
|
vf = pya.RdbItemValue(1.0)
|
||||||
|
vs = pya.RdbItemValue("a string")
|
||||||
|
vb = pya.RdbItemValue(pya.DBox(0, 10, 20, 30))
|
||||||
|
vl = pya.RdbItemValue(pya.DText("abc", pya.DTrans()))
|
||||||
|
vp = pya.RdbItemValue(pya.DPolygon(pya.DBox(100, 101, 102, 103)))
|
||||||
|
ve = pya.RdbItemValue(pya.DEdge(pya.DPoint(0, 10), pya.DPoint(20, 30)))
|
||||||
|
vee = pya.RdbItemValue(pya.DEdgePair(pya.DEdge(0, 10, 5, 15), pya.DEdge(20, 30, 25, 35)))
|
||||||
|
|
||||||
|
self.assertEqual(vf.__str__(), "float: 1")
|
||||||
|
self.assertEqual(vs.__str__(), "text: 'a string'")
|
||||||
|
self.assertEqual(vb.__str__(), "box: (0,10;20,30)")
|
||||||
|
self.assertEqual(vp.__str__(), "polygon: (100,101;100,103;102,103;102,101)")
|
||||||
|
self.assertEqual(vl.__str__(), "label: ('abc',r0 0,0)");
|
||||||
|
self.assertEqual(ve.__str__(), "edge: (0,10;20,30)")
|
||||||
|
self.assertEqual(vee.__str__(), "edge-pair: (0,10;5,15)/(20,30;25,35)")
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vf.__str__()).__str__(), vf.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vs.__str__()).__str__(), vs.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vb.__str__()).__str__(), vb.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vp.__str__()).__str__(), vp.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vl.__str__()).__str__(), vl.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(ve.__str__()).__str__(), ve.__str__())
|
||||||
|
self.assertEqual(pya.RdbItemValue.from_s(vee.__str__()).__str__(), vee.__str__())
|
||||||
|
|
||||||
|
self.assertEqual(vf.is_float(), True)
|
||||||
|
self.assertEqual(vf.is_string(), False)
|
||||||
|
self.assertEqual(vf.is_polygon(), False)
|
||||||
|
self.assertEqual(vf.is_text(), False)
|
||||||
|
self.assertEqual(vf.is_edge(), False)
|
||||||
|
self.assertEqual(vf.is_box(), False)
|
||||||
|
self.assertEqual(vf.is_edge_pair(), False)
|
||||||
|
self.assertEqual(vf.float(), 1)
|
||||||
|
self.assertEqual(vf.string(), "1")
|
||||||
|
|
||||||
|
self.assertEqual(vs.is_float(), False)
|
||||||
|
self.assertEqual(vs.is_string(), True)
|
||||||
|
self.assertEqual(vs.is_polygon(), False)
|
||||||
|
self.assertEqual(vs.is_text(), False)
|
||||||
|
self.assertEqual(vs.is_edge(), False)
|
||||||
|
self.assertEqual(vs.is_box(), False)
|
||||||
|
self.assertEqual(vs.is_edge_pair(), False)
|
||||||
|
self.assertEqual(vs.string(), "a string")
|
||||||
|
|
||||||
|
self.assertEqual(vl.is_float(), False)
|
||||||
|
self.assertEqual(vl.is_string(), False)
|
||||||
|
self.assertEqual(vl.is_polygon(), False)
|
||||||
|
self.assertEqual(vl.is_text(), True)
|
||||||
|
self.assertEqual(vl.is_edge(), False)
|
||||||
|
self.assertEqual(vl.is_box(), False)
|
||||||
|
self.assertEqual(vl.is_edge_pair(), False)
|
||||||
|
self.assertEqual(vl.text().__str__(), "('abc',r0 0,0)")
|
||||||
|
self.assertEqual(vl.string(), "label: " + vl.text().__str__())
|
||||||
|
|
||||||
|
self.assertEqual(vp.is_float(), False)
|
||||||
|
self.assertEqual(vp.is_string(), False)
|
||||||
|
self.assertEqual(vp.is_polygon(), True)
|
||||||
|
self.assertEqual(vp.is_text(), False)
|
||||||
|
self.assertEqual(vp.is_edge(), False)
|
||||||
|
self.assertEqual(vp.is_box(), False)
|
||||||
|
self.assertEqual(vp.is_edge_pair(), False)
|
||||||
|
self.assertEqual(vp.polygon().__str__(), "(100,101;100,103;102,103;102,101)")
|
||||||
|
self.assertEqual(vp.string(), "polygon: " + vp.polygon().__str__())
|
||||||
|
|
||||||
|
self.assertEqual(ve.is_float(), False)
|
||||||
|
self.assertEqual(ve.is_string(), False)
|
||||||
|
self.assertEqual(ve.is_polygon(), False)
|
||||||
|
self.assertEqual(ve.is_text(), False)
|
||||||
|
self.assertEqual(ve.is_edge(), True)
|
||||||
|
self.assertEqual(ve.is_box(), False)
|
||||||
|
self.assertEqual(ve.is_edge_pair(), False)
|
||||||
|
self.assertEqual(ve.edge().__str__(), "(0,10;20,30)")
|
||||||
|
self.assertEqual(ve.string(), "edge: " + ve.edge().__str__())
|
||||||
|
|
||||||
|
self.assertEqual(vb.is_float(), False)
|
||||||
|
self.assertEqual(vb.is_string(), False)
|
||||||
|
self.assertEqual(vb.is_polygon(), False)
|
||||||
|
self.assertEqual(vb.is_text(), False)
|
||||||
|
self.assertEqual(vb.is_edge(), False)
|
||||||
|
self.assertEqual(vb.is_box(), True)
|
||||||
|
self.assertEqual(vb.is_edge_pair(), False)
|
||||||
|
self.assertEqual(vb.box().__str__(), "(0,10;20,30)")
|
||||||
|
self.assertEqual(vb.string(), "box: " + vb.box().__str__())
|
||||||
|
|
||||||
|
self.assertEqual(vee.is_float(), False)
|
||||||
|
self.assertEqual(vee.is_string(), False)
|
||||||
|
self.assertEqual(vee.is_polygon(), False)
|
||||||
|
self.assertEqual(vee.is_text(), False)
|
||||||
|
self.assertEqual(vee.is_edge(), False)
|
||||||
|
self.assertEqual(vee.is_box(), False)
|
||||||
|
self.assertEqual(vee.is_edge_pair(), True)
|
||||||
|
self.assertEqual(vee.edge_pair().__str__(), "(0,10;5,15)/(20,30;25,35)")
|
||||||
|
self.assertEqual(vee.string(), "edge-pair: " + vee.edge_pair().__str__())
|
||||||
|
|
||||||
|
# RdbItem
|
||||||
|
def test_4(self):
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
item = db.create_item(cell.rdb_id(), cat.rdb_id())
|
||||||
|
self.assertEqual(item.database().__repr__(), db.__repr__())
|
||||||
|
self.assertEqual(item.cell_id(), cell.rdb_id())
|
||||||
|
self.assertEqual(item.category_id(), cat.rdb_id())
|
||||||
|
|
||||||
|
self.assertEqual(db.user_tag_id("x1") != 0, True)
|
||||||
|
self.assertEqual(db.tag_id("x1") != 0, True)
|
||||||
|
self.assertEqual(db.tag_id("x1") == db.user_tag_id("x1"), False)
|
||||||
|
db.set_tag_description(db.user_tag_id("x1"), "D")
|
||||||
|
self.assertEqual(db.tag_description(db.user_tag_id("x1")), "D")
|
||||||
|
self.assertEqual(db.tag_name(db.user_tag_id("x1")), "x1")
|
||||||
|
self.assertEqual(db.tag_description(db.tag_id("x1")), "")
|
||||||
|
self.assertEqual(db.tag_name(db.tag_id("x1")), "x1")
|
||||||
|
db.set_tag_description(db.tag_id("x1"), "U")
|
||||||
|
self.assertEqual(db.tag_description(db.user_tag_id("x1")), "D")
|
||||||
|
self.assertEqual(db.tag_description(db.tag_id("x1")), "U")
|
||||||
|
|
||||||
|
item.add_tag(db.tag_id("x1"))
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x2")), False)
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x1")), True)
|
||||||
|
self.assertEqual(item.tags_str, "x1")
|
||||||
|
item.add_tag(db.tag_id("x2"))
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x2")), True)
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x1")), True)
|
||||||
|
self.assertEqual(item.tags_str, "x1,x2")
|
||||||
|
item.remove_tag(db.tag_id("x1"))
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x2")), True)
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x1")), False)
|
||||||
|
self.assertEqual(item.tags_str, "x2")
|
||||||
|
|
||||||
|
item.tags_str="x2,x1"
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x2")), True)
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x1")), True)
|
||||||
|
self.assertEqual(item.tags_str, "x1,x2")
|
||||||
|
|
||||||
|
item.tags_str=""
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x2")), False)
|
||||||
|
self.assertEqual(item.has_tag(db.tag_id("x1")), False)
|
||||||
|
self.assertEqual(item.tags_str, "")
|
||||||
|
|
||||||
|
self.assertEqual(item.image_str, "")
|
||||||
|
self.assertEqual(item.has_image(), False)
|
||||||
|
|
||||||
|
self.assertEqual(item.comment, "")
|
||||||
|
item.comment = "abc"
|
||||||
|
self.assertEqual(item.comment, "abc")
|
||||||
|
|
||||||
|
# can actually by any string, but only base64-encoded PNG images make sense
|
||||||
|
istr="iVBORw0KGgoAAAANSUhEUgAAACoAAAA0CAIAAABzfT3nAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAA0SAAANOgHo3ZneAAAA3UlEQVRYhe2WwQ3DIAxFoco8XaGZIaeO43FyYgZYgYXcQ6SWuDGgBhWq/qccIvGCEd9SbAwAAPSGaW2lFR2rfWDpXrPpSe2SP10fvnn/PZHZH9IwbKFVZZ/Z6wMtZcjW02Bn2FVpZYdWdkr2nvh23S2FyDNJuVITpwmRjTGbNr0v20U5byNtJuuJt/fO2f93+UlbEJl5UjVPr3Y71EQ/PoPPlU+lDJtWlCt3GwCMG33BuJGAcWMEMG6c1jBudCyf/nzV8nbZPRohclFLHdGbZ8eNSjN1fmf0AACA1jwA4hKxu4C6P7EAAAAASUVORK5CYII="
|
||||||
|
item.image_str=istr
|
||||||
|
self.assertEqual(item.image_str, istr)
|
||||||
|
self.assertEqual(item.has_image(), True)
|
||||||
|
|
||||||
|
if "image_pixels" in item.__dict__:
|
||||||
|
px=item.image_pixels
|
||||||
|
self.assertEqual(px.width, 42)
|
||||||
|
self.assertEqual(px.height, 52)
|
||||||
|
item.image = px
|
||||||
|
px2=item.image_pixels
|
||||||
|
self.assertEqual(px == px2, True)
|
||||||
|
|
||||||
|
if "image" in item.__dict__:
|
||||||
|
px=item.image
|
||||||
|
self.assertEqual(px.width, 42)
|
||||||
|
self.assertEqual(px.height, 52)
|
||||||
|
item.image = px
|
||||||
|
px2=item.image
|
||||||
|
self.assertEqual(px2.width, 42)
|
||||||
|
self.assertEqual(px2.height, 52)
|
||||||
|
|
||||||
|
vs = pya.RdbItemValue("a string")
|
||||||
|
vs2 = pya.RdbItemValue("a string (ii)")
|
||||||
|
item.add_value(vs)
|
||||||
|
item.add_value(vs2)
|
||||||
|
|
||||||
|
vv=[ v for v in item.each_value() ]
|
||||||
|
self.assertEqual(len(vv), 2)
|
||||||
|
self.assertEqual(vv[0].__str__(), "text: 'a string'")
|
||||||
|
self.assertEqual(vv[1].__str__(), "text: 'a string (ii)'")
|
||||||
|
|
||||||
|
item.clear_values()
|
||||||
|
vv=[ v for v in item.each_value() ]
|
||||||
|
self.assertEqual(len(vv), 0)
|
||||||
|
|
||||||
|
item.clear_values()
|
||||||
|
item.add_value(1.0)
|
||||||
|
item.add_value("hello")
|
||||||
|
item.add_value(pya.DPolygon(pya.DBox(1, 2, 3, 4)))
|
||||||
|
item.add_value(pya.DBox(11, 12, 13, 14))
|
||||||
|
item.add_value(pya.DEdge(21, 22, 23, 24))
|
||||||
|
item.add_value(pya.DEdgePair(pya.DEdge(31, 32, 33, 34), pya.DEdge(41, 42, 43, 44)))
|
||||||
|
shapes = pya.Shapes()
|
||||||
|
pts = [ pya.Point(0, 0), pya.Point(50, 150) ]
|
||||||
|
shapes.insert(pya.Path(pts, 100))
|
||||||
|
for s in shapes:
|
||||||
|
item.add_value(s, pya.CplxTrans(0.001))
|
||||||
|
vv=[ v for v in item.each_value() ]
|
||||||
|
self.assertEqual(len(vv), 7)
|
||||||
|
self.assertEqual(vv[0].__str__(), "float: 1")
|
||||||
|
self.assertEqual(vv[1].__str__(), "text: hello")
|
||||||
|
self.assertEqual(vv[2].__str__(), "polygon: (1,2;1,4;3,4;3,2)")
|
||||||
|
self.assertEqual(vv[3].__str__(), "box: (11,12;13,14)")
|
||||||
|
self.assertEqual(vv[4].__str__(), "edge: (21,22;23,24)")
|
||||||
|
self.assertEqual(vv[5].__str__(), "edge-pair: (31,32;33,34)/(41,42;43,44)")
|
||||||
|
self.assertEqual(vv[6].__str__(), "path: (0,0;0.05,0.15) w=0.1 bx=0 ex=0 r=false")
|
||||||
|
|
||||||
|
# Multiple items
|
||||||
|
def test_5(self):
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
r = pya.Region(pya.Box(0, 0, 100, 200))
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), r)
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "polygon: (0,0;0,0.2;0.1,0.2;0.1,0)")
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
p = pya.Polygon(pya.Box(0, 0, 100, 200))
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), [p])
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "polygon: (0,0;0,0.2;0.1,0.2;0.1,0)")
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
r = pya.Edges(pya.Edge(0, 0, 100, 200))
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), r)
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "edge: (0,0;0.1,0.2)")
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
r = [ pya.Edge(0, 0, 100, 200) ]
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), r)
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "edge: (0,0;0.1,0.2)")
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
r = pya.EdgePairs()
|
||||||
|
r.insert(pya.EdgePair(pya.Edge(0, 0, 100, 200), pya.Edge(10, 10, 50, 150)))
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), r)
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "edge-pair: (0,0;0.1,0.2)/(0.01,0.01;0.05,0.15)")
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
|
||||||
|
r = [ pya.EdgePair(pya.Edge(0, 0, 100, 200), pya.Edge(10, 10, 50, 150)) ]
|
||||||
|
db.create_items(cell.rdb_id(), cat.rdb_id(), pya.CplxTrans(0.001), r)
|
||||||
|
a = []
|
||||||
|
for item in db.each_item_per_category(cat.rdb_id()):
|
||||||
|
for v in item.each_value():
|
||||||
|
a.append(str(v))
|
||||||
|
self.assertEqual(";".join(a), "edge-pair: (0,0;0.1,0.2)/(0.01,0.01;0.05,0.15)")
|
||||||
|
|
||||||
|
# ReportDatabase
|
||||||
|
def test_6(self):
|
||||||
|
|
||||||
|
db = pya.ReportDatabase("name")
|
||||||
|
db.reset_modified()
|
||||||
|
self.assertEqual(db.is_modified(), False)
|
||||||
|
self.assertEqual(db.name(), "name")
|
||||||
|
db.description = "desc"
|
||||||
|
db.generator = "gg"
|
||||||
|
db.top_cell_name = "top"
|
||||||
|
db.original_file = "of"
|
||||||
|
self.assertEqual(db.description, "desc")
|
||||||
|
self.assertEqual(db.generator, "gg")
|
||||||
|
self.assertEqual(db.top_cell_name, "top")
|
||||||
|
self.assertEqual(db.original_file, "of")
|
||||||
|
|
||||||
|
self.assertEqual(db.is_modified(), True)
|
||||||
|
db.reset_modified()
|
||||||
|
self.assertEqual(db.is_modified(), False)
|
||||||
|
|
||||||
|
tag_id = db.tag_id("x")
|
||||||
|
self.assertEqual(tag_id, 1)
|
||||||
|
db.set_tag_description(tag_id, "xdesc")
|
||||||
|
self.assertEqual(db.tag_description(tag_id), "xdesc")
|
||||||
|
|
||||||
|
cell = db.create_cell("cell_name")
|
||||||
|
cc = [ c for c in db.each_cell() ]
|
||||||
|
self.assertEqual(len(cc), 1)
|
||||||
|
self.assertEqual(cc[0].rdb_id(), cell.rdb_id())
|
||||||
|
|
||||||
|
cat = db.create_category("cat")
|
||||||
|
cc = [ c for c in db.each_category() ]
|
||||||
|
self.assertEqual(len(cc), 1)
|
||||||
|
self.assertEqual(cc[0].rdb_id(), cat.rdb_id())
|
||||||
|
|
||||||
|
cats = db.create_category(cat, "subcat")
|
||||||
|
c = db.category_by_path("x")
|
||||||
|
self.assertEqual(c, None)
|
||||||
|
c = db.category_by_path("cat")
|
||||||
|
self.assertEqual(c.rdb_id(), cat.rdb_id())
|
||||||
|
c = db.category_by_path("cat.subcat")
|
||||||
|
self.assertEqual(c.rdb_id(), cats.rdb_id())
|
||||||
|
|
||||||
|
self.assertEqual(db.category_by_id(cat.rdb_id()).rdb_id(), cat.rdb_id())
|
||||||
|
self.assertEqual(db.category_by_id(cats.rdb_id()).rdb_id(), cats.rdb_id())
|
||||||
|
|
||||||
|
item = db.create_item(cell.rdb_id(), cat.rdb_id())
|
||||||
|
v = pya.RdbItemValue("a")
|
||||||
|
v.tag_id = db.user_tag_id("x2")
|
||||||
|
item.add_value(v)
|
||||||
|
v = pya.RdbItemValue("b")
|
||||||
|
v.tag_id = db.tag_id("x1")
|
||||||
|
item.add_value(v)
|
||||||
|
item.add_tag(db.tag_id("x1"))
|
||||||
|
item.add_tag(db.user_tag_id("x2"))
|
||||||
|
istr="iVBORw0KGgoAAAANSUhEUgAAACoAAAA0CAIAAABzfT3nAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAA0SAAANOgHo3ZneAAAA3UlEQVRYhe2WwQ3DIAxFoco8XaGZIaeO43FyYgZYgYXcQ6SWuDGgBhWq/qccIvGCEd9SbAwAAPSGaW2lFR2rfWDpXrPpSe2SP10fvnn/PZHZH9IwbKFVZZ/Z6wMtZcjW02Bn2FVpZYdWdkr2nvh23S2FyDNJuVITpwmRjTGbNr0v20U5byNtJuuJt/fO2f93+UlbEJl5UjVPr3Y71EQ/PoPPlU+lDJtWlCt3GwCMG33BuJGAcWMEMG6c1jBudCyf/nzV8nbZPRohclFLHdGbZ8eNSjN1fmf0AACA1jwA4hKxu4C6P7EAAAAASUVORK5CYII="
|
||||||
|
if "image_str" in item.__dict__:
|
||||||
|
item.image_str=istr
|
||||||
|
|
||||||
|
ut_testtmp = os.getenv("TESTTMP", ".")
|
||||||
|
tmp = os.path.join(ut_testtmp, "tmp.lyrdb")
|
||||||
|
|
||||||
|
self.assertEqual(db.filename(), "")
|
||||||
|
db.save(tmp)
|
||||||
|
self.assertEqual(db.filename(), tmp)
|
||||||
|
|
||||||
|
# load and save
|
||||||
|
db = None
|
||||||
|
|
||||||
|
db2 = pya.ReportDatabase("neu")
|
||||||
|
db2.load(tmp)
|
||||||
|
self.assertEqual(os.path.basename(db2.filename()), os.path.basename(tmp))
|
||||||
|
self.assertEqual(db2.name(), os.path.basename(tmp))
|
||||||
|
|
||||||
|
self.assertEqual(db2.description, "desc")
|
||||||
|
self.assertEqual(db2.generator, "gg")
|
||||||
|
self.assertEqual(db2.top_cell_name, "top")
|
||||||
|
self.assertEqual(db2.original_file, "of")
|
||||||
|
|
||||||
|
c = db2.category_by_path("cat.subcat")
|
||||||
|
self.assertEqual(c.path(), "cat.subcat")
|
||||||
|
|
||||||
|
cc = [ c for c in db2.each_category() ]
|
||||||
|
self.assertEqual(len(cc), 1)
|
||||||
|
|
||||||
|
ii = [ i for i in db2.each_item() ]
|
||||||
|
self.assertEqual(len(ii), 1)
|
||||||
|
self.assertEqual(ii[0].tags_str, "x1,#x2")
|
||||||
|
self.assertEqual(ii[0].has_tag(db2.user_tag_id("x2")), True)
|
||||||
|
self.assertEqual(ii[0].has_tag(db2.tag_id("x1")), True)
|
||||||
|
self.assertEqual(ii[0].has_tag(db2.tag_id("x")), False)
|
||||||
|
# Only the first 30 bytes count ... the remaining part is too different for different versions of Qt
|
||||||
|
if "image_str" in ii[0].__dict__:
|
||||||
|
self.assertEqual(ii[0].image_str[range(0, 30)], istr[range(0, 30)])
|
||||||
|
self.assertEqual(db2.cell_by_id(ii[0].cell_id()).qname(), "cell_name")
|
||||||
|
self.assertEqual(db2.category_by_id(ii[0].category_id()).path(), "cat")
|
||||||
|
vs = ""
|
||||||
|
for v in ii[0].each_value():
|
||||||
|
vs += v.string()
|
||||||
|
self.assertEqual(vs, "ab")
|
||||||
|
vs = ""
|
||||||
|
for v in ii[0].each_value():
|
||||||
|
if v.tag_id == db2.tag_id("x1"):
|
||||||
|
vs += v.string()
|
||||||
|
self.assertEqual(vs, "b")
|
||||||
|
vs = ""
|
||||||
|
for v in ii[0].each_value():
|
||||||
|
if v.tag_id == db2.user_tag_id("x1"):
|
||||||
|
vs += v.string()
|
||||||
|
self.assertEqual(vs, "")
|
||||||
|
vs = ""
|
||||||
|
for v in ii[0].each_value():
|
||||||
|
if v.tag_id == db2.user_tag_id("x2"):
|
||||||
|
vs += v.string()
|
||||||
|
self.assertEqual(vs, "a")
|
||||||
|
|
||||||
|
# LayoutView
|
||||||
|
def test_10(self):
|
||||||
|
|
||||||
|
if not "Application" in pya.__dict__:
|
||||||
|
return
|
||||||
|
|
||||||
|
mw = pya.Application.instance().main_window()
|
||||||
|
mw.create_layout(1)
|
||||||
|
view = mw.current_view()
|
||||||
|
|
||||||
|
self.ot = 0
|
||||||
|
def on_changed():
|
||||||
|
self.ot += 1
|
||||||
|
view.on_rdb_list_changed += on_changed
|
||||||
|
|
||||||
|
rdb_index = view.create_rdb("NEW_RDB")
|
||||||
|
self.assertEqual(view.num_rdbs(), 1)
|
||||||
|
self.assertEqual(self.ot, 1)
|
||||||
|
self.assertEqual(view.rdb(rdb_index).name(), "NEW_RDB")
|
||||||
|
view.remove_rdb(rdb_index)
|
||||||
|
self.assertEqual(view.num_rdbs(), 0)
|
||||||
|
self.assertEqual(self.ot, 2)
|
||||||
|
|
||||||
|
view.on_rdb_list_changed -= on_changed
|
||||||
|
|
||||||
|
self.ot = 0
|
||||||
|
rdb_index = view.create_rdb("NEW_RDB2")
|
||||||
|
self.assertEqual(view.rdb(rdb_index).name(), "NEW_RDB2")
|
||||||
|
self.assertEqual(self.ot, 0)
|
||||||
|
self.assertEqual(view.num_rdbs(), 1)
|
||||||
|
rdb_index = view.create_rdb("NEW_RDB3")
|
||||||
|
self.assertEqual(view.rdb(rdb_index).name(), "NEW_RDB3")
|
||||||
|
self.assertEqual(view.num_rdbs(), 2)
|
||||||
|
self.assertEqual(self.ot, 0)
|
||||||
|
|
||||||
|
mw.close_current_view()
|
||||||
|
|
||||||
|
# scan_... methods
|
||||||
|
def test_11(self):
|
||||||
|
|
||||||
|
ly = pya.Layout()
|
||||||
|
c0 = ly.create_cell("c0")
|
||||||
|
c1 = ly.create_cell("c1")
|
||||||
|
c2 = ly.create_cell("c2")
|
||||||
|
c3 = ly.create_cell("c3")
|
||||||
|
c1.insert(pya.CellInstArray(c2.cell_index(), pya.Trans(10, 20)))
|
||||||
|
c2.insert(pya.CellInstArray(c3.cell_index(), pya.Trans(11, 21)))
|
||||||
|
l1 = ly.insert_layer(pya.LayerInfo(1, 0))
|
||||||
|
prop_id = ly.properties_id([[ "a", 17 ]])
|
||||||
|
c0.shapes(l1).insert(pya.Box(0, 1, 2, 3), prop_id)
|
||||||
|
prop_id = ly.properties_id([[ "a", 21 ]])
|
||||||
|
c1.shapes(l1).insert(pya.Box(0, 1, 20, 30), prop_id)
|
||||||
|
c2.shapes(l1).insert(pya.Box(0, 1, 21, 31))
|
||||||
|
c3.shapes(l1).insert(pya.Box(0, 1, 22, 32))
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1)
|
||||||
|
self.assertEqual(rdb.tag_name(1), "a") # from property
|
||||||
|
self.assertEqual(cat.num_items(), 4)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c0[];c1[];c2[];c3[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c0[polygon: (0,0.001;0,0.003;0.002,0.003;0.002,0.001)/float: 17];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1, None, -1, False)
|
||||||
|
self.assertEqual(cat.num_items(), 4)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c0[];c1[];c2[];c3[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c0[polygon: (0,0.001;0,0.003;0.002,0.003;0.002,0.001)];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1, c1)
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1, c1, 0)
|
||||||
|
self.assertEqual(cat.num_items(), 1)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1, c1, -1)
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_layer(ly, l1, c1, 1)
|
||||||
|
self.assertEqual(cat.num_items(), 2)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[];c2[c1->r0 *1 0.01,0.02]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_shapes(c1.begin_shapes_rec(l1)) # hierarchical scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_shapes(c1.begin_shapes_rec(l1), False, False) # hierarchical scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
cat.scan_shapes(c1.begin_shapes_rec(l1), True) # flat scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21,polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
r = pya.Region(c1.begin_shapes_rec(l1))
|
||||||
|
cat.scan_collection(rdb.create_cell("TOP"), pya.CplxTrans(0.001), r) # hierarchical scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[];c1[TOP->r0 *1 0,0];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
r = pya.Region(c1.begin_shapes_rec(l1))
|
||||||
|
cat.scan_collection(rdb.create_cell("TOP"), pya.CplxTrans(0.001), r, True) # flat scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)/float: 21,polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
r = pya.Region(c1.begin_shapes_rec(l1))
|
||||||
|
cat.scan_collection(rdb.create_cell("TOP"), pya.CplxTrans(0.001), r, True, False) # flat scan
|
||||||
|
self.assertEqual(cat.num_items(), 3)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001),polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat = rdb.create_category("l1")
|
||||||
|
r = pya.Region(c1.begin_shapes_rec(l1)).merged()
|
||||||
|
cat.scan_collection(rdb.create_cell("TOP"), pya.CplxTrans(0.001), r, True) # flat scan
|
||||||
|
self.assertEqual(cat.num_items(), 1)
|
||||||
|
cn = [ c.to_s_test() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[]")
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "TOP[polygon: (0,0.001;0,0.03;0.01,0.03;0.01,0.051;0.021,0.051;0.021,0.073;0.043,0.073;0.043,0.042;0.031,0.042;0.031,0.021;0.02,0.021;0.02,0.001)]")
|
||||||
|
|
||||||
|
# shape insertion from shape, shapes, recursive iterator
|
||||||
|
def test_12(self):
|
||||||
|
|
||||||
|
ly = pya.Layout()
|
||||||
|
c0 = ly.create_cell("c0")
|
||||||
|
c1 = ly.create_cell("c1")
|
||||||
|
c2 = ly.create_cell("c2")
|
||||||
|
c3 = ly.create_cell("c3")
|
||||||
|
c1.insert(pya.CellInstArray(c2.cell_index(), pya.Trans(10, 20)))
|
||||||
|
c2.insert(pya.CellInstArray(c3.cell_index(), pya.Trans(11, 21)))
|
||||||
|
l1 = ly.insert_layer(pya.LayerInfo(1, 0))
|
||||||
|
c0.shapes(l1).insert(pya.Box(0, 1, 2, 3))
|
||||||
|
c1.shapes(l1).insert(pya.Text("Hello, world!", pya.Trans()))
|
||||||
|
c2.shapes(l1).insert(pya.Edge(0, 1, 21, 31))
|
||||||
|
c3.shapes(l1).insert(pya.Polygon(pya.Box(0, 1, 22, 32)))
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat1 = rdb.create_category("l1")
|
||||||
|
cell1 = rdb.create_cell("c1")
|
||||||
|
for s in c0.shapes(l1).each():
|
||||||
|
rdb.create_item(cell1.rdb_id(), cat1.rdb_id(), pya.CplxTrans(ly.dbu), s)
|
||||||
|
self.assertEqual(cat1.num_items(), 1)
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.003;0.002,0.003;0.002,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat1 = rdb.create_category("l1")
|
||||||
|
cell1 = rdb.create_cell("c1")
|
||||||
|
rdb.create_items(cell1.rdb_id(), cat1.rdb_id(), pya.CplxTrans(ly.dbu), c0.shapes(l1))
|
||||||
|
self.assertEqual(cat1.num_items(), 1)
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[polygon: (0,0.001;0,0.003;0.002,0.003;0.002,0.001)]")
|
||||||
|
|
||||||
|
rdb = pya.ReportDatabase("neu")
|
||||||
|
cat1 = rdb.create_category("l1")
|
||||||
|
cell1 = rdb.create_cell("c1")
|
||||||
|
rdb.create_items(cell1.rdb_id(), cat1.rdb_id(), c1.begin_shapes_rec(l1))
|
||||||
|
self.assertEqual(cat1.num_items(), 3)
|
||||||
|
cn = [ c.to_s_items() for c in rdb.each_cell() ]
|
||||||
|
self.assertEqual(";".join(cn), "c1[label: ('Hello, world!',r0 0,0),edge: (0.01,0.021;0.031,0.051),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]")
|
||||||
|
|
||||||
|
def test_13(self):
|
||||||
|
|
||||||
|
# manipulations
|
||||||
|
rdb = pya.ReportDatabase("")
|
||||||
|
|
||||||
|
_cell = rdb.create_cell("CELL")
|
||||||
|
_cat = rdb.create_category("cat")
|
||||||
|
_subcat = rdb.create_category(_cat, "subcat")
|
||||||
|
_subcat.description = "subcat_d"
|
||||||
|
_item1 = rdb.create_item(_cell.rdb_id(), _subcat.rdb_id())
|
||||||
|
_item1.add_value(17.5)
|
||||||
|
_item1.add_value("string")
|
||||||
|
_item2 = rdb.create_item(_cell.rdb_id(), _subcat.rdb_id())
|
||||||
|
_item2.add_value("b")
|
||||||
|
_subsubcat = rdb.create_category(_subcat, "subsubcat")
|
||||||
|
_cat2 = rdb.create_category("cat2")
|
||||||
|
|
||||||
|
cell = rdb.cell_by_id(_cell.rdb_id())
|
||||||
|
self.assertEqual(cell._is_const_object(), False)
|
||||||
|
self.assertEqual([ c for c in rdb.each_cell() ][0]._is_const_object(), False)
|
||||||
|
|
||||||
|
cell = rdb.cell_by_qname("CELL")
|
||||||
|
self.assertEqual(cell._is_const_object(), False)
|
||||||
|
|
||||||
|
cat = rdb.category_by_id(_cat.rdb_id())
|
||||||
|
self.assertEqual(cat._is_const_object(), False)
|
||||||
|
|
||||||
|
cat = rdb.category_by_path("cat")
|
||||||
|
self.assertEqual(cat._is_const_object(), False)
|
||||||
|
subcat = rdb.category_by_path("cat.subcat")
|
||||||
|
|
||||||
|
self.assertEqual([ c for c in rdb.each_category() ][0]._is_const_object(), False)
|
||||||
|
self.assertEqual(",".join([ c.name() for c in rdb.each_category() ]), "cat,cat2")
|
||||||
|
self.assertEqual(subcat._is_const_object(), False)
|
||||||
|
self.assertEqual(subcat.database()._is_const_object(), False)
|
||||||
|
self.assertEqual(subcat.name(), "subcat")
|
||||||
|
self.assertEqual(subcat.parent().name(), "cat")
|
||||||
|
|
||||||
|
self.assertEqual(subcat.description, "subcat_d")
|
||||||
|
subcat.description = "changed"
|
||||||
|
self.assertEqual(subcat.description, "changed")
|
||||||
|
|
||||||
|
self.assertEqual(";".join([ "/".join([ str(v) for v in item.each_value() ]) for item in rdb.each_item_per_category(subcat.rdb_id())]), "float: 17.5/text: string;text: b")
|
||||||
|
|
||||||
|
item1 = [ i for i in rdb.each_item_per_category(subcat.rdb_id())][0]
|
||||||
|
self.assertEqual(item1._is_const_object(), False)
|
||||||
|
item1.clear_values()
|
||||||
|
self.assertEqual(";".join([ "/".join([ str(v) for v in item.each_value() ]) for item in rdb.each_item_per_category(subcat.rdb_id())]), ";text: b")
|
||||||
|
item1.add_value("x")
|
||||||
|
self.assertEqual(";".join([ "/".join([ str(v) for v in item.each_value() ]) for item in rdb.each_item_per_category(subcat.rdb_id())]), "text: x;text: b")
|
||||||
|
item1.add_tag(17)
|
||||||
|
self.assertEqual(item1.has_tag(17), True)
|
||||||
|
self.assertEqual(item1.has_tag(16), False)
|
||||||
|
|
||||||
|
item1 = [ i for i in rdb.each_item() ][0]
|
||||||
|
self.assertEqual(item1._is_const_object(), False)
|
||||||
|
self.assertEqual(item1.has_tag(17), True)
|
||||||
|
|
||||||
|
item1 = [ i for i in rdb.each_item_per_cell(cell.rdb_id()) ][0]
|
||||||
|
self.assertEqual(item1._is_const_object(), False)
|
||||||
|
self.assertEqual(item1.has_tag(17), True)
|
||||||
|
|
||||||
|
item1 = [ i for i in rdb.each_item_per_cell_and_category(cell.rdb_id(), subcat.rdb_id())][0]
|
||||||
|
self.assertEqual(item1._is_const_object(), False)
|
||||||
|
self.assertEqual(item1.has_tag(17), True)
|
||||||
|
|
||||||
|
item1 = [ i for i in cell.each_item()][0]
|
||||||
|
self.assertEqual(item1._is_const_object(), False)
|
||||||
|
self.assertEqual(item1.has_tag(17), True)
|
||||||
|
|
||||||
|
def test_14(self):
|
||||||
|
|
||||||
|
# same names do not generate a new category
|
||||||
|
rdb = pya.ReportDatabase("")
|
||||||
|
|
||||||
|
_cell = rdb.create_cell("CELL")
|
||||||
|
|
||||||
|
_cat = rdb.create_category("cat")
|
||||||
|
_cat_same = rdb.create_category("cat")
|
||||||
|
self.assertEqual(_cat.rdb_id(), _cat_same.rdb_id())
|
||||||
|
|
||||||
|
_subcat = rdb.create_category(_cat, "subcat")
|
||||||
|
_subcat_same = rdb.create_category(_cat_same, "subcat")
|
||||||
|
self.assertEqual(_subcat.rdb_id(), _subcat_same.rdb_id())
|
||||||
|
|
||||||
|
# testing whether decrementing the reference count would do harm
|
||||||
|
_cat = None
|
||||||
|
_cat_same = None
|
||||||
|
self.assertEqual(_subcat.rdb_id(), _subcat_same.rdb_id())
|
||||||
|
|
||||||
|
# run unit tests
|
||||||
|
if __name__ == '__main__':
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(RDB_TestClass)
|
||||||
|
|
||||||
|
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
if !$:.member?(File::dirname($0))
|
||||||
|
$:.push(File::dirname($0))
|
||||||
|
end
|
||||||
|
|
||||||
|
load("test_prologue.rb")
|
||||||
|
|
||||||
|
class MyVisitor < RBA::EdgeNeighborhoodVisitor
|
||||||
|
|
||||||
|
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 begin_polygon(layout, cell, polygon)
|
||||||
|
polygon = polygon.dup
|
||||||
|
output(polygon)
|
||||||
|
@log[polygon] ||= []
|
||||||
|
@current_log = @log[polygon]
|
||||||
|
@current_log << "Polygon: #{polygon}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
def end_polygon
|
||||||
|
@current_log << "/Polygon\n"
|
||||||
|
@current_log = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_edge(layout, cell, edge, neighborhood)
|
||||||
|
@current_log << "edge = #{edge}\n"
|
||||||
|
neighborhood.each do |n|
|
||||||
|
x1, x2 = n[0]
|
||||||
|
polygons = n[1]
|
||||||
|
polygons.each do |inp, poly|
|
||||||
|
poly_str = poly.collect { |p| p.to_s }.join("/")
|
||||||
|
@current_log << " #{x1},#{x2} -> #{inp}: #{poly_str}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class MyVisitor2 < RBA::EdgeNeighborhoodVisitor
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
self.result_type = RBA::CompoundRegionOperationNode::ResultType::EdgePairs
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_edge(layout, cell, edge, neighborhood)
|
||||||
|
neighborhood.each do |n|
|
||||||
|
polygons = n[1]
|
||||||
|
polygons.each do |inp, poly|
|
||||||
|
poly.each do |p|
|
||||||
|
bbox = p.bbox
|
||||||
|
t = RBA::EdgeNeighborhoodVisitor.to_original_trans(edge)
|
||||||
|
ep = RBA::EdgePair::new(edge, t * RBA::Edge::new(bbox.p1, RBA::Point::new(bbox.right, bbox.bottom)))
|
||||||
|
output(ep)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class DBEdgeNeighborhood_TestClass < TestBase
|
||||||
|
|
||||||
|
# basic events
|
||||||
|
def test_1
|
||||||
|
|
||||||
|
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 = MyVisitor::new
|
||||||
|
|
||||||
|
visitor.result_type = RBA::CompoundRegionOperationNode::ResultType::Region
|
||||||
|
assert_equal(visitor.result_type, RBA::CompoundRegionOperationNode::ResultType::Region)
|
||||||
|
|
||||||
|
bext = 0
|
||||||
|
eext = 0
|
||||||
|
din = 10
|
||||||
|
dout = 100
|
||||||
|
|
||||||
|
children = [
|
||||||
|
RBA::CompoundRegionOperationNode::new_foreign
|
||||||
|
]
|
||||||
|
|
||||||
|
node = RBA::CompoundRegionOperationNode::new_edge_neighborhood(children, visitor, bext, eext, din, dout)
|
||||||
|
res = prim.complex_op(node)
|
||||||
|
|
||||||
|
assert_equal(visitor.log,
|
||||||
|
"Polygon: (-1100,0;-1100,1000;-100,1000;-100,0)\n" +
|
||||||
|
"edge = (-1100,0;-1100,1000)\n" +
|
||||||
|
"edge = (-1100,1000;-100,1000)\n" +
|
||||||
|
"edge = (-100,1000;-100,0)\n" +
|
||||||
|
" 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100)\n" +
|
||||||
|
"edge = (-100,0;-1100,0)\n" +
|
||||||
|
"/Polygon\n" +
|
||||||
|
"Polygon: (0,0;0,1000;1000,1000;1000,0)\n" +
|
||||||
|
"edge = (0,0;0,1000)\n" +
|
||||||
|
" 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100)\n" +
|
||||||
|
"edge = (0,1000;1000,1000)\n" +
|
||||||
|
"edge = (1000,1000;1000,0)\n" +
|
||||||
|
"edge = (1000,0;0,0)\n" +
|
||||||
|
"/Polygon\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal(res.to_s, "(-1100,0;-1100,1000;-100,1000;-100,0);(0,0;0,1000;1000,1000;1000,0)")
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
bext = 0
|
||||||
|
eext = 0
|
||||||
|
din = 10
|
||||||
|
dout = 100
|
||||||
|
|
||||||
|
children = [
|
||||||
|
RBA::CompoundRegionOperationNode::new_foreign
|
||||||
|
]
|
||||||
|
|
||||||
|
node = RBA::CompoundRegionOperationNode::new_edge_neighborhood(children, visitor, bext, eext, din, dout)
|
||||||
|
res = prim.complex_op(node)
|
||||||
|
|
||||||
|
assert_equal(res.to_s, "(-100,1000;-100,0)/(0,1000;0,0);(0,0;0,1000)/(-100,0;-100,1000)")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
load("test_epilogue.rb")
|
||||||
|
|
||||||
|
|
@ -253,6 +253,7 @@ class DBMatrix_TestClass < TestBase
|
||||||
assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
||||||
assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
||||||
assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-10,-18;20,35)")
|
assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-10,-18;20,35)")
|
||||||
|
assert_equal((m * RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(0, -10), RBA::Point::new(15, 20)), RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)))).to_s, "(-5,-20;25,33)/(-10,-18;20,35)")
|
||||||
assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(5,-25;-10,-18;5,43;20,35)")
|
||||||
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
|
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
|
||||||
r.transform(m)
|
r.transform(m)
|
||||||
|
|
@ -273,6 +274,7 @@ class DBMatrix_TestClass < TestBase
|
||||||
assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)")
|
assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)")
|
||||||
assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)")
|
assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)")
|
||||||
assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-10,-17.5;20,35)")
|
assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-10,-17.5;20,35)")
|
||||||
|
assert_equal((m * RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(0, -10), RBA::DPoint::new(15, 20)), RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20)))).to_s, "(-5,-20;25,32.5)/(-10,-17.5;20,35)")
|
||||||
|
|
||||||
m = RBA::IMatrix3d::new(1.0, 0.5, 1.0, -0.5, 2.0, 0.0, 0.0, 0.0, 1.0)
|
m = RBA::IMatrix3d::new(1.0, 0.5, 1.0, -0.5, 2.0, 0.0, 0.0, 0.0, 1.0)
|
||||||
assert_equal((m * RBA::Point::new(10, 20)).to_s, "21,35")
|
assert_equal((m * RBA::Point::new(10, 20)).to_s, "21,35")
|
||||||
|
|
@ -281,6 +283,7 @@ class DBMatrix_TestClass < TestBase
|
||||||
assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
||||||
assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
||||||
assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-9,-18;21,35)")
|
assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-9,-18;21,35)")
|
||||||
|
assert_equal((m * RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(0, -10), RBA::Point::new(15, 20)), RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)))).to_s, "(-4,-20;26,33)/(-9,-18;21,35)")
|
||||||
assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(6,-25;-9,-18;6,43;21,35)")
|
||||||
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
|
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
|
||||||
r.transform(m)
|
r.transform(m)
|
||||||
|
|
@ -301,6 +304,7 @@ class DBMatrix_TestClass < TestBase
|
||||||
assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)")
|
assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)")
|
||||||
assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)")
|
assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)")
|
||||||
assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-9,-17.5;21,35)")
|
assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-9,-17.5;21,35)")
|
||||||
|
assert_equal((m * RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(0, -10), RBA::DPoint::new(15, 20)), RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20)))).to_s, "(-4,-20;26,32.5)/(-9,-17.5;21,35)")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1017,6 +1017,23 @@ class RDB_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_14
|
||||||
|
|
||||||
|
# same names do not generate a new category
|
||||||
|
rdb = RBA::ReportDatabase::new("")
|
||||||
|
|
||||||
|
_cell = rdb.create_cell("CELL")
|
||||||
|
|
||||||
|
_cat = rdb.create_category("cat")
|
||||||
|
_cat_same = rdb.create_category("cat")
|
||||||
|
assert_equal(_cat.rdb_id, _cat_same.rdb_id)
|
||||||
|
|
||||||
|
_subcat = rdb.create_category(_cat, "subcat")
|
||||||
|
_subcat_same = rdb.create_category(_cat_same, "subcat")
|
||||||
|
assert_equal(_subcat.rdb_id, _subcat_same.rdb_id)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue