mirror of https://github.com/KLayout/klayout.git
Merge pull request #2266 from KLayout/bugfix/issue-2262
Bugfix/issue 2262
This commit is contained in:
commit
3f9d8906f9
|
|
@ -29,11 +29,13 @@
|
|||
#include "dbSaveLayoutOptions.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbCellGraphUtils.h"
|
||||
#include "gsiExpression.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlThreadedWorkers.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlOptional.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -271,17 +273,19 @@ HealingTileLayoutOutputReceiver::output (const db::Box &box)
|
|||
struct ResultDescriptor
|
||||
{
|
||||
ResultDescriptor ()
|
||||
: shape_count (0), layer_a (-1), layer_b (-1), layer_output (-1), layout (0), top_cell (0)
|
||||
: shape_count (0), flat_shape_count (0), layer_a (-1), layer_b (-1), layer_output (-1), layout (0), top_cell (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
size_t shape_count;
|
||||
size_t flat_shape_count;
|
||||
int layer_a;
|
||||
int layer_b;
|
||||
int layer_output;
|
||||
db::Layout *layout;
|
||||
db::cell_index_type top_cell;
|
||||
tl::optional<db::Region> results;
|
||||
|
||||
size_t count () const
|
||||
{
|
||||
|
|
@ -296,6 +300,20 @@ struct ResultDescriptor
|
|||
}
|
||||
}
|
||||
|
||||
size_t flat_count () const
|
||||
{
|
||||
if (layout && layer_output >= 0) {
|
||||
size_t res = 0;
|
||||
db::CellCounter counter (layout, top_cell);
|
||||
for (db::Layout::const_iterator c = layout->begin (); c != layout->end (); ++c) {
|
||||
res += c->shapes (layer_output).size () * counter.weight (c->cell_index ());
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return flat_shape_count;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_empty () const
|
||||
{
|
||||
if (layout && layer_output >= 0) {
|
||||
|
|
@ -586,12 +604,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
|
||||
const char *line_format = " %-10s %-12s %s";
|
||||
|
||||
std::string headline;
|
||||
if (deep) {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)")));
|
||||
} else {
|
||||
headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)")));
|
||||
}
|
||||
std::string headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")),
|
||||
deep ? tl::to_string (tr ("Differences (hierarchical/flat count)")) : tl::to_string (tr ("Differences (shape count)")));
|
||||
|
||||
const char *sep = " ----------------------------------------------------------------";
|
||||
|
||||
|
|
@ -619,7 +633,11 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
if (r->second.layer_output >= 0 && r->second.layout) {
|
||||
out = r->second.layout->get_properties (r->second.layer_output).to_string ();
|
||||
}
|
||||
value = tl::to_string (r->second.count ());
|
||||
if (deep) {
|
||||
value = tl::sprintf (tl::to_string (tr ("%-6lu / %-6lu")), r->second.count (), r->second.flat_count ());
|
||||
} else {
|
||||
value = tl::to_string (r->second.count ());
|
||||
}
|
||||
}
|
||||
if (! value.empty ()) {
|
||||
tl::info << tl::sprintf (line_format, r->first.second.to_string (), out, value);
|
||||
|
|
@ -767,8 +785,15 @@ bool run_tiled_xor (const XORData &xor_data)
|
|||
proc.execute ("Running XOR");
|
||||
}
|
||||
|
||||
// no stored results currently
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end (); ++r) {
|
||||
tl_assert (! r->second.results.has_value ());
|
||||
}
|
||||
|
||||
// Determines the output status
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) {
|
||||
// no stored results currently
|
||||
tl_assert (! r->second.results.has_value ());
|
||||
result = r->second.is_empty ();
|
||||
}
|
||||
|
||||
|
|
@ -846,29 +871,40 @@ public:
|
|||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ());
|
||||
|
||||
db::RecursiveShapeIterator ri_a, ri_b;
|
||||
|
||||
if (m_la >= 0) {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
|
||||
} else {
|
||||
ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_a.set_for_merged_input (true);
|
||||
|
||||
if (m_lb >= 0) {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
|
||||
} else {
|
||||
ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector<unsigned int> ());
|
||||
}
|
||||
ri_b.set_for_merged_input (true);
|
||||
|
||||
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
|
||||
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
|
||||
|
||||
db::Region xor_res;
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
|
||||
if (m_la < 0) {
|
||||
|
||||
tl_assert (m_lb >= 0);
|
||||
|
||||
db::RecursiveShapeIterator ri_b (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
|
||||
xor_res = db::Region (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
|
||||
|
||||
} else if (m_lb < 0) {
|
||||
|
||||
db::RecursiveShapeIterator ri_a (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
|
||||
xor_res = db::Region (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
|
||||
|
||||
} else {
|
||||
|
||||
db::RecursiveShapeIterator ri_a (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la);
|
||||
db::RecursiveShapeIterator ri_b (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb);
|
||||
|
||||
db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu));
|
||||
db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu));
|
||||
|
||||
bool a_empty = in_a.empty ();
|
||||
bool b_empty = in_b.empty ();
|
||||
|
||||
if (a_empty && ! b_empty) {
|
||||
xor_res = in_b;
|
||||
} else if (! a_empty && b_empty) {
|
||||
xor_res = in_a;
|
||||
} else if (! a_empty && ! b_empty) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ());
|
||||
xor_res = in_a ^ in_b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int tol_index = 0;
|
||||
|
|
@ -896,9 +932,12 @@ public:
|
|||
|
||||
if (mp_xor_data->output_layout) {
|
||||
result.layer_output = result.layout->insert_layer (lp);
|
||||
xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output);
|
||||
if (! xor_res.empty ()) {
|
||||
result.results = xor_res;
|
||||
}
|
||||
} else {
|
||||
result.shape_count = xor_res.hier_count ();
|
||||
result.flat_shape_count = xor_res.count ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -964,6 +1003,22 @@ bool run_deep_xor (const XORData &xor_data)
|
|||
job.start ();
|
||||
job.wait ();
|
||||
|
||||
// Deliver the outputs
|
||||
// NOTE: this is done single-threaded and in a delayed fashion as it is not efficient during
|
||||
// computation and shifting hierarchy of the working layout
|
||||
|
||||
if (xor_data.output_layout) {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, "Result delivery");
|
||||
|
||||
for (std::map<std::pair<int, db::LayerProperties>, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end (); ++r) {
|
||||
if (r->second.results.has_value ()) {
|
||||
r->second.results.value ().insert_into (xor_data.output_layout, xor_data.output_cell, r->second.layer_output);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Determine the output status
|
||||
|
||||
bool result = (xor_data.layers_missing == 0);
|
||||
|
|
|
|||
|
|
@ -146,11 +146,11 @@ TEST(1A_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" Layer Output Differences (hierarchical/flat count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
" 3/0 3/0 3 / 30 \n"
|
||||
" 6/0 6/0 314 / 314 \n"
|
||||
" 8/1 8/1 1 / 1 \n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
"\n"
|
||||
);
|
||||
|
|
@ -188,11 +188,11 @@ TEST(1A_DeepNoEmptyCells)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" Layer Output Differences (hierarchical/flat count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
" 3/0 3/0 3 / 30 \n"
|
||||
" 6/0 6/0 314 / 314 \n"
|
||||
" 8/1 8/1 1 / 1 \n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
"\n"
|
||||
);
|
||||
|
|
@ -248,11 +248,11 @@ TEST(1B_Deep)
|
|||
"Layer 10/0 is not present in first layout, but in second\n"
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" Layer Output Differences (hierarchical/flat count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 - 3\n"
|
||||
" 6/0 - 314\n"
|
||||
" 8/1 - 1\n"
|
||||
" 3/0 - 3 / 30 \n"
|
||||
" 6/0 - 314 / 314 \n"
|
||||
" 8/1 - 1 / 1 \n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
"\n"
|
||||
);
|
||||
|
|
@ -830,10 +830,10 @@ TEST(7_OptimizeDeep)
|
|||
EXPECT_EQ (cap.captured_text (),
|
||||
"Result summary (layers without differences are not shown):\n"
|
||||
"\n"
|
||||
" Layer Output Differences (hierarchical shape count)\n"
|
||||
" Layer Output Differences (hierarchical/flat count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 2/0 2/0 1\n"
|
||||
" 3/0 3/0 8\n"
|
||||
" 2/0 2/0 1 / 12 \n"
|
||||
" 3/0 3/0 8 / 8 \n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ SOURCES = \
|
|||
dbCellGraphUtils.cc \
|
||||
dbCellHullGenerator.cc \
|
||||
dbCellInst.cc \
|
||||
dbCellInstanceSetHasher.cc \
|
||||
dbCellMapping.cc \
|
||||
dbClipboard.cc \
|
||||
dbClipboardData.cc \
|
||||
|
|
@ -253,6 +254,7 @@ HEADERS = \
|
|||
dbCell.h \
|
||||
dbCellHullGenerator.h \
|
||||
dbCellInst.h \
|
||||
dbCellInstanceSetHasher.h \
|
||||
dbCellMapping.h \
|
||||
dbClipboardData.h \
|
||||
dbClipboard.h \
|
||||
|
|
|
|||
|
|
@ -981,9 +981,10 @@ public:
|
|||
* Only after sorting the query iterators are available.
|
||||
* Sorting complexity is approx O(N*log(N)).
|
||||
*/
|
||||
void sort (const BoxConv &conv)
|
||||
template <class BC>
|
||||
void sort (const BC &conv)
|
||||
{
|
||||
typename BoxConv::complexity complexity_tag;
|
||||
typename BC::complexity complexity_tag;
|
||||
sort (conv, complexity_tag);
|
||||
}
|
||||
|
||||
|
|
@ -1192,7 +1193,8 @@ private:
|
|||
box_tree_node *mp_root;
|
||||
|
||||
/// Sort implementation for simple bboxes - no caching
|
||||
void sort (const BoxConv &conv, const db::simple_bbox_tag &/*complexity*/)
|
||||
template <class BC>
|
||||
void sort (const BC &conv, const db::simple_bbox_tag &/*complexity*/)
|
||||
{
|
||||
m_elements.clear ();
|
||||
m_elements.reserve (m_objects.size ());
|
||||
|
|
@ -1204,7 +1206,7 @@ private:
|
|||
|
||||
if (! m_objects.empty ()) {
|
||||
|
||||
box_tree_picker_type picker (conv);
|
||||
box_tree_picker<Box, Obj, BC, obj_vector_type> picker (conv);
|
||||
|
||||
box_type bbox;
|
||||
for (typename obj_vector_type::const_iterator o = m_objects.begin (); o != m_objects.end (); ++o) {
|
||||
|
|
@ -1221,7 +1223,8 @@ private:
|
|||
}
|
||||
|
||||
/// Sort implementation for complex bboxes - with caching
|
||||
void sort (const box_conv_type &conv, const db::complex_bbox_tag &/*complexity*/)
|
||||
template <class BC>
|
||||
void sort (const BC &conv, const db::complex_bbox_tag &/*complexity*/)
|
||||
{
|
||||
m_elements.clear ();
|
||||
m_elements.reserve (m_objects.size ());
|
||||
|
|
@ -1233,7 +1236,7 @@ private:
|
|||
|
||||
if (! m_objects.empty ()) {
|
||||
|
||||
box_tree_cached_picker<object_type, box_type, box_conv_type, obj_vector_type> picker (conv, m_objects.begin (), m_objects.end ());
|
||||
box_tree_cached_picker<object_type, box_type, BC, obj_vector_type> picker (conv, m_objects.begin (), m_objects.end ());
|
||||
|
||||
for (typename obj_vector_type::const_iterator o = m_objects.begin (); o != m_objects.end (); ++o) {
|
||||
m_elements.push_back (o.index ());
|
||||
|
|
@ -1997,9 +2000,10 @@ public:
|
|||
* Only after sorting the query iterators are available.
|
||||
* Sorting complexity is approx O(N*log(N)).
|
||||
*/
|
||||
void sort (const BoxConv &conv)
|
||||
template <class BC>
|
||||
void sort (const BC &conv)
|
||||
{
|
||||
typename BoxConv::complexity complexity_tag;
|
||||
typename BC::complexity complexity_tag;
|
||||
sort (conv, complexity_tag);
|
||||
}
|
||||
|
||||
|
|
@ -2159,13 +2163,14 @@ private:
|
|||
box_tree_node *mp_root;
|
||||
|
||||
/// Sort implementation for simple bboxes - no caching
|
||||
void sort (const BoxConv &conv, const db::simple_bbox_tag &/*complexity*/)
|
||||
template <class BC>
|
||||
void sort (const BC &conv, const db::simple_bbox_tag &/*complexity*/)
|
||||
{
|
||||
if (m_objects.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
box_tree_picker_type picker (conv);
|
||||
box_tree_picker<box_type, object_type, BC, obj_vector_type> picker (conv);
|
||||
|
||||
if (mp_root) {
|
||||
delete mp_root;
|
||||
|
|
@ -2184,13 +2189,14 @@ private:
|
|||
}
|
||||
|
||||
/// Sort implementation for complex bboxes - with caching
|
||||
void sort (const box_conv_type &conv, const db::complex_bbox_tag &/*complexity*/)
|
||||
template <class BC>
|
||||
void sort (const BC &conv, const db::complex_bbox_tag &/*complexity*/)
|
||||
{
|
||||
if (m_objects.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
box_tree_cached_picker<object_type, box_type, box_conv_type, obj_vector_type> picker (conv, m_objects.begin (), m_objects.end ());
|
||||
box_tree_cached_picker<object_type, box_type, BC, obj_vector_type> picker (conv, m_objects.begin (), m_objects.end ());
|
||||
|
||||
if (mp_root) {
|
||||
delete mp_root;
|
||||
|
|
|
|||
|
|
@ -274,11 +274,67 @@ Cell::is_shape_bbox_dirty () const
|
|||
return false;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief An alternative box converter for CellInst which does not call layout.update
|
||||
*
|
||||
* Using this converter in cell.update_bbox() prevents recursive calls to layout.update.
|
||||
*/
|
||||
class InternalCellInstBoxConverter
|
||||
{
|
||||
public:
|
||||
typedef db::Cell::box_type box_type;
|
||||
typedef db::simple_bbox_tag complexity;
|
||||
|
||||
InternalCellInstBoxConverter (const db::Layout *layout)
|
||||
: mp_layout (layout)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
box_type operator() (const db::CellInst &cell_inst) const
|
||||
{
|
||||
return mp_layout->cell (cell_inst.cell_index ()).bbox_with_empty_no_update ();
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout *mp_layout;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An alternative box converter for CellInst and layer which does not call layout.update
|
||||
*
|
||||
* Using this converter in cell.update_bbox() prevents recursive calls to layout.update.
|
||||
*/
|
||||
class InternalPerLayerCellInstBoxConverter
|
||||
{
|
||||
public:
|
||||
typedef db::Cell::box_type box_type;
|
||||
typedef db::simple_bbox_tag complexity;
|
||||
|
||||
InternalPerLayerCellInstBoxConverter (const db::Layout *layout, unsigned int layer)
|
||||
: mp_layout (layout), m_layer (layer)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
box_type operator() (const db::CellInst &cell_inst) const
|
||||
{
|
||||
return mp_layout->cell (cell_inst.cell_index ()).bbox_no_update (m_layer);
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout *mp_layout;
|
||||
unsigned int m_layer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::update_bbox (unsigned int layers)
|
||||
{
|
||||
unsigned int l;
|
||||
|
||||
// determine the bounding box
|
||||
box_type org_bbox = m_bbox;
|
||||
m_bbox = box_type ();
|
||||
|
|
@ -308,10 +364,10 @@ Cell::update_bbox (unsigned int layers)
|
|||
++o;
|
||||
}
|
||||
|
||||
for (l = 0; l < layers; ++l) {
|
||||
for (unsigned int l = 0; l < layers; ++l) {
|
||||
|
||||
// the per-layer bounding boxes
|
||||
db::box_convert <cell_inst_type> bc (*mp_layout, l);
|
||||
InternalPerLayerCellInstBoxConverter bc (mp_layout, l);
|
||||
box_type lbox = o1_inst->bbox_from_raw_bbox (raw_box, bc);
|
||||
|
||||
if (! lbox.empty ()) {
|
||||
|
|
@ -326,7 +382,7 @@ Cell::update_bbox (unsigned int layers)
|
|||
|
||||
}
|
||||
|
||||
db::box_convert <cell_inst_type, false> bc_we (*mp_layout);
|
||||
InternalCellInstBoxConverter bc_we (mp_layout);
|
||||
m_bbox_with_empty += o1_inst->bbox_from_raw_bbox (raw_box, bc_we);
|
||||
|
||||
}
|
||||
|
|
@ -486,6 +542,17 @@ Cell::bbox (unsigned int l) const
|
|||
}
|
||||
}
|
||||
|
||||
const Cell::box_type &
|
||||
Cell::bbox_no_update (unsigned int l) const
|
||||
{
|
||||
box_map::const_iterator b = m_bboxes.find (l);
|
||||
if (b != m_bboxes.end ()) {
|
||||
return b->second;
|
||||
} else {
|
||||
return ms_empty_box;
|
||||
}
|
||||
}
|
||||
|
||||
Cell::const_iterator
|
||||
Cell::begin () const
|
||||
{
|
||||
|
|
@ -725,7 +792,7 @@ Cell::count_hier_levels () const
|
|||
{
|
||||
unsigned int l = 0;
|
||||
|
||||
for (const_iterator c = begin (); !c.at_end (); ++c) {
|
||||
for (const_iterator c = m_instances.begin (); !c.at_end (); ++c) {
|
||||
l = std::max (l, (unsigned int) mp_layout->cell (c->cell_index ()).m_hier_levels + 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -522,21 +522,6 @@ public:
|
|||
*/
|
||||
bool is_shape_bbox_dirty () const;
|
||||
|
||||
/**
|
||||
* @brief Updates the bbox
|
||||
*
|
||||
* This will update the bbox from the shapes and instances.
|
||||
* This requires the bboxes of the child cells to be computed
|
||||
* before. Practically this will be done by computing the
|
||||
* bboxes bottom-up in the hierarchy.
|
||||
* In addition, the number of hierarchy levels below is also
|
||||
* updated.
|
||||
*
|
||||
* @param layers The max. number of layers in the child cells
|
||||
* @return true, if the bounding box has changed.
|
||||
*/
|
||||
bool update_bbox (unsigned int layers);
|
||||
|
||||
/**
|
||||
* @brief Sorts the shapes lists
|
||||
*
|
||||
|
|
@ -1110,6 +1095,23 @@ public:
|
|||
*/
|
||||
void move_shapes (db::Cell &source_cell, const db::LayerMapping &layer_mapping);
|
||||
|
||||
/**
|
||||
* @brief Gets the current bounding box (with empty) without calling Layout::update
|
||||
*
|
||||
* This method is intended for internal purposes only.
|
||||
*/
|
||||
const box_type &bbox_with_empty_no_update () const
|
||||
{
|
||||
return m_bbox_with_empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the current per-layer bounding box without calling Layout::update
|
||||
*
|
||||
* This method is intended for internal purposes only.
|
||||
*/
|
||||
const box_type &bbox_no_update (unsigned int l) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Standard constructor: create an empty cell object
|
||||
|
|
@ -1135,6 +1137,7 @@ protected:
|
|||
virtual Cell *clone (db::Layout &layout) const;
|
||||
|
||||
private:
|
||||
friend class db::Layout;
|
||||
cell_index_type m_cell_index;
|
||||
mutable db::Layout *mp_layout;
|
||||
shapes_map m_shapes_map;
|
||||
|
|
@ -1170,9 +1173,9 @@ private:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Return a reference to the instances object
|
||||
* @brief Return a reference to the instances object
|
||||
*/
|
||||
instances_type &instances ()
|
||||
const instances_type &instances () const
|
||||
{
|
||||
return m_instances;
|
||||
}
|
||||
|
|
@ -1180,7 +1183,7 @@ private:
|
|||
/**
|
||||
* @brief Return a reference to the instances object
|
||||
*/
|
||||
const instances_type &instances () const
|
||||
instances_type &instances ()
|
||||
{
|
||||
return m_instances;
|
||||
}
|
||||
|
|
@ -1219,6 +1222,21 @@ private:
|
|||
* @param force Force sorting, even if not strictly needed
|
||||
*/
|
||||
void sort_inst_tree (bool force);
|
||||
|
||||
/**
|
||||
* @brief Updates the bbox
|
||||
*
|
||||
* This will update the bbox from the shapes and instances.
|
||||
* This requires the bboxes of the child cells to be computed
|
||||
* before. Practically this will be done by computing the
|
||||
* bboxes bottom-up in the hierarchy.
|
||||
* In addition, the number of hierarchy levels below is also
|
||||
* updated.
|
||||
*
|
||||
* @param layers The max. number of layers in the child cells
|
||||
* @return true, if the bounding box has changed.
|
||||
*/
|
||||
bool update_bbox (unsigned int layers);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCellInstanceSetHasher.h"
|
||||
#include "dbHash.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
CellInstanceSetHasher::MatrixHash::MatrixHash (double s)
|
||||
: db::IMatrix3d (s, 0, 0, 0, s, 0, 0, 0, s)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
CellInstanceSetHasher::MatrixHash::MatrixHash (const db::ICplxTrans &trans)
|
||||
: db::IMatrix3d (trans)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
CellInstanceSetHasher::MatrixHash::MatrixHash (const db::CellInstArray &array)
|
||||
: db::IMatrix3d (array.complex_trans ())
|
||||
{
|
||||
db::Vector a, b;
|
||||
unsigned long na = 0, nb = 0;
|
||||
|
||||
if (array.is_regular_array (a, b, na, nb)) {
|
||||
|
||||
na = std::max ((unsigned long) 1, na);
|
||||
nb = std::max ((unsigned long) 1, nb);
|
||||
|
||||
// compute the sum of all individual matrices
|
||||
*this *= double (na * nb);
|
||||
|
||||
db::DVector dab = db::DVector (a) * double ((nb * (na - 1) * na) / 2) + db::DVector (b) * double ((na * (nb - 1) * nb) / 2);
|
||||
m() [0][2] += dab.x ();
|
||||
m() [1][2] += dab.y ();
|
||||
|
||||
} else if (array.is_iterated_array ()) {
|
||||
|
||||
db::DVector dab;
|
||||
double n = 0.0;
|
||||
|
||||
tl_assert (! array.begin ().at_end ());
|
||||
db::DVector d0 = db::DVector ((*array.begin ()).disp ());
|
||||
for (auto i = array.begin (); ! i.at_end (); ++i) {
|
||||
n += 1.0;
|
||||
dab += db::DVector ((*i).disp ()) - d0;
|
||||
}
|
||||
|
||||
*this *= n;
|
||||
|
||||
m() [0][2] += dab.x ();
|
||||
m() [1][2] += dab.y ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t d2h (double d)
|
||||
{
|
||||
return d < 0 ? size_t (d - 0.5) : size_t (d + 0.5);
|
||||
}
|
||||
|
||||
size_t
|
||||
CellInstanceSetHasher::MatrixHash::hash_value () const
|
||||
{
|
||||
// The "close-to-unity" elements are scaled with this value, so
|
||||
// after rounding to int for the hash value we are able to
|
||||
// resolve a certain level of details. This applies to the
|
||||
// rotation/shear/scale submatrix elements (m11, m12, m21, m22).
|
||||
const double res = 1024.0;
|
||||
|
||||
size_t h = d2h (m ()[0][0] * res);
|
||||
h = tl::hcombine (d2h (m ()[0][1] * res), h);
|
||||
h = tl::hcombine (d2h (m ()[0][2]), h);
|
||||
h = tl::hcombine (d2h (m ()[1][0] * res), h);
|
||||
h = tl::hcombine (d2h (m ()[1][1] * res), h);
|
||||
h = tl::hcombine (d2h (m ()[1][2]), h);
|
||||
// m31 and m32 are always zero, so we don't count them here
|
||||
h = tl::hcombine (d2h (m ()[2][2]), h);
|
||||
return h;
|
||||
}
|
||||
|
||||
CellInstanceSetHasher::CellInstanceSetHasher (const db::Layout *layout, db::cell_index_type top_cell, const std::set<db::cell_index_type> *selection)
|
||||
: mp_layout (layout), m_top_cell (top_cell), mp_selection (selection)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
size_t
|
||||
CellInstanceSetHasher::instance_set_hash (db::cell_index_type for_cell)
|
||||
{
|
||||
return get_hash (for_cell).hash_value ();
|
||||
}
|
||||
|
||||
CellInstanceSetHasher::MatrixHash
|
||||
CellInstanceSetHasher::get_hash (cell_index_type for_cell)
|
||||
{
|
||||
auto c = m_cache.find (for_cell);
|
||||
if (c != m_cache.end ()) {
|
||||
return c->second;
|
||||
} else {
|
||||
MatrixHash hm = get_hash_uncached (for_cell);
|
||||
m_cache [for_cell] = hm;
|
||||
return hm;
|
||||
}
|
||||
}
|
||||
|
||||
CellInstanceSetHasher::MatrixHash
|
||||
CellInstanceSetHasher::get_hash_uncached (cell_index_type for_cell)
|
||||
{
|
||||
if (for_cell == m_top_cell) {
|
||||
|
||||
return MatrixHash ();
|
||||
|
||||
} else {
|
||||
|
||||
const db::Cell &fc = mp_layout->cell (for_cell);
|
||||
|
||||
MatrixHash hm (0.0);
|
||||
for (auto pi = fc.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
auto pci = pi->parent_cell_index ();
|
||||
if (! mp_selection || mp_selection->find (pci) != mp_selection->end ()) {
|
||||
hm += get_hash (pci) * MatrixHash (pi->child_inst ().cell_inst ());
|
||||
}
|
||||
}
|
||||
|
||||
return hm;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCellInstanceSetHasher
|
||||
#define HDR_dbCellInstanceSetHasher
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbMatrix.h"
|
||||
#include "dbLayout.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A hasher for a set of cell instances
|
||||
*
|
||||
* The hasher starts with a layout, a top cell and optionally a
|
||||
* set of cells selected. Only selected cells will be considered
|
||||
* in the cell tree (the "cone").
|
||||
*
|
||||
* The hasher allows to compute a hash value for a given cell,
|
||||
* representative for the flat set of instances of that cell in
|
||||
* the top cell.
|
||||
*/
|
||||
class DB_PUBLIC CellInstanceSetHasher
|
||||
{
|
||||
public:
|
||||
class DB_PUBLIC MatrixHash
|
||||
: public IMatrix3d
|
||||
{
|
||||
public:
|
||||
MatrixHash (double s = 1.0);
|
||||
MatrixHash (const db::ICplxTrans &trans);
|
||||
MatrixHash (const db::CellInstArray &array);
|
||||
|
||||
size_t hash_value () const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a new cell instance set hasher
|
||||
*
|
||||
* @param layout The layout the hasher refers to
|
||||
* @param top_cell The top cell the hasher starts with
|
||||
* @param selection A set of selected cells or a null pointer if all cells should be considered
|
||||
*
|
||||
* The hasher will not take ownership over the layout, nor
|
||||
* the selected cell set.
|
||||
*/
|
||||
CellInstanceSetHasher (const db::Layout *layout, db::cell_index_type top_cell, const std::set<db::cell_index_type> *selection = 0);
|
||||
|
||||
/**
|
||||
* @brief Computes the hash value representative for the flat instance set of the given cell in the top cell and the selection
|
||||
*/
|
||||
size_t instance_set_hash (db::cell_index_type for_cell);
|
||||
|
||||
private:
|
||||
const db::Layout *mp_layout;
|
||||
db::cell_index_type m_top_cell;
|
||||
const std::set<db::cell_index_type> *mp_selection;
|
||||
std::map<db::cell_index_type, MatrixHash> m_cache;
|
||||
|
||||
MatrixHash get_hash (db::cell_index_type for_cell);
|
||||
MatrixHash get_hash_uncached (db::cell_index_type for_cell);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -25,6 +25,8 @@
|
|||
#include "dbCellGraphUtils.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "dbCellInstanceSetHasher.h"
|
||||
#include "dbHash.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
|
|
@ -122,28 +124,30 @@ private:
|
|||
// Some utility class: a compare function for a instance set of two cells in the context
|
||||
// of two layouts and two initial cells.
|
||||
|
||||
class InstanceSetCompareFunction
|
||||
#if 1
|
||||
|
||||
class InstanceSetCompareFunction
|
||||
{
|
||||
public:
|
||||
typedef std::multiset<db::ICplxTrans, db::trans_less_func<db::ICplxTrans> > trans_set_t;
|
||||
typedef std::unordered_multiset<db::ICplxTrans> trans_set_t;
|
||||
|
||||
InstanceSetCompareFunction (const db::Layout &layout_a, db::cell_index_type initial_cell_a, const db::Layout &layout_b, db::cell_index_type initial_cell_b)
|
||||
: m_layout_a (layout_a), m_initial_cell_a (initial_cell_a),
|
||||
m_layout_b (layout_b), m_initial_cell_b (initial_cell_b),
|
||||
InstanceSetCompareFunction (const db::Layout &layout_a, db::cell_index_type initial_cell_a, const std::set<db::cell_index_type> *selection_cone_a, const db::Layout &layout_b, db::cell_index_type initial_cell_b, const std::set<db::cell_index_type> *selection_cone_b)
|
||||
: m_layout_a (layout_a), m_initial_cell_a (initial_cell_a), m_selection_cone_a (selection_cone_a),
|
||||
m_layout_b (layout_b), m_initial_cell_b (initial_cell_b), m_selection_cone_b (selection_cone_b),
|
||||
m_cell_a (std::numeric_limits<db::cell_index_type>::max ()),
|
||||
m_repr_set (false)
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
bool compare (db::cell_index_type cell_a, const std::set<db::cell_index_type> &selection_cone_a, db::cell_index_type cell_b, const std::set<db::cell_index_type> &selection_cone_b)
|
||||
bool compare (db::cell_index_type cell_a, db::cell_index_type cell_b)
|
||||
{
|
||||
if (cell_a != m_cell_a) {
|
||||
|
||||
m_cell_a = cell_a;
|
||||
|
||||
m_callers_a.clear ();
|
||||
m_layout_a.cell (cell_a).collect_caller_cells (m_callers_a, selection_cone_a, -1);
|
||||
m_layout_a.cell (cell_a).collect_caller_cells (m_callers_a, *m_selection_cone_a, -1);
|
||||
m_callers_a.insert (cell_a);
|
||||
|
||||
m_trans.clear ();
|
||||
|
|
@ -162,7 +166,7 @@ public:
|
|||
}
|
||||
|
||||
std::set<db::cell_index_type> callers_b;
|
||||
m_layout_b.cell (cell_b).collect_caller_cells (callers_b, selection_cone_b, -1);
|
||||
m_layout_b.cell (cell_b).collect_caller_cells (callers_b, *m_selection_cone_b, -1);
|
||||
callers_b.insert (cell_b);
|
||||
|
||||
trans_set_t trans (m_trans);
|
||||
|
|
@ -175,11 +179,18 @@ public:
|
|||
return trans.empty ();
|
||||
}
|
||||
|
||||
void make_endpoint (db::cell_index_type /*cell_a*/, db::cell_index_type /*cell_b*/)
|
||||
{
|
||||
// not supported by this version
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout &m_layout_a;
|
||||
db::cell_index_type m_initial_cell_a;
|
||||
const std::set<db::cell_index_type> *m_selection_cone_a;
|
||||
const db::Layout &m_layout_b;
|
||||
db::cell_index_type m_initial_cell_b;
|
||||
const std::set<db::cell_index_type> *m_selection_cone_b;
|
||||
db::cell_index_type m_cell_a;
|
||||
std::set<db::cell_index_type> m_callers_a;
|
||||
trans_set_t m_trans;
|
||||
|
|
@ -256,6 +267,146 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* An alternative implementation of the InstanceSetCompareFunction
|
||||
*
|
||||
* This allows setting "endpoints" which are basically known cell identities
|
||||
* and allow shortcutting the instance set comparison. They act as additional
|
||||
* top cells.
|
||||
*
|
||||
* This implementation has a potential for higher performance, but
|
||||
* in general it requires somewhat more memory and is not fully optimized yet.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
struct InstanceSetCompareFunctionHash
|
||||
{
|
||||
std::size_t operator () (const std::pair<unsigned int, db::ICplxTrans> &p) const
|
||||
{
|
||||
return tl::hcombine (p.first, tl::hfunc (p.second));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class InstanceSetCompareFunction
|
||||
{
|
||||
public:
|
||||
typedef std::unordered_multiset<std::pair<unsigned int, db::ICplxTrans>, InstanceSetCompareFunctionHash> trans_set_t;
|
||||
|
||||
InstanceSetCompareFunction (const db::Layout &layout_a, db::cell_index_type initial_cell_a, const std::set<db::cell_index_type> *selection_cone_a, const db::Layout &layout_b, db::cell_index_type initial_cell_b, const std::set<db::cell_index_type> *selection_cone_b)
|
||||
: m_layout_a (layout_a), m_selection_cone_a (selection_cone_a),
|
||||
m_layout_b (layout_b), m_selection_cone_b (selection_cone_b),
|
||||
m_cell_a (std::numeric_limits<db::cell_index_type>::max ()),
|
||||
m_ep_index (0)
|
||||
{
|
||||
m_endpoints_a.insert (std::make_pair (initial_cell_a, m_ep_index));
|
||||
m_endpoints_b.insert (std::make_pair (initial_cell_b, m_ep_index));
|
||||
++m_ep_index;
|
||||
}
|
||||
|
||||
bool compare (db::cell_index_type cell_a, db::cell_index_type cell_b)
|
||||
{
|
||||
if (cell_a != m_cell_a) {
|
||||
m_cell_a = cell_a;
|
||||
m_trans.clear ();
|
||||
collect_or_compare_trans_set (true, m_trans, m_endpoints_a, m_layout_a, *m_selection_cone_a, m_cell_a, db::ICplxTrans ());
|
||||
}
|
||||
|
||||
trans_set_t trans (m_trans);
|
||||
|
||||
double mag = m_layout_b.dbu () / m_layout_a.dbu ();
|
||||
if (! collect_or_compare_trans_set (false, trans, m_endpoints_b, m_layout_b, *m_selection_cone_b, cell_b, db::ICplxTrans (mag))) {
|
||||
return false;
|
||||
} else {
|
||||
return trans.empty ();
|
||||
}
|
||||
}
|
||||
|
||||
void make_endpoint (db::cell_index_type cell_a, db::cell_index_type cell_b)
|
||||
{
|
||||
m_cell_a = std::numeric_limits<db::cell_index_type>::max ();
|
||||
m_trans.clear ();
|
||||
|
||||
m_endpoints_a.insert (std::make_pair (cell_a, m_ep_index));
|
||||
m_endpoints_b.insert (std::make_pair (cell_b, m_ep_index));
|
||||
++m_ep_index;
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout &m_layout_a;
|
||||
const std::set<db::cell_index_type> *m_selection_cone_a;
|
||||
const db::Layout &m_layout_b;
|
||||
const std::set<db::cell_index_type> *m_selection_cone_b;
|
||||
db::cell_index_type m_cell_a;
|
||||
std::set<db::cell_index_type> m_callers_a;
|
||||
trans_set_t m_trans;
|
||||
std::map<db::cell_index_type, unsigned int> m_endpoints_a, m_endpoints_b;
|
||||
unsigned int m_ep_index;
|
||||
|
||||
bool collect_or_compare_trans_set (bool collect,
|
||||
trans_set_t &ts,
|
||||
const std::map<db::cell_index_type, unsigned int> &endpoints,
|
||||
const db::Layout &layout,
|
||||
const std::set<db::cell_index_type> &selection,
|
||||
db::cell_index_type for_cell,
|
||||
const db::ICplxTrans &child_trans)
|
||||
{
|
||||
auto ep = endpoints.find (for_cell);
|
||||
if (ep != endpoints.end ()) {
|
||||
|
||||
auto key = std::make_pair (ep->second, child_trans);
|
||||
|
||||
if (collect) {
|
||||
ts.insert (key);
|
||||
return true;
|
||||
} else {
|
||||
auto i = ts.find (key);
|
||||
if (i == ts.end () || *i != key) {
|
||||
return false;
|
||||
} else {
|
||||
ts.erase (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const db::Cell &fc = layout.cell (for_cell);
|
||||
|
||||
std::set<db::cell_index_type> selected_parents;
|
||||
for (auto p = fc.begin_parent_cells (); p != fc.end_parent_cells (); ++p) {
|
||||
if (selection.find (*p) != selection.end ()) {
|
||||
selected_parents.insert (*p);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto p = fc.begin_parent_insts (); ! p.at_end (); ++p) {
|
||||
|
||||
db::cell_index_type parent_cell = p->parent_cell_index ();
|
||||
if (selected_parents.find (parent_cell) != selected_parents.end ()) {
|
||||
|
||||
const db::CellInstArray &inst = *p->basic_child_inst ();
|
||||
for (auto a = inst.begin (); ! a.at_end (); ++a) {
|
||||
auto ta = inst.complex_trans (*a);
|
||||
if (! collect_or_compare_trans_set (collect, ts, endpoints, layout, selection, parent_cell, ta * child_trans)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// CellMapping implementation
|
||||
|
||||
|
|
@ -438,18 +589,18 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
db::CellCounter cc_b (&layout_b, cell_index_b);
|
||||
|
||||
std::multimap<size_t, db::cell_index_type> cm_b;
|
||||
for (db::CellCounter::selection_iterator c = cc_b.begin (); c != cc_b.end (); ++c) {
|
||||
for (auto c = cc_b.begin (); c != cc_b.end (); ++c) {
|
||||
cm_b.insert (std::make_pair (*c == cell_index_b ? 0 : cc_b.weight (*c), *c));
|
||||
}
|
||||
|
||||
std::multimap<size_t, db::cell_index_type> cm_a;
|
||||
for (db::CellCounter::selection_iterator c = cc_a.begin (); c != cc_a.end (); ++c) {
|
||||
for (auto c = cc_a.begin (); c != cc_a.end (); ++c) {
|
||||
cm_a.insert (std::make_pair (*c == cell_index_a ? 0 : cc_a.weight (*c), *c));
|
||||
}
|
||||
|
||||
std::map <db::cell_index_type, std::vector<db::cell_index_type> > candidates; // key = index(a), value = indices(b)
|
||||
|
||||
InstanceSetCompareFunction cmp (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
InstanceSetCompareFunction cmp (layout_a, cell_index_a, &cc_a.selection (), layout_b, cell_index_b, &cc_b.selection ());
|
||||
|
||||
std::multimap<size_t, db::cell_index_type>::const_iterator a = cm_a.begin (), b = cm_b.begin ();
|
||||
while (a != cm_a.end () && b != cm_b.end ()) {
|
||||
|
|
@ -480,10 +631,11 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
unsigned int g = 0;
|
||||
std::map <unsigned int, std::vector <db::cell_index_type> > b_group;
|
||||
std::map <db::cell_index_type, unsigned int> b_group_of_cell;
|
||||
std::map <db::cell_index_type, std::vector<db::cell_index_type> > new_candidates;
|
||||
|
||||
while (a != cm_a.end () && a->first == w) {
|
||||
|
||||
candidates.insert (std::make_pair (a->second, std::vector <db::cell_index_type> ()));
|
||||
new_candidates.insert (std::make_pair (a->second, std::vector <db::cell_index_type> ()));
|
||||
|
||||
std::set <unsigned int> groups_taken;
|
||||
|
||||
|
|
@ -494,16 +646,16 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
if (bg != b_group_of_cell.end ()) {
|
||||
|
||||
if (groups_taken.find (bg->second) == groups_taken.end ()) {
|
||||
if (cmp.compare (a->second, cc_a.selection (), bb->second, cc_b.selection ())) {
|
||||
candidates [a->second] = b_group [bg->second];
|
||||
if (cmp.compare (a->second, bb->second)) {
|
||||
new_candidates [a->second] = b_group [bg->second];
|
||||
groups_taken.insert (bg->second);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (cmp.compare (a->second, cc_a.selection (), bb->second, cc_b.selection ())) {
|
||||
candidates [a->second].push_back (bb->second);
|
||||
if (cmp.compare (a->second, bb->second)) {
|
||||
new_candidates [a->second].push_back (bb->second);
|
||||
b_group_of_cell.insert (std::make_pair (bb->second, g));
|
||||
b_group.insert (std::make_pair (g, std::vector <db::cell_index_type> ())).first->second.push_back (bb->second);
|
||||
}
|
||||
|
|
@ -527,6 +679,15 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
|
|||
++b;
|
||||
}
|
||||
|
||||
for (auto c = new_candidates.begin (); c != new_candidates.end (); ++c) {
|
||||
if (c->second.size () == 1) {
|
||||
// a single candidate: establish as new endpoint for the comparer
|
||||
cmp.make_endpoint (c->first, c->second.front ());
|
||||
}
|
||||
}
|
||||
|
||||
candidates.insert (new_candidates.begin (), new_candidates.end ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -390,10 +390,12 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
|
|||
if (all) {
|
||||
|
||||
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set<db::Box> ());
|
||||
|
||||
// NOTE: this will set m_cm_new_entry
|
||||
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
|
||||
|
||||
// for new cells, create this instance
|
||||
if (m_cell_stack.back ().first) {
|
||||
if (m_cell_stack.back ().first || m_cm_new_entry) {
|
||||
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
|
||||
new_inst.object () = db::CellInst (new_cell);
|
||||
new_inst.transform (always_apply);
|
||||
|
|
@ -432,10 +434,12 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
|
|||
}
|
||||
|
||||
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second);
|
||||
|
||||
// NOTE: this will set m_cm_new_entry
|
||||
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) {
|
||||
if (m_cell_stack.back ().first || m_cm_new_entry) {
|
||||
db::CellInstArray new_inst (db::CellInst (new_cell), always_apply * trans);
|
||||
new_inst.transform_into (m_trans);
|
||||
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
|
||||
|
|
|
|||
|
|
@ -1524,6 +1524,84 @@ Instances::sort_child_insts (bool force)
|
|||
std::sort (m_insts_by_cell_index.begin (), m_insts_by_cell_index.end (), cell_inst_compare_f<basic_inst_type> ());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief An alternative box converter for CellInst which does not call layout.update
|
||||
*
|
||||
* Using this converter in cell.update_bbox() prevents recursive calls to layout.update.
|
||||
*/
|
||||
class InternalCellInstBoxConverter
|
||||
{
|
||||
public:
|
||||
typedef db::Cell::box_type box_type;
|
||||
typedef db::simple_bbox_tag complexity;
|
||||
|
||||
InternalCellInstBoxConverter (const db::Layout *layout)
|
||||
: mp_layout (layout)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::Cell::box_type operator() (const db::Cell::cell_inst_type &cell_inst) const
|
||||
{
|
||||
return mp_layout->cell (cell_inst.cell_index ()).bbox_with_empty_no_update ();
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout *mp_layout;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An alternative box converter for CellInstArray with properties which does not call layout.update
|
||||
*
|
||||
* Using this converter in cell.update_bbox() prevents recursive calls to layout.update.
|
||||
*/
|
||||
struct InternalCellInstArrayWithPropertiesBoxConverter
|
||||
{
|
||||
typedef db::Cell::cell_inst_array_type cell_inst_array;
|
||||
typedef db::Cell::box_type box_type;
|
||||
typedef db::complex_bbox_tag complexity;
|
||||
|
||||
InternalCellInstArrayWithPropertiesBoxConverter (const db::Layout *layout)
|
||||
: m_bc (layout)
|
||||
{ }
|
||||
|
||||
box_type operator() (const db::object_with_properties<cell_inst_array> &array) const
|
||||
{
|
||||
return array.bbox (m_bc);
|
||||
}
|
||||
|
||||
private:
|
||||
InternalCellInstBoxConverter m_bc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An alternative box converter for CellInstArray which does not call layout.update
|
||||
*
|
||||
* Using this converter in cell.update_bbox() prevents recursive calls to layout.update.
|
||||
*/
|
||||
struct InternalCellInstArrayBoxConverter
|
||||
{
|
||||
typedef db::Cell::cell_inst_array_type cell_inst_array;
|
||||
typedef db::Cell::box_type box_type;
|
||||
typedef db::complex_bbox_tag complexity;
|
||||
|
||||
InternalCellInstArrayBoxConverter (const db::Layout *layout)
|
||||
: m_bc (layout)
|
||||
{ }
|
||||
|
||||
box_type operator() (const cell_inst_array &array) const
|
||||
{
|
||||
return array.bbox (m_bc);
|
||||
}
|
||||
|
||||
private:
|
||||
InternalCellInstBoxConverter m_bc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Instances::sort_inst_tree (const Layout *g, bool force)
|
||||
{
|
||||
|
|
@ -1534,18 +1612,18 @@ Instances::sort_inst_tree (const Layout *g, bool force)
|
|||
|
||||
if (m_generic.any) {
|
||||
if (is_editable ()) {
|
||||
m_generic.stable_tree->sort (cell_inst_array_box_converter (*g));
|
||||
m_generic.stable_tree->sort (InternalCellInstArrayBoxConverter (g));
|
||||
} else {
|
||||
m_generic.unstable_tree->sort (cell_inst_array_box_converter (*g));
|
||||
m_generic.unstable_tree->sort (InternalCellInstArrayBoxConverter (g));
|
||||
// since we use unstable instance trees in non-editable mode, we need to resort the child instances in this case
|
||||
sort_child_insts (true);
|
||||
}
|
||||
}
|
||||
if (m_generic_wp.any) {
|
||||
if (is_editable ()) {
|
||||
m_generic_wp.stable_tree->sort (cell_inst_wp_array_box_converter (*g));
|
||||
m_generic_wp.stable_tree->sort (InternalCellInstArrayWithPropertiesBoxConverter (g));
|
||||
} else {
|
||||
m_generic_wp.unstable_tree->sort (cell_inst_wp_array_box_converter (*g));
|
||||
m_generic_wp.unstable_tree->sort (InternalCellInstArrayWithPropertiesBoxConverter (g));
|
||||
// since we use unstable instance trees in non-editable mode, we need to resort the child instances in this case
|
||||
sort_child_insts (true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public:
|
|||
/**
|
||||
* @brief Add a layer mapping
|
||||
*
|
||||
* @param layer_b The index of the layer in layout_a (the source of the mapping)
|
||||
* @param layer_b The index of the layer in layout_b (the source of the mapping)
|
||||
* @param layer_a The index of the layer in layout_a (the target of the mapping)
|
||||
*/
|
||||
void map (unsigned int layer_b, unsigned int layer_a)
|
||||
|
|
|
|||
|
|
@ -1532,6 +1532,8 @@ Layout::rename_cell (cell_index_type id, const char *name)
|
|||
bool
|
||||
Layout::topological_sort ()
|
||||
{
|
||||
// NOTE: using cell.instances methods instead of the corresponding cell methods avoids a recursive update call
|
||||
|
||||
m_top_cells = 0;
|
||||
m_top_down_list.clear ();
|
||||
|
||||
|
|
@ -1559,7 +1561,7 @@ Layout::topological_sort ()
|
|||
// child cells.
|
||||
|
||||
for (const_iterator c = begin (); c != end (); ++c) {
|
||||
if (c->parent_cells () == num_parents [c->cell_index ()]) {
|
||||
if (c->instances ().parent_cells () == num_parents [c->cell_index ()]) {
|
||||
m_top_down_list.push_back (c->cell_index ());
|
||||
num_parents [c->cell_index ()] = std::numeric_limits<cell_index_type>::max ();
|
||||
}
|
||||
|
|
@ -1568,7 +1570,7 @@ Layout::topological_sort ()
|
|||
// For all these a cells, increment the reported parent instance
|
||||
// count in all the child cells.
|
||||
for (cell_index_vector::const_iterator ii = m_top_down_list.begin () + n_top_down_cells; ii != m_top_down_list.end (); ++ii) {
|
||||
for (cell_type::child_cell_iterator cc = cell (*ii).begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
for (cell_type::child_cell_iterator cc = cell (*ii).instances ().begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
tl_assert (num_parents [*cc] != std::numeric_limits<cell_index_type>::max ());
|
||||
num_parents [*cc] += 1;
|
||||
}
|
||||
|
|
@ -1583,7 +1585,7 @@ Layout::topological_sort ()
|
|||
}
|
||||
|
||||
// Determine the number of top cells
|
||||
for (top_down_iterator e = m_top_down_list.begin (); e != m_top_down_list.end () && cell (*e).is_top (); ++e) {
|
||||
for (top_down_iterator e = m_top_down_list.begin (); e != m_top_down_list.end () && cell (*e).instances ().is_top (); ++e) {
|
||||
++m_top_cells;
|
||||
}
|
||||
|
||||
|
|
@ -1815,6 +1817,8 @@ Layout::force_update_no_lock () const
|
|||
void
|
||||
Layout::update () const
|
||||
{
|
||||
tl::MutexLocker locker (&lock ());
|
||||
|
||||
// NOTE: the assumption is that either one thread is writing or
|
||||
// multiple threads are reading. Hence, we do not need to lock hier_dirty() or bboxes_dirty().
|
||||
// We still do double checking as another thread might do the update as well.
|
||||
|
|
@ -1822,8 +1826,6 @@ Layout::update () const
|
|||
return;
|
||||
}
|
||||
|
||||
tl::MutexLocker locker (&lock ());
|
||||
|
||||
if (! under_construction ()) {
|
||||
force_update_no_lock ();
|
||||
}
|
||||
|
|
@ -1886,13 +1888,14 @@ Layout::do_update ()
|
|||
unsigned int layers = 0;
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Updating bounding boxes")));
|
||||
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
||||
for (bottom_up_iterator c = m_top_down_list.rbegin (); c != m_top_down_list.rend (); ++c) {
|
||||
++*pr;
|
||||
cell_type &cp (cell (*c));
|
||||
if (cp.is_shape_bbox_dirty () || dirty_parents.find (*c) != dirty_parents.end ()) {
|
||||
if (cp.update_bbox (layers)) {
|
||||
// the bounding box has changed - need to insert parents into "dirty parents" list
|
||||
for (cell_type::parent_cell_iterator p = cp.begin_parent_cells (); p != cp.end_parent_cells (); ++p) {
|
||||
// NOTE: using "instances" instead of the cell directly avoids a recursive update call
|
||||
for (cell_type::parent_cell_iterator p = cp.instances ().begin_parent_cells (); p != cp.instances ().end_parent_cells (); ++p) {
|
||||
dirty_parents.insert (*p);
|
||||
}
|
||||
}
|
||||
|
|
@ -1907,7 +1910,7 @@ Layout::do_update ()
|
|||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting shapes");
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Sorting shapes")));
|
||||
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
||||
for (bottom_up_iterator c = m_top_down_list.rbegin (); c != m_top_down_list.rend (); ++c) {
|
||||
++*pr;
|
||||
cell_type &cp (cell (*c));
|
||||
cp.sort_shapes ();
|
||||
|
|
@ -1922,7 +1925,7 @@ Layout::do_update ()
|
|||
size_t layers = 0;
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Sorting instances")));
|
||||
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
||||
for (bottom_up_iterator c = m_top_down_list.rbegin (); c != m_top_down_list.rend (); ++c) {
|
||||
++*pr;
|
||||
cell_type &cp (cell (*c));
|
||||
bool force_sort_inst_tree = dirty_parents.find (*c) != dirty_parents.end ();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2025 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCellInstanceSetHasher.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash ().to_string (), "(1,0,0) (0,1,0) (0,0,1)");
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash (0).to_string (), "(0,0,0) (0,0,0) (0,0,0)");
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash (db::ICplxTrans (2.0, 90.0, false, db::Vector (1, 2))).to_string (), "(0,-2,1) (2,0,2) (0,0,1)");
|
||||
|
||||
db::ICplxTrans t0 (2.0, 90.0, false, db::Vector (1, 2));
|
||||
|
||||
db::CellInstArray array (db::CellInst (0), t0, db::Vector (0, 100), db::Vector (100, 0), 2, 3);
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash (array).to_string (), "(0,-12,606) (12,0,312) (0,0,6)");
|
||||
|
||||
// emulate the regular array with an iterated array
|
||||
std::vector<db::Vector> dd;
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
for (unsigned int j = 0; j < 3; ++j) {
|
||||
db::Vector d = db::Vector (0, 100 * i) + db::Vector (100 * j, 0);
|
||||
dd.push_back (d);
|
||||
}
|
||||
}
|
||||
db::CellInstArray iter_array (db::CellInst (0), t0, dd.begin (), dd.end ());
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash (iter_array).to_string (), db::CellInstanceSetHasher::MatrixHash (array).to_string ());
|
||||
|
||||
// equivalence of sum of matrices and computed matrix for array
|
||||
db::CellInstanceSetHasher::MatrixHash hm (0.0);
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
for (unsigned int j = 0; j < 3; ++j) {
|
||||
db::Vector d = db::Vector (0, 100 * i) + db::Vector (100 * j, 0);
|
||||
hm += db::CellInstanceSetHasher::MatrixHash (db::ICplxTrans (d) * t0);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (db::CellInstanceSetHasher::MatrixHash (hm).to_string (), db::CellInstanceSetHasher::MatrixHash (array).to_string ());
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
db::Layout ly;
|
||||
|
||||
db::cell_index_type top = ly.add_cell ("TOP");
|
||||
db::cell_index_type c1 = ly.add_cell ("C1");
|
||||
db::cell_index_type c2 = ly.add_cell ("C2");
|
||||
db::cell_index_type c3 = ly.add_cell ("C3");
|
||||
db::cell_index_type c4a = ly.add_cell ("C4A");
|
||||
db::cell_index_type c5a = ly.add_cell ("C5A");
|
||||
db::cell_index_type c4b = ly.add_cell ("C4B");
|
||||
db::cell_index_type c5b = ly.add_cell ("C5B");
|
||||
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c1), db::Trans (1, true, db::Vector (0, 0))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c1), db::Trans (0, false, db::Vector (0, 10000))));
|
||||
|
||||
ly.cell (c1).insert (db::CellInstArray (db::CellInst (c2), db::Trans (1, true, db::Vector (100, 200)), db::Vector (0, 1000), db::Vector (1000, 0), 2l, 3l));
|
||||
|
||||
// C4 and C5 are single instances in C2, C5 with mag 2
|
||||
ly.cell (c2).insert (db::CellInstArray (db::CellInst (c4a), db::ICplxTrans (1.0, 0.0, false, db::Vector (10, 20))));
|
||||
ly.cell (c2).insert (db::CellInstArray (db::CellInst (c5a), db::ICplxTrans (2.0, 0.0, false, db::Vector (10, 20))));
|
||||
|
||||
// C3 has same instances as C2, but flat
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (100, 10200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (100, 11200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (1100, 10200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (1100, 11200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (2100, 10200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (1, true, db::Vector (2100, 11200))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (200, 100))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (200, 1100))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (200, 2100))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (1200, 100))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (1200, 1100))));
|
||||
ly.cell (top).insert (db::CellInstArray (db::CellInst (c3), db::Trans (0, false, db::Vector (1200, 2100))));
|
||||
|
||||
// C4 and C5 are single instances in C3, C5 with a different complex angle (45 degree)
|
||||
ly.cell (c3).insert (db::CellInstArray (db::CellInst (c4b), db::ICplxTrans (1.0, 0.0, false, db::Vector (10, 20))));
|
||||
ly.cell (c3).insert (db::CellInstArray (db::CellInst (c5b), db::ICplxTrans (1.0, 45.0, false, db::Vector (10, 20))));
|
||||
|
||||
db::CellInstanceSetHasher hasher1 (&ly, top, 0);
|
||||
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (top)), "00004450");
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (c1)), "00023711");
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (c2)), "001260aa");
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c3), hasher1.instance_set_hash (c2));
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (c4a)), "001270ba");
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c4a), hasher1.instance_set_hash (c4b));
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (c5a)), "0010da3a"); // != hash of C4A because of mag 2
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher1.instance_set_hash (c5b)), "0011d5c4"); // != hash of C5A because of 45 degree angle
|
||||
|
||||
std::set<db::cell_index_type> set1;
|
||||
set1.insert (top);
|
||||
set1.insert (c1);
|
||||
set1.insert (c2);
|
||||
set1.insert (c3);
|
||||
set1.insert (c4a);
|
||||
set1.insert (c5a);
|
||||
set1.insert (c4b);
|
||||
set1.insert (c5b);
|
||||
db::CellInstanceSetHasher hasher2 (&ly, top, &set1);
|
||||
|
||||
EXPECT_EQ (hasher1.instance_set_hash (top), hasher2.instance_set_hash (top));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c1), hasher2.instance_set_hash (c1));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c2), hasher2.instance_set_hash (c2));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c3), hasher2.instance_set_hash (c3));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c4a), hasher2.instance_set_hash (c4a));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c4b), hasher2.instance_set_hash (c4b));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c5a), hasher2.instance_set_hash (c5a));
|
||||
EXPECT_EQ (hasher1.instance_set_hash (c5b), hasher2.instance_set_hash (c5b));
|
||||
|
||||
std::set<db::cell_index_type> set2 = set1;
|
||||
// Remove C1 from selected set
|
||||
set2.erase (c1);
|
||||
db::CellInstanceSetHasher hasher3 (&ly, top, &set2);
|
||||
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (top)), "00004450");
|
||||
// NOTE: C1 hash is not valid as this cell is not selected
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c2)), "00000000"); // no path to TOP
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c3)), "001260aa");
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c4a)), "00000000"); // no path to TOP
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c4b)), "001270ba");
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c5a)), "00000000"); // no path to TOP
|
||||
EXPECT_EQ (tl::sprintf ("%08lx", hasher3.instance_set_hash (c5b)), "0011d5c4"); // != hash of C5A because of 45 degree angle
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ TARGET = db_tests
|
|||
include($$PWD/../../lib_ut.pri)
|
||||
|
||||
SOURCES = \
|
||||
dbCellInstanceSetHasherTests.cc \
|
||||
dbCompoundOperationTests.cc \
|
||||
dbEdgeNeighborhoodTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
|
|
|
|||
Loading…
Reference in New Issue