1 /* EINA - EFL data type library
2 * Copyright (C) 2010 Cedric Bail
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
20 * @page tutorial_quadtree_page QuadTree Tutorial
37 #include "eina_quadtree.h"
38 #include "eina_magic.h"
39 #include "eina_mempool.h"
40 #include "eina_list.h"
41 #include "eina_inlist.h"
42 #include "eina_trash.h"
44 #include "eina_rectangle.h"
46 #include "eina_private.h"
48 typedef struct _Eina_QuadTree_Root Eina_QuadTree_Root;
50 static const char EINA_MAGIC_QUADTREE_STR[] = "Eina QuadTree";
51 static const char EINA_MAGIC_QUADTREE_ROOT_STR[] = "Eina QuadTree Root";
52 static const char EINA_MAGIC_QUADTREE_ITEM_STR[] = "Eina QuadTree Item";
54 #define EINA_MAGIC_CHECK_QUADTREE(d, ...) \
56 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE)) \
58 EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE); \
63 #define EINA_MAGIC_CHECK_QUADTREE_ROOT(d, ...) \
65 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ROOT)) \
67 EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ROOT); \
72 #define EINA_MAGIC_CHECK_QUADTREE_ITEM(d, ...) \
74 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ITEM)) \
76 EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ITEM); \
83 Eina_QuadTree_Root *root;
90 Eina_Trash *items_trash;
91 Eina_Trash *root_trash;
95 Eina_Rectangle target;
101 Eina_Quad_Callback v;
102 Eina_Quad_Callback h;
111 Eina_Bool resize : 1;
117 struct _Eina_QuadTree_Root
119 Eina_QuadTree_Root *parent;
120 Eina_QuadTree_Root *left;
121 Eina_QuadTree_Root *right;
125 Eina_Bool sorted : 1;
130 struct _Eina_QuadTree_Item
135 Eina_QuadTree_Root *root;
141 Eina_Bool change : 1;
142 Eina_Bool delete_me : 1;
143 Eina_Bool visible : 1;
144 Eina_Bool hidden : 1;
149 static int _eina_quadtree_log_dom = -1;
150 static Eina_Mempool *eina_quadtree_root_mp = NULL;
151 static Eina_Mempool *_eina_quadtree_items_mp = NULL;
156 #define ERR(...) EINA_LOG_DOM_ERR(_eina_quadtree_log_dom, __VA_ARGS__)
161 #define DBG(...) EINA_LOG_DOM_DBG(_eina_quadtree_log_dom, __VA_ARGS__)
165 _eina_quadtree_item_cmp(const void *a, const void *b)
167 const Eina_QuadTree_Item *i = a;
168 const Eina_QuadTree_Item *j = b;
170 return i->index - j->index;
173 static Eina_QuadTree_Root *
174 eina_quadtree_root_free(Eina_QuadTree *q, Eina_QuadTree_Root *root)
176 Eina_QuadTree_Item *item;
181 EINA_MAGIC_CHECK_QUADTREE_ROOT(root, NULL);
183 EINA_LIST_FREE(root->both, item)
184 eina_mempool_free(_eina_quadtree_items_mp, item);
186 root->left = eina_quadtree_root_free(q, root->left);
187 root->right = eina_quadtree_root_free(q, root->right);
189 EINA_MAGIC_SET(root, 0);
190 eina_mempool_free(eina_quadtree_root_mp, root);
195 static Eina_QuadTree_Root *
196 eina_quadtree_root_rebuild_pre(Eina_QuadTree *q,
197 Eina_Inlist **change,
198 Eina_QuadTree_Root *root)
200 Eina_QuadTree_Item *item;
205 EINA_LIST_FREE(root->both, item)
208 *change = eina_inlist_append(*change, EINA_INLIST_GET(item));
209 else if (!item->hidden)
211 q->hidden = eina_list_append(q->hidden, item);
212 item->hidden = EINA_TRUE;
217 root->left = eina_quadtree_root_rebuild_pre(q, change, root->left);
218 root->right = eina_quadtree_root_rebuild_pre(q, change, root->right);
220 EINA_MAGIC_SET(root, 0);
221 if (q->root_count > 50)
222 eina_mempool_free(eina_quadtree_root_mp, root);
225 eina_trash_push(&q->root_trash, root);
233 _eina_quadtree_split(Eina_Inlist *objects,
234 Eina_QuadTree_Root *root,
237 Eina_Quad_Callback func,
241 Eina_QuadTree_Item *object;
248 object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item);
249 objects = objects->next;
251 object->change = EINA_FALSE;
252 if (!object->visible)
256 object->hidden = EINA_TRUE;
257 object->quad->hidden = eina_list_append(
258 object->quad->hidden,
267 object->hidden = EINA_FALSE;
268 object->quad->hidden = eina_list_remove(object->quad->hidden,
272 if (!object->delete_me)
275 root->both = eina_list_sorted_insert(root->both,
276 _eina_quadtree_item_cmp,
279 root->both = eina_list_append(root->both, object);
284 eina_quadtree_del(object);
289 object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item);
290 objects = objects->next;
292 object->change = EINA_FALSE;
293 if (!object->visible)
297 object->hidden = EINA_TRUE;
298 object->quad->hidden = eina_list_append(
299 object->quad->hidden,
308 object->hidden = EINA_FALSE;
309 object->quad->hidden = eina_list_remove(object->quad->hidden,
313 if (!object->delete_me)
315 switch (func(object->object, border + middle))
318 *left = eina_inlist_append(*left, EINA_INLIST_GET(object));
321 case EINA_QUAD_RIGHT:
323 eina_inlist_append(*right, EINA_INLIST_GET(object));
327 root->both = eina_list_append(root->both, object);
336 eina_quadtree_del(object);
343 static Eina_QuadTree_Root *
344 _eina_quadtree_update(Eina_QuadTree *q, Eina_QuadTree_Root *parent,
345 Eina_QuadTree_Root *root, Eina_Inlist *objects,
346 Eina_Bool direction, Eina_Rectangle *size)
348 Eina_Inlist *right = NULL;
349 Eina_Inlist *left = NULL;
358 root = eina_trash_pop(&q->root_trash);
360 root = eina_mempool_malloc(eina_quadtree_root_mp, sizeof (Eina_QuadTree_Root));
365 /* FIXME: NOT GOOD TIMING, WE ARE GOING TO LEAK MORE MEMORY */
368 root->parent = parent;
372 root->sorted = EINA_TRUE;
374 EINA_MAGIC_SET(root, EINA_MAGIC_QUADTREE_ROOT);
381 w2 = _eina_quadtree_split(objects, root,
383 q->func.h, size->x, size->w);
385 h2 = _eina_quadtree_split(objects, root,
387 q->func.v, size->y, size->h);
389 size->w -= w2; size->h -= h2;
390 root->left = _eina_quadtree_update(q, root,
393 size->x += w2; size->y += h2;
394 root->right = _eina_quadtree_update(q, root,
397 size->x -= w2; size->y -= h2;
398 size->w += w2; size->h += h2;
404 _eina_quadtree_merge(Eina_Inlist *result,
407 Eina_QuadTree_Item *item;
408 Eina_QuadTree_Item *b;
418 EINA_LIST_FOREACH(both, l, item)
420 result = eina_inlist_append(result, EINA_INLIST_GET(item));
427 item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
428 b = eina_list_data_get(both);
430 while (both && moving)
434 both = eina_list_next(both);
435 b = eina_list_data_get(both);
439 if (_eina_quadtree_item_cmp(item, b) < 0)
441 /* moving is still lower than item, so we can continue to the next one. */
442 moving = moving->next;
443 item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
447 /* we just get above the limit of both, so insert it */
448 result = eina_inlist_prepend_relative(result,
451 both = eina_list_next(both);
452 b = eina_list_data_get(both);
456 item = EINA_INLIST_CONTAINER_GET(result->last, Eina_QuadTree_Item);
460 b = eina_list_data_get(both);
463 if (_eina_quadtree_item_cmp(item, b) < 0)
466 result = eina_inlist_prepend_relative(result,
471 both = eina_list_next(both);
476 b = eina_list_data_get(both);
478 result = eina_inlist_append(result, EINA_INLIST_GET(b));
480 both = eina_list_next(both);
487 _eina_quadtree_collide(Eina_Inlist *result,
488 Eina_QuadTree_Root *root,
489 Eina_Bool direction, Eina_Rectangle *size,
490 Eina_Rectangle *target)
497 root->both = eina_list_sort(root->both, -1, _eina_quadtree_item_cmp);
498 root->sorted = EINA_TRUE;
501 result = _eina_quadtree_merge(result, root->both);
502 DBG("%p: %i in both for (%i, %i - %i, %i)",
503 root, eina_list_count(root->both),
504 size->x, size->y, size->w, size->h);
508 int middle = size->w / 2;
511 if (eina_spans_intersect(size->x, size->w, target->x, target->w))
512 result = _eina_quadtree_collide(result, root->left,
517 if (eina_spans_intersect(size->x, size->w, target->x, target->w))
518 result = _eina_quadtree_collide(result, root->right,
527 int middle = size->h / 2;
530 if (eina_spans_intersect(size->y, size->h, target->y, target->h))
531 result = _eina_quadtree_collide(result, root->left,
536 if (eina_spans_intersect(size->y, size->h, target->y, target->h))
537 result = _eina_quadtree_collide(result, root->right,
549 _eina_quadtree_remove(Eina_QuadTree_Item *object)
554 object->root->both = eina_list_remove(object->root->both, object);
555 if (object->root->both)
558 if (object->root->left)
561 if (object->root->right)
564 /* The root is not useful anymore... */
565 if (object->root->parent)
567 if (object->root->parent->left == object->root)
568 object->root->parent->left = NULL;
570 object->root->parent->right = NULL;
572 object->root->parent = NULL;
575 object->quad->root = NULL;
577 if (object->quad->root_count > 50)
578 eina_mempool_free(eina_quadtree_root_mp, object->root);
581 eina_trash_push(&object->quad->root_trash, object->root);
582 object->quad->root_count++;
590 eina_quadtree_new(size_t w, size_t h,
591 Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal)
593 Eina_QuadTree *result;
595 if (!vertical || !horizontal || h == 0 || w == 0)
598 result = calloc(1, sizeof (Eina_QuadTree));
602 result->func.v = vertical;
603 result->func.h = horizontal;
608 result->change = NULL;
610 result->lost = EINA_TRUE;
612 EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE);
618 eina_quadtree_free(Eina_QuadTree *q)
620 Eina_QuadTree_Item *item;
625 EINA_MAGIC_CHECK_QUADTREE(q);
629 item = EINA_INLIST_CONTAINER_GET(q->change, Eina_QuadTree_Item);
630 q->change = q->change->next;
632 eina_mempool_free(_eina_quadtree_items_mp, item);
635 EINA_LIST_FREE(q->hidden, item)
636 eina_mempool_free(_eina_quadtree_items_mp, item);
638 eina_quadtree_root_free(q, q->root);
640 while (q->items_trash)
642 item = eina_trash_pop(&q->items_trash);
643 eina_mempool_free(_eina_quadtree_items_mp, item);
646 while (q->root_trash)
648 Eina_QuadTree_Root *root;
650 root = eina_trash_pop(&q->root_trash);
651 eina_mempool_free(eina_quadtree_root_mp, root);
654 EINA_MAGIC_SET(q, 0);
658 EAPI Eina_QuadTree_Item *
659 eina_quadtree_add(Eina_QuadTree *q, const void *object)
661 Eina_QuadTree_Item *result;
663 EINA_MAGIC_CHECK_QUADTREE(q, NULL);
668 result = eina_trash_pop(&q->items_trash);
670 result = eina_mempool_malloc(_eina_quadtree_items_mp, sizeof (Eina_QuadTree_Item));
679 result->object = object;
681 result->index = q->index++;
683 result->change = EINA_TRUE;
684 result->delete_me = EINA_FALSE;
685 result->visible = EINA_TRUE;
686 result->hidden = EINA_FALSE;
688 EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE_ITEM);
690 /* Insertion is delayed until we really need to use it */
691 q->change = eina_inlist_append(q->change, EINA_INLIST_GET(result));
697 eina_quadtree_del(Eina_QuadTree_Item *object)
702 EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
704 _eina_quadtree_remove(object);
708 /* This object is still in the update array, delaying it's removal !*/
709 object->delete_me = EINA_TRUE;
710 object->visible = EINA_TRUE;
716 object->quad->hidden = eina_list_remove(object->quad->hidden, object);
717 object->hidden = EINA_TRUE;
720 /* This object is not anymore inside the tree, we can remove it now !*/
721 EINA_MAGIC_SET(object, 0);
722 if (object->quad->items_count > 256)
723 eina_mempool_free(_eina_quadtree_items_mp, object);
726 object->quad->items_count++;
727 eina_trash_push(&object->quad->items_trash, object);
734 eina_quadtree_change(Eina_QuadTree_Item *object)
736 EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
738 if (object->delete_me || !object->visible)
741 if (object->quad->resize)
744 /* Delaying change until needed */
746 object->quad->change = eina_inlist_append(object->quad->change,
747 EINA_INLIST_GET(object));
749 object->change = EINA_TRUE;
751 _eina_quadtree_remove(object);
757 eina_quadtree_hide(Eina_QuadTree_Item *object)
759 EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
761 object->visible = EINA_FALSE;
767 eina_quadtree_show(Eina_QuadTree_Item *object)
769 EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
771 object->quad->lost = EINA_TRUE;
776 object->visible = EINA_TRUE;
778 return eina_quadtree_change(object);
784 eina_quadtree_collide(Eina_QuadTree *q, int x, int y, int w, int h)
786 Eina_Rectangle canvas;
788 EINA_MAGIC_CHECK_QUADTREE(q, NULL);
790 /* Now we need the tree to be up to date, so it's time */
791 if (q->resize) /* Full rebuild needed ! */
793 DBG("resizing quadtree");
794 q->root = eina_quadtree_root_rebuild_pre(q, &q->change, q->root);
795 q->resize = EINA_FALSE;
798 EINA_RECTANGLE_SET(&canvas, 0, 0, q->geom.w, q->geom.h);
802 DBG("updating quadtree content");
803 q->root = _eina_quadtree_update(q, NULL, q->root, q->change,
804 EINA_FALSE, &canvas);
815 EINA_RECTANGLE_SET(&q->target, x, y, w, h);
821 DBG("computing collide");
822 q->cached = _eina_quadtree_collide(NULL, q->root,
825 q->lost = EINA_FALSE;
832 eina_quadtree_object(Eina_Inlist *item)
834 Eina_QuadTree_Item *qi;
839 qi = EINA_INLIST_CONTAINER_GET(item, Eina_QuadTree_Item);
843 EINA_MAGIC_CHECK_QUADTREE_ITEM(qi, NULL);
848 return (void *)qi->object;
852 eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h)
854 EINA_MAGIC_CHECK_QUADTREE(q);
860 q->resize = EINA_TRUE;
866 eina_quadtree_cycle(Eina_QuadTree *q)
868 EINA_MAGIC_CHECK_QUADTREE(q);
874 eina_quadtree_increase(Eina_QuadTree_Item *object)
878 tmp = object->quad->index++;
879 if (object->index == tmp)
884 object->root->sorted = EINA_FALSE;
888 eina_quadtree_init(void)
890 const char *choice, *tmp;
892 _eina_quadtree_log_dom = eina_log_domain_register("eina_quadtree",
893 EINA_LOG_COLOR_DEFAULT);
894 if (_eina_quadtree_log_dom < 0)
896 EINA_LOG_ERR("Could not register log domain: eina_quadtree");
900 #define EMS(n) eina_magic_string_static_set(n, n ## _STR)
901 EMS(EINA_MAGIC_QUADTREE);
902 EMS(EINA_MAGIC_QUADTREE_ROOT);
903 EMS(EINA_MAGIC_QUADTREE_ITEM);
906 #ifdef EINA_DEFAULT_MEMPOOL
907 choice = "pass_through";
909 choice = "chained_mempool";
911 tmp = getenv("EINA_MEMPOOL");
915 _eina_quadtree_items_mp = eina_mempool_add(choice, "QuadTree Item", NULL,
916 sizeof (Eina_QuadTree_Item), 32);
917 eina_quadtree_root_mp = eina_mempool_add(choice, "QuadTree Root", NULL,
918 sizeof (Eina_QuadTree_Root), 8);
924 eina_quadtree_shutdown(void)
926 eina_mempool_del(eina_quadtree_root_mp);
927 eina_mempool_del(_eina_quadtree_items_mp);
929 eina_log_domain_unregister(_eina_quadtree_log_dom);
930 _eina_quadtree_log_dom = -1;