914b940c5e2d0423aeef47bb3ea370f070b1c606
[ardour.git] / libs / canvas / test / group.cc
1 #include "canvas/group.h"
2 #include "canvas/types.h"
3 #include "canvas/rectangle.h"
4 #include "canvas/canvas.h"
5 #include "group.h"
6
7 using namespace std;
8 using namespace ArdourCanvas;
9
10 CPPUNIT_TEST_SUITE_REGISTRATION (GroupTest);
11
12 /* Do some basic checks on the group's computation of its bounding box */
13 void
14 GroupTest::bounding_box ()
15 {
16         /* a group with 4 rectangles in it */
17         ImageCanvas canvas;
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 ();
27
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);
34
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 ();
38
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);
44 }
45
46 /* Check that a group containing only items with no bounding box itself has no bounding box */
47 void
48 GroupTest::null_bounding_box ()
49 {
50         ImageCanvas canvas;
51
52         Group empty (canvas.root());
53
54         boost::optional<Rect> bbox = empty.bounding_box ();
55         CPPUNIT_ASSERT (!bbox.is_initialized ());
56 }
57
58 /* Do some basic tests on layering */
59 void
60 GroupTest::layers ()
61 {
62         /* Set up 4 rectangles; order from the bottom is
63            a - b - c - d
64         */
65         ImageCanvas canvas;
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));
70
71         /* Put a on top and check */
72         a.raise_to_top ();
73
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);
79
80         /* Put a on the bottom and check */
81         a.lower_to_bottom ();
82
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);
88
89         /* Check raise by a number of levels */
90         
91         a.raise (2);
92
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);
98
99         a.raise (4);
100
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);
106 }
107
108 /* Check that groups notice when their children change */
109 void
110 GroupTest::children_changing ()
111 {
112         ImageCanvas canvas;
113
114         /* Put a rectangle in the root group */
115         Rectangle a (canvas.root(), Rect (0, 0, 32, 32));
116         a.set_outline_width (0);
117
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);
125
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);
134
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);
143 }
144
145 /* Check that a group notices when its grandchildren change */
146 void
147 GroupTest::grandchildren_changing ()
148 {
149         ImageCanvas canvas;
150
151         /* Put a child group B in the root group */
152         Group B (canvas.root());
153
154         /* Grandchild rectangle */
155         Rectangle a (&B, Rect (0, 0, 32, 32));
156         a.set_outline_width (0);
157
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);
165
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);
172
173         /* Change the grandchild and check its parent and grandparent */
174         a.set (Rect (0, 0, 48, 48));
175
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);
182
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);
189 }
190
191 /* Basic tests on the code to find items at a particular point */
192 void
193 GroupTest::add_items_at_point ()
194 {
195         ImageCanvas canvas;
196         
197         Group gA (canvas.root());
198         gA.set_position (Duple (128, 64));
199
200         Group gB (&gA);
201         gB.set_position (Duple (64, 32));
202
203         /* two rectangles in the same place, rB on top of rA */
204         Rectangle rA (&gB);
205         rA.set_position (Duple (4, 2));
206         rA.set (Rect (0, 0, 8, 4));
207         Rectangle rB (&gB);
208         rB.set_position (Duple (4, 2));
209         rB.set (Rect (0, 0, 8, 4));
210
211         /* rC below those two */
212         Rectangle rC (&gB);
213         rC.set_position (Duple (12, 6));
214         rC.set (Rect (0, 0, 8, 4));
215
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);
225
226         items.clear ();
227         canvas.root()->add_items_at_point (Duple (128 + 64 + 12 + 4, 64 + 32 + 6 + 2), items);
228         CPPUNIT_ASSERT (items.size() == 4);
229         i = items.begin ();
230         CPPUNIT_ASSERT (*i++ == canvas.root ());
231         CPPUNIT_ASSERT (*i++ == &gA);
232         CPPUNIT_ASSERT (*i++ == &gB);
233         CPPUNIT_ASSERT (*i++ == &rC);
234 }
235
236 static double
237 double_random ()
238 {
239         return ((double) rand() / RAND_MAX);
240 }
241
242 /* Check the find items at point code more thoroughly */
243 void
244 GroupTest::torture_add_items_at_point ()
245 {
246         int const n_rectangles = 10000;
247         int const n_tests = 1000;
248         double const rough_size = 1000;
249         srand (1);
250
251         ImageCanvas canvas;
252
253         list<Item*> rectangles;
254
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);
263         }
264
265         for (int i = 0; i < n_tests; ++i) {
266                 Duple test (double_random() * rough_size, double_random() * rough_size);
267
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);
271
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());
276                 }
277                 
278                 for (list<Item*>::iterator j = rectangles.begin(); j != rectangles.end(); ++j) {
279                         boost::optional<Rect> bbox = (*j)->bounding_box ();
280                         assert (bbox);
281                         if (bbox.get().contains (test)) {
282                                 items_B.push_back (*j);
283                         }
284                 }
285
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);
291                         ++j;
292                         ++k;
293                 }
294         }
295 }
296