--- /dev/null
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_QUADTREE_H_
+#define EINA_QUADTREE_H_
+
+#include "eina_config.h"
+
+#include "eina_array.h"
+
+typedef struct _Eina_QuadTree Eina_QuadTree;
+typedef struct _Eina_QuadTree_Item Eina_QuadTree_Item;
+
+typedef enum {
+ EINA_QUAD_LEFT,
+ EINA_QUAD_RIGHT,
+ EINA_QUAD_BOTH
+} Eina_Quad_Direction;
+
+typedef Eina_Quad_Direction (*Eina_Quad_Callback)(const void *object, size_t middle);
+
+EAPI Eina_QuadTree *eina_quadtree_new(size_t w, size_t h,
+ Eina_Quad_Callback vertical,
+ Eina_Quad_Callback horizontal);
+EAPI void eina_quadtree_free(Eina_QuadTree *q);
+EAPI void eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h);
+
+EAPI Eina_QuadTree_Item *eina_quadtree_add(Eina_QuadTree *q, const void *object);
+EAPI Eina_Bool eina_quadtree_del(Eina_QuadTree_Item *object);
+EAPI Eina_Bool eina_quadtree_change(Eina_QuadTree_Item *object);
+EAPI Eina_Bool eina_quadtree_hide(Eina_QuadTree_Item *object);
+EAPI Eina_Bool eina_quadtree_show(Eina_QuadTree_Item *object);
+
+EAPI void eina_quadtree_collide(Eina_Array *result, Eina_QuadTree *q,
+ int x, int y, size_t w, size_t h);
+
+#endif
--- /dev/null
+/*
+ * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
+ */
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page tutorial_quadtree_page QuadTree Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "eina_quadtree.h"
+#include "eina_magic.h"
+#include "eina_mempool.h"
+#include "eina_list.h"
+#include "eina_inlist.h"
+#include "eina_trash.h"
+#include "eina_log.h"
+#include "eina_rectangle.h"
+
+#include "eina_private.h"
+
+typedef struct _Eina_QuadTree_Root Eina_QuadTree_Root;
+
+static const char EINA_MAGIC_QUADTREE_STR[] = "Eina QuadTree";
+static const char EINA_MAGIC_QUADTREE_ROOT_STR[] = "Eina QuadTree Root";
+static const char EINA_MAGIC_QUADTREE_ITEM_STR[] = "Eina QuadTree Item";
+
+#define EINA_MAGIC_CHECK_QUADTREE(d, ...) \
+ do { \
+ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE)) \
+ { \
+ EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE); \
+ return __VA_ARGS__; \
+ } \
+ } while(0);
+
+#define EINA_MAGIC_CHECK_QUADTREE_ROOT(d, ...) \
+ do { \
+ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ROOT)) \
+ { \
+ EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ROOT); \
+ return __VA_ARGS__; \
+ } \
+ } while(0);
+
+#define EINA_MAGIC_CHECK_QUADTREE_ITEM(d, ...) \
+ do { \
+ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ITEM)) \
+ { \
+ EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ITEM); \
+ return __VA_ARGS__; \
+ } \
+ } while(0);
+
+struct _Eina_QuadTree
+{
+ EINA_MAGIC;
+
+ Eina_QuadTree_Root *root;
+
+ Eina_List *hidden;
+ Eina_Array *change;
+
+ size_t items_count;
+ Eina_Trash *items_trash;
+ Eina_Mempool *items_mp;
+
+ size_t root_count;
+ Eina_Trash *root_trash;
+ Eina_Mempool *root_mp;
+
+ size_t index;
+
+ struct {
+ Eina_Quad_Callback v;
+ Eina_Quad_Callback h;
+ } func;
+
+ struct {
+ size_t w;
+ size_t h;
+ } geom;
+
+ Eina_Bool resize : 1;
+};
+
+struct _Eina_QuadTree_Root
+{
+ EINA_MAGIC;
+
+ Eina_QuadTree_Root *parent;
+ Eina_QuadTree_Root *left;
+ Eina_QuadTree_Root *right;
+
+ Eina_List *both;
+
+ Eina_Bool sorted : 1;
+};
+
+struct _Eina_QuadTree_Item
+{
+ EINA_MAGIC;
+ EINA_INLIST;
+
+ Eina_QuadTree *quad;
+ Eina_QuadTree_Root *root;
+
+ const void *object;
+
+ size_t index;
+
+ Eina_Bool change : 1;
+ Eina_Bool delete_me : 1;
+ Eina_Bool visible : 1;
+ Eina_Bool hidden : 1;
+};
+
+static int _eina_log_qd_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_log_qd_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_log_qd_dom, __VA_ARGS__)
+
+
+static int
+_eina_quadtree_item_cmp(const void *a, const void *b)
+{
+ const Eina_QuadTree_Item *i = a;
+ const Eina_QuadTree_Item *j = b;
+
+ return i->index - j->index;
+}
+
+static Eina_QuadTree_Root *
+eina_quadtree_root_free(Eina_QuadTree *q, Eina_QuadTree_Root *root)
+{
+ if (!root) return NULL;
+
+ EINA_MAGIC_CHECK_QUADTREE_ROOT(root, NULL);
+
+ root->both = eina_list_free(root->both);
+
+ root->left = eina_quadtree_root_free(q, root->left);
+ root->right = eina_quadtree_root_free(q, root->right);
+
+ EINA_MAGIC_SET(root, 0);
+ /* eina_quadtree_root_free is only called when the memory pool will be destroyed */
+
+ return NULL;
+}
+
+static Eina_QuadTree_Root *
+eina_quadtree_root_rebuild_pre(Eina_QuadTree *q,
+ Eina_Array *change,
+ Eina_QuadTree_Root *root)
+{
+ Eina_QuadTree_Item *item;
+
+ if (!root) return NULL;
+
+ EINA_LIST_FREE(root->both, item)
+ {
+ if (item->visible)
+ {
+ eina_array_push(change, item);
+ }
+ else if (!item->hidden)
+ {
+ q->hidden = eina_list_append(q->hidden, item);
+ item->hidden = EINA_TRUE;
+ item->root = NULL;
+ }
+ }
+
+ root->left = eina_quadtree_root_rebuild_pre(q, change, root->left);
+ root->right = eina_quadtree_root_rebuild_pre(q, change, root->right);
+
+ EINA_MAGIC_SET(root, 0);
+ if (q->root_count > 50)
+ {
+ eina_mempool_free(q->root_mp, root);
+ }
+ else
+ {
+ eina_trash_push(&q->root_trash, root);
+ q->root_count++;
+ }
+
+ return NULL;
+}
+
+static size_t
+_eina_quadtree_split(Eina_Array *objects,
+ Eina_QuadTree_Root *root,
+ Eina_Array *left,
+ Eina_Array *right,
+ Eina_Quad_Callback func,
+ int border,
+ int middle)
+{
+ Eina_QuadTree_Item *object;
+ Eina_Array_Iterator it;
+ unsigned int i;
+
+ middle /= 2;
+
+ if (middle <= 4)
+ {
+ EINA_ARRAY_ITER_NEXT(objects, i, object, it)
+ {
+ object->change = EINA_FALSE;
+ if (!object->visible)
+ {
+ if (!object->hidden)
+ {
+ object->hidden = EINA_TRUE;
+ object->quad->hidden = eina_list_append(object->quad->hidden,
+ object);
+ }
+ continue;
+ }
+ if (object->hidden)
+ {
+ object->hidden = EINA_FALSE;
+ object->quad->hidden = eina_list_remove(object->quad->hidden, object);
+ }
+ if (!object->delete_me)
+ {
+ if (root->sorted)
+ {
+ root->both = eina_list_sorted_insert(root->both,
+ _eina_quadtree_item_cmp,
+ object);
+ }
+ else
+ {
+ root->both = eina_list_append(root->both, object);
+ }
+ object->root = root;
+ }
+ else
+ {
+ eina_quadtree_del(object);
+ }
+ }
+ }
+ else
+ {
+ EINA_ARRAY_ITER_NEXT(objects, i, object, it)
+ {
+ object->change = EINA_FALSE;
+ if (!object->visible)
+ {
+ if (!object->hidden)
+ {
+ object->hidden = EINA_TRUE;
+ object->quad->hidden = eina_list_append(object->quad->hidden,
+ object);
+ }
+ continue;
+ }
+ if (object->hidden)
+ {
+ object->hidden = EINA_FALSE;
+ object->quad->hidden = eina_list_remove(object->quad->hidden, object);
+ }
+ if (!object->delete_me)
+ {
+ switch (func(object->object, border + middle))
+ {
+ case EINA_QUAD_LEFT:
+ eina_array_push(left, object);
+ break;
+ case EINA_QUAD_RIGHT:
+ eina_array_push(right, object);
+ break;
+ case EINA_QUAD_BOTH:
+ root->both = eina_list_append(root->both, object);
+ object->root = root;
+ break;
+ default:
+ abort();
+ }
+ }
+ else
+ {
+ eina_quadtree_del(object);
+ }
+ }
+ }
+
+ eina_array_clean(objects);
+
+ return middle;
+}
+
+
+static Eina_QuadTree_Root *
+_eina_quadtree_update(Eina_QuadTree *q, Eina_QuadTree_Root *parent,
+ Eina_QuadTree_Root *root, Eina_Array *objects,
+ Eina_Bool direction, Eina_Rectangle *size)
+{
+ Eina_Array right;
+ Eina_Array left;
+ size_t w2;
+ size_t h2;
+
+ if (!objects || eina_array_count_get(objects) == 0)
+ return root;
+
+ if (!root)
+ {
+ root = eina_trash_pop(&q->root_trash);
+ if (!root) root = eina_mempool_malloc(q->root_mp, sizeof (Eina_QuadTree_Root));
+ else q->root_count--;
+ if (!root)
+ {
+ /* FIXME: NOT GOOD TIMING, WE ARE GOING TO LEAK MORE MEMORY */
+ return NULL;
+ }
+
+ root->parent = parent;
+ root->both = NULL;
+ root->left = NULL;
+ root->right = NULL;
+ root->sorted = EINA_TRUE;
+
+ EINA_MAGIC_SET(root, EINA_MAGIC_QUADTREE_ROOT);
+ }
+
+ eina_array_step_set(&right, 32);
+ eina_array_step_set(&left, 32);
+
+ w2 = 0;
+ h2 = 0;
+
+ if (direction)
+ w2 = _eina_quadtree_split(objects, root,
+ &left, &right,
+ q->func.h, size->x, size->w);
+ else
+ h2 = _eina_quadtree_split(objects, root,
+ &left, &right,
+ q->func.v, size->y, size->h);
+
+ size->w -= w2; size->h -= h2;
+ root->left = _eina_quadtree_update(q, root,
+ root->left, &left,
+ !direction, size);
+ size->x += w2; size->y += h2;
+ root->right = _eina_quadtree_update(q, root,
+ root->right, &right,
+ !direction, size);
+ size->x -= w2; size->y -= h2;
+ size->w += w2; size->h += h2;
+
+ eina_array_flush(&left);
+ eina_array_flush(&right);
+
+ return root;
+}
+
+static Eina_Inlist *
+_eina_quadtree_merge(Eina_Inlist *result,
+ Eina_List *both)
+{
+ Eina_QuadTree_Item *item;
+ Eina_QuadTree_Item *b;
+ Eina_Inlist *moving;
+
+ if (!both) return result;
+ if (!result)
+ {
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(both, l, item)
+ if (item->visible)
+ result = eina_inlist_append(result, EINA_INLIST_GET(item));
+
+ return result;
+ }
+
+ moving = result;
+
+ item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
+ b = eina_list_data_get(both);
+
+ while (both && moving)
+ {
+ if (!b->visible)
+ {
+ both = eina_list_next(both);
+ b = eina_list_data_get(both);
+ continue ;
+ }
+ if (_eina_quadtree_item_cmp(item, b) < 0)
+ {
+ /* moving is still lower than item, so we can continue to the next one. */
+ moving = moving->next;
+ item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
+ }
+ else
+ {
+ /* we just get above the limit of both, so insert it */
+ result = eina_inlist_prepend_relative(result,
+ EINA_INLIST_GET(b),
+ moving);
+ both = eina_list_next(both);
+ b = eina_list_data_get(both);
+ }
+ }
+
+ item = EINA_INLIST_CONTAINER_GET(result->last, Eina_QuadTree_Item);
+
+ while (both)
+ {
+ b = eina_list_data_get(both);
+ if (b->visible)
+ {
+ if (_eina_quadtree_item_cmp(item, b) < 0)
+ break;
+
+ result = eina_inlist_prepend_relative(result,
+ EINA_INLIST_GET(b),
+ result->last);
+ }
+ both = eina_list_next(both);
+ }
+
+ while (both)
+ {
+ b = eina_list_data_get(both);
+ if (b->visible)
+ result = eina_inlist_append(result, EINA_INLIST_GET(b));
+ both = eina_list_next(both);
+ }
+
+ return result;
+}
+
+static Eina_Inlist *
+_eina_quadtree_collide(Eina_Inlist *result,
+ Eina_QuadTree_Root *root,
+ Eina_Bool direction, Eina_Rectangle *size,
+ Eina_Rectangle *target)
+{
+ if (!root) return result;
+
+ if (!root->sorted)
+ {
+ root->both = eina_list_sort(root->both, -1, _eina_quadtree_item_cmp);
+ root->sorted = EINA_TRUE;
+ }
+
+ result = _eina_quadtree_merge(result, root->both);
+ DBG("%p: %i in both for (%i, %i - %i, %i)",
+ root, eina_list_count(root->both),
+ size->x, size->y, size->w, size->h);
+
+ if (direction)
+ {
+ int middle = size->w / 2;
+
+ size->w -= middle;
+ if (eina_spans_intersect(size->x, size->w, target->x, target->w))
+ result = _eina_quadtree_collide(result, root->left,
+ !direction, size,
+ target);
+
+ size->x += middle;
+ if (eina_spans_intersect(size->x, size->w, target->x, target->w))
+ result = _eina_quadtree_collide(result, root->right,
+ !direction, size,
+ target);
+ size->x -= middle;
+ size->w += middle;
+ }
+ else
+ {
+ int middle = size->h / 2;
+
+ size->h -= middle;
+ if (eina_spans_intersect(size->y, size->h, target->y, target->h))
+ result = _eina_quadtree_collide(result, root->left,
+ !direction, size,
+ target);
+ size->y += middle;
+ if (eina_spans_intersect(size->y, size->h, target->y, target->h))
+ result = _eina_quadtree_collide(result, root->right,
+ !direction, size,
+ target);
+ size->y -= middle;
+ size->h += middle;
+ }
+
+ return result;
+}
+
+static void
+_eina_quadtree_remove(Eina_QuadTree_Item *object)
+{
+ if (!object->root) return ;
+
+ object->root->both = eina_list_remove(object->root->both, object);
+ if (object->root->both) goto end;
+ if (object->root->left) goto end;
+ if (object->root->right) goto end;
+
+ /* The root is not usefull anymore... */
+ if (object->root->parent)
+ {
+ if (object->root->parent->left == object->root)
+ object->root->parent->left = NULL;
+ else
+ object->root->parent->right = NULL;
+ object->root->parent = NULL;
+ }
+ else
+ {
+ object->quad->root = NULL;
+ }
+
+ if (object->quad->root_count > 50)
+ {
+ eina_mempool_free(object->quad->root_mp, object->root);
+ }
+ else
+ {
+ eina_trash_push(&object->quad->root_trash, object->root);
+ object->quad->root_count++;
+ }
+
+ end:
+ object->root = NULL;
+}
+
+EAPI Eina_QuadTree *
+eina_quadtree_new(size_t w, size_t h,
+ Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal)
+{
+ Eina_QuadTree *result;
+
+ if (!vertical || !horizontal || h == 0 || w == 0)
+ return NULL;
+
+ result = calloc(1, sizeof (Eina_QuadTree));
+ if (!result)
+ return NULL;
+
+ result->func.v = vertical;
+ result->func.h = horizontal;
+
+ result->geom.w = w;
+ result->geom.h = h;
+
+ result->change = eina_array_new(32);
+
+ result->items_mp = eina_mempool_add("chained_mempool", "QuadTree Item", NULL,
+ sizeof (Eina_QuadTree_Item), 320);
+ result->root_mp = eina_mempool_add("chained_mempool", "QuadTree Root", NULL,
+ sizeof (Eina_QuadTree_Root), 32);
+
+ EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE);
+
+ return result;
+}
+
+EAPI void
+eina_quadtree_free(Eina_QuadTree *q)
+{
+ if (!q) return ;
+
+ EINA_MAGIC_CHECK_QUADTREE(q);
+
+ eina_array_free(q->change);
+ q->hidden = eina_list_free(q->hidden);
+
+ eina_quadtree_root_free(q, q->root);
+
+ eina_mempool_del(q->items_mp);
+ eina_mempool_del(q->root_mp);
+
+ EINA_MAGIC_SET(q, 0);
+ free(q);
+}
+
+EAPI Eina_QuadTree_Item *
+eina_quadtree_add(Eina_QuadTree *q, const void *object)
+{
+ Eina_QuadTree_Item *result;
+
+ EINA_MAGIC_CHECK_QUADTREE(q, NULL);
+
+ if (!object) return NULL;
+
+ result = eina_trash_pop(&q->items_trash);
+ if (!result) result = eina_mempool_malloc(q->items_mp, sizeof (Eina_QuadTree_Item));
+ else q->items_count--;
+ if (!result) return NULL;
+
+ result->quad = q;
+ result->root = NULL;
+ result->object = object;
+
+ result->change = EINA_TRUE;
+ result->delete_me = EINA_FALSE;
+ result->visible = EINA_TRUE;
+ result->hidden = EINA_FALSE;
+
+ EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE_ITEM);
+
+ /* Insertion is delayed until we really need to use it */
+ eina_array_push(q->change, result);
+
+ return result;
+}
+
+EAPI Eina_Bool
+eina_quadtree_del(Eina_QuadTree_Item *object)
+{
+ if (!object) return EINA_FALSE;
+
+ EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+ _eina_quadtree_remove(object);
+
+ if (object->change)
+ {
+ /* This object is still in the update array, delaying it's removal !*/
+ object->delete_me = EINA_TRUE;
+ object->visible = EINA_TRUE;
+ return EINA_TRUE;
+ }
+
+ if (object->hidden)
+ {
+ object->quad->hidden = eina_list_remove(object->quad->hidden, object);
+ object->hidden = EINA_TRUE;
+ }
+
+ /* This object is not anymore inside the tree, we can remove it now !*/
+ EINA_MAGIC_SET(object, 0);
+ if (object->quad->items_count > 256)
+ {
+ eina_mempool_free(object->quad->items_mp, object);
+ }
+ else
+ {
+ object->quad->items_count++;
+ eina_trash_push(&object->quad->items_trash, object);
+ }
+
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_change(Eina_QuadTree_Item *object)
+{
+ EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+ if (object->delete_me || !object->visible)
+ return EINA_FALSE;
+
+ if (object->quad->resize)
+ return EINA_TRUE;
+
+ /* Delaying change until needed */
+ if (!object->change)
+ eina_array_push(object->quad->change, object);
+ object->change = EINA_TRUE;
+
+ _eina_quadtree_remove(object);
+
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_hide(Eina_QuadTree_Item *object)
+{
+ EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+ object->visible = EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_show(Eina_QuadTree_Item *object)
+{
+ EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+ object->index = object->quad->index++;
+ if (object->root)
+ object->root->sorted = EINA_FALSE;
+
+ if (object->visible) return EINA_TRUE;
+
+ object->visible = EINA_TRUE;
+ if (!object->change)
+ return eina_quadtree_change(object);
+
+ return EINA_TRUE;
+}
+
+EAPI void
+eina_quadtree_collide(Eina_Array *result,
+ Eina_QuadTree *q, int x, int y, size_t w, size_t h)
+{
+ Eina_Inlist *head = NULL;
+ Eina_QuadTree_Item *current;
+ Eina_QuadTree_Item *p = NULL;
+ Eina_Rectangle canvas;
+ Eina_Rectangle target;
+ Eina_Array_Iterator it;
+ unsigned int i;
+
+ EINA_MAGIC_CHECK_QUADTREE(q);
+
+ /* Now we need the tree to be up to date, so it's time */
+ if (q->resize) /* Full rebuild needed ! */
+ {
+ DBG("resizing quadtree");
+ q->root = eina_quadtree_root_rebuild_pre(q, q->change, q->root);
+ q->resize = EINA_FALSE;
+ }
+
+ EINA_RECTANGLE_SET(&canvas, 0, 0, q->geom.w, q->geom.h);
+
+ if (eina_array_count_get(q->change) > 0)
+ {
+ DBG("updating quadtree content");
+ q->root = _eina_quadtree_update(q, NULL, q->root, q->change,
+ EINA_FALSE, &canvas);
+ }
+
+ EINA_RECTANGLE_SET(&target, x, y, w, h);
+
+ DBG("colliding content");
+ head = _eina_quadtree_collide(NULL, q->root,
+ EINA_FALSE, &canvas,
+ &target);
+
+ EINA_INLIST_FOREACH(head, current)
+ eina_array_push(result, current->object);
+}
+
+EAPI void
+eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h)
+{
+ EINA_MAGIC_CHECK_QUADTREE(q);
+
+ if (q->geom.w == w
+ && q->geom.h == h)
+ return ;
+
+ q->resize = EINA_TRUE;
+ q->geom.w = w;
+ q->geom.h = h;
+}
+
+Eina_Bool
+eina_quadtree_init(void)
+{
+ _eina_log_qd_dom = eina_log_domain_register("eina_quadtree", EINA_LOG_COLOR_DEFAULT);
+ if (_eina_log_qd_dom < 0)
+ {
+ EINA_LOG_ERR("Could not register log domain: eina_quadtree");
+ return EINA_FALSE;
+ }
+
+#define EMS(n) eina_magic_string_static_set(n, n##_STR)
+ EMS(EINA_MAGIC_QUADTREE);
+ EMS(EINA_MAGIC_QUADTREE_ROOT);
+ EMS(EINA_MAGIC_QUADTREE_ITEM);
+#undef EMS
+
+ return EINA_TRUE;
+}
+
+Eina_Bool
+eina_quadtree_shutdown(void)
+{
+ eina_log_domain_unregister(_eina_log_qd_dom);
+ _eina_log_qd_dom = -1;
+ return EINA_TRUE;
+}
+
+
+
--- /dev/null
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define WIDTH 720
+#define HEIGHT 576
+
+#include "eina_main.h"
+#include "eina_mempool.h"
+#include "eina_rectangle.h"
+#include "eina_quadtree.h"
+#include "eina_list.h"
+#include "eina_bench.h"
+
+static void
+eina_bench_render_loop(int request)
+{
+ Eina_List *objects = NULL;
+ Eina_Rectangle *r;
+ int i;
+ int j;
+
+ eina_init();
+
+ for (i = 0; i < request; ++i)
+ objects = eina_list_append(objects,
+ eina_rectangle_new((rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 2) / RAND_MAX,
+ (rand() * HEIGHT / 2) / RAND_MAX));
+
+ for (j = 0; j < 100; ++j)
+ {
+ Eina_Rectangle *collide;
+ Eina_List *collided = NULL;
+ Eina_List *changed = NULL;
+ Eina_List *l;
+
+ /* Delete 25% of all objects */
+ i = request * 25 / 100;
+ for (; i > 0; --i)
+ {
+ eina_rectangle_free(eina_list_data_get(objects));
+ objects = eina_list_remove_list(objects, objects);
+ }
+
+ /* Add them back */
+ i = request * 25 / 100;
+ for (; i > 0; --i)
+ {
+ r = eina_rectangle_new((rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 3) / RAND_MAX,
+ (rand() * HEIGHT / 3) / RAND_MAX);
+ objects = eina_list_prepend(objects, r);
+ changed = eina_list_append(changed, r);
+ }
+
+ /* Do one collide search */
+ collide = eina_rectangle_new((rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 4) / RAND_MAX,
+ (rand() * HEIGHT / 4) / RAND_MAX);
+ EINA_LIST_FOREACH(objects, l, r)
+ if (eina_rectangles_intersect(r, collide))
+ collided = eina_list_append(collided, r);
+ collided = eina_list_free(collided);
+ eina_rectangle_free(collide);
+
+ /* Modify 50% of all objects */
+ i = request * 50 / 100;
+ for (; i > 0; --i)
+ {
+ r = eina_list_data_get(eina_list_last(objects));
+ objects = eina_list_remove_list(objects, eina_list_last(objects));
+
+ r->x = (rand() * WIDTH) / RAND_MAX;
+ r->y = (rand() * HEIGHT) / RAND_MAX;
+ r->w = (rand() * WIDTH / 3) / RAND_MAX;
+ r->h = (rand() * HEIGHT / 3) / RAND_MAX;
+
+ objects = eina_list_prepend(objects, r);
+ changed = eina_list_append(changed, r);
+ }
+
+ /* Emulating the render loop by colliding all modified
+ object with all intersecting object */
+ EINA_LIST_FREE(changed, r)
+ {
+ EINA_LIST_FOREACH(objects, l, collide)
+ if (r != collide && eina_rectangles_intersect(collide, r))
+ collided = eina_list_append(collided, collide);
+
+ collided = eina_list_append(collided, r);
+ }
+
+ /* Ok, we compute it, now it's done */
+ collided = eina_list_free(collided);
+ }
+
+ EINA_LIST_FREE(objects, r)
+ eina_rectangle_free(r);
+
+ eina_shutdown();
+}
+
+typedef struct _Eina_Bench_Quad Eina_Bench_Quad;
+struct _Eina_Bench_Quad
+{
+ Eina_Rectangle r;
+ Eina_QuadTree_Item *item;
+};
+
+static Eina_Quad_Direction
+_eina_bench_quadtree_vertical(const void *object, size_t middle)
+{
+ const Eina_Bench_Quad *b = object;
+ size_t y;
+
+ y = b->r.y < 0 ? 0 : (size_t) b->r.y;
+
+ if (y + b->r.h < middle)
+ return EINA_QUAD_LEFT;
+ if (y > middle)
+ return EINA_QUAD_RIGHT;
+ return EINA_QUAD_BOTH;
+}
+
+static Eina_Quad_Direction
+_eina_bench_quadtree_horizontal(const void *object, size_t middle)
+{
+ const Eina_Bench_Quad *b = object;
+ size_t x;
+
+ x = b->r.x < 0 ? 0 : (size_t) b->r.x;
+
+ if (x + b->r.w < middle)
+ return EINA_QUAD_LEFT;
+ if (x > middle)
+ return EINA_QUAD_RIGHT;
+ return EINA_QUAD_BOTH;
+}
+
+static void
+eina_bench_quadtree_render_loop(int request)
+{
+ Eina_List *objects = NULL;
+ Eina_Array *possibility;
+ Eina_Bench_Quad *b;
+ Eina_QuadTree *q;
+ Eina_Mempool *mp;
+ int i;
+ int j;
+
+ eina_init();
+
+ mp = eina_mempool_add("chained_mempool", "bench-quad", NULL,
+ sizeof (Eina_Bench_Quad), 320);
+
+ q = eina_quadtree_new(WIDTH, HEIGHT,
+ _eina_bench_quadtree_vertical,
+ _eina_bench_quadtree_horizontal);
+
+ /* Create requested object */
+ for (i = 0; i < request; ++i)
+ {
+ b = eina_mempool_malloc(mp, sizeof (Eina_Bench_Quad));
+ EINA_RECTANGLE_SET(&b->r,
+ (rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 2) / RAND_MAX,
+ (rand() * HEIGHT / 2) / RAND_MAX);
+ b->item = eina_quadtree_add(q, b);
+
+ objects = eina_list_append(objects, b);
+ }
+
+ possibility = eina_array_new(64);
+
+ for (j = 0; j < 100; ++j)
+ {
+ Eina_Bench_Quad *collide;
+ Eina_List *changed = NULL;
+ Eina_List *collided = NULL;
+ Eina_Array_Iterator it;
+ unsigned int k;
+
+ /* Delete 25% of all objects */
+ i = request * 25 / 100;
+ for (; i > 0; --i)
+ {
+ b = eina_list_data_get(objects);
+ eina_quadtree_del(b->item);
+ eina_mempool_free(mp, b);
+
+ objects = eina_list_remove_list(objects, objects);
+ }
+
+ /* Add them back */
+ i = request * 25 / 100;
+ for (; i > 0; --i)
+ {
+ b = eina_mempool_malloc(mp, sizeof (Eina_Bench_Quad));
+ EINA_RECTANGLE_SET(&b->r,
+ (rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 3) / RAND_MAX,
+ (rand() * HEIGHT / 3) / RAND_MAX);
+ b->item = eina_quadtree_add(q, b);
+
+ objects = eina_list_prepend(objects, b);
+ changed = eina_list_append(changed, b);
+ }
+
+ /* Do one collide search */
+ collide = eina_mempool_malloc(mp, sizeof (Eina_Bench_Quad));
+ EINA_RECTANGLE_SET(&collide->r,
+ (rand() * WIDTH) / RAND_MAX,
+ (rand() * HEIGHT) / RAND_MAX,
+ (rand() * WIDTH / 4) / RAND_MAX,
+ (rand() * HEIGHT / 4) / RAND_MAX);
+ eina_quadtree_collide(possibility, q,
+ collide->r.x, collide->r.y,
+ collide->r.w, collide->r.h);
+ EINA_ARRAY_ITER_NEXT(possibility, k, b, it)
+ if (eina_rectangles_intersect(&b->r, &collide->r))
+ collided = eina_list_append(collided, b);
+ collided = eina_list_free(collided);
+ eina_mempool_free(mp, collide);
+ eina_array_clean(possibility);
+
+ /* Modify 50% of all objects */
+ i = request * 50 / 100;
+ for (; i > 0; --i)
+ {
+ b = eina_list_data_get(eina_list_last(objects));
+ objects = eina_list_remove_list(objects, eina_list_last(objects));
+
+ b->r.x = (rand() * WIDTH) / RAND_MAX;
+ b->r.y = (rand() * HEIGHT) / RAND_MAX;
+ b->r.w = (rand() * WIDTH / 3) / RAND_MAX;
+ b->r.h = (rand() * HEIGHT / 3) / RAND_MAX;
+
+ eina_quadtree_change(b->item);
+
+ objects = eina_list_prepend(objects, b);
+ changed = eina_list_append(changed, b);
+ }
+
+ /* Emulating the render loop by colliding all modified
+ object with all intersecting object */
+ EINA_LIST_FREE(changed, b)
+ {
+ eina_quadtree_collide(possibility, q,
+ b->r.x, b->r.y, b->r.w, b->r.h);
+ EINA_ARRAY_ITER_NEXT(possibility, k, collide, it)
+ if (collide != b && eina_rectangles_intersect(&b->r, &collide->r))
+ collided = eina_list_append(collided, collide);
+ eina_array_clean(possibility);
+
+ collided = eina_list_append(collided, b);
+ }
+
+ /* Ok, we compute it, now it's done */
+ collided = eina_list_free(collided);
+ }
+
+ EINA_LIST_FREE(objects, b)
+ {
+ eina_quadtree_del(b->item);
+ eina_mempool_free(mp, b);
+ }
+
+ eina_mempool_del(mp);
+
+ eina_quadtree_free(q);
+
+ eina_shutdown();
+}
+
+void
+eina_bench_quadtree(Eina_Benchmark *bench)
+{
+ eina_benchmark_register(bench, "collide-all",
+ EINA_BENCHMARK(eina_bench_render_loop),
+ 100, 1500, 50);
+ eina_benchmark_register(bench, "collide-quad-tree",
+ EINA_BENCHMARK(eina_bench_quadtree_render_loop),
+ 100, 1500, 50);
+}
--- /dev/null
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <stdio.h>
+
+#include "eina_suite.h"
+#include "Eina.h"
+
+START_TEST(eina_fp_cos)
+{
+ Eina_F32p32 fc;
+ Eina_F32p32 fl;
+ Eina_F32p32 step;
+ Eina_F32p32 fresult;
+ double dc;
+ double dresult;
+ double delta;
+
+ fail_if(!eina_init());
+
+ fl = eina_f32p32_scale(EINA_F32P32_PI, 4);
+ step = eina_f32p32_div(fl, eina_f32p32_int_from(2048));
+
+ for (fc = 0; fc < fl; fc += step)
+ {
+ fresult = eina_f32p32_cos(fc);
+ dc = eina_f32p32_double_to(fc);
+ dresult = cos(dc);
+
+ delta = fabs(dresult - eina_f32p32_double_to(fresult));
+ fail_if(delta > 0.005);
+ }
+
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_fp_sin)
+{
+ Eina_F32p32 fc;
+ Eina_F32p32 fl;
+ Eina_F32p32 step;
+ Eina_F32p32 fresult;
+ double dc;
+ double dresult;
+ double delta;
+
+ fail_if(!eina_init());
+
+ fl = eina_f32p32_scale(EINA_F32P32_PI, 4);
+ step = eina_f32p32_div(fl, eina_f32p32_int_from(2048));
+
+ for (fc = 0; fc < fl; fc += step)
+ {
+ fresult = eina_f32p32_sin(fc);
+ dc = eina_f32p32_double_to(fc);
+ dresult = sin(dc);
+
+ delta = fabs(dresult - eina_f32p32_double_to(fresult));
+ fail_if(delta > 0.005);
+ }
+
+
+ eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_fp(TCase *tc)
+{
+ tcase_add_test(tc, eina_fp_cos);
+ tcase_add_test(tc, eina_fp_sin);
+}
--- /dev/null
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_suite.h"
+#include "Eina.h"
+
+static Eina_Quad_Direction
+_eina_quadtree_rectangle_vert(const void *object, size_t middle)
+{
+ const Eina_Rectangle *r = object;
+
+ if (r->y + r->h < (int) middle)
+ return EINA_QUAD_LEFT;
+ if (r->y > (int) middle)
+ return EINA_QUAD_RIGHT;
+ return EINA_QUAD_BOTH;
+}
+
+static Eina_Quad_Direction
+_eina_quadtree_rectangle_hort(const void *object, size_t middle)
+{
+ const Eina_Rectangle *r = object;
+
+ if (r->x + r->w < (int) middle)
+ return EINA_QUAD_LEFT;
+ if (r->x > (int) middle)
+ return EINA_QUAD_RIGHT;
+ return EINA_QUAD_BOTH;
+}
+
+START_TEST(eina_quadtree_collision)
+{
+ struct {
+ Eina_Rectangle r;
+ Eina_QuadTree_Item *item;
+ } objects[] = {
+ { { 10, 10, 30, 30 }, NULL },
+ { { 20, 20, 30, 30 }, NULL },
+ { { 5, 30, 30, 30 }, NULL },
+ { { 70, 130, 100, 100 }, NULL },
+ { { 10, 220, 50, 40 }, NULL },
+ { { 310, 20, 50, 30 }, NULL },
+ { { 300, 220, 40, 40 }, NULL },
+ { { 500, 150, 40, 40 }, NULL },
+ { { 500, 220, 40, 40 }, NULL },
+ { { 330, 250, 40, 40 }, NULL },
+ { { 300, 400, 40, 40 }, NULL },
+ { { 10, 400, 40, 40 }, NULL },
+ { { 0, 0, 0, 0 }, NULL }
+ };
+ struct {
+ Eina_Rectangle r;
+ int count;
+ int result[20];
+ } tests [] = {
+ { { 600, 400, 40, 40 }, 4, { 4, 6, 8, 10 } },
+ { { 20, 30, 10, 10 }, 7, { 0, 1, 2, 4, 5, 6, 8 } },
+ { { 0, 0, 0, 0 }, -1, {} },
+ };
+ int hidden[] = { 4, 5, 6, 8, 10 };
+ int show[] = { 0, 1, 2 };
+ Eina_QuadTree *q;
+ Eina_Array *result;
+ Eina_Rectangle *r;
+ Eina_Array_Iterator it;
+ unsigned int j;
+ int i;
+
+ fail_if(!eina_init());
+
+ q = eina_quadtree_new(640, 480,
+ _eina_quadtree_rectangle_vert,
+ _eina_quadtree_rectangle_hort);
+
+ fail_if(!q);
+
+ for (i = 0; objects[i].r.w != 0 && objects[i].r.h != 0; ++i)
+ {
+ objects[i].item = eina_quadtree_add(q, &objects[i].r);
+ fail_if(!objects[i].item);
+ fail_if(!eina_quadtree_show(objects[i].item));
+ }
+
+ result = eina_array_new(16);
+ fail_if(!result);
+
+ for (i = 0; tests[i].count != -1; ++i)
+ {
+ eina_quadtree_collide(result, q,
+ tests[i].r.x, tests[i].r.y, tests[i].r.w, tests[i].r.h);
+ fail_if(eina_array_count_get(result) != (unsigned int) tests[i].count);
+
+ EINA_ARRAY_ITER_NEXT(result, j, r, it)
+ {
+ int k;
+
+ for (k = 0; k < tests[i].count; ++k)
+ {
+ if (&objects[tests[i].result[k]].r == r)
+ break;
+ }
+ fail_if(k == tests[i].count);
+ }
+
+ eina_array_clean(result);
+ }
+
+ for (j = 0; j < sizeof (hidden) / sizeof (int); ++j)
+ eina_quadtree_hide(objects[hidden[j]].item);
+ for (j = 0; j < sizeof (show) / sizeof (int); ++j)
+ eina_quadtree_show(objects[show[j]].item);
+
+ eina_quadtree_collide(result, q,
+ tests[1].r.x, tests[1].r.y, tests[1].r.w, tests[1].r.h);
+ fail_if(eina_array_count_get(result) != 3);
+
+ EINA_ARRAY_ITER_NEXT(result, j, r, it)
+ fail_if(r != &objects[tests[1].result[show[j]]].r);
+
+ eina_array_clean(result);
+
+ eina_quadtree_show(objects[4].item);
+ eina_quadtree_show(objects[5].item);
+ eina_quadtree_del(objects[5].item);
+ eina_quadtree_change(objects[10].item);
+
+ eina_quadtree_collide(result, q,
+ tests[0].r.x, tests[0].r.y, tests[0].r.w, tests[0].r.h);
+ fail_if(eina_array_count_get(result) != 1);
+
+ eina_quadtree_free(q);
+
+ eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_quadtree(TCase *tc)
+{
+ tcase_add_test(tc, eina_quadtree_collision);
+}