Flutter Engine
 
Loading...
Searching...
No Matches
flutter::DlRTree Class Reference

#include <dl_rtree.h>

Inheritance diagram for flutter::DlRTree:

Public Member Functions

 DlRTree (const DlRect rects[], int N, const int ids[]=nullptr, bool predicate(int id)=[](int) { return true;}, int invalid_id=-1)
 
void search (const DlRect &query, std::vector< int > *results) const
 
int id (int result_index) const
 
const DlRectbounds () const
 
const DlRectbounds (int result_index) const
 
size_t bytes_used () const
 Returns the bytes used by the object and all of its node data.
 
int leaf_count () const
 
int node_count () const
 
std::list< DlRectsearchAndConsolidateRects (const DlRect &query, bool deband=true) const
 
const DlRegionregion () const
 
DlRegion region (const DlRect &query) const
 

Detailed Description

An R-Tree that stores a list of bounding rectangles with optional associated IDs.

The R-Tree can be searched in one of two ways:

  • Query for a list of hits among the original rectangles
    See also
    |search|
  • Query for a set of non-overlapping rectangles that are joined from the original rectangles that intersect a query rect
    See also
    |searchAndConsolidateRects|

Definition at line 27 of file dl_rtree.h.

Constructor & Destructor Documentation

◆ DlRTree()

flutter::DlRTree::DlRTree ( const DlRect  rects[],
int  N,
const int  ids[] = nullptr,
bool   predicateint id = [](int) { return true; },
int  invalid_id = -1 
)

Construct an R-Tree from the list of rectangles respecting the order in which they appear in the list. An optional array of IDs can be provided to tag each rectangle with information needed by the caller as well as an optional predicate that can filter out rectangles with IDs that should not be stored in the R-Tree.

If an array of IDs is not specified then all leaf nodes will be represented by the |invalid_id| (which defaults to -1).

Invalid rects that are empty or contain a NaN value will not be stored in the R-Tree. And, if a |predicate| function is provided, that function will be called to query if the rectangle associated with the ID should be included.

Duplicate rectangles and IDs are allowed and not processed in any way except to eliminate invalid rectangles and IDs that are rejected by the optional predicate function.

Definition at line 12 of file dl_rtree.cc.

