mirror of https://github.com/KLayout/klayout.git
WIP
* Maybe fixed a performance issue on box-trees: the iterator wasn't going down to the very bottom of the tree on initialization * Added array quad skipping in display of shape arrays
This commit is contained in:
parent
0c0d247c23
commit
cdf4d08fd3
|
|
@ -70,7 +70,11 @@ struct basic_array_iterator
|
|||
|
||||
virtual long index_a () const { return -1; }
|
||||
virtual long index_b () const { return -1; }
|
||||
|
||||
|
||||
virtual size_t quad_id () const { return 0; }
|
||||
virtual box_type quad_box () const { return box_type::world (); }
|
||||
virtual void skip_quad () { }
|
||||
|
||||
virtual disp_type get () const = 0;
|
||||
|
||||
virtual basic_array_iterator<Coord> *clone () const = 0;
|
||||
|
|
@ -806,6 +810,21 @@ struct iterated_array_iterator
|
|||
return new iterated_array_iterator <Coord> (*this);
|
||||
}
|
||||
|
||||
virtual size_t quad_id () const
|
||||
{
|
||||
return m_t.quad_id ();
|
||||
}
|
||||
|
||||
virtual box_type quad_box () const
|
||||
{
|
||||
return m_t.quad_box ();
|
||||
}
|
||||
|
||||
virtual void skip_quad ()
|
||||
{
|
||||
m_t.skip_quad ();
|
||||
}
|
||||
|
||||
private:
|
||||
box_tree_const_iterator m_b, m_e;
|
||||
box_tree_touching_iterator m_t;
|
||||
|
|
@ -1420,6 +1439,36 @@ struct array_iterator
|
|||
return mp_base ? mp_base->index_b () : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For iterators supporting quads (iterated arrays), this method will return the quad ID
|
||||
*/
|
||||
size_t quad_id () const
|
||||
{
|
||||
return mp_base ? mp_base->quad_id () : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For iterators supporting quads (iterated arrays), this method will return the quad bounding box
|
||||
*
|
||||
* Note that this method will only return a valid quad box is the quad_id is non-null.
|
||||
*
|
||||
* This method will return the bounding box of all array offsets in the quad.
|
||||
*/
|
||||
db::box<Coord> quad_box () const
|
||||
{
|
||||
return mp_base ? mp_base->quad_box () : db::box<Coord>::world ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For iterators supporting quads (iterated arrays), this method will skip the current quad
|
||||
*/
|
||||
void skip_quad ()
|
||||
{
|
||||
if (mp_base) {
|
||||
mp_base->skip_quad ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
trans_type m_trans;
|
||||
basic_array_iterator <Coord> *mp_base;
|
||||
|
|
@ -1835,6 +1884,32 @@ struct array
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the bounding box from the iterator's current quad
|
||||
*
|
||||
* The bounding box is that of all objects in the current quad and
|
||||
* is confined to the array's total bounding box.
|
||||
*/
|
||||
template <class Iter, class BoxConv>
|
||||
box_type quad_box (const Iter &iter, const BoxConv &bc) const
|
||||
{
|
||||
box_type bb;
|
||||
if (mp_base) {
|
||||
bb = mp_base->bbox (box_type (0, 0, 0, 0));
|
||||
}
|
||||
bb &= iter.quad_box ();
|
||||
|
||||
if (mp_base) {
|
||||
if (mp_base->is_complex ()) {
|
||||
return bb * box_type (mp_base->complex_trans (simple_trans_type (m_trans)) * bc (m_obj));
|
||||
} else {
|
||||
return bb * (m_trans * bc (m_obj));
|
||||
}
|
||||
} else {
|
||||
return bb * (m_trans * bc (m_obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The number of single instances in the array
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -577,12 +577,16 @@ private:
|
|||
return m_quad < 4;
|
||||
}
|
||||
|
||||
// down one level
|
||||
// down as many levels as required for the next non-empty quad
|
||||
// returns true if this is possible
|
||||
bool down ()
|
||||
{
|
||||
box_tree_node *c = mp_node->child (m_quad);
|
||||
if (c) {
|
||||
while (true) {
|
||||
|
||||
box_tree_node *c = mp_node->child (m_quad);
|
||||
if (! c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mp_node = c;
|
||||
m_quad = -1;
|
||||
|
|
@ -595,12 +599,11 @@ private:
|
|||
// nothing to visit: up again
|
||||
up ();
|
||||
return false;
|
||||
} else {
|
||||
} else if (m_quad < 0) {
|
||||
// stay in main chunk
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -670,7 +673,7 @@ private:
|
|||
* whose box overlaps or touches a specified test box.
|
||||
*/
|
||||
|
||||
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
|
||||
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
|
||||
class box_tree
|
||||
{
|
||||
public:
|
||||
|
|
@ -1175,7 +1178,16 @@ private:
|
|||
|
||||
// the bins are: overall, ur, ul, ll, lr, empty
|
||||
element_iterator qloc [6] = { from, from, from, from, from, from };
|
||||
point_type center (bbox.center ());
|
||||
point_type center;
|
||||
if (bbox.width () < thin_aspect * bbox.height ()) {
|
||||
// separate by height only
|
||||
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
|
||||
} else if (bbox.height () < thin_aspect * bbox.width ()) {
|
||||
// separate by width only
|
||||
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
|
||||
} else {
|
||||
center = bbox.center ();
|
||||
}
|
||||
|
||||
for (element_iterator e = from; e != to; ++e) {
|
||||
|
||||
|
|
@ -1578,12 +1590,16 @@ private:
|
|||
return m_quad < 4;
|
||||
}
|
||||
|
||||
// down one level
|
||||
// down as many levels as required for the next non-empty quad
|
||||
// returns true if this is possible
|
||||
bool down ()
|
||||
{
|
||||
box_tree_node *c = mp_node->child (m_quad);
|
||||
if (c) {
|
||||
while (true) {
|
||||
|
||||
box_tree_node *c = mp_node->child (m_quad);
|
||||
if (! c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mp_node = c;
|
||||
m_quad = -1;
|
||||
|
|
@ -1596,12 +1612,11 @@ private:
|
|||
// nothing to visit: up again
|
||||
up ();
|
||||
return false;
|
||||
} else {
|
||||
} else if (m_quad < 0) {
|
||||
// stay in main chunk
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1638,7 +1653,7 @@ private:
|
|||
* is sorted.
|
||||
*/
|
||||
|
||||
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
|
||||
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
|
||||
class unstable_box_tree
|
||||
{
|
||||
public:
|
||||
|
|
@ -2103,7 +2118,16 @@ private:
|
|||
}
|
||||
|
||||
obj_iterator qloc [5] = { from, from, from, from, from };
|
||||
point_type center (bbox.center ());
|
||||
point_type center;
|
||||
if (bbox.width () < thin_aspect * bbox.height ()) {
|
||||
// separate by height only
|
||||
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
|
||||
} else if (bbox.height () < thin_aspect * bbox.width ()) {
|
||||
// separate by width only
|
||||
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
|
||||
} else {
|
||||
center = bbox.center ();
|
||||
}
|
||||
|
||||
for (obj_iterator e = from; e != to; ++e) {
|
||||
|
||||
|
|
|
|||
|
|
@ -640,13 +640,12 @@ OverlappingInstanceIteratorTraits::init (instance_iterator<OverlappingInstanceIt
|
|||
// ChildCellIterator implementation
|
||||
|
||||
ChildCellIterator::ChildCellIterator ()
|
||||
: m_iter (), m_end (), mp_insts (0)
|
||||
: m_iter (), m_end ()
|
||||
{ }
|
||||
|
||||
ChildCellIterator::ChildCellIterator (const instances_type *insts)
|
||||
: m_iter (insts->begin_sorted_insts ()),
|
||||
m_end (insts->end_sorted_insts ()),
|
||||
mp_insts (insts)
|
||||
m_end (insts->end_sorted_insts ())
|
||||
{ }
|
||||
|
||||
cell_index_type
|
||||
|
|
|
|||
|
|
@ -945,7 +945,6 @@ public:
|
|||
|
||||
private:
|
||||
inst_iterator_type m_iter, m_end;
|
||||
const instances_type *mp_insts;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -546,9 +546,13 @@ ShapeIterator::advance_aref (int &mode)
|
|||
|
||||
if (mode && m_array_iterator_valid) {
|
||||
|
||||
if (mode > 0) {
|
||||
if (mode == 1) {
|
||||
array_iterator *arr_iter = (array_iterator *) m_ad.iter;
|
||||
++*arr_iter;
|
||||
} else if (mode == 2) {
|
||||
// skip array quad -> skip rest of array quad and move to shape in the next quad or to end
|
||||
do_skip_array_quad ();
|
||||
mode = 1;
|
||||
} else {
|
||||
// skip quad -> skip rest of array and move to next shape array
|
||||
skip_array (); // sets m_array_iterator_valid = false
|
||||
|
|
@ -810,9 +814,100 @@ ShapeIterator::quad_box () const
|
|||
return quad_box_generic<OverlappingRegionTag, db::unstable_layer_tag> ();
|
||||
}
|
||||
}
|
||||
|
||||
return db::Box ();
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
void
|
||||
ShapeIterator::do_skip_array_quad_iter ()
|
||||
{
|
||||
Iter *arr_iter = (Iter *) m_ad.iter;
|
||||
arr_iter->skip_quad ();
|
||||
}
|
||||
|
||||
void
|
||||
ShapeIterator::do_skip_array_quad ()
|
||||
{
|
||||
if (m_array_iterator_valid) {
|
||||
if (m_type == PolygonPtrArray) {
|
||||
do_skip_array_quad_iter<polygon_ptr_array_iterator_type> ();
|
||||
} else if (m_type == SimplePolygonPtrArray) {
|
||||
do_skip_array_quad_iter<simple_polygon_ptr_array_iterator_type> ();
|
||||
} else if (m_type == PathPtrArray) {
|
||||
do_skip_array_quad_iter<path_ptr_array_iterator_type> ();
|
||||
} else if (m_type == TextPtrArray) {
|
||||
do_skip_array_quad_iter<text_ptr_array_iterator_type> ();
|
||||
} else if (m_type == BoxArray) {
|
||||
do_skip_array_quad_iter<box_array_iterator_type> ();
|
||||
} else if (m_type == ShortBoxArray) {
|
||||
do_skip_array_quad_iter<short_box_array_iterator_type> ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
size_t
|
||||
ShapeIterator::get_array_quad_id () const
|
||||
{
|
||||
Iter *arr_iter = (Iter *) m_ad.iter;
|
||||
return arr_iter->quad_id ();
|
||||
}
|
||||
|
||||
size_t
|
||||
ShapeIterator::array_quad_id () const
|
||||
{
|
||||
if (m_array_iterator_valid) {
|
||||
if (m_type == PolygonPtrArray) {
|
||||
return get_array_quad_id<polygon_ptr_array_iterator_type> ();
|
||||
} else if (m_type == SimplePolygonPtrArray) {
|
||||
return get_array_quad_id<simple_polygon_ptr_array_iterator_type> ();
|
||||
} else if (m_type == PathPtrArray) {
|
||||
return get_array_quad_id<path_ptr_array_iterator_type> ();
|
||||
} else if (m_type == TextPtrArray) {
|
||||
return get_array_quad_id<text_ptr_array_iterator_type> ();
|
||||
} else if (m_type == BoxArray) {
|
||||
return get_array_quad_id<box_array_iterator_type> ();
|
||||
} else if (m_type == ShortBoxArray) {
|
||||
return get_array_quad_id<short_box_array_iterator_type> ();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Iter, class Array>
|
||||
db::Box
|
||||
ShapeIterator::get_array_quad_box () const
|
||||
{
|
||||
const Array *arr = m_array.basic_ptr (typename Array::tag ());
|
||||
Iter *arr_iter = (Iter *) m_ad.iter;
|
||||
db::box_convert<typename Array::object_type> bc;
|
||||
return arr->quad_box (*arr_iter, bc);
|
||||
}
|
||||
|
||||
db::Box
|
||||
ShapeIterator::array_quad_box () const
|
||||
{
|
||||
if (m_array_iterator_valid) {
|
||||
if (m_type == PolygonPtrArray) {
|
||||
return get_array_quad_box<polygon_ptr_array_iterator_type, polygon_ptr_array_type> ();
|
||||
} else if (m_type == SimplePolygonPtrArray) {
|
||||
return get_array_quad_box<simple_polygon_ptr_array_iterator_type, simple_polygon_ptr_array_type> ();
|
||||
} else if (m_type == PathPtrArray) {
|
||||
return get_array_quad_box<path_ptr_array_iterator_type, path_ptr_array_type> ();
|
||||
} else if (m_type == TextPtrArray) {
|
||||
return get_array_quad_box<text_ptr_array_iterator_type, text_ptr_array_type> ();
|
||||
} else if (m_type == BoxArray) {
|
||||
return get_array_quad_box<box_array_iterator_type, box_array_type> ();
|
||||
} else if (m_type == ShortBoxArray) {
|
||||
return get_array_quad_box<short_box_array_iterator_type, short_box_array_type> ();
|
||||
}
|
||||
}
|
||||
|
||||
return db::Box::world ();
|
||||
}
|
||||
|
||||
void
|
||||
ShapeIterator::cleanup ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -319,6 +319,33 @@ public:
|
|||
advance (-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the arrays quad ID
|
||||
*
|
||||
* The arrays quad ID is a unique identifier for the current quad for iterated arrays. This can be used to
|
||||
* detect whether the iterator entered a new quad and optimize the search in that case.
|
||||
*/
|
||||
size_t array_quad_id () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the quad box
|
||||
*
|
||||
* Gets the box the current quad uses. This box may be larger than the actual shape containers
|
||||
* bounding box. Specifically if there is no quad tree at all, this box is the world box.
|
||||
*/
|
||||
db::Box array_quad_box () const;
|
||||
|
||||
/**
|
||||
* @brief Skips the current quad
|
||||
*
|
||||
* Moves to the next quad. This method can be used to shortcut searching if we are inside
|
||||
* a quad that is not relevant for the search.
|
||||
*/
|
||||
void skip_array_quad ()
|
||||
{
|
||||
advance (2);
|
||||
}
|
||||
|
||||
private:
|
||||
// a helper union for the iter_size union
|
||||
// (basically computing the size required for all iterators for a certain shape/array type)
|
||||
|
|
@ -418,6 +445,12 @@ private:
|
|||
template <class Sh, class StableTag> db::Box quad_box_by_shape (OverlappingRegionTag) const;
|
||||
template <class RegionTag, class StableTag> db::Box quad_box_generic () const;
|
||||
|
||||
template <class Iter, class Array> db::Box get_array_quad_box () const;
|
||||
template <class Iter> size_t get_array_quad_id () const;
|
||||
|
||||
template <class Iter> void do_skip_array_quad_iter ();
|
||||
void do_skip_array_quad ();
|
||||
|
||||
template <class Sh, class StableTag, class RegionTag> bool advance_shape (int &mode);
|
||||
template <class Array> void init_array_iter (NoRegionTag);
|
||||
template <class Array> void init_array_iter (TouchingRegionTag);
|
||||
|
|
|
|||
|
|
@ -1031,4 +1031,92 @@ TEST(6U)
|
|||
|
||||
}
|
||||
|
||||
TEST(7)
|
||||
{
|
||||
Box2Box conv;
|
||||
TestTree t;
|
||||
|
||||
int n = 200000;
|
||||
|
||||
for (int i = n - 1; i >= 0; --i) {
|
||||
t.insert (db::Box (i * 10, 0, i * 10 + 5, 5));
|
||||
}
|
||||
t.sort (conv);
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("test 7 lookup");
|
||||
size_t n = 0;
|
||||
for (unsigned int i = 0; i < 2000; ++i) {
|
||||
db::Coord sx = 0, sy = 0;
|
||||
TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv);
|
||||
while (!it.at_end ()) {
|
||||
sx += abs (it->left ());
|
||||
sy += abs (it->bottom ());
|
||||
++it;
|
||||
++n;
|
||||
}
|
||||
EXPECT_EQ (sx, 252500);
|
||||
EXPECT_EQ (sy, 0);
|
||||
}
|
||||
EXPECT_EQ (n, size_t (101 * 2000));
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("test 7 traverse");
|
||||
db::Coord m = std::numeric_limits<db::Coord>::max ();
|
||||
size_t n = 0;
|
||||
for (unsigned int i = 0; i < 10; ++i) {
|
||||
TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv);
|
||||
while (!it.at_end ()) {
|
||||
++it;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (n, t.size () * 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(7U)
|
||||
{
|
||||
Box2Box conv;
|
||||
UnstableTestTree t;
|
||||
|
||||
int n = 200000;
|
||||
|
||||
for (int i = n - 1; i >= 0; --i) {
|
||||
t.insert (db::Box (i * 10, 0, i * 10 + 5, 5));
|
||||
}
|
||||
t.sort (conv);
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("test 7U lookup");
|
||||
size_t n = 0;
|
||||
for (unsigned int i = 0; i < 2000; ++i) {
|
||||
db::Coord sx = 0, sy = 0;
|
||||
UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv);
|
||||
while (!it.at_end ()) {
|
||||
sx += abs (it->left ());
|
||||
sy += abs (it->bottom ());
|
||||
++it;
|
||||
++n;
|
||||
}
|
||||
EXPECT_EQ (sx, 252500);
|
||||
EXPECT_EQ (sy, 0);
|
||||
}
|
||||
EXPECT_EQ (n, size_t (101 * 2000));
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("test 7U traverse");
|
||||
db::Coord m = std::numeric_limits<db::Coord>::max ();
|
||||
size_t n = 0;
|
||||
for (unsigned int i = 0; i < 10; ++i) {
|
||||
UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv);
|
||||
while (!it.at_end ()) {
|
||||
++it;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (n, t.size () * 10);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1582,6 +1582,8 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
|
|||
db::Shape last_array;
|
||||
|
||||
size_t current_quad_id = 0;
|
||||
size_t current_array_quad_id = 0;
|
||||
|
||||
db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
|
|
@ -1596,16 +1598,18 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
|
|||
}
|
||||
|
||||
if (skip) {
|
||||
|
||||
shape.skip_quad ();
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (shape.in_array ()) {
|
||||
|
||||
bool simplified = false;
|
||||
|
||||
if (shape.in_array () && last_array != shape.array ()) {
|
||||
if (last_array != shape.array ()) {
|
||||
|
||||
last_array = shape.array ();
|
||||
current_array_quad_id = 0;
|
||||
|
||||
bool simplified = false;
|
||||
|
||||
if (last_array.type () == db::Shape::PolygonPtrArray) {
|
||||
simplified = draw_array_simplified<db::Shape::polygon_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
|
||||
|
|
@ -1619,17 +1623,42 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
|
|||
simplified = draw_array_simplified<db::Shape::short_box_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
|
||||
}
|
||||
|
||||
if (simplified) {
|
||||
shape.finish_array ();
|
||||
// continue with the next shape, array or quad
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (simplified) {
|
||||
shape.finish_array ();
|
||||
} else {
|
||||
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
|
||||
++shape;
|
||||
} else {
|
||||
current_array_quad_id = 0;
|
||||
}
|
||||
|
||||
// try whether the array quad can be simplified
|
||||
|
||||
size_t aqid = shape.array_quad_id ();
|
||||
if (aqid != 0 && aqid != current_array_quad_id) {
|
||||
|
||||
current_array_quad_id = aqid;
|
||||
|
||||
db::DBox qbbox = trans * shape.array_quad_box ();
|
||||
if (qbbox.width () < 1.5 && qbbox.height () < 1.5) {
|
||||
|
||||
// draw a single box instead of the quad
|
||||
mp_renderer->draw (qbbox, fill, frame, vertex, text);
|
||||
shape.skip_array_quad ();
|
||||
|
||||
// continue with the next shape, array or quad
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
|
||||
++shape;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue