File Coverage

XS.xs
Criterion Covered Total %
statement 174 175 99.4
branch 73 82 89.0
condition n/a
subroutine n/a
pod n/a
total 247 257 96.1


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