17 : invalid_id_(invalid_id) {
18 if (N <= 0) {
19 FML_DCHECK(N >= 0);
20 return;
21 }
22 FML_DCHECK(rects != nullptr);
23
24 // Count the number of rectangles we actually want to track,
25 // which includes only non-empty rectangles whose optional
26 // ID is not filtered by the predicate.
27 int leaf_count = 0;
28 for (int i = 0; i < N; i++) {
29 if (!rects[i].IsEmpty()) {
30 if (ids == nullptr || p(ids[i])) {
31 leaf_count++;
32 }
33 }
34 }
35 leaf_count_ = leaf_count;
36
37 // Count the total number of nodes (leaf and internal) up front
38 // so we can resize the vector just once.
39 uint32_t total_node_count = leaf_count;
40 uint32_t gen_count = leaf_count;
41 while (gen_count > 1) {
42 uint32_t family_count = (gen_count + kMaxChildren - 1u) / kMaxChildren;
43 total_node_count += family_count;
44 gen_count = family_count;
45 }
46
47 nodes_.resize(total_node_count);
48
49 // Now place only the tracked rectangles into the nodes array
50 // in the first leaf_count_ entries.
51 int leaf_index = 0;
52 int id = invalid_id;
53 for (int i = 0; i < N; i++) {
54 if (!rects[i].IsEmpty()) {
55 if (ids == nullptr || p(id = ids[i])) {
56 Node& node = nodes_[leaf_index++];
57 node.bounds = rects[i];
58 node.id = id;
59 }
60 }
61 }
62 FML_DCHECK(leaf_index == leaf_count);
63
64 // --- Implementation note ---
65 // Many R-Tree algorithms attempt to consolidate nearby rectangles
66 // into branches of the tree in order to maximize the benefit of
67 // bounds testing against whole sub-trees. The Skia code from which
68 // this was based, though, indicated that empirical tests against a
69 // browser client showed little gains in rendering performance while
70 // costing 17% performance in bulk loading the rects into the R-Tree:
71 // https://github.com/google/skia/blob/12b6bd042f7cdffb9012c90c3b4885601fc7be95/src/core/SkRTree.cpp#L96
72 //
73 // Given that this class will most often be used to track rendering
74 // operations that were drawn in an app that performs a type of
75 // "page layout" with rendering proceeding in a linear fashion from
76 // top to bottom (and left to right or right to left), the rectangles
77 // are likely nearly sorted when they are delivered to this constructor
78 // so leaving them in their original order should show similar results
79 // to what Skia found in their empirical browser tests.
80 // ---
81
82 // Continually process the previous level (generation) of nodes,
83 // combining them into a new generation of parent groups each grouping
84 // at most |kMaxChildren| children and joining their bounds into its
85 // parent bounds.
86 // Each generation will end up reduced by a factor of up to kMaxChildren
87 // until there is just one node left, which is the root node of
88 // the R-Tree.
89 uint32_t gen_start = 0;
90 gen_count = leaf_count;
91 while (gen_count > 1) {
92 uint32_t gen_end = gen_start + gen_count;
93
94 uint32_t family_count = (gen_count + kMaxChildren - 1u) / kMaxChildren;
95 FML_DCHECK(gen_end + family_count <= total_node_count);
96
97 // D here is similar to the variable in a Bresenham line algorithm where
98 // we want to slowly move |family_count| steps along the minor axis as
99 // we move |gen_count| steps along the major axis.
100 //
101 // Each inner loop increments D by family_count.
102 // The inner loop executes a total of gen_count times.
103 // Every time D exceeds 0 we subtract gen_count and move to a new parent.
104 // All told we will increment D by family_count a total of gen_count times.
105 // All told we will decrement D by gen_count a total of family_count times.
106 // This leaves D back at its starting value.
107 //
108 // We could bias/balance where the extra children are placed by varying
109 // the initial count of D from 0 to (1 - family_count), but we aren't
110 // looking at this process aesthetically so we just use 0 as an initial
111 // value. Using 0 provides a "greedy" allocation of the extra children.
112 // Bresenham also uses double the size of the steps we use here also to
113 // have better rounding of when the minor axis steps occur, but again we
114 // don't care about the distribution of the extra children.
115 int D = 0;
116
117 uint32_t sibling_index = gen_start;
118 uint32_t parent_index = gen_end;
119 Node* parent = nullptr;
120 while (sibling_index < gen_end) {
121 if ((D += family_count) > 0) {
122 D -= gen_count;
123 FML_DCHECK(parent_index < gen_end + family_count);
124 parent = &nodes_[parent_index++];
125 parent->bounds = kEmpty;
126 parent->child.index = sibling_index;
127 parent->child.count = 0;
128 }
129 FML_DCHECK(parent != nullptr);
130 parent->bounds = parent->bounds.Union(nodes_[sibling_index++].bounds);
131 parent->child.count++;
132 }
133 FML_DCHECK(D == 0);
134 FML_DCHECK(sibling_index == gen_end);
135 FML_DCHECK(parent_index == gen_end + family_count);
136 gen_start = gen_end;
137 gen_count = family_count;
138 }
139 FML_DCHECK(gen_start + gen_count == total_node_count);
140}
int leaf_count() const
Definition dl_rtree.h:109
const DlRect & bounds() const
Definition dl_rtree.cc:215
#define FML_DCHECK(condition)
Definition logging.h:122
const uintptr_t id

References bounds(), FML_DCHECK, i, id, and leaf_count().

Member Function Documentation

◆ bounds() [1/2]

const DlRect & flutter::DlRTree::bounds ( ) const

Returns maximum and minimum axis values of rectangles in this R-Tree. If R-Tree is empty returns an empty DlRect.

Definition at line 215 of file dl_rtree.cc.

215 {
216 if (!nodes_.empty()) {
217 return nodes_.back().bounds;
218 } else {
219 return kEmpty;
220 }
221}

Referenced by DlRTree(), region(), searchAndConsolidateRects(), flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().

◆ bounds() [2/2]

const DlRect & flutter::DlRTree::bounds ( int  result_index) const
inline

Return the rectangle bounds for the indicated result of a query or an empty rect if the index is not a valid leaf node index.

Definition at line 95 of file dl_rtree.h.

95 {
96 return (result_index >= 0 && result_index < leaf_count_)
97 ? nodes_[result_index].bounds
98 : kEmpty;
99 }

◆ bytes_used()

size_t flutter::DlRTree::bytes_used ( ) const
inline

Returns the bytes used by the object and all of its node data.

Definition at line 102 of file dl_rtree.h.

102 {
103 return sizeof(DlRTree) + sizeof(Node) * nodes_.size();
104 }
DlRTree(const DlRect rects[], int N, const int ids[]=nullptr, bool predicate(int id)=[](int) { return true;}, int invalid_id=-1)
Definition dl_rtree.cc:12

◆ id()

int flutter::DlRTree::id ( int  result_index) const
inline

Return the ID for the indicated result of a query or invalid_id if the index is not a valid leaf node index.

Definition at line 83 of file dl_rtree.h.

83 {
84 return (result_index >= 0 && result_index < leaf_count_)
85 ? nodes_[result_index].id
86 : invalid_id_;
87 }

Referenced by flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().

◆ leaf_count()

int flutter::DlRTree::leaf_count ( ) const
inline

Returns the number of leaf nodes corresponding to non-empty rectangles that were passed in the constructor and not filtered out by the predicate.

Definition at line 109 of file dl_rtree.h.

109{ return leaf_count_; }

