1 #include "canvas/group.h"
2 #include "canvas/types.h"
3 #include "canvas/rectangle.h"
4 #include "canvas/canvas.h"
8 using namespace ArdourCanvas;
10 CPPUNIT_TEST_SUITE_REGISTRATION (GroupTest);
12 /* Do some basic checks on the group's computation of its bounding box */
14 GroupTest::bounding_box ()
16 /* a group with 4 rectangles in it */
18 Rectangle a (canvas.root(), Rect (0, 0, 32, 32));
19 a.set_outline_width (0);
20 Rectangle b (canvas.root(), Rect (0, 33, 32, 64));
21 b.set_outline_width (0);
22 Rectangle c (canvas.root(), Rect (33, 0, 64, 32));
23 c.set_outline_width (0);
24 Rectangle d (canvas.root(), Rect (33, 33, 64, 64));
25 d.set_outline_width (0);
26 boost::optional<Rect> bbox = canvas.root()->bounding_box ();
28 /* check the bounding box */
29 CPPUNIT_ASSERT (bbox.is_initialized ());
30 CPPUNIT_ASSERT (bbox.get().x0 == 0);
31 CPPUNIT_ASSERT (bbox.get().y0 == 0);
32 CPPUNIT_ASSERT (bbox.get().x1 == 64);
33 CPPUNIT_ASSERT (bbox.get().y1 == 64);
35 /* check that adding an item resets the bbox */
36 Rectangle e (canvas.root(), Rect (64, 64, 128, 128));
37 bbox = canvas.root()->bounding_box ();
39 CPPUNIT_ASSERT (bbox.is_initialized ());
40 CPPUNIT_ASSERT (bbox.get().x0 == 0);
41 CPPUNIT_ASSERT (bbox.get().y0 == 0);
42 CPPUNIT_ASSERT (bbox.get().x1 == 128.25);
43 CPPUNIT_ASSERT (bbox.get().y1 == 128.25);
46 /* Check that a group containing only items with no bounding box itself has no bounding box */
48 GroupTest::null_bounding_box ()
52 Group empty (canvas.root());
54 boost::optional<Rect> bbox = empty.bounding_box ();
55 CPPUNIT_ASSERT (!bbox.is_initialized ());
58 /* Do some basic tests on layering */
62 /* Set up 4 rectangles; order from the bottom is
66 Rectangle a (canvas.root(), Rect (0, 0, 32, 32));
67 Rectangle b (canvas.root(), Rect (0, 0, 32, 32));
68 Rectangle c (canvas.root(), Rect (0, 0, 32, 32));
69 Rectangle d (canvas.root(), Rect (0, 0, 32, 32));
71 /* Put a on top and check */
74 list<Item*>::const_iterator i = canvas.root()->items().begin();
75 CPPUNIT_ASSERT (*i++ == &b);
76 CPPUNIT_ASSERT (*i++ == &c);
77 CPPUNIT_ASSERT (*i++ == &d);
78 CPPUNIT_ASSERT (*i++ == &a);
80 /* Put a on the bottom and check */
83 i = canvas.root()->items().begin();
84 CPPUNIT_ASSERT (*i++ == &a);
85 CPPUNIT_ASSERT (*i++ == &b);
86 CPPUNIT_ASSERT (*i++ == &c);
87 CPPUNIT_ASSERT (*i++ == &d);
89 /* Check raise by a number of levels */
93 i = canvas.root()->items().begin();
94 CPPUNIT_ASSERT (*i++ == &b);
95 CPPUNIT_ASSERT (*i++ == &c);
96 CPPUNIT_ASSERT (*i++ == &a);
97 CPPUNIT_ASSERT (*i++ == &d);
101 i = canvas.root()->items().begin();
102 CPPUNIT_ASSERT (*i++ == &b);
103 CPPUNIT_ASSERT (*i++ == &c);
104 CPPUNIT_ASSERT (*i++ == &d);
105 CPPUNIT_ASSERT (*i++ == &a);
108 /* Check that groups notice when their children change */
110 GroupTest::children_changing ()
114 /* Put a rectangle in the root group */
115 Rectangle a (canvas.root(), Rect (0, 0, 32, 32));
116 a.set_outline_width (0);
118 /* Check that initial bbox */
119 boost::optional<Rect> bbox = canvas.root()->bounding_box ();
120 CPPUNIT_ASSERT (bbox.is_initialized ());
121 CPPUNIT_ASSERT (bbox.get().x0 == 0);
122 CPPUNIT_ASSERT (bbox.get().y0 == 0);
123 CPPUNIT_ASSERT (bbox.get().x1 == 32);
124 CPPUNIT_ASSERT (bbox.get().y1 == 32);
126 /* Change the rectangle's size and check the parent */
127 a.set (Rect (0, 0, 48, 48));
128 bbox = canvas.root()->bounding_box ();
129 CPPUNIT_ASSERT (bbox.is_initialized ());
130 CPPUNIT_ASSERT (bbox.get().x0 == 0);
131 CPPUNIT_ASSERT (bbox.get().y0 == 0);
132 CPPUNIT_ASSERT (bbox.get().x1 == 48);
133 CPPUNIT_ASSERT (bbox.get().y1 == 48);
135 /* Change the rectangle's line width and check the parent */
136 a.set_outline_width (1);
137 bbox = canvas.root()->bounding_box ();
138 CPPUNIT_ASSERT (bbox.is_initialized ());
139 CPPUNIT_ASSERT (bbox.get().x0 == -0.5);
140 CPPUNIT_ASSERT (bbox.get().y0 == -0.5);
141 CPPUNIT_ASSERT (bbox.get().x1 == 48.5);
142 CPPUNIT_ASSERT (bbox.get().y1 == 48.5);
145 /* Check that a group notices when its grandchildren change */
147 GroupTest::grandchildren_changing ()
151 /* Put a child group B in the root group */
152 Group B (canvas.root());
154 /* Grandchild rectangle */
155 Rectangle a (&B, Rect (0, 0, 32, 32));
156 a.set_outline_width (0);
158 /* Check the initial bboxes */
159 boost::optional<Rect> bbox = canvas.root()->bounding_box ();
160 CPPUNIT_ASSERT (bbox.is_initialized ());
161 CPPUNIT_ASSERT (bbox.get().x0 == 0);
162 CPPUNIT_ASSERT (bbox.get().y0 == 0);
163 CPPUNIT_ASSERT (bbox.get().x1 == 32);
164 CPPUNIT_ASSERT (bbox.get().y1 == 32);
166 bbox = B.bounding_box ();
167 CPPUNIT_ASSERT (bbox.is_initialized ());
168 CPPUNIT_ASSERT (bbox.get().x0 == 0);
169 CPPUNIT_ASSERT (bbox.get().y0 == 0);
170 CPPUNIT_ASSERT (bbox.get().x1 == 32);
171 CPPUNIT_ASSERT (bbox.get().y1 == 32);
173 /* Change the grandchild and check its parent and grandparent */
174 a.set (Rect (0, 0, 48, 48));
176 bbox = canvas.root()->bounding_box ();
177 CPPUNIT_ASSERT (bbox.is_initialized ());
178 CPPUNIT_ASSERT (bbox.get().x0 == 0);
179 CPPUNIT_ASSERT (bbox.get().y0 == 0);
180 CPPUNIT_ASSERT (bbox.get().x1 == 48);
181 CPPUNIT_ASSERT (bbox.get().y1 == 48);
183 bbox = B.bounding_box ();
184 CPPUNIT_ASSERT (bbox.is_initialized ());
185 CPPUNIT_ASSERT (bbox.get().x0 == 0);
186 CPPUNIT_ASSERT (bbox.get().y0 == 0);
187 CPPUNIT_ASSERT (bbox.get().x1 == 48);
188 CPPUNIT_ASSERT (bbox.get().y1 == 48);
191 /* Basic tests on the code to find items at a particular point */
193 GroupTest::add_items_at_point ()
197 Group gA (canvas.root());
198 gA.set_position (Duple (128, 64));
201 gB.set_position (Duple (64, 32));
203 /* two rectangles in the same place, rB on top of rA */
205 rA.set_position (Duple (4, 2));
206 rA.set (Rect (0, 0, 8, 4));
208 rB.set_position (Duple (4, 2));
209 rB.set (Rect (0, 0, 8, 4));
211 /* rC below those two */
213 rC.set_position (Duple (12, 6));
214 rC.set (Rect (0, 0, 8, 4));
216 vector<Item const *> items;
217 canvas.root()->add_items_at_point (Duple (128 + 64 + 4 + 4, 64 + 32 + 2 + 2), items);
218 CPPUNIT_ASSERT (items.size() == 5);
219 vector<Item const *>::iterator i = items.begin ();
220 CPPUNIT_ASSERT (*i++ == canvas.root ());
221 CPPUNIT_ASSERT (*i++ == &gA);
222 CPPUNIT_ASSERT (*i++ == &gB);
223 CPPUNIT_ASSERT (*i++ == &rA);
224 CPPUNIT_ASSERT (*i++ == &rB);
227 canvas.root()->add_items_at_point (Duple (128 + 64 + 12 + 4, 64 + 32 + 6 + 2), items);
228 CPPUNIT_ASSERT (items.size() == 4);
230 CPPUNIT_ASSERT (*i++ == canvas.root ());
231 CPPUNIT_ASSERT (*i++ == &gA);
232 CPPUNIT_ASSERT (*i++ == &gB);
233 CPPUNIT_ASSERT (*i++ == &rC);
239 return ((double) rand() / RAND_MAX);
242 /* Check the find items at point code more thoroughly */
244 GroupTest::torture_add_items_at_point ()
246 int const n_rectangles = 10000;
247 int const n_tests = 1000;
248 double const rough_size = 1000;
253 list<Item*> rectangles;
255 for (int i = 0; i < n_rectangles; ++i) {
256 Rectangle* r = new Rectangle (canvas.root());
257 double const x = double_random () * rough_size / 2;
258 double const y = double_random () * rough_size / 2;
259 double const w = double_random () * rough_size / 2;
260 double const h = double_random () * rough_size / 2;
261 r->set (Rect (x, y, x + w, y + h));
262 rectangles.push_back (r);
265 for (int i = 0; i < n_tests; ++i) {
266 Duple test (double_random() * rough_size, double_random() * rough_size);
268 /* ask the group what's at this point */
269 vector<Item const *> items_A;
270 canvas.root()->add_items_at_point (test, items_A);
272 /* work it out ourselves */
273 vector<Item*> items_B;
274 if (canvas.root()->bounding_box() && canvas.root()->bounding_box().get().contains (test)) {
275 items_B.push_back (canvas.root());
278 for (list<Item*>::iterator j = rectangles.begin(); j != rectangles.end(); ++j) {
279 boost::optional<Rect> bbox = (*j)->bounding_box ();
281 if (bbox.get().contains (test)) {
282 items_B.push_back (*j);
286 CPPUNIT_ASSERT (items_A.size() == items_B.size());
287 vector<Item const *>::iterator j = items_A.begin ();
288 vector<Item*>::iterator k = items_B.begin ();
289 while (j != items_A.end ()) {
290 CPPUNIT_ASSERT (*j == *k);