* 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:
Matthias Koefferlein 2020-06-01 23:28:04 +02:00
parent 0c0d247c23
commit cdf4d08fd3
8 changed files with 374 additions and 32 deletions

View File

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

View File

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

View File

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

View File

@ -945,7 +945,6 @@ public:
private:
inst_iterator_type m_iter, m_end;
const instances_type *mp_insts;
};
/**

View File

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

View File

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

View File

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

View File

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