* eina: oops forgotten file.
authorcedric <cedric@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 29 Apr 2010 17:30:18 +0000 (17:30 +0000)
committercedric <cedric@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 29 Apr 2010 17:30:18 +0000 (17:30 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@48425 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/include/eina_quadtree.h [new file with mode: 0644]
src/lib/eina_quadtree.c [new file with mode: 0644]
src/tests/eina_bench_quad.c [new file with mode: 0644]
src/tests/eina_test_fp.c [new file with mode: 0644]
src/tests/eina_test_quadtree.c [new file with mode: 0644]

diff --git a/src/include/eina_quadtree.h b/src/include/eina_quadtree.h
new file mode 100644 (file)
index 0000000..14ebaca
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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
diff --git a/src/lib/eina_quadtree.c b/src/lib/eina_quadtree.c
new file mode 100644 (file)
index 0000000..bcb10d8
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * 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;
+}
+
+
+
diff --git a/src/tests/eina_bench_quad.c b/src/tests/eina_bench_quad.c
new file mode 100644 (file)
index 0000000..600926a
--- /dev/null
@@ -0,0 +1,304 @@
+/* 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);
+}
diff --git a/src/tests/eina_test_fp.c b/src/tests/eina_test_fp.c
new file mode 100644 (file)
index 0000000..0df9f8a
--- /dev/null
@@ -0,0 +1,93 @@
+/* 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);
+}
diff --git a/src/tests/eina_test_quadtree.c b/src/tests/eina_test_quadtree.c
new file mode 100644 (file)
index 0000000..015792b
--- /dev/null
@@ -0,0 +1,160 @@
+/* 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);
+}