File Coverage

XS.xs
Criterion Covered Total %
statement 186 187 99.4
branch 84 94 89.3
condition n/a
subroutine n/a
pod n/a
total 270 281 96.0


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #include "ppport.h"
6              
7             #define CHILDREN_PER_NODE 4
8              
9             typedef struct QuadTreeNode QuadTreeNode;
10             typedef struct QuadTreeRootNode QuadTreeRootNode;
11             typedef struct DynArr DynArr;
12             typedef struct Shape Shape;
13              
14             typedef enum ShapeType ShapeType;
15              
16             struct QuadTreeNode {
17             QuadTreeNode *children;
18             QuadTreeNode *parent;
19             DynArr *values;
20             double xmin, ymin, xmax, ymax;
21             bool has_objects;
22             };
23              
24             struct QuadTreeRootNode {
25             QuadTreeNode *node;
26             HV *backref;
27             };
28              
29             struct DynArr {
30             void **ptr;
31             unsigned int count;
32             unsigned int max_size;
33             };
34              
35             enum ShapeType {
36             shape_rectangle,
37             shape_circle
38             };
39              
40             struct Shape {
41             ShapeType type;
42             double dimensions[4];
43             };
44              
45 163           DynArr* create_array()
46             {
47 163           DynArr *arr = malloc(sizeof *arr);
48 163           arr->count = 0;
49 163           arr->max_size = 0;
50              
51 163           return arr;
52             }
53              
54 163           void destroy_array(DynArr* arr)
55             {
56 163 100         if (arr->max_size > 0) {
57 95           free(arr->ptr);
58             }
59              
60 163           free(arr);
61 163           }
62              
63 110           void destroy_array_SV(DynArr* arr)
64             {
65             int i;
66 169 100         for (i = 0; i < arr->count; ++i) {
67 59           SvREFCNT_dec((SV*) arr->ptr[i]);
68             }
69              
70 110           destroy_array(arr);
71 110           }
72              
73 117           void push_array(DynArr *arr, void *ptr)
74             {
75 117 100         if (arr->max_size == 0) {
76 95           arr->max_size = 2;
77 95           arr->ptr = malloc(arr->max_size * sizeof *arr->ptr);
78             }
79 22 100         else if (arr->count == arr->max_size) {
80 2           arr->max_size *= 2;
81              
82 2           void *enlarged = realloc(arr->ptr, arr->max_size * sizeof *arr->ptr);
83             assert(enlarged != NULL);
84              
85 2           arr->ptr = enlarged;
86             }
87              
88 117           arr->ptr[arr->count] = ptr;
89 117           arr->count += 1;
90 117           }
91              
92 59           void push_array_SV(DynArr *arr, SV *ptr)
93             {
94 59           push_array(arr, ptr);
95 59           SvREFCNT_inc(ptr);
96 59           }
97              
98 20           QuadTreeNode* create_nodes(int count, QuadTreeNode *parent)
99             {
100 20           QuadTreeNode *node = malloc(count * sizeof *node);
101              
102             int i;
103 88 100         for (i = 0; i < count; ++i) {
104 68           node[i].values = NULL;
105 68           node[i].children = NULL;
106 68           node[i].parent = parent;
107 68           node[i].has_objects = false;
108             }
109              
110 20           return node;
111             }
112              
113             // NOTE: does not actually free the node, but frees its children nodes
114 68           void destroy_node(QuadTreeNode *node)
115             {
116 68 100         if (node->values != NULL) {
117 52           destroy_array_SV(node->values);
118             }
119             else {
120             int i;
121 80 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
122 64           destroy_node(&node->children[i]);
123             }
124              
125 16           free(node->children);
126             }
127 68           }
128              
129 133           void clear_has_objects (QuadTreeNode *node)
130             {
131 133 100         if (node->values == NULL) {
132             int i;
133 233 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
134 202 100         if (node->children[i].has_objects) return;
135             }
136             }
137              
138 88           node->has_objects = false;
139 88 100         if (node->parent != NULL) {
140 76           clear_has_objects(node->parent);
141             }
142             }
143              
144 4           QuadTreeRootNode* create_root()
145             {
146 4           QuadTreeRootNode *root = malloc(sizeof *root);
147 4           root->node = create_nodes(1, NULL);
148 4           root->backref = newHV();
149              
150 4           return root;
151             }
152              
153 58           void store_backref(QuadTreeRootNode *root, QuadTreeNode* node, SV *value)
154             {
155             DynArr *list;
156 58 100         if (!hv_exists_ent(root->backref, value, 0)) {
157 53           list = create_array();
158 53           hv_store_ent(root->backref, value, newSViv((unsigned long) list), 0);
159             }
160             else {
161 5 50         list = (DynArr*) SvIV(HeVAL(hv_fetch_ent(root->backref, value, 0, 0)));
162             }
163              
164 58           push_array(list, node);
165 58           }
166              
167 68           void node_add_level(QuadTreeNode* node, double xmin, double ymin, double xmax, double ymax, int depth)
168             {
169 68           bool last = --depth == 0;
170              
171 68           node->xmin = xmin;
172 68           node->ymin = ymin;
173 68           node->xmax = xmax;
174 68           node->ymax = ymax;
175              
176 68 100         if (last) {
177 52           node->values = create_array();
178             }
179             else {
180 16           node->children = create_nodes(CHILDREN_PER_NODE, node);
181 16           double xmid = xmin + (xmax - xmin) / 2;
182 16           double ymid = ymin + (ymax - ymin) / 2;
183              
184 16           node_add_level(&node->children[0], xmin, ymin, xmid, ymid, depth);
185 16           node_add_level(&node->children[1], xmin, ymid, xmid, ymax, depth);
186 16           node_add_level(&node->children[2], xmid, ymin, xmax, ymid, depth);
187 16           node_add_level(&node->children[3], xmid, ymid, xmax, ymax, depth);
188             }
189 68           }
190              
191 1014           bool is_within_node_rect(QuadTreeNode *node, double xmin, double ymin, double xmax, double ymax)
192             {
193 1834 100         return (xmin <= node->xmax && xmax >= node->xmin)
194 1834 100         && (ymin <= node->ymax && ymax >= node->ymin);
    100          
    100          
195             }
196              
197 39           bool is_within_node_circ(QuadTreeNode *node, double x, double y, double radius)
198             {
199 78           double check_x = x < node->xmin
200             ? node->xmin
201 64 100         : x > node->xmax
202             ? node->xmax
203 25 100         : x
204             ;
205              
206 78           double check_y = y < node->ymin
207             ? node->ymin
208 64 100         : y > node->ymax
209             ? node->ymax
210 25 100         : y
211             ;
212              
213 39           check_x -= x;
214 39           check_y -= y;
215              
216 39           return check_x * check_x + check_y * check_y <= radius * radius;
217             }
218              
219 1053           bool is_within_node(QuadTreeNode *node, Shape *param)
220             {
221 1053           switch (param->type) {
222             case shape_rectangle:
223 1014           return is_within_node_rect(node, param->dimensions[0], param->dimensions[1], param->dimensions[2], param->dimensions[3]);
224             case shape_circle:
225 39           return is_within_node_circ(node, param->dimensions[0], param->dimensions[1], param->dimensions[2]);
226             }
227 0           }
228              
229 608           void find_nodes(QuadTreeNode *node, AV *ret, Shape *param)
230             {
231 608 100         if (!node->has_objects || !is_within_node(node, param)) return;
    100          
232              
233             int i;
234              
235 270 100         if (node->values != NULL) {
236 284 100         for (i = 0; i < node->values->count; ++i) {
237 150           SV *fetched = (SV*) node->values->ptr[i];
238 150           SvREFCNT_inc(fetched);
239 150           av_push(ret, fetched);
240             }
241             }
242             else {
243 680 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
244 544           find_nodes(&node->children[i], ret, param);
245             }
246             }
247             }
248              
249 465           void fill_nodes(QuadTreeRootNode *root, QuadTreeNode *node, SV *value, Shape *param)
250             {
251 465 100         if (!is_within_node(node, param)) return;
252              
253 161           node->has_objects = true;
254 161 100         if (node->values != NULL) {
255 58           push_array_SV(node->values, value);
256 58           store_backref(root, node, value);
257             }
258             else {
259             int i;
260 515 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
261 412           fill_nodes(root, &node->children[i], value, param);
262             }
263             }
264             }
265              
266             // XS helpers
267              
268 290           SV* get_hash_key (HV* hash, const char* key)
269             {
270 290           SV **value = hv_fetch(hash, key, strlen(key), 0);
271              
272             assert(value != NULL);
273 290           return *value;
274             }
275              
276 125           QuadTreeRootNode* get_root_from_perl(SV *self)
277             {
278 125           HV *params = (HV*) SvRV(self);
279              
280 125 50         return (QuadTreeRootNode*) SvIV(get_hash_key(params, "ROOT"));
281             }
282              
283 7           void clear_tree(QuadTreeRootNode *root)
284             {
285             char *key;
286             I32 retlen;
287             SV *value;
288             int i;
289              
290 7           hv_iterinit(root->backref);
291 59 100         while ((value = hv_iternextsv(root->backref, &key, &retlen)) != NULL) {
292 52 50         DynArr *list = (DynArr*) SvIV(value);
293 109 100         for (i = 0; i < list->count; ++i) {
294 57           QuadTreeNode *node = (QuadTreeNode*) list->ptr[i];
295 57           destroy_array_SV(node->values);
296 57           node->values = create_array();
297 57           clear_has_objects(node);
298             }
299              
300 52           destroy_array(list);
301             }
302              
303 7           hv_clear(root->backref);
304 7           }
305              
306             // proper XS Code starts here
307              
308             MODULE = Algorithm::QuadTree::XS PACKAGE = Algorithm::QuadTree::XS
309              
310             PROTOTYPES: DISABLE
311              
312             void
313             _AQT_init(obj)
314             SV *obj
315             CODE:
316 4           QuadTreeRootNode *root = create_root();
317              
318 4           HV *params = (HV*) SvRV(obj);
319              
320 40 50         node_add_level(root->node,
    50          
    50          
321 8           SvNV(get_hash_key(params, "XMIN")),
322 8           SvNV(get_hash_key(params, "YMIN")),
323 8           SvNV(get_hash_key(params, "XMAX")),
324 8           SvNV(get_hash_key(params, "YMAX")),
325 8           SvIV(get_hash_key(params, "DEPTH"))
326             );
327              
328 4           SV *root_sv = newSViv((unsigned long) root);
329 4           SvREADONLY_on(root_sv);
330 4           hv_stores(params, "ROOT", root_sv);
331              
332             void
333             _AQT_deinit(self)
334             SV *self
335             CODE:
336 4           QuadTreeRootNode *root = get_root_from_perl(self);
337              
338 4           clear_tree(root);
339 4           destroy_node(root->node);
340 4           free(root->node);
341 4           SvREFCNT_dec((SV*) root->backref);
342              
343 4           free(root);
344              
345              
346             void
347             _AQT_addObject(self, object, x, y, x2_or_radius, ...)
348             SV *self
349             SV *object
350             double x
351             double y
352             double x2_or_radius
353             CODE:
354 53           QuadTreeRootNode *root = get_root_from_perl(self);
355              
356             Shape param;
357 53           param.type = shape_circle;
358 53           param.dimensions[0] = x;
359 53           param.dimensions[1] = y;
360 53           param.dimensions[2] = x2_or_radius;
361              
362 53 100         if (items > 5) {
363 48           param.type = shape_rectangle;
364 48 50         param.dimensions[3] = SvNV(ST(5));
365             }
366              
367 53           fill_nodes(root, root->node, object, ¶m);
368              
369             SV*
370             _AQT_findObjects(self, x, y, x2_or_radius, ...)
371             SV *self
372             double x
373             double y
374             double x2_or_radius
375             CODE:
376 64           QuadTreeRootNode *root = get_root_from_perl(self);
377              
378 64           AV *ret = newAV();
379              
380             Shape param;
381 64           param.type = shape_circle;
382 64           param.dimensions[0] = x;
383 64           param.dimensions[1] = y;
384 64           param.dimensions[2] = x2_or_radius;
385              
386 64 100         if (items > 4) {
387 61           param.type = shape_rectangle;
388 61 100         param.dimensions[3] = SvNV(ST(4));
389             }
390              
391 64           find_nodes(root->node, ret, ¶m);
392              
393 64           RETVAL = newRV_noinc((SV*) ret);
394             OUTPUT:
395             RETVAL
396              
397             void
398             _AQT_delete(self, object)
399             SV *self
400             SV *object
401             CODE:
402 1           QuadTreeRootNode *root = get_root_from_perl(self);
403              
404 1 50         if (hv_exists_ent(root->backref, object, 0)) {
405 1 50         DynArr* list = (DynArr*) SvIV(HeVAL(hv_fetch_ent(root->backref, object, 0, 0)));
406              
407             int i, j;
408 2 100         for (i = 0; i < list->count; ++i) {
409 1           QuadTreeNode *node = (QuadTreeNode*) list->ptr[i];
410 1           DynArr* new_list = create_array();
411              
412 3 100         for(j = 0; j < node->values->count; ++j) {
413 2           SV *fetched = (SV*) node->values->ptr[j];
414 2 100         if (!sv_eq(fetched, object)) {
415 1           push_array_SV(new_list, fetched);
416             }
417             }
418              
419 1           destroy_array_SV(node->values);
420 1           node->values = new_list;
421 1 50         if (new_list->count == 0) clear_has_objects(node);
422             }
423              
424 1           destroy_array(list);
425 1           hv_delete_ent(root->backref, object, 0, 0);
426             }
427              
428             void
429             _AQT_clear(self)
430             SV* self
431             CODE:
432 3           QuadTreeRootNode *root = get_root_from_perl(self);
433 3           clear_tree(root);
434