mirror of https://github.com/KLayout/klayout.git
Merge pull request #928 from KLayout/performance
Performance enhancements for hierarchical processor
This commit is contained in:
commit
c63ffb928c
|
|
@ -452,6 +452,22 @@ run_demo gen, "input1.drc(enclosing(input2) < 2.0.um)", "drc_enc1u.png"
|
|||
run_demo gen, "input1.drc(enclosing(input2,\n"+
|
||||
" projection) < 2.0.um)", "drc_enc2u.png"
|
||||
|
||||
class Gen
|
||||
def produce(s1, s2)
|
||||
s1.insert(RBA::Box::new(3000, 0, 6000, 6000))
|
||||
s2.insert(RBA::Box::new(0, 1000, 6000, 7000))
|
||||
end
|
||||
end
|
||||
|
||||
gen = Gen::new
|
||||
|
||||
run_demo gen, "input1.enclosed(input2, 2.0.um)", "drc_encd1.png"
|
||||
run_demo gen, "input1.enclosed(input2, 2.0.um, projection)", "drc_encd2.png"
|
||||
|
||||
run_demo gen, "input1.drc(enclosed(input2) < 2.0.um)", "drc_encd1u.png"
|
||||
run_demo gen, "input1.drc(enclosed(input2,\n"+
|
||||
" projection) < 2.0.um)", "drc_encd2u.png"
|
||||
|
||||
|
||||
class Gen
|
||||
def produce(s1, s2)
|
||||
|
|
|
|||
|
|
@ -554,6 +554,7 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
{
|
||||
db::DeepShapeStore dss;
|
||||
dss.set_threads (xor_data.threads);
|
||||
dss.set_keep_layouts (true); // avoids excessive cell mapping
|
||||
|
||||
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
|
||||
|
||||
|
|
@ -598,6 +599,8 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (ll->second.first >= 0) {
|
||||
|
|
@ -612,15 +615,14 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
xor_res = in_a ^ in_b;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::log << "Running XOR on layer " << ll->first.to_string () << " with tolerance " << *t;
|
||||
}
|
||||
|
||||
db::LayerProperties lp = ll->first;
|
||||
if (lp.layer >= 0) {
|
||||
lp.layer += tol_index * xor_data.tolerance_bump;
|
||||
|
|
@ -633,6 +635,7 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
result.top_cell = xor_data.output_cell;
|
||||
|
||||
if (*t > db::epsilon) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
|
||||
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ SOURCES = \
|
|||
dbRecursiveInstanceIterator.cc \
|
||||
dbRecursiveShapeIterator.cc \
|
||||
dbRegion.cc \
|
||||
dbRegionCheckUtils.cc \
|
||||
dbRegionLocalOperations.cc \
|
||||
dbSaveLayoutOptions.cc \
|
||||
dbShape.cc \
|
||||
|
|
@ -290,6 +291,7 @@ HEADERS = \
|
|||
dbRecursiveInstanceIterator.h \
|
||||
dbRecursiveShapeIterator.h \
|
||||
dbRegion.h \
|
||||
dbRegionCheckUtils.h \
|
||||
dbRegionLocalOperations.h \
|
||||
dbSaveLayoutOptions.h \
|
||||
dbShape.h \
|
||||
|
|
|
|||
|
|
@ -964,9 +964,7 @@ struct iterated_array
|
|||
virtual std::pair <basic_array_iterator <Coord> *, bool>
|
||||
begin_touching (const box_type &b) const
|
||||
{
|
||||
if (b.empty ()) {
|
||||
return std::make_pair (new iterated_array_iterator <Coord> (m_v.begin (), m_v.end ()), false);
|
||||
} else if (! b.touches (m_box)) {
|
||||
if (b.empty () || ! b.touches (m_box)) {
|
||||
return std::make_pair (new iterated_array_iterator <Coord> (m_v.end (), m_v.end ()), false);
|
||||
} else {
|
||||
box_convert_type bc;
|
||||
|
|
@ -1699,7 +1697,7 @@ struct array
|
|||
* an appropriate basic_array object using a complex transformation.
|
||||
*/
|
||||
array (const Obj &obj, const complex_trans_type &ct, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
|
||||
: m_obj (obj), m_trans (ct), mp_base (new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax))
|
||||
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax) : new regular_array <coord_type> (a, b, amax, bmax))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1727,7 +1725,7 @@ struct array
|
|||
* it's own storage.
|
||||
*/
|
||||
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
|
||||
: m_obj (obj), m_trans (ct), mp_base (rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)))
|
||||
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)) : rep.insert (regular_array <coord_type> (a, b, amax, bmax)))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1766,7 +1764,7 @@ struct array
|
|||
*/
|
||||
template <class Iter>
|
||||
array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to)
|
||||
: m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to))
|
||||
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to) : new iterated_array <coord_type> (from, to))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1779,7 +1777,7 @@ struct array
|
|||
*/
|
||||
explicit
|
||||
array (const Obj &obj, const complex_trans_type &ct)
|
||||
: m_obj (obj), m_trans (ct), mp_base (new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()))
|
||||
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()) : 0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1809,7 +1807,7 @@ struct array
|
|||
*/
|
||||
explicit
|
||||
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep)
|
||||
: m_obj (obj), m_trans (ct), mp_base (rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())))
|
||||
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())) : 0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1890,17 +1888,13 @@ struct array
|
|||
array_iterator <coord_type, Trans> begin_touching (const box_type &b, const BoxConv &bc) const
|
||||
{
|
||||
if (b.empty ()) {
|
||||
if (mp_base) {
|
||||
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
|
||||
} else {
|
||||
return array_iterator <coord_type, Trans> (m_trans, true);
|
||||
}
|
||||
return array_iterator <coord_type, Trans> (m_trans, true);
|
||||
} else if (b == box_type::world ()) {
|
||||
return begin ();
|
||||
} else if (mp_base) {
|
||||
box_type ob (bc (m_obj));
|
||||
if (ob.empty ()) {
|
||||
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
|
||||
return array_iterator <coord_type, Trans> (m_trans, true);
|
||||
} else {
|
||||
if (mp_base->is_complex ()) {
|
||||
complex_trans_type ct = mp_base->complex_trans (simple_trans_type (m_trans));
|
||||
|
|
|
|||
|
|
@ -1150,7 +1150,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
|
|||
|
||||
size_t n = 0;
|
||||
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
||||
poly_check.enter (*p, n);
|
||||
poly_check.single (*p, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -487,6 +487,41 @@ Cell::hierarchy_levels () const
|
|||
return m_hier_levels;
|
||||
}
|
||||
|
||||
static bool
|
||||
has_shapes_touching_impl (const db::Cell &cell, unsigned int layer, const db::Box &box)
|
||||
{
|
||||
if (! cell.shapes (layer).begin_touching (box, db::ShapeIterator::All).at_end ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (db::Cell::touching_iterator i = cell.begin_touching (box); ! i.at_end (); ++i) {
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (box, db::box_convert<db::CellInst> (*cell.layout (), layer)); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::Box cbox;
|
||||
if (i->is_complex ()) {
|
||||
cbox = i->complex_trans (*ia).inverted () * box;
|
||||
} else {
|
||||
cbox = (*ia).inverted () * box;
|
||||
}
|
||||
|
||||
if (has_shapes_touching_impl (cell.layout ()->cell (i->cell_index ()), layer, cbox)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::has_shapes_touching (unsigned int layer, const db::Box &box) const
|
||||
{
|
||||
return has_shapes_touching_impl (*this, layer, box);
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_caller_cells (std::set<cell_index_type> &callers) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -721,6 +721,11 @@ public:
|
|||
return shapes (layer).begin_touching (box, flags, prop_sel, inv_prop_sel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A quick, recursive test whether the cell has shapes touching the given box on the given layer
|
||||
*/
|
||||
bool has_shapes_touching (unsigned int layer, const db::Box &box) const;
|
||||
|
||||
/**
|
||||
* @brief Collect all calling cells (either calling this cell directly or indirectly)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -269,6 +269,11 @@ void CellMapping::clear ()
|
|||
m_b2a_mapping.clear ();
|
||||
}
|
||||
|
||||
void CellMapping::swap (CellMapping &other)
|
||||
{
|
||||
m_b2a_mapping.swap (other.m_b2a_mapping);
|
||||
}
|
||||
|
||||
std::vector<db::cell_index_type> CellMapping::source_cells () const
|
||||
{
|
||||
std::vector<db::cell_index_type> s;
|
||||
|
|
@ -409,9 +414,9 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
|
|||
void
|
||||
CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Cell mapping")));
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Cell mapping")));
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Cell mapping - first step: mapping instance count and instance identity";
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +454,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
++a;
|
||||
} else {
|
||||
|
||||
if (tl::verbosity () >= 30) {
|
||||
if (tl::verbosity () >= 50) {
|
||||
size_t na = 0, nb = 0;
|
||||
for (std::multimap<size_t, db::cell_index_type>::const_iterator aa = a; aa != cm_a.end () && aa->first == w; ++aa) {
|
||||
++na;
|
||||
|
|
@ -497,7 +502,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "Checked cell " << layout_a.cell_name (a->second) << ": " << candidates [a->second].size () << " candidates remaining.";
|
||||
}
|
||||
|
||||
|
|
@ -519,7 +524,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
++a;
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "Mapping candidates:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
|
@ -536,7 +541,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
reduction = false;
|
||||
++iteration;
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Cell mapping - iteration " << iteration << ": cross-instance cone reduction";
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +558,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
refined_cand.clear ();
|
||||
refined_cand.insert (refined_cand.end (), cand->second.begin (), cand->second.end ());
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
if (tl::verbosity () >= 70) {
|
||||
tl::info << "--- Cell: " << layout_a.cell_name (cand->first);
|
||||
tl::info << "Before reduction: " << tl::noendl;
|
||||
for (size_t i = 0; i < refined_cand.size (); ++i) {
|
||||
|
|
@ -582,7 +587,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
}
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
|
||||
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
|
||||
tl::info << "Reduction because of caller mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
|
||||
tl::info << " -> " << tl::noendl;
|
||||
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
|
||||
|
|
@ -619,7 +624,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
}
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
|
||||
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
|
||||
tl::info << "Reduction because of callee mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
|
||||
tl::info << " -> " << tl::noendl;
|
||||
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
|
||||
|
|
@ -646,7 +651,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
|
||||
if (ed < uc->second.second) {
|
||||
uc->second = std::make_pair (ca, ed);
|
||||
if (tl::verbosity () >= 40) {
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as new unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
|
||||
}
|
||||
}
|
||||
|
|
@ -654,7 +659,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
} else {
|
||||
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
|
||||
unique_candidates.insert (std::make_pair (cb, std::make_pair (ca, ed)));
|
||||
if (tl::verbosity () >= 40) {
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
|
||||
}
|
||||
}
|
||||
|
|
@ -679,12 +684,12 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "Further refined candidates:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Cell mapping - iteration " << iteration << ": removal of uniquely mapped cells on B side";
|
||||
}
|
||||
|
||||
|
|
@ -709,15 +714,15 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
|
||||
if (tl::verbosity () >= 60) {
|
||||
tl::info << "After reduction of mapped cells on b side:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
|
||||
int total = 0;
|
||||
int not_mapped = 0;
|
||||
|
|
@ -747,7 +752,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
|
||||
// Resolve mapping according to string match
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Cell mapping - string mapping as last resort";
|
||||
}
|
||||
|
||||
|
|
@ -784,7 +789,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
|
||||
int total = 0;
|
||||
int not_mapped = 0;
|
||||
|
|
@ -795,7 +800,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
++total;
|
||||
if (cand->second.size () == 0) {
|
||||
if (tl::verbosity () >= 30) {
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "Unmapped cell: " << layout_a.cell_name (cand->first);
|
||||
}
|
||||
++not_mapped;
|
||||
|
|
@ -823,12 +828,12 @@ CellMapping::extract_unique (std::map <db::cell_index_type, std::vector<db::cell
|
|||
{
|
||||
if (cand->second.size () == 1) {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << " (U) " << layout_a.cell_name (cand->first) << " -> " << layout_b.cell_name (cand->second.front ()) << " (" << cand->first << " -> " << cand->second.front () << ")";
|
||||
}
|
||||
unique_mapping.insert (std::make_pair (cand->second.front (), cand->first));
|
||||
|
||||
} else if (tl::verbosity () >= 30) {
|
||||
} else if (tl::verbosity () >= 50) {
|
||||
|
||||
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
|
||||
int n = 5;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ public:
|
|||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Swaps the cell mapping with another one
|
||||
*/
|
||||
void swap (CellMapping &other);
|
||||
|
||||
/**
|
||||
* @brief Create a single cell mapping
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -1725,7 +1725,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c
|
|||
s->polygon (poly);
|
||||
|
||||
do {
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,17 +154,25 @@ DeepLayer::add_from (const DeepLayer &dl)
|
|||
db::cell_index_type source_cell = dl.initial_cell ().cell_index ();
|
||||
const db::Layout *source_layout = &dl.layout ();
|
||||
|
||||
db::CellMapping cm;
|
||||
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
|
||||
// create or reuse a layout mapping
|
||||
|
||||
// Actually copy the shapes
|
||||
const db::CellMapping *cell_mapping = 0;
|
||||
db::CellMapping cm;
|
||||
if (store () == dl.store ()) {
|
||||
cell_mapping = &const_cast<db::DeepShapeStore *> (mp_store.get ())->internal_cell_mapping (layout_index (), dl.layout_index ());
|
||||
} else {
|
||||
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
|
||||
cell_mapping = &cm;
|
||||
}
|
||||
|
||||
// actually copy the shapes
|
||||
|
||||
std::map<unsigned int, unsigned int> lm;
|
||||
lm.insert (std::make_pair (dl.layer (), layer ()));
|
||||
|
||||
std::vector <db::cell_index_type> source_cells;
|
||||
source_cells.push_back (source_cell);
|
||||
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm);
|
||||
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cell_mapping->table (), lm);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -418,11 +426,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
|
|||
}
|
||||
|
||||
DeepShapeStore::DeepShapeStore ()
|
||||
: m_keep_layouts (false)
|
||||
{
|
||||
++s_instance_count;
|
||||
}
|
||||
|
||||
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
|
||||
: m_keep_layouts (false)
|
||||
{
|
||||
++s_instance_count;
|
||||
|
||||
|
|
@ -711,6 +721,16 @@ db::Layout &DeepShapeStore::layout (unsigned int n)
|
|||
return m_layouts [n]->layout;
|
||||
}
|
||||
|
||||
unsigned int DeepShapeStore::layout_index (const db::Layout *layout) const
|
||||
{
|
||||
for (std::vector<LayoutHolder *>::const_iterator i = m_layouts.begin (); i != m_layouts.end (); ++i) {
|
||||
if (&(*i)->layout == layout) {
|
||||
return (unsigned int) (i - m_layouts.begin ());
|
||||
}
|
||||
}
|
||||
tl_assert (false);
|
||||
}
|
||||
|
||||
size_t DeepShapeStore::instance_count ()
|
||||
{
|
||||
return s_instance_count;
|
||||
|
|
@ -743,7 +763,7 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
|
|||
|
||||
}
|
||||
|
||||
if ((m_layouts[layout]->refs -= 1) <= 0) {
|
||||
if ((m_layouts[layout]->refs -= 1) <= 0 && ! m_keep_layouts) {
|
||||
delete m_layouts[layout];
|
||||
m_layouts[layout] = 0;
|
||||
clear_breakout_cells (layout);
|
||||
|
|
@ -768,7 +788,6 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
|
|||
}
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
|
||||
if (si.layout ()) {
|
||||
layout.dbu (si.layout ()->dbu () / trans.mag ());
|
||||
}
|
||||
|
|
@ -792,7 +811,6 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
|
|||
m_layouts[layout_index] = new LayoutHolder (trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
|
||||
if (si.layout ()) {
|
||||
layout.dbu (si.layout ()->dbu () / trans.mag ());
|
||||
}
|
||||
|
|
@ -915,17 +933,9 @@ DeepLayer DeepShapeStore::create_text_layer (const db::RecursiveShapeIterator &s
|
|||
return create_custom_layer (si, &refs, trans);
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::invalidate_hier ()
|
||||
{
|
||||
m_delivery_mapping_cache.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map)
|
||||
{
|
||||
invalidate_hier ();
|
||||
|
||||
db::HierarchyBuilder &builder = m_layouts [layout_index]->builder;
|
||||
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) {
|
||||
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
|
|
@ -934,6 +944,28 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::ce
|
|||
}
|
||||
}
|
||||
|
||||
const db::CellMapping &
|
||||
DeepShapeStore::internal_cell_mapping (unsigned int into_layout_index, unsigned int from_layout_index)
|
||||
{
|
||||
db::Layout &into_layout = layout (into_layout_index);
|
||||
db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
|
||||
const db::Layout &source_layout = layout (from_layout_index);
|
||||
db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
|
||||
|
||||
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
|
||||
if (cm == m_internal_mapping_cache.end () || ! cm->second.is_valid (into_layout, source_layout)) {
|
||||
|
||||
cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), CellMappingWithGenerationIds ())).first;
|
||||
|
||||
cm->second.clear ();
|
||||
cm->second.create_from_geometry_full (into_layout, into_cell, source_layout, source_cell);
|
||||
cm->second.set_generation_ids (into_layout, source_layout);
|
||||
|
||||
}
|
||||
|
||||
return cm->second;
|
||||
}
|
||||
|
||||
const db::CellMapping &
|
||||
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells, const std::set<db::cell_index_type> *included_cells)
|
||||
{
|
||||
|
|
@ -953,10 +985,11 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
|
|||
|
||||
DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell);
|
||||
|
||||
std::map<DeliveryMappingCacheKey, db::CellMapping>::iterator cm = m_delivery_mapping_cache.find (key);
|
||||
if (cm == m_delivery_mapping_cache.end ()) {
|
||||
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds>::iterator cm = m_delivery_mapping_cache.find (key);
|
||||
if (cm == m_delivery_mapping_cache.end () || ! cm->second.is_valid (*into_layout, *source_layout)) {
|
||||
|
||||
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
|
||||
cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first;
|
||||
cm->second.clear ();
|
||||
|
||||
// collects the cell mappings we skip because they are variants (variant building or box variants)
|
||||
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey> cm_skipped_variants;
|
||||
|
|
@ -1045,6 +1078,8 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
|
|||
into_layout->delete_cells (cells_to_delete);
|
||||
}
|
||||
|
||||
cm->second.set_generation_ids (*into_layout, *source_layout);
|
||||
|
||||
}
|
||||
|
||||
return cm->second;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "dbLayout.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
#include "dbHierarchyBuilder.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "gsiObject.h"
|
||||
|
||||
#include <set>
|
||||
|
|
@ -291,6 +292,41 @@ struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An object holding a cell mapping with the hierarchy generation Ids of the involved layouts
|
||||
*/
|
||||
class DB_PUBLIC CellMappingWithGenerationIds
|
||||
: public db::CellMapping
|
||||
{
|
||||
public:
|
||||
CellMappingWithGenerationIds ()
|
||||
: db::CellMapping (), m_into_generation_id (0), m_from_generation_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void swap (CellMappingWithGenerationIds &other)
|
||||
{
|
||||
db::CellMapping::swap (other);
|
||||
std::swap (m_into_generation_id, other.m_into_generation_id);
|
||||
std::swap (m_from_generation_id, other.m_from_generation_id);
|
||||
}
|
||||
|
||||
bool is_valid (const db::Layout &into_layout, const db::Layout &from_layout) const
|
||||
{
|
||||
return into_layout.hier_generation_id () == m_into_generation_id && from_layout.hier_generation_id () == m_from_generation_id;
|
||||
}
|
||||
|
||||
void set_generation_ids (const db::Layout &into_layout, const db::Layout &from_layout)
|
||||
{
|
||||
m_into_generation_id = into_layout.hier_generation_id ();
|
||||
m_from_generation_id = from_layout.hier_generation_id ();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_into_generation_id, m_from_generation_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The "deep shape store" is a working model for the hierarchical ("deep") processor
|
||||
*
|
||||
|
|
@ -339,6 +375,25 @@ public:
|
|||
*/
|
||||
bool is_singular () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether to keep layouts
|
||||
*
|
||||
* If this value is set to true, layouts are not released when their reference count
|
||||
* goes down to zero.
|
||||
*/
|
||||
void set_keep_layouts (bool f)
|
||||
{
|
||||
m_keep_layouts = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether to keep layouts
|
||||
*/
|
||||
bool keep_layouts () const
|
||||
{
|
||||
return m_keep_layouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new layer from a flat region (or the region is made flat)
|
||||
*
|
||||
|
|
@ -479,6 +534,11 @@ public:
|
|||
*/
|
||||
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0, const std::set<db::cell_index_type> *included_cells = 0);
|
||||
|
||||
/**
|
||||
* @brief Gets the cell mapping from one internal layout to another
|
||||
*/
|
||||
const db::CellMapping &internal_cell_mapping (unsigned int from_layout_index, unsigned int into_layout_index);
|
||||
|
||||
/**
|
||||
* @brief Create cell variants from the given variant collector
|
||||
*
|
||||
|
|
@ -544,6 +604,11 @@ public:
|
|||
*/
|
||||
db::Cell &initial_cell (unsigned int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the layout index for a given internal layout
|
||||
*/
|
||||
unsigned int layout_index (const db::Layout *layout) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the singular layout (const version)
|
||||
*
|
||||
|
|
@ -730,7 +795,6 @@ private:
|
|||
|
||||
struct LayoutHolder;
|
||||
|
||||
void invalidate_hier ();
|
||||
void add_ref (unsigned int layout, unsigned int layer);
|
||||
void remove_ref (unsigned int layout, unsigned int layer);
|
||||
|
||||
|
|
@ -752,6 +816,7 @@ private:
|
|||
layout_map_type m_layout_map;
|
||||
DeepShapeStoreState m_state;
|
||||
std::list<DeepShapeStoreState> m_state_stack;
|
||||
bool m_keep_layouts;
|
||||
tl::Mutex m_lock;
|
||||
|
||||
struct DeliveryMappingCacheKey
|
||||
|
|
@ -780,7 +845,8 @@ private:
|
|||
db::cell_index_type into_cell;
|
||||
};
|
||||
|
||||
std::map<DeliveryMappingCacheKey, db::CellMapping> m_delivery_mapping_cache;
|
||||
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds> m_delivery_mapping_cache;
|
||||
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds> m_internal_mapping_cache;
|
||||
};
|
||||
|
||||
template <class VarCollector>
|
||||
|
|
|
|||
|
|
@ -1015,79 +1015,15 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2, db::Coord dist)
|
||||
{
|
||||
// TODO: this algorithm is not in particular effective for identical arrays
|
||||
|
||||
const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ());
|
||||
const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ());
|
||||
db::box_convert <db::CellInst, true> inst2_bc (*layout2, layer2);
|
||||
|
||||
std::unordered_set<db::ICplxTrans> relative_trans_seen;
|
||||
|
||||
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
|
||||
|
||||
db::ICplxTrans tn1 = inst1->complex_trans (*n);
|
||||
db::ICplxTrans tni1 = tn1.inverted ();
|
||||
db::Box ibox1 = tn1 * cell1.bbox (layer1).enlarged (db::Vector (dist, dist));
|
||||
|
||||
if (! ibox1.empty ()) {
|
||||
|
||||
// TODO: in some cases, it may be possible to optimize this for arrays
|
||||
|
||||
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
|
||||
|
||||
if (inst1 == inst2 && *n == *k) {
|
||||
// skip self-interactions - this is handled inside the cell
|
||||
continue;
|
||||
}
|
||||
|
||||
db::ICplxTrans tn2 = inst2->complex_trans (*k);
|
||||
|
||||
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
|
||||
db::Box ibox2 = tn2 * cell2.bbox (layer2).enlarged (db::Vector (dist, dist));
|
||||
|
||||
db::ICplxTrans tn21 = tni1 * tn2;
|
||||
if (! relative_trans_seen.insert (tn21).second) {
|
||||
// this relative transformation was already seen
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Box cbox = ibox1 & ibox2;
|
||||
if (! cbox.empty ()) {
|
||||
|
||||
db::ICplxTrans tni2 = tn2.inverted ();
|
||||
|
||||
// not very strong, but already useful: the cells interact if there is a layer1 in cell1
|
||||
// in the common box and a layer2 in the cell2 in the common box
|
||||
// NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
|
||||
// objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
|
||||
// argument)
|
||||
if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
|
||||
! db::RecursiveShapeIterator (*layout2, cell2, layer2, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class TS, class TI, class TR>
|
||||
struct interaction_registration_inst2inst
|
||||
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, db::CellInstArray, unsigned int>
|
||||
{
|
||||
public:
|
||||
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > interaction_value_type;
|
||||
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
|
||||
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
|
||||
|
||||
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, std::unordered_map<const db::CellInstArray *, interaction_value_type> *result)
|
||||
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, interactions_type *result)
|
||||
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result), m_foreign (foreign)
|
||||
{
|
||||
// nothing yet ..
|
||||
|
|
@ -1109,8 +1045,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (! ignore && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) {
|
||||
(*mp_result) [inst1].first.insert (inst2);
|
||||
if (! ignore) {
|
||||
collect_instance_interactions (inst1, inst2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1120,66 +1056,176 @@ private:
|
|||
const db::Layout *mp_subject_layout, *mp_intruder_layout;
|
||||
unsigned int m_subject_layer, m_intruder_layer;
|
||||
db::Coord m_dist;
|
||||
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
|
||||
interactions_type *mp_result;
|
||||
std::unordered_set<std::pair<unsigned int, unsigned int> > m_interactions;
|
||||
bool m_foreign;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
static bool
|
||||
instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist)
|
||||
{
|
||||
const db::Cell &cell = layout->cell (inst->object ().cell_index ());
|
||||
db::box_convert <db::CellInst, true> inst_bc (*layout, layer);
|
||||
db::Box rbox = db::box_convert<T> () (ref);
|
||||
void
|
||||
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
|
||||
{
|
||||
#if 0
|
||||
printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
|
||||
mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert<db::CellInst> (*mp_subject_layout)).to_string()).c_str(),
|
||||
mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert<db::CellInst> (*mp_intruder_layout)).to_string()).c_str()); // @@@
|
||||
#endif
|
||||
// TODO: this algorithm is not in particular effective for identical arrays
|
||||
|
||||
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
|
||||
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
|
||||
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
|
||||
db::box_convert <db::CellInst, true> inst2_bc (*mp_intruder_layout, m_intruder_layer);
|
||||
|
||||
db::ICplxTrans tn = inst->complex_trans (*n);
|
||||
db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
|
||||
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > > interactions_cache;
|
||||
|
||||
if (! cbox.empty ()) {
|
||||
#if 0
|
||||
printf("@@@ -> #%d\n", int(inst1->size()));
|
||||
#endif
|
||||
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
|
||||
|
||||
db::ICplxTrans tni = tn.inverted ();
|
||||
db::ICplxTrans tn1 = inst1->complex_trans (*n);
|
||||
db::ICplxTrans tni1 = tn1.inverted ();
|
||||
db::Box ibox1 = tn1 * cell1.bbox (m_subject_layer).enlarged (db::Vector (m_dist, m_dist));
|
||||
|
||||
std::set<db::CellInstArray> *insts = 0;
|
||||
|
||||
if (! ibox1.empty ()) {
|
||||
|
||||
// TODO: in some cases, it may be possible to optimize this for arrays
|
||||
|
||||
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
|
||||
|
||||
if (inst1 == inst2 && *n == *k) {
|
||||
// skip self-interactions - this is handled inside the cell
|
||||
continue;
|
||||
}
|
||||
|
||||
db::ICplxTrans tn2 = inst2->complex_trans (*k);
|
||||
|
||||
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
|
||||
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
|
||||
|
||||
db::Box cbox = ibox1 & ibox2;
|
||||
if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) {
|
||||
|
||||
db::ICplxTrans tn21 = tni1 * tn2;
|
||||
|
||||
std::list<std::pair<db::cell_index_type, db::ICplxTrans> > *interactions = 0;
|
||||
|
||||
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > >::iterator ic = interactions_cache.find (tn21);
|
||||
if (ic != interactions_cache.end ()) {
|
||||
interactions = &ic->second;
|
||||
} else {
|
||||
interactions = &interactions_cache [tn21];
|
||||
collect_intruder_tree_interactions (cell1, cell2, tni1, tn21, cbox, *interactions);
|
||||
}
|
||||
|
||||
for (std::list<std::pair<db::cell_index_type, db::ICplxTrans> >::const_iterator i = interactions->begin (); i != interactions->end (); ++i) {
|
||||
if (! insts) {
|
||||
insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
|
||||
}
|
||||
insts->insert (db::CellInstArray (db::CellInst (i->first), i->second));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// not very strong, but already useful: the cells interact if there is a layer in cell
|
||||
// in the common box
|
||||
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
|
||||
// dot edges. Instead safe-shrink the search box and use touching mode.
|
||||
if (! db::RecursiveShapeIterator (*layout, cell, layer, safe_box_enlarged (tni * cbox, -1, -1), false).at_end ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > &interactions)
|
||||
{
|
||||
db::ICplxTrans tni2 = tn21.inverted () * tni1;
|
||||
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
|
||||
|
||||
template <class T>
|
||||
if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
|
||||
|
||||
// we're overlapping with shapes from the intruder - do not recursive further
|
||||
interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
|
||||
|
||||
const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (tbox2, db::box_convert<db::CellInst> (*mp_intruder_layout, m_intruder_layer)); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans it = i->complex_trans (*ia);
|
||||
|
||||
db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
|
||||
db::Box ccbox = cbox & ibox2;
|
||||
|
||||
if (! ccbox.empty ()) {
|
||||
collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
struct interaction_registration_inst2shape
|
||||
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, T, unsigned int>
|
||||
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, TI, unsigned int>
|
||||
{
|
||||
public:
|
||||
interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *result)
|
||||
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
|
||||
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
|
||||
|
||||
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
|
||||
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result)
|
||||
{
|
||||
// nothing yet ..
|
||||
}
|
||||
|
||||
void add (const db::CellInstArray *inst, unsigned int, const T *ref, unsigned int layer)
|
||||
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
|
||||
{
|
||||
if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) {
|
||||
(*mp_result) [inst].second [layer].insert (*ref);
|
||||
}
|
||||
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout *mp_subject_layout;
|
||||
db::Layout *mp_subject_layout;
|
||||
unsigned int m_subject_layer;
|
||||
db::Coord m_dist;
|
||||
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
|
||||
interactions_type *mp_result;
|
||||
|
||||
void
|
||||
collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist)
|
||||
{
|
||||
const db::Cell &cell = mp_subject_layout->cell (inst->object ().cell_index ());
|
||||
db::box_convert <db::CellInst, true> inst_bc (*mp_subject_layout, m_subject_layer);
|
||||
db::Box rbox = db::box_convert<TI> () (ref);
|
||||
|
||||
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
|
||||
|
||||
db::ICplxTrans tn = inst->complex_trans (*n);
|
||||
db::Box cbox = (tn * cell.bbox (m_subject_layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
|
||||
|
||||
if (! cbox.empty ()) {
|
||||
|
||||
db::ICplxTrans tni = tn.inverted ();
|
||||
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
|
||||
|
||||
std::set<TI> *shapes = 0;
|
||||
|
||||
// not very strong, but already useful: the cells interact if there is a layer in cell
|
||||
// in the common box
|
||||
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
|
||||
// dot edges. Instead safe-shrink the search box and use touching mode.
|
||||
for (db::RecursiveShapeIterator s (*mp_subject_layout, cell, m_subject_layer, safe_box_enlarged (tni * cbox, -1, -1), false); !s.at_end (); ++s) {
|
||||
if (! shapes) {
|
||||
shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer];
|
||||
}
|
||||
shapes->insert (rt (ref));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1436,6 +1482,9 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
|
||||
db::Coord dist) const
|
||||
{
|
||||
#if 0 // @@@
|
||||
printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (subject_cell->cell_index ()), subject_cell_inst.to_string().c_str()); fflush(stdout);
|
||||
#endif
|
||||
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
|
||||
|
||||
if (tl::verbosity () >= m_base_verbosity + 20) {
|
||||
|
|
@ -1504,26 +1553,32 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
|
||||
if (! subject_cell->begin ().at_end ()) {
|
||||
|
||||
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<TI> > > interaction_value_type;
|
||||
|
||||
std::unordered_map<const db::CellInstArray *, interaction_value_type> interactions;
|
||||
// Key: single instance given by cell index and transformation
|
||||
// Value the contexts for the child cell for this instance
|
||||
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
|
||||
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
|
||||
interactions_type interactions;
|
||||
|
||||
// insert dummy interactions to handle at least the child cell vs. itself
|
||||
// - this is important so we will always handle the instances unless they are
|
||||
// entirely empty in the subject layer
|
||||
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
|
||||
if (! inst_bcs (i->cell_inst ()).empty ()) {
|
||||
interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ()));
|
||||
db::cell_index_type ci = i->cell_inst ().object ().cell_index ();
|
||||
for (db::CellInstArray::iterator n = i->begin (); ! n.at_end (); ++n) {
|
||||
db::ICplxTrans tn = i->complex_trans (*n);
|
||||
interactions.insert (std::make_pair (std::make_pair (ci, tn), interactions_value_type ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can we shortcut this if interactions is empty?
|
||||
// TODO: can we shortcut this if interactions is empty?
|
||||
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
|
||||
|
||||
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
|
||||
|
||||
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
|
||||
interaction_registration_inst2inst<TI> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
|
||||
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
|
||||
|
||||
unsigned int id = 0;
|
||||
|
||||
|
|
@ -1570,10 +1625,10 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
|
||||
}
|
||||
|
||||
// TODO: can we shortcut this if interactions is empty?
|
||||
// TODO: can we shortcut this if interactions is empty?
|
||||
{
|
||||
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
|
||||
interaction_registration_inst2shape<TI> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
|
||||
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
|
||||
|
||||
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
|
||||
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
|
||||
|
|
@ -1596,76 +1651,13 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
|
||||
}
|
||||
|
||||
// this cache should reduce the effort of checking array vs. array
|
||||
typedef std::pair<unsigned int, std::pair<db::cell_index_type, db::ICplxTrans> > effective_instance_cache_key_type;
|
||||
typedef std::map<effective_instance_cache_key_type, std::pair<bool, db::CellInstArray> > effective_instance_cache_type;
|
||||
effective_instance_cache_type effective_instance_cache;
|
||||
// produce the tasks for computing the next-level interactions
|
||||
for (typename interactions_type::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
for (typename std::unordered_map<const db::CellInstArray *, interaction_value_type>::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
|
||||
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
|
||||
|
||||
db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
|
||||
|
||||
for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) {
|
||||
|
||||
db::ICplxTrans tn = i->first->complex_trans (*n);
|
||||
db::ICplxTrans tni = tn.inverted ();
|
||||
db::Box nbox = tn * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
|
||||
|
||||
if (! nbox.empty ()) {
|
||||
|
||||
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders_below;
|
||||
|
||||
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
|
||||
|
||||
for (typename std::map<unsigned int, std::unordered_set<TI> >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
|
||||
std::set<TI> &out = intruders_below.second [pl->first];
|
||||
for (typename std::unordered_set<TI>::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
|
||||
if (nbox.overlaps (db::box_convert<TI> () (*p))) {
|
||||
out.insert (rt (*p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: in some cases, it may be possible to optimize this for arrays
|
||||
|
||||
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
|
||||
|
||||
unsigned int ail = contexts.actual_intruder_layer (*il);
|
||||
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, ail);
|
||||
|
||||
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
|
||||
|
||||
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
|
||||
|
||||
db::ICplxTrans tk = (*j)->complex_trans (*k);
|
||||
// NOTE: no self-interactions
|
||||
if (i->first != *j || tn != tk) {
|
||||
|
||||
// optimize the intruder instance so it will be as low as possible
|
||||
effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
|
||||
effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
|
||||
if (cached == effective_instance_cache.end ()) {
|
||||
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
|
||||
cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
|
||||
}
|
||||
if (cached->second.first) {
|
||||
intruders_below.first.insert (cached->second.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0);
|
||||
issue_compute_contexts (contexts, cell_context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1673,63 +1665,6 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a cell instance array suitable for adding as intruder
|
||||
*
|
||||
* The given intruder cell with the transformation ti2s - which transforms the intruder instance into
|
||||
* the coordinate system of the subject cell - is analysed and either this instance or a sub-instance
|
||||
* is chosen.
|
||||
* Sub-instances are chosen if the intruder cell does not have shapes which interact with the subject
|
||||
* cell and there is exactly one sub-instance interacting with the subject cell.
|
||||
*/
|
||||
template <class TS, class TI, class TR>
|
||||
std::pair<bool, db::CellInstArray>
|
||||
local_processor<TS, TI, TR>::effective_instance (unsigned int subject_layer, db::cell_index_type subject_cell_index, unsigned int intruder_layer, db::cell_index_type intruder_cell_index, const db::ICplxTrans &ti2s, db::Coord dist) const
|
||||
{
|
||||
db::Box bbox = safe_box_enlarged (mp_subject_layout->cell (subject_cell_index).bbox (subject_layer), dist - 1, dist - 1);
|
||||
if (bbox.empty ()) {
|
||||
// should not happen, but skip if it does
|
||||
return std::make_pair (false, db::CellInstArray ());
|
||||
}
|
||||
|
||||
db::Box ibbox = bbox.transformed (ti2s.inverted ());
|
||||
|
||||
const db::Cell &intruder_cell = mp_intruder_layout->cell (intruder_cell_index);
|
||||
const db::Shapes &intruder_shapes = intruder_cell.shapes (intruder_layer);
|
||||
if (! intruder_shapes.empty () && ! intruder_shapes.begin_touching (ibbox, db::ShapeIterator::All).at_end ()) {
|
||||
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
|
||||
}
|
||||
|
||||
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, intruder_layer);
|
||||
|
||||
size_t ni = 0;
|
||||
db::cell_index_type eff_cell_index = 0;
|
||||
db::ICplxTrans eff_trans;
|
||||
|
||||
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (ibbox); ! i.at_end() && ni < 2; ++i) {
|
||||
const db::CellInstArray &ci = i->cell_inst ();
|
||||
db::Box cbox = mp_intruder_layout->cell (ci.object ().cell_index ()).bbox (intruder_layer);
|
||||
for (db::CellInstArray::iterator k = ci.begin_touching (ibbox, inst_bcii); ! k.at_end () && ni < 2; ++k) {
|
||||
db::ICplxTrans tk = ci.complex_trans (*k);
|
||||
if (ibbox.overlaps (cbox.transformed (tk))) {
|
||||
eff_trans = tk;
|
||||
eff_cell_index = ci.object ().cell_index ();
|
||||
++ni;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ni == 0) {
|
||||
// should not happen, but skip if it does
|
||||
return std::make_pair (false, db::CellInstArray ());
|
||||
} else if (ni == 1) {
|
||||
// one instance - dive down
|
||||
return effective_instance (subject_layer, subject_cell_index, intruder_layer, eff_cell_index, ti2s * eff_trans, dist);
|
||||
} else {
|
||||
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
|
||||
}
|
||||
}
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
void
|
||||
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers) const
|
||||
|
|
|
|||
|
|
@ -381,14 +381,16 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
|
|||
|
||||
} else {
|
||||
|
||||
db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
|
||||
db::cell_index_type inst_cell = inst.object ().cell_index ();
|
||||
|
||||
db::Box cell_bbox = iter->cell_bbox (inst_cell);
|
||||
std::pair<bool, std::set<db::Box> > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region);
|
||||
if (! clip_variant.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
|
||||
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
|
||||
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second);
|
||||
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell));
|
||||
|
||||
// for a new cell, create this instance
|
||||
if (m_cell_stack.back ().first) {
|
||||
|
|
|
|||
|
|
@ -528,12 +528,12 @@ public:
|
|||
*
|
||||
* The editable mode will be taken from db::default_editable_mode.
|
||||
*/
|
||||
Layout (db::Manager *manager = 0);
|
||||
explicit Layout (db::Manager *manager = 0);
|
||||
|
||||
/**
|
||||
* @brief Standard constructor which allows one to specify editable mode
|
||||
*/
|
||||
Layout (bool editable, db::Manager *manager = 0);
|
||||
explicit Layout (bool editable, db::Manager *manager = 0);
|
||||
|
||||
/**
|
||||
* @brief The copy ctor
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ namespace db
|
|||
{
|
||||
|
||||
LayoutStateModel::LayoutStateModel (bool busy)
|
||||
: m_hier_dirty (false), m_all_bboxes_dirty (false), m_busy (busy)
|
||||
: m_hier_dirty (false), m_hier_generation_id (0), m_all_bboxes_dirty (false), m_busy (busy)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
LayoutStateModel::LayoutStateModel (const LayoutStateModel &d)
|
||||
: m_hier_dirty (d.m_hier_dirty), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
|
||||
: m_hier_dirty (d.m_hier_dirty), m_hier_generation_id (d.m_hier_generation_id), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ LayoutStateModel &
|
|||
LayoutStateModel::operator= (const LayoutStateModel &d)
|
||||
{
|
||||
m_hier_dirty = d.m_hier_dirty;
|
||||
m_hier_generation_id = d.m_hier_generation_id;
|
||||
m_bboxes_dirty = d.m_bboxes_dirty;
|
||||
m_all_bboxes_dirty = d.m_all_bboxes_dirty;
|
||||
m_busy = d.m_busy;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ public:
|
|||
*/
|
||||
void invalidate_hier ()
|
||||
{
|
||||
++m_hier_generation_id;
|
||||
if (! m_hier_dirty || m_busy) {
|
||||
do_invalidate_hier (); // must be called before the hierarchy is invalidated (stopping of redraw thread requires this)
|
||||
m_hier_dirty = true;
|
||||
|
|
@ -127,6 +128,17 @@ public:
|
|||
return m_hier_dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the hierarchy generation ID
|
||||
*
|
||||
* The hierarchy generation ID is a number which is incremented on every hierarchy
|
||||
* change.
|
||||
*/
|
||||
size_t hier_generation_id () const
|
||||
{
|
||||
return m_hier_generation_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The "dirty bounding box" attribute
|
||||
*
|
||||
|
|
@ -195,6 +207,7 @@ public:
|
|||
|
||||
private:
|
||||
bool m_hier_dirty;
|
||||
size_t m_hier_generation_id;
|
||||
std::vector<bool> m_bboxes_dirty;
|
||||
bool m_all_bboxes_dirty;
|
||||
bool m_busy;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -2827,5 +2827,93 @@ decompose_trapezoids (const db::SimplePolygon &sp, TrapezoidDecompositionMode mo
|
|||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Polygon snapping
|
||||
|
||||
db::Polygon
|
||||
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Polygon
|
||||
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Vector
|
||||
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
|
||||
{
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
|
||||
return db::Vector (db::Coord (x), db::Coord (y));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -760,6 +760,37 @@ void DB_PUBLIC decompose_trapezoids (const db::Polygon &p, TrapezoidDecompositio
|
|||
*/
|
||||
void DB_PUBLIC decompose_trapezoids (const db::SimplePolygon &p, TrapezoidDecompositionMode mode, SimplePolygonSink &sink);
|
||||
|
||||
template <class C>
|
||||
static inline C snap_to_grid (C c, C g)
|
||||
{
|
||||
// This form of snapping always snaps g/2 to right/top.
|
||||
if (c < 0) {
|
||||
c = -g * ((-c + (g - 1) / 2) / g);
|
||||
} else {
|
||||
c = g * ((c + g / 2) / g);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
*/
|
||||
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a vector to the given grid
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const
|
|||
|
||||
// skip insts outside the complex region
|
||||
if (! m_inst.at_end ()) {
|
||||
if (! is_outside_complex_region (m_inst->bbox ())) {
|
||||
if (! is_outside_complex_region (m_inst->bbox (m_box_convert))) {
|
||||
break;
|
||||
} else {
|
||||
++m_inst;
|
||||
|
|
@ -775,7 +775,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
|
|||
// compute the region inside the new cell
|
||||
if (new_region != m_region) {
|
||||
new_region = m_trans.inverted () * m_region;
|
||||
new_region &= cell ()->bbox ();
|
||||
new_region &= cell_bbox (cell_index ());
|
||||
}
|
||||
m_local_region_stack.push_back (new_region);
|
||||
|
||||
|
|
@ -969,7 +969,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
|
|||
|
||||
// skip instance array members not part of the complex region
|
||||
while (! m_inst_array.at_end ()) {
|
||||
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ());
|
||||
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
|
||||
if (! is_outside_complex_region (ia_box)) {
|
||||
break;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -665,6 +665,16 @@ public:
|
|||
return reinterpret_cast<const cell_type *> (c - (c & size_t (3)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the current cell's bounding box
|
||||
*
|
||||
* The returned box is limited to the selected layer if applicable.
|
||||
*/
|
||||
box_type cell_bbox (db::cell_index_type cell_index) const
|
||||
{
|
||||
return m_box_convert (db::CellInst (cell_index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increments the iterator (operator version)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,638 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2021 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 "dbRegionCheckUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "tlSelect.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Edge2EdgeCheckBase implementation
|
||||
|
||||
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
|
||||
m_first_pseudo (std::numeric_limits<size_t>::max ()),
|
||||
m_with_shielding (with_shielding),
|
||||
m_symmetric_edges (symmetric_edges),
|
||||
m_has_edge_pair_output (true),
|
||||
m_has_negative_edge_output (false),
|
||||
m_pass (0)
|
||||
{
|
||||
m_distance = check.distance ();
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::prepare_next_pass ()
|
||||
{
|
||||
++m_pass;
|
||||
|
||||
if (m_pass == 1) {
|
||||
|
||||
m_first_pseudo = m_ep.size ();
|
||||
|
||||
if (m_with_shielding && ! m_ep.empty ()) {
|
||||
|
||||
m_ep_discarded.resize (m_ep.size (), false);
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
} else if (m_has_negative_edge_output) {
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! m_ep.empty () && m_has_edge_pair_output) {
|
||||
|
||||
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
|
||||
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
|
||||
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
|
||||
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
|
||||
bool use_result = true;
|
||||
if (d != m_ep_discarded.end ()) {
|
||||
use_result = ! *d;
|
||||
++d;
|
||||
}
|
||||
if (use_result) {
|
||||
put (*ep, *i);
|
||||
}
|
||||
++ep;
|
||||
++i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
|
||||
{
|
||||
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
|
||||
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
|
||||
|
||||
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
|
||||
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
|
||||
|
||||
if (ip1.first && ip2.first) {
|
||||
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
|
||||
{
|
||||
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o, p);
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
|
||||
|
||||
bool fully_removed = false;
|
||||
bool any = false;
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
any = true;
|
||||
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
|
||||
}
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
|
||||
put_negative (*o, (int) p);
|
||||
|
||||
} else if (! fully_removed) {
|
||||
|
||||
std::set<db::Edge> partial_edges;
|
||||
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
|
||||
ec.add (o, 0);
|
||||
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
ec.finish ();
|
||||
|
||||
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
|
||||
put_negative (*e, (int) p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
|
||||
{
|
||||
if (m_pass == 1) {
|
||||
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
|
||||
scanner.insert (&e->first, e->second);
|
||||
}
|
||||
return ! m_pseudo_edges.empty ();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
if (requires_different_polygons) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges from the same polygon are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate intra-polygon markers.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (((p1 ^ p2) & size_t (1)) == 0) {
|
||||
if (requires_different_layers) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges on the same layer are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate inter-polygon markers between them.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
if (m_pass == 0) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
ep.set_symmetric (m_symmetric_edges);
|
||||
|
||||
// found a violation: store inside the local buffer for now. In the second
|
||||
// pass we will eliminate those which are shielded completely (with shielding)
|
||||
// and/or compute the negative edges.
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2);
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
if (m_has_negative_edge_output) {
|
||||
|
||||
bool antiparallel = (mp_check->relation () == WidthRelation || mp_check->relation () == SpaceRelation);
|
||||
|
||||
// pseudo1 and pseudo2 are the connecting edges of the edge pairs. Together with the
|
||||
// original edges they form a quadrangle.
|
||||
db::Edge pseudo1 (ep.first ().p1 (), antiparallel ? ep.second ().p2 () : ep.second ().p1 ());
|
||||
db::Edge pseudo2 (antiparallel ? ep.second ().p1 () : ep.second ().p2 (), ep.first ().p2 ());
|
||||
|
||||
m_pseudo_edges.insert (std::make_pair (pseudo1, p1));
|
||||
m_pseudo_edges.insert (std::make_pair (pseudo2, p1));
|
||||
if (p1 != p2) {
|
||||
m_pseudo_edges.insert (std::make_pair (pseudo1, p2));
|
||||
m_pseudo_edges.insert (std::make_pair (pseudo2, p2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// set the discarded flags for shielded output
|
||||
if (m_with_shielding) {
|
||||
|
||||
// a simple (complete) shielding implementation which is based on the
|
||||
// assumption that shielding is relevant as soon as a foreign edge cuts through
|
||||
// both of the edge pair's connecting edges.
|
||||
|
||||
// TODO: this implementation does not take into account the nature of the
|
||||
// EdgePair - because of "whole_edge" it may not reflect the part actually
|
||||
// violating the distance.
|
||||
|
||||
std::vector<size_t> n1, n2;
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o1, p1);
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
|
||||
n1.push_back (n);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (n1.begin (), n1.end ());
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::vector<size_t> nn;
|
||||
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
|
||||
|
||||
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::EdgePair ep = m_ep [*i].normalized ();
|
||||
if (shields (ep, *o2)) {
|
||||
m_ep_discarded [*i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for negative output edges are cancelled by short interactions perpendicular to them
|
||||
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
|
||||
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
|
||||
// negative case this means we cancel a real edge.
|
||||
|
||||
if (m_has_negative_edge_output &&
|
||||
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::requires_different_layers () const
|
||||
{
|
||||
return m_requires_different_layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
|
||||
{
|
||||
m_requires_different_layers = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::different_polygons () const
|
||||
{
|
||||
return m_different_polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_different_polygons (bool f)
|
||||
{
|
||||
m_different_polygons = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type
|
||||
Edge2EdgeCheckBase::distance () const
|
||||
{
|
||||
return m_distance;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Poly2PolyCheckBase implementation
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
|
||||
: mp_output (& output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check ()
|
||||
: mp_output (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static size_t vertices (const db::Polygon &p)
|
||||
{
|
||||
return p.vertices ();
|
||||
}
|
||||
|
||||
static size_t vertices (const db::PolygonRef &p)
|
||||
{
|
||||
return p.obj ().vertices ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::single (const PolygonType &o, size_t p)
|
||||
{
|
||||
tl_assert (! mp_output->requires_different_layers () && ! mp_output->different_polygons ());
|
||||
|
||||
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o));
|
||||
|
||||
m_edge_heap.clear ();
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edge_heap.push_back (*e);
|
||||
m_scanner.insert (& m_edge_heap.back (), p);
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::connect (Edge2EdgeCheckBase &output)
|
||||
{
|
||||
mp_output = &output;
|
||||
clear ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::clear ()
|
||||
{
|
||||
m_scanner.clear ();
|
||||
m_edge_heap.clear ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
|
||||
{
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edge_heap.push_back (*e);
|
||||
m_scanner.insert (& m_edge_heap.back (), p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to generic header
|
||||
static bool interact (const db::Box &box, const db::Edge &e)
|
||||
{
|
||||
if (! e.bbox ().touches (box)) {
|
||||
return false;
|
||||
} else if (e.is_ortho ()) {
|
||||
return true;
|
||||
} else {
|
||||
return e.clipped (box).first;
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p, const poly2poly_check<PolygonType>::box_type &box)
|
||||
{
|
||||
if (box.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (interact (box, *e)) {
|
||||
m_edge_heap.push_back (*e);
|
||||
m_scanner.insert (& m_edge_heap.back (), p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::process ()
|
||||
{
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class poly2poly_check<db::Polygon>;
|
||||
template class poly2poly_check<db::PolygonRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToEdgeInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, e);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an edge interact if the edge is either inside completely
|
||||
// of at least one edge of the polygon intersects with the edge
|
||||
bool interacts = false;
|
||||
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
|
||||
interacts = true;
|
||||
} else {
|
||||
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
|
||||
if ((*pe).intersect (*e)) {
|
||||
interacts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interacts) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToTextInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, t);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an text interact if the text is either inside completely
|
||||
// of at least one text of the polygon intersects with the text
|
||||
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
|
||||
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,427 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2021 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_dbRegionCheckUtils
|
||||
#define HDR_dbRegionCheckUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbEdgePairRelations.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
class DB_PUBLIC Edge2EdgeCheckBase
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
|
||||
|
||||
/**
|
||||
* @brief Call this to initiate a new pass until the return value is false
|
||||
*/
|
||||
bool prepare_next_pass ();
|
||||
|
||||
/**
|
||||
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
|
||||
* (required for negative edge output - cancellation of perpendicular edges)
|
||||
*/
|
||||
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void finish (const Edge *o, size_t);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool requires_different_layers () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_requires_different_layers (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool different_polygons () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_different_polygons (bool f);
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
void set_has_negative_edge_output (bool f)
|
||||
{
|
||||
m_has_negative_edge_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
bool has_negative_edge_output () const
|
||||
{
|
||||
return m_has_negative_edge_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
void set_has_edge_pair_output (bool f)
|
||||
{
|
||||
m_has_edge_pair_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
bool has_edge_pair_output () const
|
||||
{
|
||||
return m_has_edge_pair_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type distance () const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Normal edge pair output (violations)
|
||||
*/
|
||||
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
|
||||
|
||||
/**
|
||||
* @brief Negative edge output
|
||||
*/
|
||||
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
|
||||
|
||||
private:
|
||||
const EdgeRelationFilter *mp_check;
|
||||
bool m_requires_different_layers;
|
||||
bool m_different_polygons;
|
||||
EdgeRelationFilter::distance_type m_distance;
|
||||
std::vector<db::EdgePair> m_ep;
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
|
||||
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
|
||||
size_t m_first_pseudo;
|
||||
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
|
||||
bool m_with_shielding;
|
||||
bool m_symmetric_edges;
|
||||
bool m_has_edge_pair_output;
|
||||
bool m_has_negative_edge_output;
|
||||
unsigned int m_pass;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
void put (const db::EdgePair &edge, bool inter_polygon) const
|
||||
{
|
||||
if (! inter_polygon || ! mp_output_intra) {
|
||||
mp_output_inter->insert (edge);
|
||||
} else {
|
||||
mp_output_intra->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Output *mp_output_inter;
|
||||
Output *mp_output_intra;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version allows delivery of the negative edges.
|
||||
*/
|
||||
template <class Output, class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has only negative edge output.
|
||||
*/
|
||||
template <class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
set_has_negative_edge_output (true);
|
||||
set_has_edge_pair_output (false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has positive or negative output. Negative output is mapped to edge pairs
|
||||
* as well.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
|
||||
}
|
||||
#if 0
|
||||
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
|
||||
// the outer edges to not represent the actual contour.
|
||||
if (layer == 1) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
template <class PolygonType>
|
||||
class DB_PUBLIC poly2poly_check
|
||||
{
|
||||
public:
|
||||
typedef typename PolygonType::box_type box_type;
|
||||
|
||||
poly2poly_check (Edge2EdgeCheckBase &output);
|
||||
poly2poly_check ();
|
||||
|
||||
void clear ();
|
||||
|
||||
void single (const PolygonType&o, size_t p);
|
||||
|
||||
void connect (Edge2EdgeCheckBase &output);
|
||||
void enter (const PolygonType &o, size_t p);
|
||||
void enter (const PolygonType &o, size_t p, const box_type &search_box);
|
||||
void process ();
|
||||
|
||||
private:
|
||||
db::Edge2EdgeCheckBase *mp_output;
|
||||
db::box_scanner<db::Edge, size_t> m_scanner;
|
||||
std::list<db::Edge> m_edge_heap;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
class DB_PUBLIC region_to_edge_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
|
||||
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &res) const
|
||||
{
|
||||
mp_output->insert (res);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
class DB_PUBLIC region_to_text_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const TextType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
|
||||
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &poly) const
|
||||
{
|
||||
mp_output->insert (poly);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionLocalOperations.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbLocalOperationUtils.h"
|
||||
#include "dbHierProcessor.h"
|
||||
|
||||
|
|
@ -209,7 +209,6 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
poly2poly_check<TS> poly_check (edge_check);
|
||||
|
||||
std::list<TS> heap;
|
||||
db::box_scanner<TS, size_t> scanner;
|
||||
std::unordered_set<TI> polygons;
|
||||
|
||||
std::set<unsigned int> ids;
|
||||
|
|
@ -219,12 +218,40 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
}
|
||||
}
|
||||
|
||||
bool take_all = edge_check.has_negative_edge_output () || interactions.num_intruders () == 0;
|
||||
|
||||
db::Box common_box;
|
||||
if (! take_all) {
|
||||
|
||||
db::Vector e (edge_check.distance (), edge_check.distance ());
|
||||
|
||||
db::Box subject_box;
|
||||
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
subject_box += db::box_convert<TS> () (interactions.subject_shape (i->first));
|
||||
}
|
||||
|
||||
if (edge_check.requires_different_layers ()) {
|
||||
db::Box intruder_box;
|
||||
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
||||
intruder_box += db::box_convert<TI> () (interactions.intruder_shape (*id).second);
|
||||
}
|
||||
common_box = subject_box.enlarged (e) & intruder_box.enlarged (e);
|
||||
} else {
|
||||
common_box = subject_box.enlarged (e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (m_has_other) {
|
||||
|
||||
size_t n = 0;
|
||||
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const TS &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (push_polygon_to_heap (layout, subject, heap), n);
|
||||
if (! take_all) {
|
||||
poly_check.enter (subject, n, common_box);
|
||||
} else {
|
||||
poly_check.enter (subject, n);
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +289,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
|
||||
n = 1;
|
||||
for (typename std::unordered_set<TI>::const_iterator o = polygons.begin (); o != polygons.end (); ++o) {
|
||||
scanner.insert (push_polygon_to_heap (layout, *o, heap), n);
|
||||
if (! take_all) {
|
||||
poly_check.enter (*o, n, common_box);
|
||||
} else {
|
||||
poly_check.enter (*o, n);
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +301,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
|
||||
n = 1;
|
||||
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
||||
scanner.insert (push_polygon_to_heap (layout, interactions.intruder_shape (*id).second, heap), n);
|
||||
if (! take_all) {
|
||||
poly_check.enter (interactions.intruder_shape (*id).second, n, common_box);
|
||||
} else {
|
||||
poly_check.enter (interactions.intruder_shape (*id).second, n);
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +321,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
// we can't directly insert because TS may be != TI
|
||||
const TS &ts = interactions.subject_shape (i->first);
|
||||
insert_into_hash (polygons, ts);
|
||||
scanner.insert (push_polygon_to_heap (layout, ts, heap), n);
|
||||
if (! take_all) {
|
||||
poly_check.enter (ts, n, common_box);
|
||||
} else {
|
||||
poly_check.enter (ts, n);
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +334,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
||||
const TI &ti = interactions.intruder_shape (*id).second;
|
||||
if (polygons.find (ti) == polygons.end ()) {
|
||||
scanner.insert (push_polygon_to_heap (layout, ti, heap), n);
|
||||
if (! take_all) {
|
||||
poly_check.enter (ti, n, common_box);
|
||||
} else {
|
||||
poly_check.enter (ti, n);
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,7 +346,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
}
|
||||
|
||||
do {
|
||||
scanner.process (poly_check, m_check.distance (), db::box_convert<TS> ());
|
||||
poly_check.process ();
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
// detect and remove parts of the result which have or do not have results "opposite"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "dbEdgePairRelations.h"
|
||||
#include "dbLocalOperation.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "tlSelect.h"
|
||||
|
|
@ -29,496 +30,6 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Edge2EdgeCheckBase implementation
|
||||
|
||||
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
|
||||
m_first_pseudo (std::numeric_limits<size_t>::max ()),
|
||||
m_with_shielding (with_shielding),
|
||||
m_symmetric_edges (symmetric_edges),
|
||||
m_has_edge_pair_output (true),
|
||||
m_has_negative_edge_output (false),
|
||||
m_pass (0)
|
||||
{
|
||||
m_distance = check.distance ();
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::prepare_next_pass ()
|
||||
{
|
||||
++m_pass;
|
||||
|
||||
if (m_pass == 1) {
|
||||
|
||||
m_first_pseudo = m_ep.size ();
|
||||
|
||||
if (m_with_shielding && ! m_ep.empty ()) {
|
||||
|
||||
m_ep_discarded.resize (m_ep.size (), false);
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
} else if (m_has_negative_edge_output) {
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! m_ep.empty () && m_has_edge_pair_output) {
|
||||
|
||||
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
|
||||
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
|
||||
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
|
||||
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
|
||||
bool use_result = true;
|
||||
if (d != m_ep_discarded.end ()) {
|
||||
use_result = ! *d;
|
||||
++d;
|
||||
}
|
||||
if (use_result) {
|
||||
put (*ep, *i);
|
||||
}
|
||||
++ep;
|
||||
++i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
|
||||
{
|
||||
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
|
||||
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
|
||||
|
||||
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
|
||||
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
|
||||
|
||||
if (ip1.first && ip2.first) {
|
||||
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
|
||||
{
|
||||
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o, p);
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
|
||||
|
||||
bool fully_removed = false;
|
||||
bool any = false;
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
any = true;
|
||||
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
|
||||
}
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
|
||||
put_negative (*o, (int) p);
|
||||
|
||||
} else if (! fully_removed) {
|
||||
|
||||
std::set<db::Edge> partial_edges;
|
||||
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
|
||||
ec.add (o, 0);
|
||||
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
ec.finish ();
|
||||
|
||||
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
|
||||
put_negative (*e, (int) p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
|
||||
{
|
||||
if (m_pass == 1) {
|
||||
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
|
||||
scanner.insert (&e->first, e->second);
|
||||
}
|
||||
return ! m_pseudo_edges.empty ();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
if (requires_different_polygons) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges from the same polygon are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate intra-polygon markers.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (((p1 ^ p2) & size_t (1)) == 0) {
|
||||
if (requires_different_layers) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges on the same layer are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate inter-polygon markers between them.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
if (m_pass == 0) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
ep.set_symmetric (m_symmetric_edges);
|
||||
|
||||
// found a violation: store inside the local buffer for now. In the second
|
||||
// pass we will eliminate those which are shielded completely (with shielding)
|
||||
// and/or compute the negative edges.
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2);
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
if (m_has_negative_edge_output) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1));
|
||||
if (p1 != p2) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// set the discarded flags for shielded output
|
||||
if (m_with_shielding) {
|
||||
|
||||
// a simple (complete) shielding implementation which is based on the
|
||||
// assumption that shielding is relevant as soon as a foreign edge cuts through
|
||||
// both of the edge pair's connecting edges.
|
||||
|
||||
// TODO: this implementation does not take into account the nature of the
|
||||
// EdgePair - because of "whole_edge" it may not reflect the part actually
|
||||
// violating the distance.
|
||||
|
||||
std::vector<size_t> n1, n2;
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o1, p1);
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
|
||||
n1.push_back (n);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (n1.begin (), n1.end ());
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::vector<size_t> nn;
|
||||
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
|
||||
|
||||
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::EdgePair ep = m_ep [*i].normalized ();
|
||||
if (shields (ep, *o2)) {
|
||||
m_ep_discarded [*i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for negative output edges are cancelled by short interactions perpendicular to them
|
||||
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
|
||||
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
|
||||
// negative case this means we cancel a real edge.
|
||||
|
||||
if (m_has_negative_edge_output &&
|
||||
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::requires_different_layers () const
|
||||
{
|
||||
return m_requires_different_layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
|
||||
{
|
||||
m_requires_different_layers = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::different_polygons () const
|
||||
{
|
||||
return m_different_polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_different_polygons (bool f)
|
||||
{
|
||||
m_different_polygons = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type
|
||||
Edge2EdgeCheckBase::distance () const
|
||||
{
|
||||
return m_distance;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Poly2PolyCheckBase implementation
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
|
||||
: mp_output (& output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::finish (const PolygonType *o, size_t p)
|
||||
{
|
||||
enter (*o, p);
|
||||
}
|
||||
|
||||
static size_t vertices (const db::Polygon &p)
|
||||
{
|
||||
return p.vertices ();
|
||||
}
|
||||
|
||||
static size_t vertices (const db::PolygonRef &p)
|
||||
{
|
||||
return p.obj ().vertices ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
|
||||
{
|
||||
if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) {
|
||||
|
||||
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o));
|
||||
|
||||
m_edges.clear ();
|
||||
m_edges.reserve (vertices (o));
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p);
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
tl_assert (m_edges.size () == vertices (o));
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2)
|
||||
{
|
||||
enter (*o1, p1, *o2, p2);
|
||||
}
|
||||
|
||||
static bool interact (const db::Box &box, const db::Edge &e)
|
||||
{
|
||||
if (! e.bbox ().touches (box)) {
|
||||
return false;
|
||||
} else if (e.is_ortho ()) {
|
||||
return true;
|
||||
} else {
|
||||
return e.clipped (box).first;
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
|
||||
{
|
||||
if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
|
||||
|
||||
bool take_all = mp_output->has_negative_edge_output ();
|
||||
|
||||
db::Box common_box;
|
||||
if (! take_all) {
|
||||
db::Vector e (mp_output->distance (), mp_output->distance ());
|
||||
common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
|
||||
if (common_box.empty ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o1) + vertices (o2));
|
||||
|
||||
m_edges.clear ();
|
||||
m_edges.reserve (vertices (o1) + vertices (o2));
|
||||
|
||||
bool any_o1 = false, any_o2 = false;
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p1);
|
||||
any_o1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p2);
|
||||
any_o2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! take_all && (! any_o1 || ! any_o2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
// temporarily disable intra-polygon check in that step .. we do that later in finish()
|
||||
// if required (#650).
|
||||
bool no_intra = mp_output->different_polygons ();
|
||||
mp_output->set_different_polygons (true);
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
|
||||
mp_output->set_different_polygons (no_intra);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class poly2poly_check<db::Polygon>;
|
||||
template class poly2poly_check<db::PolygonRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionPerimeterFilter implementation
|
||||
|
||||
|
|
@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector<db::EdgePai
|
|||
poly2poly_check<db::Polygon> poly_check (edge_check);
|
||||
|
||||
do {
|
||||
poly_check.enter (polygon, 0);
|
||||
poly_check.single (polygon, 0);
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
res.insert (res.end (), result.begin (), result.end ());
|
||||
|
|
@ -967,219 +478,4 @@ HullExtractionProcessor::process (const db::Polygon &poly, std::vector<db::Polyg
|
|||
res.back ().assign_hull (poly.begin_hull (), poly.end_hull ());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToEdgeInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, e);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an edge interact if the edge is either inside completely
|
||||
// of at least one edge of the polygon intersects with the edge
|
||||
bool interacts = false;
|
||||
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
|
||||
interacts = true;
|
||||
} else {
|
||||
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
|
||||
if ((*pe).intersect (*e)) {
|
||||
interacts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interacts) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToTextInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, t);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an text interact if the text is either inside completely
|
||||
// of at least one text of the polygon intersects with the text
|
||||
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
|
||||
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Polygon snapping
|
||||
|
||||
db::Polygon
|
||||
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Polygon
|
||||
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Vector
|
||||
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
|
||||
{
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
|
||||
return db::Vector (db::Coord (x), db::Coord (y));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,298 +583,6 @@ public:
|
|||
virtual bool result_must_not_be_merged () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
class DB_PUBLIC Edge2EdgeCheckBase
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
|
||||
|
||||
/**
|
||||
* @brief Call this to initiate a new pass until the return value is false
|
||||
*/
|
||||
bool prepare_next_pass ();
|
||||
|
||||
/**
|
||||
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
|
||||
* (required for negative edge output - cancellation of perpendicular edges)
|
||||
*/
|
||||
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void finish (const Edge *o, size_t);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool requires_different_layers () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_requires_different_layers (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool different_polygons () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_different_polygons (bool f);
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
void set_has_negative_edge_output (bool f)
|
||||
{
|
||||
m_has_negative_edge_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
bool has_negative_edge_output () const
|
||||
{
|
||||
return m_has_negative_edge_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
void set_has_edge_pair_output (bool f)
|
||||
{
|
||||
m_has_edge_pair_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
bool has_edge_pair_output () const
|
||||
{
|
||||
return m_has_edge_pair_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type distance () const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Normal edge pair output (violations)
|
||||
*/
|
||||
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
|
||||
|
||||
/**
|
||||
* @brief Negative edge output
|
||||
*/
|
||||
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
|
||||
|
||||
private:
|
||||
const EdgeRelationFilter *mp_check;
|
||||
bool m_requires_different_layers;
|
||||
bool m_different_polygons;
|
||||
EdgeRelationFilter::distance_type m_distance;
|
||||
std::vector<db::EdgePair> m_ep;
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
|
||||
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
|
||||
size_t m_first_pseudo;
|
||||
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
|
||||
bool m_with_shielding;
|
||||
bool m_symmetric_edges;
|
||||
bool m_has_edge_pair_output;
|
||||
bool m_has_negative_edge_output;
|
||||
unsigned int m_pass;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
void put (const db::EdgePair &edge, bool inter_polygon) const
|
||||
{
|
||||
if (! inter_polygon || ! mp_output_intra) {
|
||||
mp_output_inter->insert (edge);
|
||||
} else {
|
||||
mp_output_intra->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Output *mp_output_inter;
|
||||
Output *mp_output_intra;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version allows delivery of the negative edges.
|
||||
*/
|
||||
template <class Output, class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has only negative edge output.
|
||||
*/
|
||||
template <class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
set_has_negative_edge_output (true);
|
||||
set_has_edge_pair_output (false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has positive or negative output. Negative output is mapped to edge pairs
|
||||
* as well.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
|
||||
}
|
||||
#if 0
|
||||
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
|
||||
// the outer edges to not represent the actual contour.
|
||||
if (layer == 1) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
template <class PolygonType>
|
||||
class DB_PUBLIC poly2poly_check
|
||||
: public db::box_scanner_receiver<PolygonType, size_t>
|
||||
{
|
||||
public:
|
||||
poly2poly_check (Edge2EdgeCheckBase &output);
|
||||
|
||||
void finish (const PolygonType *o, size_t p);
|
||||
void enter (const PolygonType&o, size_t p);
|
||||
void add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2);
|
||||
void enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2);
|
||||
|
||||
private:
|
||||
db::Edge2EdgeCheckBase *mp_output;
|
||||
db::box_scanner<db::Edge, size_t> m_scanner;
|
||||
std::vector<db::Edge> m_edges;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class wrapping the single-polygon checks into a polygon-to-edge pair processor
|
||||
*/
|
||||
|
|
@ -892,129 +600,6 @@ private:
|
|||
db::RegionCheckOptions m_options;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
class DB_PUBLIC region_to_edge_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
|
||||
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &res) const
|
||||
{
|
||||
mp_output->insert (res);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
class DB_PUBLIC region_to_text_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const TextType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
|
||||
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &poly) const
|
||||
{
|
||||
mp_output->insert (poly);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
static inline C snap_to_grid (C c, C g)
|
||||
{
|
||||
// This form of snapping always snaps g/2 to right/top.
|
||||
if (c < 0) {
|
||||
c = -g * ((-c + (g - 1) / 2) / g);
|
||||
} else {
|
||||
c = g * ((c + g / 2) / g);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
*/
|
||||
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a vector to the given grid
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2011,6 +2011,15 @@ public:
|
|||
m_mag = m_mag < 0.0 ? -m : m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the transformation is a complex one
|
||||
* The transformation can safely be converted to a simple transformation if this value is false.
|
||||
*/
|
||||
bool is_complex () const
|
||||
{
|
||||
return is_mag () || ! is_ortho ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test, if the transformation is mirroring
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -460,6 +460,11 @@ static db::CompoundRegionOperationNode *new_enclosing_check (db::CompoundRegionO
|
|||
return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
|
||||
}
|
||||
|
||||
static db::CompoundRegionOperationNode *new_enclosed_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative)
|
||||
{
|
||||
return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
|
||||
}
|
||||
|
||||
static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::perimeter_type pmin, db::coord_traits<db::Coord>::perimeter_type pmax)
|
||||
{
|
||||
check_non_null (input, "input");
|
||||
|
|
@ -673,6 +678,11 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
|
|||
gsi::constructor ("new_enclosing_check", &new_enclosing_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
|
||||
"@brief Creates a node providing an inside (enclosure) check.\n"
|
||||
) +
|
||||
gsi::constructor ("new_enclosed_check", &new_enclosed_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
|
||||
"@brief Creates a node providing an enclosed (secondary enclosing primary) check.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27.5.\n"
|
||||
) +
|
||||
gsi::constructor ("new_perimeter_filter", &new_perimeter_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::perimeter_type>::max (), "max"),
|
||||
"@brief Creates a node filtering the input by perimeter.\n"
|
||||
"This node renders the input if the perimeter is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
|
||||
|
|
|
|||
|
|
@ -1160,7 +1160,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
|
||||
"If you don't want to specify one threshold, pass nil to the respective value.\n"
|
||||
) +
|
||||
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
|
||||
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
|
||||
"@brief Performs an inside check with options\n"
|
||||
"@param d The minimum distance for which the edges are checked\n"
|
||||
"@param other The other edge collection against which to check\n"
|
||||
|
|
@ -1186,6 +1186,8 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"It is sufficient if the projection of one edge on the other matches the specified condition. "
|
||||
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
|
||||
"If you don't want to specify one threshold, pass nil to the respective value.\n"
|
||||
"\n"
|
||||
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
|
||||
) +
|
||||
method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
|
||||
"@brief Performs an enclosing check with options\n"
|
||||
|
|
|
|||
|
|
@ -2591,7 +2591,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27."
|
||||
) +
|
||||
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
|
||||
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
|
||||
"@brief Performs an inside check with options\n"
|
||||
"@param d The minimum distance for which the polygons are checked\n"
|
||||
"@param other The other region against which to check\n"
|
||||
|
|
@ -2639,6 +2639,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
|
||||
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
|
||||
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
|
||||
) +
|
||||
method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
|
||||
"@brief Performs an overlap check with options\n"
|
||||
|
|
|
|||
|
|
@ -916,7 +916,7 @@ struct cplx_trans_defs
|
|||
"@brief Gets the magnification\n"
|
||||
) +
|
||||
method ("is_mag?", &C::is_mag,
|
||||
"@brief Test, if the transformation is a magnifying one\n"
|
||||
"@brief Tests, if the transformation is a magnifying one\n"
|
||||
"\n"
|
||||
"This is the recommended test for checking if the transformation represents\n"
|
||||
"a magnification.\n"
|
||||
|
|
@ -925,6 +925,15 @@ struct cplx_trans_defs
|
|||
"@brief Sets the magnification\n"
|
||||
"@param m The new magnification"
|
||||
) +
|
||||
method ("is_complex?", &C::is_complex,
|
||||
"@brief Returns true if the transformation is a complex one\n"
|
||||
"\n"
|
||||
"If this predicate is false, the transformation can safely be converted to a simple transformation.\n"
|
||||
"Otherwise, this conversion will be lossy.\n"
|
||||
"The predicate value is equivalent to 'is_mag || !is_ortho'.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.5."
|
||||
) +
|
||||
method ("R0", &trans_r0,
|
||||
"@brief A constant giving \"unrotated\" (unit) transformation\n"
|
||||
"The previous integer constant has been turned into a transformation in version 0.25."
|
||||
|
|
|
|||
|
|
@ -1014,3 +1014,49 @@ TEST(6)
|
|||
|
||||
}
|
||||
|
||||
TEST(10_HasShapesTouching)
|
||||
{
|
||||
db::Layout ly;
|
||||
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
|
||||
|
||||
db::Cell &a = ly.cell (ly.add_cell ("A"));
|
||||
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
|
||||
|
||||
a.shapes (l1).insert (db::Box (-100, -100, 0, 0));
|
||||
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 1, 100, 100)), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, -1, 100, 100)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (-1, -1, -1, -1)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (1, 1, 1, 1)), false);
|
||||
}
|
||||
|
||||
TEST(11_HasShapesTouchingWithHier)
|
||||
{
|
||||
db::Layout ly;
|
||||
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
|
||||
unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0));
|
||||
|
||||
db::Cell &a = ly.cell (ly.add_cell ("A"));
|
||||
db::Cell &b = ly.cell (ly.add_cell ("B"));
|
||||
|
||||
a.insert (db::CellInstArray (b.cell_index (), db::Trans (db::Vector (100, 100)), db::Vector (0, 200), db::Vector (200, 0), 2, 2));
|
||||
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l2, db::Box ()), false);
|
||||
|
||||
b.shapes (l1).insert (db::Box (0, 0, 10, 10));
|
||||
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l2, db::Box (0, 0, 100, 100)), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 99, 100)), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 99)), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (100, 100, 110, 110)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (150, 150, 160, 160)), false);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 300, 310, 310)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 100, 310, 110)), true);
|
||||
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 400, 310, 410)), false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,12 +261,15 @@ TEST(2)
|
|||
db::cell_index_type ci;
|
||||
db::Cell *top;
|
||||
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (0));
|
||||
|
||||
ci = g.add_cell ("TOP");
|
||||
|
||||
EXPECT_EQ (el.flags, (unsigned int) 0);
|
||||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, true);
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (1));
|
||||
|
||||
el.reset ();
|
||||
top = &g.cell (ci);
|
||||
|
|
@ -276,6 +279,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (2));
|
||||
|
||||
el.reset ();
|
||||
top->insert (db::CellInstArray (ci, db::Trans ()));
|
||||
|
|
@ -284,10 +288,12 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, true);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, true);
|
||||
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (3));
|
||||
|
||||
g.clear ();
|
||||
g.update ();
|
||||
el.reset ();
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (4));
|
||||
|
||||
ci = g.add_cell ("TOP");
|
||||
|
||||
|
|
@ -295,6 +301,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, true);
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (5));
|
||||
|
||||
el.reset ();
|
||||
g.update ();
|
||||
|
|
@ -305,6 +312,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, true); // OK - see above
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (6));
|
||||
|
||||
el.reset ();
|
||||
g.update ();
|
||||
|
|
@ -314,6 +322,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, true);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, true);
|
||||
EXPECT_EQ (el.hier_dirty, true); // OK - see above
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (7));
|
||||
|
||||
// busy mode will make events issued always
|
||||
g.clear ();
|
||||
|
|
@ -326,6 +335,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, true);
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (9));
|
||||
|
||||
el.reset ();
|
||||
top = &g.cell (ci);
|
||||
|
|
@ -335,6 +345,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, false);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, false);
|
||||
EXPECT_EQ (el.hier_dirty, true); // OK - see above
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (10));
|
||||
|
||||
el.reset ();
|
||||
top->insert (db::CellInstArray (ci, db::Trans ()));
|
||||
|
|
@ -343,6 +354,7 @@ TEST(2)
|
|||
EXPECT_EQ (el.bboxes_dirty, true);
|
||||
EXPECT_EQ (el.bboxes_all_dirty, true);
|
||||
EXPECT_EQ (el.hier_dirty, true); // OK - see above
|
||||
EXPECT_EQ (g.hier_generation_id (), size_t (11));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "tlUnitTest.h"
|
||||
#include "tlStringEx.h"
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
|
||||
TEST(1_SimpleLShape)
|
||||
{
|
||||
|
|
@ -52,7 +52,7 @@ TEST(1_SimpleLShape)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(2000,1000;1000,1000)|(1000,2000;2000,2000)");
|
||||
|
|
@ -85,7 +85,7 @@ TEST(1s_SimpleLShape)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)");
|
||||
|
|
@ -118,7 +118,7 @@ TEST(2_SimpleLWithBigPart)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0)");
|
||||
|
|
@ -153,7 +153,7 @@ TEST(3_SimpleTWithBigPart)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(0,2500;0,3500)|(1000,3500;1000,2500)");
|
||||
|
|
@ -188,7 +188,7 @@ TEST(4_SimpleNotch)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1000,1000;2000,1000)|(2000,2000;1000,2000)");
|
||||
|
|
@ -225,7 +225,7 @@ TEST(5_LShapeNotch)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1500,500;2000,500)|(2000,1500;1500,1500),(1500,1500;1500,2500)|(500,2500;500,1500)");
|
||||
|
|
@ -264,14 +264,12 @@ TEST(6_SeparationLvsBox)
|
|||
db::Polygon poly2;
|
||||
poly2.assign_hull (pts2, pts2 + sizeof (pts2) / sizeof (pts2[0]));
|
||||
|
||||
db::box_scanner<db::Polygon, size_t> scanner;
|
||||
scanner.insert (&poly1, 0); // layer 0
|
||||
scanner.insert (&poly2, 1); // layer 1
|
||||
|
||||
db::poly2poly_check<db::Polygon> poly_check (e2e);
|
||||
poly_check.enter (poly1, 0); // layer 0
|
||||
poly_check.enter (poly2, 1); // layer 1
|
||||
|
||||
do {
|
||||
scanner.process (poly_check, er.distance (), db::box_convert<db::Polygon> ());
|
||||
poly_check.process ();
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1000,1000;1000,0)/(2000,0;2000,1000),(3000,2000;2000,2000)/(2000,1000;3000,1000)");
|
||||
|
|
@ -10,7 +10,7 @@ SOURCES = \
|
|||
dbCompoundOperationTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
dbRecursiveInstanceIteratorTests.cc \
|
||||
dbRegionUtilsTests.cc \
|
||||
dbRegionCheckUtilsTests.cc \
|
||||
dbUtilsTests.cc \
|
||||
dbWriterTools.cc \
|
||||
dbLoadLayoutOptionsTests.cc \
|
||||
|
|
|
|||
|
|
@ -1888,7 +1888,7 @@ class DRCOpNodeCheck < DRCOpNodeWithCompare
|
|||
factory = { :width => :new_width_check, :space => :new_space_check,
|
||||
:notch => :new_notch_check, :separation => :new_separation_check,
|
||||
:isolated => :new_isolated_check, :overlap => :new_overlap_check,
|
||||
:enclosing => :new_enclosing_check }[self.check]
|
||||
:enclosing => :new_enclosing_check, :enclosed => :new_enclosed_check }[self.check]
|
||||
|
||||
oargs = []
|
||||
if self.other
|
||||
|
|
|
|||
|
|
@ -1042,6 +1042,84 @@ CODE
|
|||
# actual edges from the first input (see \separation for an example).
|
||||
#
|
||||
|
||||
# %DRC%
|
||||
# @name enclosed
|
||||
# @brief Performs an enclosing check (other enclosing layer)
|
||||
# @synopsis enclosed(other [, options ]) (in conditions)
|
||||
# @synopsis enclosed(layer, other [, options ])
|
||||
#
|
||||
# This check verifies if the polygons of the input layer are enclosed by shapes
|
||||
# of the other input layer by a certain distance.
|
||||
# It has manifold options. See \Layer#width for the basic options such
|
||||
# as metrics, projection and angle constraints etc. This check also features
|
||||
# opposite and rectangle filtering. See \Layer#separation for details about opposite and
|
||||
# rectangle error filtering.
|
||||
#
|
||||
# This function is essentially the reverse of \enclosing. In case of
|
||||
# "enclosed", the other layer must be bigger than the primary layer.
|
||||
#
|
||||
# @h3 Classic mode @/h3
|
||||
#
|
||||
# This function can be used in classic mode with a layer argument. In this case it
|
||||
# is equivalent to "layer.enclosed" (see \Layer#enclosed).
|
||||
#
|
||||
# @code
|
||||
# # classic "enclosed" check for < 0.2 um
|
||||
# in = layer(1, 0)
|
||||
# other = layer(2, 0)
|
||||
# errors = enclosed(in, other, 0.2.um)
|
||||
# @/code
|
||||
#
|
||||
# @h3 Universal DRC @/h3
|
||||
#
|
||||
# The version without a first layer is intended for use within \DRC# expressions
|
||||
# together with the "universal DRC" method \Layer#drc. In this case, this function needs to be
|
||||
# put into a condition to specify the check constraints. The other options
|
||||
# of \Layer#enclosed (e.g. metrics, projection constraints, angle limits etc.)
|
||||
# apply to this version too:
|
||||
#
|
||||
# @code
|
||||
# # universal DRC "enclosed" check for < 0.2 um
|
||||
# in = layer(1, 0)
|
||||
# other = layer(2, 0)
|
||||
# errors = in.drc(enclosed(other) < 0.2.um)
|
||||
# @/code
|
||||
#
|
||||
# The conditions may involve an upper and lower limit. The following examples
|
||||
# illustrate the use of this function with conditions:
|
||||
#
|
||||
# @code
|
||||
# out = in.drc(enclosed(other) < 0.2.um)
|
||||
# out = in.drc(enclosed(other) <= 0.2.um)
|
||||
# out = in.drc(enclosed(other) > 0.2.um)
|
||||
# out = in.drc(enclosed(other) >= 0.2.um)
|
||||
# out = in.drc(enclosed(other) == 0.2.um)
|
||||
# out = in.drc(enclosed(other) != 0.2.um)
|
||||
# out = in.drc(0.1.um <= enclosed(other) < 0.2.um)
|
||||
# @/code
|
||||
#
|
||||
# The result of the enclosed check are edges or edge pairs forming the markers.
|
||||
# These markers indicate the presence of the specified condition.
|
||||
#
|
||||
# With a lower and upper limit, the results are edges marking the positions on the
|
||||
# primary shape where the condition is met.
|
||||
# With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
|
||||
# the primary shape. Without an upper limit only, the first edge of the marker is attached to the
|
||||
# primary shape while the second edge is attached to the shape of the "other" layer.
|
||||
#
|
||||
# @table
|
||||
# @tr
|
||||
# @td @img(/images/drc_encd1u.png) @/td
|
||||
# @td @img(/images/drc_encd2u.png) @/td
|
||||
# @/tr
|
||||
# @/table
|
||||
#
|
||||
# When "larger than" constraints are used, this function will produce the edges from the
|
||||
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
|
||||
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
|
||||
# actual edges from the first input (see \separation for an example).
|
||||
#
|
||||
|
||||
# %DRC%
|
||||
# @name separation
|
||||
# @brief Performs a separation check
|
||||
|
|
@ -1367,6 +1445,7 @@ CODE
|
|||
|
||||
%w(
|
||||
enclosing
|
||||
enclosed
|
||||
isolated
|
||||
notch
|
||||
overlap
|
||||
|
|
|
|||
|
|
@ -1751,6 +1751,7 @@ CODE
|
|||
%w(
|
||||
enc
|
||||
enclosing
|
||||
enclosed
|
||||
overlap
|
||||
sep
|
||||
separation
|
||||
|
|
|
|||
|
|
@ -3547,7 +3547,7 @@ CODE
|
|||
# method will only report space violations to other polygons. \separation is a two-layer
|
||||
# space check where space is checked against polygons of another layer.
|
||||
#
|
||||
# Like for the \width method, merged semantics applies.
|
||||
# As for the other DRC methods, merged semantics applies.
|
||||
#
|
||||
# Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
# database units). To explicitly specify the unit, use the unit denominators.
|
||||
|
|
@ -3632,6 +3632,7 @@ CODE
|
|||
# will also trigger an error while for \space it will not.
|
||||
#
|
||||
# As for the other DRC methods, merged semantics applies.
|
||||
#
|
||||
# Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
# database units). To explicitly specify the unit, use the unit denominators.
|
||||
#
|
||||
|
|
@ -3739,8 +3740,8 @@ CODE
|
|||
# the orientation of the edges matters: only edges which run back to back with their
|
||||
# inside side pointing towards each other are checked for distance.
|
||||
#
|
||||
# As for the other DRC methods, merged semantics applies. The options available
|
||||
# are the same than for \width.
|
||||
# As for the other DRC methods, merged semantics applies.
|
||||
#
|
||||
# Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
# database units). To explicitly specify the unit, use the unit denominators.
|
||||
#
|
||||
|
|
@ -3755,7 +3756,7 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name enclosing
|
||||
# @brief An enclosing check
|
||||
# @brief An enclosing check (layer enclosing other_layer)
|
||||
# @synopsis layer.enclosing(other_layer, value [, options])
|
||||
# @synopsis layer.enc(other_layer, value [, options])
|
||||
#
|
||||
|
|
@ -3763,8 +3764,8 @@ CODE
|
|||
# the \DRC framework. These variants have more options and are more intuitive to use.
|
||||
# See \global#enclosing for more details.
|
||||
#
|
||||
# This method checks whether layer encloses (is bigger than) other_layer by the
|
||||
# given dimension. Locations, where this is not the case will be reported in form
|
||||
# This method checks whether layer encloses (is bigger than) other_layer by not less than the
|
||||
# given distance value. Locations, where the distance is less will be reported in form
|
||||
# of edge pair error markers.
|
||||
# Locations, where both edges coincide will be reported as errors as well. Formally
|
||||
# such locations form an enclosure with a distance of 0. Locations, where other_layer
|
||||
|
|
@ -3779,8 +3780,8 @@ CODE
|
|||
# the orientation of the edges matters and only edges looking into the same direction
|
||||
# are checked.
|
||||
#
|
||||
# As for the other DRC methods, merged semantics applies. The options available
|
||||
# are the same than for \width.
|
||||
# As for the other DRC methods, merged semantics applies.
|
||||
#
|
||||
# Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
# database units). To explicitly specify the unit, use the unit denominators.
|
||||
#
|
||||
|
|
@ -3793,13 +3794,48 @@ CODE
|
|||
# @/tr
|
||||
# @/table
|
||||
|
||||
%w(width space overlap enclosing separation isolated notch).each do |f|
|
||||
# %DRC%
|
||||
# @name enclosed
|
||||
# @brief An enclosing check (other_layer enclosing layer)
|
||||
# @synopsis layer.enclosed(other_layer, value [, options])
|
||||
#
|
||||
# @b Note: @/b "enclosed" is available as operators for the "universal DRC" function \drc within
|
||||
# the \DRC framework. These variants have more options and are more intuitive to use.
|
||||
# See \global#enclosed for more details.
|
||||
#
|
||||
# This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
|
||||
# given distance value. Locations, where the distance is less will be reported in form
|
||||
# of edge pair error markers.
|
||||
# Locations, where both edges coincide will be reported as errors as well. Formally
|
||||
# such locations form an enclosure with a distance of 0. Locations, where other_layer
|
||||
# is inside layer will not be reported as errors. Such regions can be detected
|
||||
# by \inside or a boolean "not" operation.
|
||||
#
|
||||
# The options are the same as for \separation.
|
||||
#
|
||||
# This method is available for edge and polygon layers.
|
||||
#
|
||||
# As for the other DRC methods, merged semantics applies.
|
||||
#
|
||||
# Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
# database units). To explicitly specify the unit, use the unit denominators.
|
||||
#
|
||||
# The following images show the effect of two enclosed checks (red: input1, blue: input2):
|
||||
#
|
||||
# @table
|
||||
# @tr
|
||||
# @td @img(/images/drc_encd1.png) @/td
|
||||
# @td @img(/images/drc_encd2.png) @/td
|
||||
# @/tr
|
||||
# @/table
|
||||
|
||||
%w(width space overlap enclosing enclosed separation isolated notch).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
|
||||
@engine._context("#{f}") do
|
||||
|
||||
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation
|
||||
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosed || :#{f} == :enclosing || :#{f} == :separation
|
||||
requires_edges_or_region
|
||||
else
|
||||
requires_region
|
||||
|
|
|
|||
|
|
@ -536,6 +536,85 @@ See <a href="/about/drc_ref_source.xml#edges">Source#edges</a> for a description
|
|||
<p>
|
||||
"enc" is the short form for <a href="#enclosing">enclosing</a>.
|
||||
</p>
|
||||
<a name="enclosed"/><h2>"enclosed" - Performs an enclosing check (other enclosing layer)</h2>
|
||||
<keyword name="enclosed"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>enclosed(other [, options ]) (in conditions)</tt></li>
|
||||
<li><tt>enclosed(layer, other [, options ])</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
This check verifies if the polygons of the input layer are enclosed by shapes
|
||||
of the other input layer by a certain distance.
|
||||
It has manifold options. See <a href="/about/drc_ref_layer.xml#width">Layer#width</a> for the basic options such
|
||||
as metrics, projection and angle constraints etc. This check also features
|
||||
opposite and rectangle filtering. See <a href="/about/drc_ref_layer.xml#separation">Layer#separation</a> for details about opposite and
|
||||
rectangle error filtering.
|
||||
</p><p>
|
||||
This function is essentially the reverse of <a href="#enclosing">enclosing</a>. In case of
|
||||
"enclosed", the other layer must be bigger than the primary layer.
|
||||
</p><p>
|
||||
<h3>Classic mode </h3>
|
||||
</p><p>
|
||||
This function can be used in classic mode with a layer argument. In this case it
|
||||
is equivalent to "layer.enclosed" (see <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a>).
|
||||
</p><p>
|
||||
<pre>
|
||||
# classic "enclosed" check for < 0.2 um
|
||||
in = layer(1, 0)
|
||||
other = layer(2, 0)
|
||||
errors = enclosed(in, other, 0.2.um)
|
||||
</pre>
|
||||
</p><p>
|
||||
<h3>Universal DRC </h3>
|
||||
</p><p>
|
||||
The version without a first layer is intended for use within <a href="/about/drc_ref_drc.xml">DRC</a> expressions
|
||||
together with the "universal DRC" method <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a>. In this case, this function needs to be
|
||||
put into a condition to specify the check constraints. The other options
|
||||
of <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a> (e.g. metrics, projection constraints, angle limits etc.)
|
||||
apply to this version too:
|
||||
</p><p>
|
||||
<pre>
|
||||
# universal DRC "enclosed" check for < 0.2 um
|
||||
in = layer(1, 0)
|
||||
other = layer(2, 0)
|
||||
errors = in.drc(enclosed(other) < 0.2.um)
|
||||
</pre>
|
||||
</p><p>
|
||||
The conditions may involve an upper and lower limit. The following examples
|
||||
illustrate the use of this function with conditions:
|
||||
</p><p>
|
||||
<pre>
|
||||
out = in.drc(enclosed(other) < 0.2.um)
|
||||
out = in.drc(enclosed(other) <= 0.2.um)
|
||||
out = in.drc(enclosed(other) > 0.2.um)
|
||||
out = in.drc(enclosed(other) >= 0.2.um)
|
||||
out = in.drc(enclosed(other) == 0.2.um)
|
||||
out = in.drc(enclosed(other) != 0.2.um)
|
||||
out = in.drc(0.1.um <= enclosed(other) < 0.2.um)
|
||||
</pre>
|
||||
</p><p>
|
||||
The result of the enclosed check are edges or edge pairs forming the markers.
|
||||
These markers indicate the presence of the specified condition.
|
||||
</p><p>
|
||||
With a lower and upper limit, the results are edges marking the positions on the
|
||||
primary shape where the condition is met.
|
||||
With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
|
||||
the primary shape. Without an upper limit only, the first edge of the marker is attached to the
|
||||
primary shape while the second edge is attached to the shape of the "other" layer.
|
||||
</p><p>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="/images/drc_encd1u.png"/></td>
|
||||
<td><img src="/images/drc_encd2u.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</p><p>
|
||||
When "larger than" constraints are used, this function will produce the edges from the
|
||||
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
|
||||
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
|
||||
actual edges from the first input (see <a href="#separation">separation</a> for an example).
|
||||
</p>
|
||||
<a name="enclosing"/><h2>"enclosing" - Performs an enclosing check</h2>
|
||||
<keyword name="enclosing"/>
|
||||
<p>Usage:</p>
|
||||
|
|
|
|||
|
|
@ -778,7 +778,44 @@ individual ones unless raw mode is chosen.
|
|||
<p>
|
||||
See <a href="#enclosing">enclosing</a> for a description of that method
|
||||
</p>
|
||||
<a name="enclosing"/><h2>"enclosing" - An enclosing check</h2>
|
||||
<a name="enclosed"/><h2>"enclosed" - An enclosing check (other_layer enclosing layer)</h2>
|
||||
<keyword name="enclosed"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
<li><tt>layer.enclosed(other_layer, value [, options])</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
<b>Note: </b>"enclosed" is available as operators for the "universal DRC" function <a href="#drc">drc</a> within
|
||||
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
|
||||
See <a href="/about/drc_ref_global.xml#enclosed">enclosed</a> for more details.
|
||||
</p><p>
|
||||
This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
|
||||
given distance value. Locations, where the distance is less will be reported in form
|
||||
of edge pair error markers.
|
||||
Locations, where both edges coincide will be reported as errors as well. Formally
|
||||
such locations form an enclosure with a distance of 0. Locations, where other_layer
|
||||
is inside layer will not be reported as errors. Such regions can be detected
|
||||
by <a href="#inside">inside</a> or a boolean "not" operation.
|
||||
</p><p>
|
||||
The options are the same as for <a href="#separation">separation</a>.
|
||||
</p><p>
|
||||
This method is available for edge and polygon layers.
|
||||
</p><p>
|
||||
As for the other DRC methods, merged semantics applies.
|
||||
</p><p>
|
||||
Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
database units). To explicitly specify the unit, use the unit denominators.
|
||||
</p><p>
|
||||
The following images show the effect of two enclosed checks (red: input1, blue: input2):
|
||||
</p><p>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="/images/drc_encd1.png"/></td>
|
||||
<td><img src="/images/drc_encd2.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<a name="enclosing"/><h2>"enclosing" - An enclosing check (layer enclosing other_layer)</h2>
|
||||
<keyword name="enclosing"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
|
|
@ -790,8 +827,8 @@ See <a href="#enclosing">enclosing</a> for a description of that method
|
|||
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
|
||||
See <a href="/about/drc_ref_global.xml#enclosing">enclosing</a> for more details.
|
||||
</p><p>
|
||||
This method checks whether layer encloses (is bigger than) other_layer by the
|
||||
given dimension. Locations, where this is not the case will be reported in form
|
||||
This method checks whether layer encloses (is bigger than) other_layer by not less than the
|
||||
given distance value. Locations, where the distance is less will be reported in form
|
||||
of edge pair error markers.
|
||||
Locations, where both edges coincide will be reported as errors as well. Formally
|
||||
such locations form an enclosure with a distance of 0. Locations, where other_layer
|
||||
|
|
@ -806,8 +843,8 @@ The enclosing method can be applied to both edge or polygon layers. On edge laye
|
|||
the orientation of the edges matters and only edges looking into the same direction
|
||||
are checked.
|
||||
</p><p>
|
||||
As for the other DRC methods, merged semantics applies. The options available
|
||||
are the same than for <a href="#width">width</a>.
|
||||
As for the other DRC methods, merged semantics applies.
|
||||
</p><p>
|
||||
Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
database units). To explicitly specify the unit, use the unit denominators.
|
||||
</p><p>
|
||||
|
|
@ -2046,8 +2083,8 @@ The overlap method can be applied to both edge or polygon layers. On edge layers
|
|||
the orientation of the edges matters: only edges which run back to back with their
|
||||
inside side pointing towards each other are checked for distance.
|
||||
</p><p>
|
||||
As for the other DRC methods, merged semantics applies. The options available
|
||||
are the same than for <a href="#width">width</a>.
|
||||
As for the other DRC methods, merged semantics applies.
|
||||
</p><p>
|
||||
Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
database units). To explicitly specify the unit, use the unit denominators.
|
||||
</p><p>
|
||||
|
|
@ -2569,6 +2606,7 @@ layers touch are also reported. More specifically, the case of zero spacing
|
|||
will also trigger an error while for <a href="#space">space</a> it will not.
|
||||
</p><p>
|
||||
As for the other DRC methods, merged semantics applies.
|
||||
</p><p>
|
||||
Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
database units). To explicitly specify the unit, use the unit denominators.
|
||||
</p><p>
|
||||
|
|
@ -2789,7 +2827,7 @@ The <a href="#notch">notch</a> method is similar, but will only report self-spac
|
|||
method will only report space violations to other polygons. <a href="#separation">separation</a> is a two-layer
|
||||
space check where space is checked against polygons of another layer.
|
||||
</p><p>
|
||||
Like for the <a href="#width">width</a> method, merged semantics applies.
|
||||
As for the other DRC methods, merged semantics applies.
|
||||
</p><p>
|
||||
Distance values can be given as floating-point values (in micron) or integer values (in
|
||||
database units). To explicitly specify the unit, use the unit denominators.
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
|
|
@ -36,6 +36,10 @@
|
|||
<file alias="drc_enc2.png">doc/images/drc_enc2.png</file>
|
||||
<file alias="drc_enc1u.png">doc/images/drc_enc1u.png</file>
|
||||
<file alias="drc_enc2u.png">doc/images/drc_enc2u.png</file>
|
||||
<file alias="drc_encd1.png">doc/images/drc_encd1.png</file>
|
||||
<file alias="drc_encd2.png">doc/images/drc_encd2.png</file>
|
||||
<file alias="drc_encd1u.png">doc/images/drc_encd1u.png</file>
|
||||
<file alias="drc_encd2u.png">doc/images/drc_encd2u.png</file>
|
||||
<file alias="drc_overlap1.png">doc/images/drc_overlap1.png</file>
|
||||
<file alias="drc_overlap2.png">doc/images/drc_overlap2.png</file>
|
||||
<file alias="drc_overlap1u.png">doc/images/drc_overlap1u.png</file>
|
||||
|
|
|
|||
|
|
@ -246,7 +246,8 @@ run_tests (const std::vector<tl::TestBase *> &selected_tests, bool editable, boo
|
|||
timer.stop();
|
||||
|
||||
ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)";
|
||||
ut::ctrl << "<x-testcase-times wall=\"" << timer.sec_wall () << "\" user=\"" << timer.sec_user () << "\" sys=\"" << timer.sec_sys () << "\"/>";
|
||||
ut::noctrl << "Memory: " << timer.memory_size () / 1024 << "k";
|
||||
ut::ctrl << "<x-testcase-times wall=\"" << timer.sec_wall () << "\" user=\"" << timer.sec_user () << "\" sys=\"" << timer.sec_sys () << "\" memory=\"" << timer.memory_size () << "\"/>";
|
||||
|
||||
} catch (tl::CancelException &) {
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -43,4 +43,5 @@ l1.drc(isolated(projection, not_opposite) < 1.0).output(144, 0)
|
|||
# enclosing, overlap
|
||||
l2.drc(enclosing(l1, projection) < 1.0).polygons.output(150, 0)
|
||||
l1.drc(overlap(l2, projection) < 2.0).polygons.output(151, 0)
|
||||
l1.drc(enclosed(l2, projection) < 1.0).polygons.output(152, 0)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -33,9 +33,17 @@ l2.separation(l1, projection, 1.0, whole_edges).output(211, 0)
|
|||
l1.drc(enclosing(l2, projection) < 1.0).output(400, 0)
|
||||
l1.drc(enclosing(l2, whole_edges, projection) < 1.0).output(401, 0)
|
||||
l1.drc(enclosing(l2, projection) >= 1.0).output(402, 0)
|
||||
|
||||
l2.drc(enclosed(l1, projection) < 1.0).output(403, 0)
|
||||
l2.drc(enclosed(l1, whole_edges, projection) < 1.0).output(404, 0)
|
||||
l2.drc(enclosed(l1, projection) >= 1.0).output(405, 0)
|
||||
|
||||
l1.enclosing(l2, projection, 1.0).output(410, 0)
|
||||
l1.enclosing(l2, projection, 1.0, whole_edges).output(411, 0)
|
||||
|
||||
l2.enclosed(l1, projection, 1.0).output(412, 0)
|
||||
l2.enclosed(l1, projection, 1.0, whole_edges).output(413, 0)
|
||||
|
||||
l1.drc(overlap(l2, projection) < 1.0).output(500, 0)
|
||||
l1.drc(overlap(l2, whole_edges, projection) < 1.0).output(501, 0)
|
||||
l1.drc(overlap(l2, projection) >= 1.0).output(502, 0)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -134,6 +134,10 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
|
|||
a.enclosing(b, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 1, dm)
|
||||
a.enclosing(b, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 2, dm)
|
||||
|
||||
b.enclosed(a, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 3, dm)
|
||||
b.enclosed(a, 0.5, whole_edges).first_edges.extended_out(0.01).output(lb + 4, dm)
|
||||
b.enclosed(a, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 5, dm)
|
||||
|
||||
lb += 10 #150
|
||||
message "--- hulls, holes #{lb}"
|
||||
|
||||
|
|
@ -323,6 +327,8 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
|
|||
else
|
||||
a.enclosing(b, 0.5, euclidian, projection_limits(0, 0.4)).polygons.output(lb + 7, dm)
|
||||
end
|
||||
|
||||
b.enclosed(a, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 8, dm)
|
||||
|
||||
lb += 10 #220
|
||||
message "--- enclosing (edges) #{lb}"
|
||||
|
|
@ -343,6 +349,8 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
|
|||
ae.enclosing(be, 0.5, euclidian, projection_limits(0, 0.4)).polygons.output(lb + 7, dm)
|
||||
end
|
||||
|
||||
be.enclosed(ae, 0.5, euclidian, whole_edges).polygons.output(lb + 8, dm)
|
||||
|
||||
lb += 10 #230
|
||||
message "--- isolated #{lb}"
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -398,6 +398,8 @@ class DBEdges_TestClass < TestBase
|
|||
r3b = RBA::Edges::new(RBA::Box::new(-10, -10, 10, 10))
|
||||
|
||||
assert_equal(r2.inside_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
|
||||
# "enclosed" alias
|
||||
assert_equal(r2.enclosed_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
|
||||
assert_equal(r2.inside_check(r3, 15, false, RBA::Edges::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,20;110,380)")
|
||||
assert_equal(r2.inside_check(r3, 15, true, RBA::Edges::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,0;110,400)")
|
||||
assert_equal(r2.inside_check(r3, 15, true, RBA::Edges::Projection, 0.0, nil, nil).to_s, "")
|
||||
|
|
|
|||
|
|
@ -454,6 +454,8 @@ class DBRegion_TestClass < TestBase
|
|||
r3b = RBA::Region::new(RBA::Box::new(-10, -10, 10, 10))
|
||||
|
||||
assert_equal(r2.inside_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
|
||||
# "enclosed" alias
|
||||
assert_equal(r2.enclosed_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
|
||||
assert_equal(r2.inside_check(r3, 15, false, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,20;110,380)")
|
||||
assert_equal(r2.inside_check(r3, 15, true, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,0;110,400)")
|
||||
assert_equal(r2.inside_check(r3, 15, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "")
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class DBTrans_TestClass < TestBase
|
|||
assert_equal( c.is_unity?, false )
|
||||
assert_equal( c.is_ortho?, true )
|
||||
assert_equal( c.is_mag?, false )
|
||||
assert_equal( c.is_complex?, false )
|
||||
assert_equal( c.is_mirror?, true )
|
||||
assert_equal( c.rot, RBA::DCplxTrans::M135.rot )
|
||||
assert_equal( c.s_trans.to_s, "m135 0,0" )
|
||||
|
|
@ -179,6 +180,7 @@ class DBTrans_TestClass < TestBase
|
|||
assert_equal( c.is_unity?, false )
|
||||
assert_equal( c.is_ortho?, true )
|
||||
assert_equal( c.is_mag?, true )
|
||||
assert_equal( c.is_complex?, true )
|
||||
assert_equal( c.is_mirror?, false )
|
||||
assert_equal( c.rot, RBA::DCplxTrans::R0.rot )
|
||||
assert_equal( c.s_trans.to_s, "r0 0,0" )
|
||||
|
|
@ -190,6 +192,7 @@ class DBTrans_TestClass < TestBase
|
|||
assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" )
|
||||
assert_equal( c.is_unity?, false )
|
||||
assert_equal( c.is_ortho?, false )
|
||||
assert_equal( c.is_complex?, true )
|
||||
assert_equal( c.is_mag?, true )
|
||||
assert_equal( c.rot, RBA::DCplxTrans::M0.rot )
|
||||
assert_equal( c.s_trans.to_s, "m0 2.5,-12.5" )
|
||||
|
|
|
|||
Loading…
Reference in New Issue