File Coverage

qtbase.c
Criterion Covered Total %
statement 211 216 97.6
branch 108 120 90.0
condition n/a
subroutine n/a
pod n/a
total 319 336 94.9


line stmt bran cond sub pod time code
1             #include "qtbase.h"
2             #include
3              
4             #define CHILDREN_PER_NODE 4
5             #define MAX_SIZE_INITIAL 4
6             #define MAX_SIZE_GROWTH 2
7             #define MAX_SIZE_CLEAR 32
8              
9 128           Shape* create_shape()
10             {
11 128           Shape *s = malloc(sizeof *s);
12 128           return s;
13             }
14              
15 185           void prepare_rectangle(Shape *s, double x, double y, double x2, double y2)
16             {
17 185           s->type = shape_rectangle;
18 185           s->x = x;
19 185           s->y = y;
20 185           s->x2 = x2;
21 185           s->y2 = y2;
22 185           }
23              
24 13           void prepare_circle(Shape *s, double x0, double y0, double radius)
25             {
26 13           s->type = shape_circle;
27 13           s->x0 = x0;
28 13           s->y0 = y0;
29 13           s->radius = radius;
30 13           s->radius_sq = radius * radius;
31              
32 13           double contained_radius = s->radius / sqrt(2);
33 13           s->x = x0 - contained_radius;
34 13           s->x2 = x0 + contained_radius;
35 13           s->y = y0 - contained_radius;
36 13           s->y2 = y0 + contained_radius;
37 13           }
38              
39 128           void destroy_shape(Shape *s)
40             {
41 128           free(s);
42 128           }
43              
44 80           DynArr* create_array()
45             {
46 80           DynArr *arr = malloc(sizeof *arr);
47 80           arr->count = 0;
48 80           arr->max_size = 0;
49              
50 80           return arr;
51             }
52              
53 80           void destroy_array(DynArr* arr)
54             {
55 80 100         if (arr->max_size > 0) {
56 45           free(arr->ptr);
57             }
58              
59 80           free(arr);
60 80           }
61              
62 67           void clear_array(DynArr *arr)
63             {
64 67           arr->count = 0;
65              
66 67 100         if (arr->max_size >= MAX_SIZE_CLEAR) {
67 1           arr->max_size = 0;
68 1           free(arr->ptr);
69             }
70 67           }
71              
72 147           void push_array(DynArr *arr, void *ptr)
73             {
74 147 100         if (arr->count == arr->max_size) {
75 54 100         if (arr->max_size == 0) {
76 46           arr->max_size = MAX_SIZE_INITIAL;
77 46           arr->ptr = malloc(arr->max_size * sizeof *arr->ptr);
78             }
79             else {
80 8           arr->max_size *= MAX_SIZE_GROWTH;
81              
82 8           void *enlarged = realloc(arr->ptr, arr->max_size * sizeof *arr->ptr);
83             assert(enlarged != NULL);
84              
85 8           arr->ptr = enlarged;
86             }
87             }
88              
89 147           arr->ptr[arr->count] = ptr;
90 147           arr->count += 1;
91 147           }
92              
93 55           void adopt_object (QuadTreeRootNode *root, SV *value, Shape *s)
94             {
95 55           push_array(root->objects, value);
96 55           SvREFCNT_inc(value);
97 55           hv_store_ent(root->backref, value, newSViv((uintptr_t) s), 0);
98 55           }
99              
100 1           void disown_object (QuadTreeRootNode *root, SV *value)
101             {
102             int i;
103 1           DynArr* new_list = create_array();
104 33 100         for(i = 0; i < root->objects->count; ++i) {
105 32           SV *fetched = (SV*) root->objects->ptr[i];
106 32 100         if (!sv_eq(fetched, value)) {
107 31           push_array(new_list, fetched);
108             }
109             else {
110 1           SvREFCNT_dec(fetched);
111             }
112             }
113              
114 1           destroy_array(root->objects);
115 1           root->objects = new_list;
116              
117             /* NOTE: no shape destruction here, since "adopt_object" does not create it */
118 1           hv_delete_ent(root->backref, value, 0, 0);
119 1           }
120              
121 22           QuadTreeNode* create_nodes(int count, QuadTreeNode *parent)
122             {
123 22           QuadTreeNode *node = malloc(count * sizeof *node);
124              
125             int i;
126 95 100         for (i = 0; i < count; ++i) {
127 73           node[i].values = NULL;
128 73           node[i].children = NULL;
129 73           node[i].parent = parent;
130 73           node[i].has_objects = false;
131             }
132              
133 22           return node;
134             }
135              
136             /* NOTE: does not actually free the node, but frees its children nodes */
137 73           void destroy_node(QuadTreeNode *node)
138             {
139 73           destroy_array(node->values);
140 73           destroy_shape(node->dimensions);
141              
142 73 100         if (node->children != NULL) {
143             int i;
144 85 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
145 68           destroy_node(&node->children[i]);
146             }
147              
148 17           free(node->children);
149             }
150 73           }
151              
152 5           QuadTreeRootNode* create_root()
153             {
154 5           QuadTreeRootNode *root = malloc(sizeof *root);
155 5           root->node = create_nodes(1, NULL);
156 5           root->backref = newHV();
157 5           root->objects = create_array();
158              
159 5           return root;
160             }
161              
162 73           void node_add_level(QuadTreeNode* node, double xmin, double ymin, double xmax, double ymax, int depth)
163             {
164 73           bool last = --depth == 0;
165              
166 73           node->dimensions = create_shape();
167 73           prepare_rectangle(node->dimensions, xmin, ymin, xmax, ymax);
168 73           node->values = create_array();
169              
170 73 100         if (!last) {
171 17           node->children = create_nodes(CHILDREN_PER_NODE, node);
172 17           double xmid = xmin + (xmax - xmin) / 2;
173 17           double ymid = ymin + (ymax - ymin) / 2;
174              
175 17           node_add_level(&node->children[0], xmin, ymin, xmid, ymid, depth);
176 17           node_add_level(&node->children[1], xmin, ymid, xmid, ymax, depth);
177 17           node_add_level(&node->children[2], xmid, ymin, xmax, ymid, depth);
178 17           node_add_level(&node->children[3], xmid, ymid, xmax, ymax, depth);
179             }
180 73           }
181              
182 1068           bool shape_contained (Shape *inner_s, Shape *s)
183             {
184 291 100         return (s->x <= inner_s->x && s->x2 >= inner_s->x2)
185 1359 100         && (s->y <= inner_s->y && s->y2 >= inner_s->y2);
    100          
    100          
186             }
187              
188 1072           bool shapes_overlap (Shape *s1, Shape *s2)
189             {
190 1072 100         if (s1->type == s2->type) {
191 1014           switch (s1->type) {
192 3           case shape_circle: {
193             /* circle vs circle */
194 3           double distance_x = s1->x0 - s2->x0;
195 3           double distance_y = s1->y0 - s2->y0;
196 3           double radius = s1->radius + s2->radius;
197              
198 3           return distance_x * distance_x + distance_y * distance_y
199 3           <= radius * radius;
200             }
201 1011           case shape_rectangle: {
202             /* rectangle vs rectangle */
203 1011 50         if (s1->type == shape_rectangle) {
204 811 100         return (s1->x <= s2->x2 && s1->x2 >= s2->x)
205 1822 100         && (s1->y <= s2->y2 && s1->y2 >= s2->y);
    100          
    100          
206             }
207             }
208             }
209             }
210              
211             /* circle vs rectangle - circle first */
212             /* circles first, if available */
213 58 50         if (s2->type == shape_circle) {
214             Shape *stemp;
215 0           stemp = s1;
216 0           s1 = s2;
217 0           s2 = stemp;
218             }
219              
220 116           double check_x = s1->x0 < s2->x
221 20           ? s2->x - s1->x0
222 96 100         : s1->x0 > s2->x2
223 5           ? s2->x2 - s1->x0
224 38 100         : 0
225             ;
226              
227 116           double check_y = s1->y0 < s2->y
228 20           ? s2->y - s1->y0
229 96 100         : s1->y0 > s2->y2
230 5           ? s2->y2 - s1->y0
231 38 100         : 0
232             ;
233              
234 58           return check_x * check_x + check_y * check_y <= s1->radius_sq;
235             }
236              
237 638           void find_nodes(QuadTreeNode *node, HV *ret, Shape *param, bool fully_contained)
238             {
239 638 100         if (!node->has_objects) return;
240              
241 606 100         fully_contained = fully_contained || shape_contained(node->dimensions, param);
    100          
242 606 100         if (!(fully_contained || shapes_overlap(param, node->dimensions))) return;
    100          
243              
244             int i;
245 440 100         for (i = 0; i < node->values->count; ++i) {
246 157           SV *fetched = (SV*) node->values->ptr[i];
247 157           SvREFCNT_inc(fetched);
248 157           hv_store_ent(ret, fetched, fetched, 0);
249             }
250              
251 283 100         if (node->children != NULL) {
252 710 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
253 568           find_nodes(&node->children[i], ret, param, fully_contained);
254             }
255             }
256             }
257              
258 475           bool fill_nodes (QuadTreeNode *node, SV *value, Shape *param)
259             {
260 475 50         if (shape_contained(node->dimensions, param)) {
261 0           push_array(node->values, value);
262             }
263             else {
264 475 100         if (!shapes_overlap(param, node->dimensions)) return false;
265              
266 165 100         if (node->children == NULL) {
267 60           push_array(node->values, value);
268             }
269             else {
270             int i;
271 525 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
272 420           fill_nodes(&node->children[i], value, param);
273             }
274             }
275             }
276              
277             /* NOTE: only first level result is important, since if the object fits in
278             * the tree area at all, it must fit into one of the leaves */
279 165           node->has_objects = true;
280 165           return true;
281             }
282              
283 9           void delete_nodes(QuadTreeNode *node, SV *value, Shape *param, bool fully_contained)
284             {
285 9 50         if (!node->has_objects) return;
286              
287 9 50         fully_contained = fully_contained || shape_contained(node->dimensions, param);
    50          
288 9 50         if (!(fully_contained || shapes_overlap(param, node->dimensions))) return;
    100          
289              
290             int i;
291              
292 3 100         if (node->values->count > 0) {
293 1           DynArr* new_list = create_array();
294              
295 3 100         for (i = 0; i < node->values->count; ++i) {
296 2           SV *fetched = (SV*) node->values->ptr[i];
297 2 100         if (!sv_eq(fetched, value)) {
298 1           push_array(new_list, fetched);
299             }
300             }
301              
302 1           destroy_array(node->values);
303 1           node->values = new_list;
304 1           node->has_objects = new_list->count > 0;
305             }
306              
307 3 100         if (node->children != NULL) {
308 10 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
309 8           delete_nodes(&node->children[i], value, param, fully_contained);
310 8 50         node->has_objects = node->has_objects || node->children[i].has_objects;
    0          
311             }
312             }
313             }
314              
315 72           void clear_node(QuadTreeNode *node)
316             {
317 72 100         if (!node->has_objects) return;
318 59           node->has_objects = false;
319 59           clear_array(node->values);
320              
321 59 100         if (node->children != NULL) {
322             int i;
323 80 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
324 64           clear_node(&node->children[i]);
325             }
326             }
327             }
328              
329 8           void clear_tree(QuadTreeRootNode *root)
330             {
331 8           clear_node(root->node);
332              
333             int i;
334             char *key;
335             I32 retlen;
336             SV *value;
337              
338 8           hv_iterinit(root->backref);
339 62 100         while ((value = hv_iternextsv(root->backref, &key, &retlen)) != NULL) {
340 54           destroy_shape((Shape*) SvIV(value));
341             }
342              
343 62 100         for (i = 0; i < root->objects->count; ++i) {
344 54           SvREFCNT_dec((SV*) root->objects->ptr[i]);
345             }
346              
347 8           hv_clear(root->backref);
348 8           clear_array(root->objects);
349 8           }
350              
351 6           void filter_geometry(HV* results, HV* shapes, Shape *s)
352             {
353             HE *he;
354              
355 6           hv_iterinit(results);
356 13 100         while ((he = hv_iternext(results)) != NULL) {
357             STRLEN len;
358 7 50         char *key = HePV(he, len);
359 7           SV **fetched = hv_fetch(shapes, key, len, 0);
360 7           Shape* s2 = (Shape*) SvIV(*fetched);
361              
362 7 100         if (!shapes_overlap(s, s2))
363 4           hv_delete(results, key, len, 0);
364             }
365 6           }
366              
367             /* XS helpers */
368              
369 229           SV* get_hash_key (HV* hash, const char* key, int len)
370             {
371 229           SV **value = hv_fetch(hash, key, len, 0);
372              
373 229 100         if (value == NULL) return NULL;
374 165           return *value;
375             }
376              
377 134           QuadTreeRootNode* get_root_from_perl(SV *self)
378             {
379 134           SV *value = get_hash_key((HV*) SvRV(self), "ROOT", 4);
380 134 50         if (value == NULL)
381 0           croak("quad tree root node is undefined");
382              
383 134           return (QuadTreeRootNode*) SvIV(value);
384             }
385              
386 70           AV* get_hash_values (HV* hash)
387             {
388 70           AV *ret = newAV();
389             HE *he;
390              
391 70           hv_iterinit(hash);
392 223 100         while ((he = hv_iternext(hash)) != NULL) {
393 153           SV *fetched = HeVAL(he);
394 153           SvREFCNT_inc(fetched);
395 153           av_push(ret, fetched);
396             }
397              
398 70           return ret;
399             }
400