File Coverage

XS.xs
Criterion Covered Total %
statement 171 172 99.4
branch 73 82 89.0
condition n/a
subroutine n/a
pod n/a
total 244 254 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             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 298           SV* get_hash_key (HV* hash, const char* key)
249             {
250 298           SV **value = hv_fetch(hash, key, strlen(key), 0);
251              
252             assert(value != NULL);
253 298           return *value;
254             }
255              
256 129           QuadTreeRootNode* get_root_from_perl(SV *self)
257             {
258 129           HV *params = (HV*) SvRV(self);
259              
260 129 50         return (QuadTreeRootNode*) SvIV(get_hash_key(params, "ROOT"));
261             }
262              
263             // proper XS Code starts here
264              
265             MODULE = Algorithm::QuadTree::XS PACKAGE = Algorithm::QuadTree::XS
266              
267             PROTOTYPES: DISABLE
268              
269             void
270             _AQT_init(obj)
271             SV *obj
272             CODE:
273 4           QuadTreeRootNode *root = create_root();
274              
275 4           HV *params = (HV*) SvRV(obj);
276              
277 40 50         node_add_level(root->node,
    50          
    50          
278 8           SvNV(get_hash_key(params, "XMIN")),
279 8           SvNV(get_hash_key(params, "YMIN")),
280 8           SvNV(get_hash_key(params, "XMAX")),
281 8           SvNV(get_hash_key(params, "YMAX")),
282 8           SvIV(get_hash_key(params, "DEPTH"))
283             );
284              
285 4           SV *root_sv = newSViv((unsigned long) root);
286 4           SvREADONLY_on(root_sv);
287 4           hv_stores(params, "ROOT", root_sv);
288              
289             void
290             _AQT_deinit(self)
291             SV *self
292             CODE:
293 4           QuadTreeRootNode *root = get_root_from_perl(self);
294              
295 4           call_method("_AQT_clear", 0);
296 4           destroy_node(root->node);
297 4           free(root->node);
298 4           SvREFCNT_dec((SV*) root->backref);
299              
300 4           free(root);
301              
302              
303             void
304             _AQT_addObject(self, object, x, y, x2_or_radius, ...)
305             SV *self
306             SV *object
307             double x
308             double y
309             double x2_or_radius
310             CODE:
311 53           QuadTreeRootNode *root = get_root_from_perl(self);
312              
313             Shape param;
314 53           param.type = shape_circle;
315 53           param.dimensions[0] = x;
316 53           param.dimensions[1] = y;
317 53           param.dimensions[2] = x2_or_radius;
318              
319 53 100         if (items > 5) {
320 48           param.type = shape_rectangle;
321 48 50         param.dimensions[3] = SvNV(ST(5));
322             }
323              
324 53           fill_nodes(root, root->node, object, ¶m);
325              
326             SV*
327             _AQT_findObjects(self, x, y, x2_or_radius, ...)
328             SV *self
329             double x
330             double y
331             double x2_or_radius
332             CODE:
333 64           QuadTreeRootNode *root = get_root_from_perl(self);
334              
335 64           AV *ret = newAV();
336              
337             Shape param;
338 64           param.type = shape_circle;
339 64           param.dimensions[0] = x;
340 64           param.dimensions[1] = y;
341 64           param.dimensions[2] = x2_or_radius;
342              
343 64 100         if (items > 4) {
344 61           param.type = shape_rectangle;
345 61 100         param.dimensions[3] = SvNV(ST(4));
346             }
347              
348 64           find_nodes(root->node, ret, ¶m);
349              
350 64           RETVAL = newRV_noinc((SV*) ret);
351             OUTPUT:
352             RETVAL
353              
354             void
355             _AQT_delete(self, object)
356             SV *self
357             SV *object
358             CODE:
359 1           QuadTreeRootNode *root = get_root_from_perl(self);
360              
361 1 50         if (hv_exists_ent(root->backref, object, 0)) {
362 1 50         DynArr* list = (DynArr*) SvIV(HeVAL(hv_fetch_ent(root->backref, object, 0, 0)));
363              
364             int i, j;
365 2 100         for (i = 0; i < list->count; ++i) {
366 1           QuadTreeNode *node = (QuadTreeNode*) list->ptr[i];
367 1           DynArr* new_list = create_array();
368              
369 3 100         for(j = 0; j < node->values->count; ++j) {
370 2           SV *fetched = (SV*) node->values->ptr[j];
371 2 100         if (!sv_eq(fetched, object)) {
372 1           push_array_SV(new_list, fetched);
373             }
374             }
375              
376 1           destroy_array_SV(node->values);
377 1           node->values = new_list;
378             }
379              
380 1           destroy_array(list);
381 1           hv_delete_ent(root->backref, object, 0, 0);
382             }
383              
384             void
385             _AQT_clear(self)
386             SV* self
387             CODE:
388 7           QuadTreeRootNode *root = get_root_from_perl(self);
389              
390             char *key;
391             I32 retlen;
392             SV *value;
393             int i;
394              
395 7           hv_iterinit(root->backref);
396 59 100         while ((value = hv_iternextsv(root->backref, &key, &retlen)) != NULL) {
397 52 50         DynArr *list = (DynArr*) SvIV(value);
398 109 100         for (i = 0; i < list->count; ++i) {
399 57           QuadTreeNode *node = (QuadTreeNode*) list->ptr[i];
400 57           destroy_array_SV(node->values);
401 57           node->values = create_array();
402             }
403              
404 52           destroy_array(list);
405             }
406              
407 7           hv_clear(root->backref);
408