Referenced by DlRTree(), flutter::testing::TEST(), flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().

◆ node_count()

int flutter::DlRTree::node_count ( ) const
inline

Return the total number of nodes used in the R-Tree, both leaf and internal consolidation nodes.

Definition at line 113 of file dl_rtree.h.

113{ return nodes_.size(); }

Referenced by flutter::testing::TEST(), flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().

◆ region() [1/2]

const DlRegion & flutter::DlRTree::region ( ) const

Returns DlRegion that represents the union of all rectangles in the R-Tree.

Definition at line 203 of file dl_rtree.cc.

203 {
204 if (!region_) {
205 std::vector<DlIRect> rects;
206 rects.resize(leaf_count_);
207 for (int i = 0; i < leaf_count_; i++) {
208 rects[i] = DlIRect::RoundOut(nodes_[i].bounds);
209 }
210 region_.emplace(rects);
211 }
212 return *region_;
213}
RoundOut(const TRect< U > &r)
Definition rect.h:679

References bounds(), i, and impeller::TRect< T >::RoundOut().

Referenced by region(), searchAndConsolidateRects(), and flutter::testing::TEST().

◆ region() [2/2]

DlRegion flutter::DlRTree::region ( const DlRect query) const
inline

Returns DlRegion that represents the union of all rectangles in the R-Tree intersected with the query rect.

Definition at line 133 of file dl_rtree.h.

133 {
135 DlRegion(DlIRect::RoundOut(query)));
136 }
const DlRegion & region() const
Definition dl_rtree.cc:203
static DlRegion MakeIntersection(const DlRegion &a, const DlRegion &b)
Definition dl_region.cc:496

References flutter::DlRegion::MakeIntersection(), region(), and impeller::TRect< T >::RoundOut().

◆ search()

void flutter::DlRTree::search ( const DlRect query,
std::vector< int > *  results 
) const

Search the rectangles and return a vector of leaf node indices for rectangles that intersect the query.

Note that the indices are internal indices of the stored data and not the index of the rectangles or ids in the constructor. The returned indices may not themselves be in numerical order, but they will represent the rectangles and IDs in the order in which they were passed into the constructor. The actual rectangle and ID associated with each index can be retrieved using the |DlRTreeid| and |DlRTreebounds| methods.

Definition at line 142 of file dl_rtree.cc.

142 {
143 FML_DCHECK(results != nullptr);
144 if (query.IsEmpty()) {
145 return;
146 }
147 if (nodes_.size() <= 0) {
148 FML_DCHECK(leaf_count_ == 0);
149 return;
150 }
151 const Node& root = nodes_.back();
152 if (root.bounds.IntersectsWithRect(query)) {
153 if (nodes_.size() == 1) {
154 FML_DCHECK(leaf_count_ == 1);
155 // The root node is the only node and it is a leaf node
156 results->push_back(0);
157 } else {
158 search(root, query, results);
159 }
160 }
161}
void search(const DlRect &query, std::vector< int > *results) const
Definition dl_rtree.cc:142

References FML_DCHECK, impeller::TRect< T >::IsEmpty(), and search().

Referenced by search(), searchAndConsolidateRects(), flutter::testing::TEST(), flutter::testing::TEST(), flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().

◆ searchAndConsolidateRects()

std::list< DlRect > flutter::DlRTree::searchAndConsolidateRects ( const DlRect query,
bool  deband = true 
) const

Finds the rects in the tree that intersect with the query rect.

The returned list of rectangles will be non-overlapping. In other words, the bounds of each rect in the result list are mutually exclusive.

If |deband| is true, then matching rectangles from adjacent DlRegion spanlines will be joined together. This reduces the number of rectangles returned, but requires some extra computation.

Definition at line 163 of file dl_rtree.cc.

164 {
165 // Get the indexes for the operations that intersect with the query rect.
166 std::vector<int> intermediary_results;
167 search(query, &intermediary_results);
168
169 std::vector<DlIRect> rects;
170 rects.reserve(intermediary_results.size());
171 for (int index : intermediary_results) {
172 DlIRect current_record_rect = DlIRect::RoundOut(bounds(index));
173 rects.push_back(current_record_rect);
174 }
175 DlRegion region(rects);
176
177 auto non_overlapping_rects = region.getRects(deband);
178 std::list<DlRect> final_results;
179 for (const auto& rect : non_overlapping_rects) {
180 final_results.push_back(DlRect::Make(rect));
181 }
182 return final_results;
183}
std::vector< DlIRect > getRects(bool deband=true) const
Definition dl_region.cc:560
impeller::IRect32 DlIRect
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:157

References bounds(), flutter::DlRegion::getRects(), impeller::TRect< Scalar >::Make(), region(), impeller::TRect< T >::RoundOut(), and search().

Referenced by flutter::testing::TEST(), flutter::testing::TEST(), flutter::testing::TEST(), and flutter::testing::TEST().


The documentation for this class was generated from the following files: