Merge pull request #1916 from KLayout/maybe-pex-support

Some add-ons for potential application in the PEX context
This commit is contained in:
Matthias Köfferlein 2024-11-28 20:43:40 +01:00 committed by GitHub
commit ec21809cc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 2250 additions and 11 deletions

View File

@ -23,6 +23,7 @@ SOURCES = \
dbCommonReader.cc \
dbCompoundOperation.cc \
dbEdge.cc \
dbEdgeNeighborhood.cc \
dbEdgePair.cc \
dbEdgePairFilters.cc \
dbEdgePairRelations.cc \
@ -119,6 +120,7 @@ SOURCES = \
gsiDeclDbCommonStreamOptions.cc \
gsiDeclDbCompoundOperation.cc \
gsiDeclDbEdge.cc \
gsiDeclDbEdgeNeighborhood.cc \
gsiDeclDbEdgePair.cc \
gsiDeclDbEdgePairs.cc \
gsiDeclDbEdgeProcessor.cc \
@ -249,6 +251,7 @@ HEADERS = \
dbCommonReader.h \
dbCompoundOperation.h \
dbEdge.h \
dbEdgeNeighborhood.h \
dbEdgePair.h \
dbEdgePairFilters.h \
dbEdgePairRelations.h \

View File

@ -266,6 +266,7 @@ protected:
virtual db::Coord computed_dist () const = 0;
virtual std::string generated_description () const;
virtual bool wants_caching () const { return true; }
private:
std::string m_description;
@ -276,23 +277,32 @@ private:
{
// 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;
uncached_results.resize (results.size ());
if (! cp.first) {
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 ());
}
}
};

View File

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

View File

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

View File

@ -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"
)
);
}

View File

@ -27,6 +27,7 @@
#include "dbBox.h"
#include "dbPolygon.h"
#include "dbEdge.h"
#include "dbEdgePair.h"
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);
}
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>
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"
"@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"),
"@brief Transforms a box with this matrix.\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);
}
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>
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"
"@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"),
"@brief Transforms a box with this matrix.\n"
"@param box The box to transform.\n"

View File

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

View File

@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \
dbCompoundOperationTests.cc \
dbEdgeNeighborhoodTests.cc \
dbFillToolTests.cc \
dbLogTests.cc \
dbRecursiveInstanceIteratorTests.cc \

View File

@ -108,6 +108,7 @@ PYTHONTEST (dbTransTest, "dbTransTest.py")
PYTHONTEST (dbLayoutToNetlist, "dbLayoutToNetlist.py")
PYTHONTEST (dbLayoutVsSchematic, "dbLayoutVsSchematic.py")
PYTHONTEST (dbNetlistCrossReference, "dbNetlistCrossReference.py")
PYTHONTEST (rdbTest, "rdbTest.py")
PYTHONTEST (layLayers, "layLayers.py")
PYTHONTEST (layObjects, "layObjects.py")
PYTHONTEST (layPixelBuffer, "layPixelBuffer.py")

View File

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

View File

@ -881,6 +881,13 @@ Categories::category_by_name (const char *path)
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
Categories::import_category (Category *category)
{
@ -1353,6 +1360,11 @@ Database::create_category (const std::string &name)
Category *
Database::create_category (Categories *container, const std::string &name)
{
Category *existing = container->category_by_raw_name (name);
if (existing) {
return existing;
}
set_modified ();
Category *cat = new Category (name);

View File

@ -413,8 +413,8 @@ public:
}
void add_category (Category *cath);
void set_database (Database *database);
Category *category_by_raw_name (const std::string &name);
};
/**

BIN
testdata/algo/edge_neighborhood.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/edge_neighborhood_au1.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/edge_neighborhood_au2.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/edge_neighborhood_au3.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/edge_neighborhood_au4.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/edge_neighborhood_au5.gds vendored Normal file

Binary file not shown.

981
testdata/python/rdbTest.py vendored Normal file
View File

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

173
testdata/ruby/dbEdgeNeighborhood.rb vendored Normal file
View File

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

View File

@ -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::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::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)")
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
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::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::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)
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::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::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)")
r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20))
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::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::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

View File

@ -1017,6 +1017,23 @@ class RDB_TestClass < TestBase
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
load("test_epilogue.rb")