diff --git a/include/QF/ecs/hierarchy.h b/include/QF/ecs/hierarchy.h index 88e9c5665..a1885ec44 100644 --- a/include/QF/ecs/hierarchy.h +++ b/include/QF/ecs/hierarchy.h @@ -57,18 +57,24 @@ typedef struct hierarchy_s { uint32_t *childCount; uint32_t *childIndex; uint32_t *parentIndex; + uint32_t *nextIndex; + uint32_t *lastIndex; const hierarchy_type_t *type; void **components; struct ecs_registry_s *reg; uint32_t href_comp; + bool tree_mode; // use for fast building } hierarchy_t; +#define nullindex (~0u) + hierarchy_t *Hierarchy_New (struct ecs_registry_s *reg, uint32_t href_comp, const hierarchy_type_t *type, int createRoot); void Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count); hierarchy_t *Hierarchy_Copy (struct ecs_registry_s *reg, uint32_t href_comp, const hierarchy_t *src); void Hierarchy_Delete (hierarchy_t *hierarchy); +void Hierarchy_SetTreeMode (hierarchy_t *hierarchy, bool tree_mode); uint32_t Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src, uint32_t dstParent, uint32_t srcRoot); diff --git a/libs/ecs/hierarchy.c b/libs/ecs/hierarchy.c index ba3f70a4a..c6e223b52 100644 --- a/libs/ecs/hierarchy.c +++ b/libs/ecs/hierarchy.c @@ -39,12 +39,14 @@ #include "QF/ecs.h" -#define nullindex (~0u) - static component_t ent_component = { .size = sizeof (uint32_t) }; static component_t childCount_component = { .size = sizeof (uint32_t) }; static component_t childIndex_component = { .size = sizeof (uint32_t) }; static component_t parentIndex_component = { .size = sizeof (uint32_t) }; +static component_t nextIndex_component = { .size = sizeof (uint32_t) }; +static component_t lastIndex_component = { .size = sizeof (uint32_t) }; + + static void hierarchy_UpdateTransformIndices (hierarchy_t *hierarchy, uint32_t start, @@ -109,6 +111,10 @@ Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count) (void **) &hierarchy->childIndex, new_max); Component_ResizeArray (&parentIndex_component, (void **) &hierarchy->parentIndex, new_max); + Component_ResizeArray (&nextIndex_component, + (void **) &hierarchy->nextIndex, new_max); + Component_ResizeArray (&lastIndex_component, + (void **) &hierarchy->lastIndex, new_max); if (hierarchy->type) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { @@ -128,6 +134,9 @@ hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count) hierarchy->num_objects += count; uint32_t dstIndex = index + count; count = hierarchy->num_objects - index - count; + if (!count) { + return; + } Component_MoveElements (&ent_component, hierarchy->ent, dstIndex, index, count); Component_MoveElements (&childCount_component, @@ -136,6 +145,10 @@ hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count) hierarchy->childIndex, dstIndex, index, count); Component_MoveElements (&parentIndex_component, hierarchy->parentIndex, dstIndex, index, count); + Component_MoveElements (&nextIndex_component, + hierarchy->nextIndex, dstIndex, index, count); + Component_MoveElements (&lastIndex_component, + hierarchy->lastIndex, dstIndex, index, count); if (hierarchy->type) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { Component_MoveElements (&hierarchy->type->components[i], @@ -162,6 +175,10 @@ hierarchy_close (hierarchy_t *hierarchy, uint32_t index, uint32_t count) hierarchy->childIndex, index, srcIndex, count); Component_MoveElements (&parentIndex_component, hierarchy->parentIndex, index, srcIndex, count); + Component_MoveElements (&nextIndex_component, + hierarchy->nextIndex, index, srcIndex, count); + Component_MoveElements (&lastIndex_component, + hierarchy->lastIndex, index, srcIndex, count); if (hierarchy->type) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { Component_MoveElements (&hierarchy->type->components[i], @@ -212,6 +229,7 @@ hierarchy_init (hierarchy_t *dst, uint32_t index, dst->parentIndex[index + i] = parentIndex; dst->childCount[index + i] = 0; dst->childIndex[index + i] = childIndex; + dst->lastIndex[index + i] = nullindex; } if (dst->type) { for (uint32_t i = 0; i < dst->type->num_components; i++) { @@ -222,8 +240,8 @@ hierarchy_init (hierarchy_t *dst, uint32_t index, } static uint32_t -hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src, - uint32_t dstParent, uint32_t *srcRoot, uint32_t count) +hierarchy_insert_flat (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot, uint32_t count) { uint32_t insertIndex; // where the objects will be inserted uint32_t childIndex; // where the objects' children will inserted @@ -273,6 +291,62 @@ hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src, return insertIndex; } +static uint32_t +hierarchy_insert_tree (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot, uint32_t count) +{ + uint32_t insertIndex; + if (dst == src) { + // reparenting within the hierarchy, so need only to update indices + // of course, easier said than done + insertIndex = *srcRoot; + uint32_t srcParent = dst->parentIndex[insertIndex]; + uint32_t *next = &dst->nextIndex[srcParent]; + while (*next != insertIndex) { + next = &dst->nextIndex[*next]; + } + if (dst->lastIndex[srcParent] == insertIndex) { + // removing src from the end of srcParent's child chain + dst->lastIndex[srcParent] = next - dst->nextIndex; + } + *next = dst->nextIndex[insertIndex]; + dst->nextIndex[insertIndex] = nullindex; + + dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex; + dst->lastIndex[dstParent] = insertIndex; + } else { + // new objecs are always appended + insertIndex = dst->num_objects; + hierarchy_open (dst, insertIndex, count); + if (dst->childCount[dstParent]) { + dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex; + } else { + dst->childIndex[dstParent] = insertIndex; + } + dst->childCount[dstParent] += count; + dst->lastIndex[dstParent] = insertIndex; + dst->nextIndex[insertIndex] = nullindex; + + if (src) { + hierarchy_move (dst, src, insertIndex, *srcRoot, count); + } else { + hierarchy_init (dst, insertIndex, dstParent, nullindex, count); + } + } + return insertIndex; +} + +static uint32_t +hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot, uint32_t count) +{ + if (dst->tree_mode) { + return hierarchy_insert_tree (dst, src, dstParent, srcRoot, count); + } else { + return hierarchy_insert_flat (dst, src, dstParent, srcRoot, count); + } +} + static void hierarchy_insert_children (hierarchy_t *dst, const hierarchy_t *src, uint32_t dstParent, uint32_t *srcRoot) @@ -363,6 +437,9 @@ void Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index, int delEntities) { + if (hierarchy->tree_mode) { + Sys_Error ("Hierarchy_RemoveHierarchy tree mode not implemented"); + } uint32_t parentIndex = hierarchy->parentIndex[index]; hierarchy_remove_children (hierarchy, index, delEntities); @@ -402,14 +479,15 @@ Hierarchy_New (ecs_registry_t *reg, uint32_t href_comp, return hierarchy; } -void -Hierarchy_Delete (hierarchy_t *hierarchy) +static void +hierarchy_delete (hierarchy_t *hierarchy) { - hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects); free (hierarchy->ent); free (hierarchy->childCount); free (hierarchy->childIndex); free (hierarchy->parentIndex); + free (hierarchy->nextIndex); + free (hierarchy->lastIndex); if (hierarchy->type) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { free (hierarchy->components[i]); @@ -421,10 +499,138 @@ Hierarchy_Delete (hierarchy_t *hierarchy) PR_RESFREE (reg->hierarchies, hierarchy); } +void +Hierarchy_Delete (hierarchy_t *hierarchy) +{ + hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects); + hierarchy_delete (hierarchy); +} + +static uint32_t +copy_one_node (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstIndex, uint32_t childIndex) +{ + uint32_t srcIndex = dst->parentIndex[dstIndex]; + uint32_t childCount = src->childCount[srcIndex]; + + dst->childIndex[dstIndex] = childIndex; + dst->childCount[dstIndex] = childCount; + dst->ent[dstIndex] = src->ent[srcIndex]; + if (dst->type) { + for (uint32_t i = 0; i < dst->type->num_components; i++) { + Component_CopyElements (&dst->type->components[i], + dst->components[i], dstIndex, + src->components[i], srcIndex, 1); + } + } + return srcIndex; +} + +static uint32_t +queue_tree_nodes (hierarchy_t *dst, const hierarchy_t *src, + uint32_t queueIndex, uint32_t srcIndex) +{ + uint32_t srcChild = src->childIndex[srcIndex]; + uint32_t childCount = src->childCount[srcIndex]; + + for (uint32_t i = 0; i < childCount; i++) { + dst->parentIndex[queueIndex + i] = srcChild; + srcChild = src->nextIndex[srcChild]; + } + return childCount; +} + +static void +copy_tree_nodes (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstIndex, uint32_t *queueIndex) +{ + auto ind = copy_one_node (dst, src, dstIndex, *queueIndex); + auto count = queue_tree_nodes (dst, src, *queueIndex, ind); + *queueIndex += count; +} + +static void +swap_pointers (void *a, void *b) +{ + void *t = *(void **)a; + *(void **)a = *(void **) b; + *(void **)b = t; +} + +void +Hierarchy_SetTreeMode (hierarchy_t *hierarchy, bool tree_mode) +{ + if (!hierarchy->tree_mode == !tree_mode) { + // no change + return; + } + hierarchy->tree_mode = tree_mode; + if (tree_mode) { + // switching from a cononical hierarchy to tree mode, noed only to + // ensure next/last indices are correct + + // root node has no siblings + hierarchy->nextIndex[0] = nullindex; + for (uint32_t i = 0; i < hierarchy->num_objects; i++) { + uint32_t count = hierarchy->childCount[i]; + uint32_t child = hierarchy->childIndex[i]; + for (uint32_t j = 0; count && j < count - 1; j++) { + hierarchy->nextIndex[child + j] = child + j + 1; + } + hierarchy->lastIndex[i] = count ? child + count - 1 : nullindex; + if (count) { + hierarchy->nextIndex[hierarchy->lastIndex[i]] = nullindex; + } else { + hierarchy->childIndex[i] = nullindex; + } + } + return; + } + + auto src = hierarchy; + auto tmp = Hierarchy_New (src->reg, src->href_comp, src->type, 0); + Hierarchy_Reserve (tmp, src->num_objects); + tmp->num_objects = src->num_objects; + + // treat parentIndex as a queue for breadth-first traversal + tmp->parentIndex[0] = 0; // start at root of src + uint32_t queueIndex = 1; + for (uint32_t i = 0; i < src->num_objects; i++) { + copy_tree_nodes (tmp, src, i, &queueIndex); + } + tmp->parentIndex[0] = nullindex; + for (uint32_t i = 0; i < src->num_objects; i++) { + for (uint32_t j = 0; j < tmp->childCount[i]; j++) { + tmp->parentIndex[tmp->childIndex[i] + j] = i; + } + } + auto href_comp = src->href_comp; + for (uint32_t i = 0; i < src->num_objects; i++) { + hierref_t *ref = Ent_GetComponent (tmp->ent[i], href_comp, tmp->reg); + ref->index = i; + } + + swap_pointers (&tmp->ent, &src->ent); + swap_pointers (&tmp->childCount, &src->childCount); + swap_pointers (&tmp->childIndex, &src->childIndex); + swap_pointers (&tmp->parentIndex, &src->parentIndex); + swap_pointers (&tmp->nextIndex, &src->nextIndex); + swap_pointers (&tmp->lastIndex, &src->lastIndex); + if (src->type) { + for (uint32_t i = 0; i < src->type->num_components; i++) { + swap_pointers (&tmp->components[i], &src->components[i]); + } + } + hierarchy_delete (tmp); +} + hierarchy_t * Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp, const hierarchy_t *src) { + if (src->tree_mode) { + Sys_Error ("Hierarchy_Copy tree mode not implemented"); + } hierarchy_t *dst = Hierarchy_New (dstReg, href_comp, src->type, 0); size_t count = src->num_objects; @@ -443,6 +649,10 @@ Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp, dst->childIndex, 0, src->childIndex, 0, count); Component_CopyElements (&parentIndex_component, dst->parentIndex, 0, src->parentIndex, 0, count); + Component_CopyElements (&nextIndex_component, + dst->nextIndex, 0, src->nextIndex, 0, count); + Component_CopyElements (&lastIndex_component, + dst->lastIndex, 0, src->lastIndex, 0, count); if (dst->type) { for (uint32_t i = 0; i < dst->type->num_components; i++) { Component_CopyElements (&dst->type->components[i], @@ -457,6 +667,9 @@ hierref_t Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent, hierarchy_t *src, uint32_t srcRoot) { + if (src->tree_mode) { + Sys_Error ("Hierarchy_SetParent tree mode not implemented"); + } hierref_t r = {}; if (dst && dstParent != nullindex) { if (dst->type != src->type) { diff --git a/libs/ecs/test/Makemodule.am b/libs/ecs/test/Makemodule.am index 351e878b8..f21722f55 100644 --- a/libs/ecs/test/Makemodule.am +++ b/libs/ecs/test/Makemodule.am @@ -3,7 +3,8 @@ libs_ecs_tests = \ libs/ecs/test/test-compops \ libs/ecs/test/test-hierarchy \ libs/ecs/test/test-registry \ - libs/ecs/test/test-subpools + libs/ecs/test/test-subpools \ + libs/ecs/test/test-treehierarchy TESTS += $(libs_ecs_tests) @@ -47,3 +48,10 @@ libs_ecs_test_test_subpools_LDADD= \ $(libs_ecs_test_libs) libs_ecs_test_test_subpools_DEPENDENCIES= \ $(libs_ecs_test_libs) + +libs_ecs_test_test_treehierarchy_SOURCES= \ + libs/ecs/test/test-treehierarchy.c +libs_ecs_test_test_treehierarchy_LDADD= \ + $(libs_ecs_test_libs) +libs_ecs_test_test_treehierarchy_DEPENDENCIES= \ + $(libs_ecs_test_libs) diff --git a/libs/ecs/test/test-treehierarchy.c b/libs/ecs/test/test-treehierarchy.c new file mode 100644 index 000000000..a7b34cbe2 --- /dev/null +++ b/libs/ecs/test/test-treehierarchy.c @@ -0,0 +1,623 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/ecs.h" + +enum { + test_href, + test_name, + test_highlight, + + test_num_components +}; + +static const component_t test_components[] = { + [test_href] = { + .size = sizeof (hierref_t), + .create = 0,//create_href, + .name = "href", + .destroy = Hierref_DestroyComponent, + }, + [test_name] = { + .size = sizeof (const char *), + .name = "name", + }, + [test_highlight] = { + .size = sizeof (byte), + .name = "highlight", + }, +}; + +ecs_registry_t *test_reg; + +#define DFL "\e[39;49m" +#define BLK "\e[30;40m" +#define RED "\e[31;40m" +#define GRN "\e[32;40m" +#define ONG "\e[33;40m" +#define BLU "\e[34;40m" +#define MAG "\e[35;40m" +#define CYN "\e[36;40m" +#define WHT "\e[37;40m" + +static int +check_hierarchy_size (hierarchy_t *h, uint32_t size) +{ + if (h->num_objects != size) { + printf ("hierarchy does not have exactly %u transform\n", size); + return 0; + } + ecs_registry_t *reg = h->reg; + for (uint32_t i = 0; i < h->num_objects; i++) { + hierref_t *ref = Ent_GetComponent (h->ent[i], test_href, reg); + char **name = Ent_GetComponent (h->ent[i], test_name, reg);; + if (ref->hierarchy != h) { + printf ("transform %d (%s) does not point to hierarchy\n", + i, *name); + } + } + return 1; +} + +static const char * +ref_index_color (uint32_t i, uint32_t rind) +{ + return rind != i ? RED : DFL; +} + +static const char * +parent_index_color (hierarchy_t *h, uint32_t i) +{ + if (!i && h->parentIndex[i] == nullindex) { + return GRN; + } + if (h->parentIndex[i] >= i) { + return RED; + } + uint32_t ci = h->childIndex[h->parentIndex[i]]; + uint32_t cc = h->childCount[h->parentIndex[i]]; + if (i < ci || i >= ci + cc) { + return ONG; + } + return DFL; +} + +static const char * +child_index_color (hierarchy_t *h, uint32_t i) +{ + if (h->tree_mode) { + if ((h->childCount[i] && h->childIndex[i] == nullindex) + || (!h->childCount[i] && h->childIndex[i] != nullindex)) { + return RED; + } + if (h->childIndex[i] != nullindex + && h->childIndex[i] >= h->num_objects) { + return RED; + } + } else { + if (h->childIndex[i] > h->num_objects + || h->childCount[i] > h->num_objects + || h->childIndex[i] + h->childCount[i] > h->num_objects) { + return RED; + } + if (h->childIndex[i] <= i) { + return ONG; + } + } + return DFL; +} + +static const char * +child_count_color (hierarchy_t *h, uint32_t i) +{ + if (h->childCount[i] > h->num_objects) { + return RED; + } + if (h->tree_mode) { + if ((h->childCount[i] && h->childIndex[i] == nullindex) + || (!h->childCount[i] && h->childIndex[i] != nullindex)) { + return RED; + } + } else { + if (h->childIndex[i] > h->num_objects + || h->childIndex[i] + h->childCount[i] > h->num_objects) { + return RED; + } + } + return DFL; +} + +static bool +check_for_loops (hierarchy_t *h) +{ + for (uint32_t i = 0; i < h->num_objects; i++) { + if (!h->childCount[i]) { + continue; + } + uint32_t n, c; + for (n = h->childIndex[i], c = h->childCount[i]; + n != nullindex; n = h->nextIndex[n], c--) { + if (!c) { + break; + } + } + if (!c && n != nullindex) { + printf ("too many children at %d\n", i); + return false; + } + if (c && n == nullindex) { + printf ("too few children at %d\n", i); + return false; + } + if (c && n != nullindex) { + printf ("what the what?!? at %d\n", i); + return false; + } + } + return true; +} + +static bool +check_next_index (hierarchy_t *h, uint32_t i) +{ + if (i == 0) { + // root never has siblings + if (h->nextIndex[i] != nullindex) { + return false; + } + return true; + } + uint32_t p; + if ((p = h->parentIndex[i]) >= h->num_objects + || h->childIndex[p] > h->num_objects + || h->childCount[p] > h->num_objects + || h->childIndex[p] + h->childCount[p] > h->num_objects + || h->lastIndex[p] >= h->num_objects + || (h->nextIndex[i] == nullindex && h->lastIndex[p] != i) + || (h->nextIndex[i] != nullindex && h->lastIndex[p] == i)) { + return false; + } + if (h->nextIndex[i] != nullindex && h->parentIndex[h->nextIndex[i]] != p) { + return false; + } + return true; +} + +static const char * +next_index_color (hierarchy_t *h, uint32_t i) +{ + return check_next_index (h, i) ? DFL : RED; +} + +static bool +check_last_index (hierarchy_t *h, uint32_t i) +{ + if ((h->childCount[i] && h->childIndex[i] >= h->num_objects) + || h->childCount[i] >= h->num_objects + || (h->childCount[i] && h->lastIndex[i] == nullindex) + || (!h->childCount[i] && h->lastIndex[i] != nullindex)) { + return false; + } + if (h->lastIndex[i] != nullindex && h->parentIndex[h->lastIndex[i]] != i) { + return false; + } + return true; +} + +static const char * +last_index_color (hierarchy_t *h, uint32_t i) +{ + return check_last_index (h, i) ? DFL : RED; +} + +static const char * +entity_color (hierarchy_t *h, uint32_t i) +{ + return h->ent[i] == nullent ? MAG : DFL; +} + +static const char * +highlight_color (hierarchy_t *h, uint32_t i) +{ + uint32_t ent = h->ent[i]; + if (ECS_EntValid (ent, test_reg) + && Ent_HasComponent (ent, test_highlight, test_reg)) { + static char color_str[] = "\e[3.;4.m"; + byte *color = Ent_GetComponent (ent, test_highlight, test_reg); + if (*color) { + byte fg = *color & 0x0f; + byte bg = *color >> 4; + color_str[3] = fg < 8 ? '0' + fg : '9'; + color_str[6] = bg < 8 ? '0' + bg : '9'; + return color_str; + } + } + return ""; +} + +static void +print_header (hierarchy_t *h) +{ + if (h->tree_mode) { + puts ("in: ri pa ci cc ni li en|name"); + } else { + puts ("in: ri pa ci cc en|name"); + } +} + +static void +print_line (hierarchy_t *h, uint32_t ind, int level) +{ + ecs_registry_t *reg = h->reg; + uint32_t rind = nullindex; + static char fake_name[] = ONG "null" DFL; + static char *fake_nameptr = fake_name; + char **name = &fake_nameptr; + if (ECS_EntValid (h->ent[ind], reg)) { + hierref_t *ref = Ent_GetComponent (h->ent[ind], test_href, reg); + rind = ref->index; + if (Ent_HasComponent (h->ent[ind], test_name, reg)) { + name = Ent_GetComponent (h->ent[ind], test_name, reg); + } + } + printf ("%2d: %s%2d %s%2d %s%2d %s%2d", ind, + ref_index_color (ind, rind), rind, + parent_index_color (h, ind), h->parentIndex[ind], + child_index_color (h, ind), h->childIndex[ind], + child_count_color (h, ind), h->childCount[ind]); + if (h->tree_mode) { + printf (" %s%2d %s%2d", + next_index_color (h, ind), h->nextIndex[ind], + last_index_color (h, ind), h->lastIndex[ind]); + } + printf (" %s%2d"DFL"|%*s%s%s"DFL"\n", + entity_color (h, ind), h->ent[ind], + level * 3, "", highlight_color (h, ind), *name); +} + +static void +dump_hierarchy (hierarchy_t *h) +{ + print_header (h); + for (uint32_t i = 0; i < h->num_objects; i++) { + print_line (h, i, 0); + } + puts (""); +} + +static void +dump_tree (hierarchy_t *h, uint32_t ind, int level) +{ + if (ind >= h->num_objects) { + printf ("index %d out of bounds (%d)\n", ind, h->num_objects); + return; + } + if (!level) { + print_header (h); + } + print_line (h, ind, level); + + if (h->tree_mode) { + uint32_t count = h->childCount[ind]; + uint32_t child; + for (child = h->childIndex[ind]; count && child != nullindex; + child = h->nextIndex[child], count--) { + dump_tree (h, child, level + 1); + } + } else { + if (h->childIndex[ind] > ind) { + for (uint32_t i = 0; i < h->childCount[ind]; i++) { + if (h->childIndex[ind] + i >= h->num_objects) { + break; + } + dump_tree (h, h->childIndex[ind] + i, level + 1); + } + } + } + if (!level) { + puts (""); + } +} + +static int +check_indices (uint32_t ent, uint32_t index, uint32_t parentIndex, + uint32_t childIndex, uint32_t childCount) +{ + ecs_registry_t *reg = test_reg; + char **entname = Ent_GetComponent (ent, test_name, reg);; + hierref_t *ref = Ent_GetComponent (ent, test_href, reg); + hierarchy_t *h = ref->hierarchy; + if (ref->index != index) { + char **name = Ent_GetComponent (h->ent[index], test_name, reg);; + printf ("%s/%s index incorrect: expect %u got %u\n", + *entname, *name, + index, ref->index); + return 0; + } + if (h->parentIndex[index] != parentIndex) { + printf ("%s parent index incorrect: expect %u got %u\n", + *entname, parentIndex, h->parentIndex[index]); + return 0; + } + if (h->childIndex[index] != childIndex) { + printf ("%s child index incorrect: expect %u got %u\n", + *entname, childIndex, h->childIndex[index]); + return 0; + } + if (h->childCount[index] != childCount) { + printf ("%s child count incorrect: expect %u got %u\n", + *entname, childCount, h->childCount[index]); + return 0; + } + return 1; +} + +static bool +check_next_last_indices (hierarchy_t *h) +{ + for (uint32_t i = 0; i < h->num_objects; i++) { + if (!check_next_index (h, i)) { + printf ("incorrect next index at %d: %d\n", i, h->nextIndex[i]); + return false; + } + if (!check_last_index (h, i)) { + printf ("incorrect last index at %d: %d\n", i, h->lastIndex[i]); + return false; + } + } + return true; +} + +static uint32_t +create_ent (uint32_t parent, const char *name) +{ + uint32_t ent = ECS_NewEntity (test_reg); + Ent_SetComponent (ent, test_name, test_reg, &name); + hierref_t *ref = Ent_AddComponent (ent, test_href, test_reg); + + if (parent != nullindex) { + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + ref->hierarchy = pref->hierarchy; + ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0, + pref->index, 0); + } else { + ref->hierarchy = Hierarchy_New (test_reg, test_href, 0, 1); + ref->index = 0; + } + ref->hierarchy->ent[ref->index] = ent; + return ent; +} +#if 0 +static void +highlight_ent (uint32_t ent, byte color) +{ + Ent_SetComponent (ent, test_highlight, test_reg, &color); +} + +static void +set_parent (uint32_t child, uint32_t parent) +{ + if (parent != nullindex) { + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + Hierarchy_SetParent (pref->hierarchy, pref->index, + cref->hierarchy, cref->index); + } else { + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + Hierarchy_SetParent (0, nullindex, cref->hierarchy, cref->index); + } +} +#endif +static int +test_build_hierarchy (void) +{ + printf ("test_build_hierarchy\n"); + + uint32_t root = create_ent (nullent, "root"); + uint32_t A = create_ent (root, "A"); + uint32_t B = create_ent (root, "B"); + uint32_t C = create_ent (root, "C"); + + hierref_t *ref = Ent_GetComponent (root, test_href, test_reg); + + if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; } + if (!check_indices (A, 1, 0, 4, 0)) { return 1; } + if (!check_indices (B, 2, 0, 4, 0)) { return 1; } + if (!check_indices (C, 3, 0, 4, 0)) { return 1; } + + uint32_t B1 = create_ent (B, "B1"); + + if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 0)) { return 1; } + if (!check_indices ( B, 2, 0, 4, 1)) { return 1; } + if (!check_indices ( C, 3, 0, 5, 0)) { return 1; } + if (!check_indices (B1, 4, 2, 5, 0)) { return 1; } + + uint32_t A1 = create_ent (A, "A1"); + + if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 1)) { return 1; } + if (!check_indices ( B, 2, 0, 5, 1)) { return 1; } + if (!check_indices ( C, 3, 0, 6, 0)) { return 1; } + if (!check_indices (A1, 4, 1, 6, 0)) { return 1; } + if (!check_indices (B1, 5, 2, 6, 0)) { return 1; } + uint32_t A1a = create_ent (A1, "A1a"); + uint32_t B2 = create_ent (B, "B2"); + uint32_t A2 = create_ent (A, "A2"); + uint32_t B3 = create_ent (B, "B3"); + uint32_t B2a = create_ent (B2, "B2a"); + + if (!check_hierarchy_size (ref->hierarchy, 11)) { return 1; } + + if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 6, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 9, 0)) { return 1; } + if (!check_indices ( A1, 4, 1, 9, 1)) { return 1; } + if (!check_indices ( A2, 5, 1, 10, 0)) { return 1; } + if (!check_indices ( B1, 6, 2, 10, 0)) { return 1; } + if (!check_indices ( B2, 7, 2, 10, 1)) { return 1; } + if (!check_indices ( B3, 8, 2, 11, 0)) { return 1; } + if (!check_indices (A1a, 9, 4, 11, 0)) { return 1; } + if (!check_indices (B2a, 10, 7, 11, 0)) { return 1; } + + uint32_t D = create_ent (root, "D"); + + if (!check_hierarchy_size (ref->hierarchy, 12)) { return 1; } + + if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 0)) { return 1; } + if (!check_indices ( D, 4, 0, 10, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 10, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 11, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 11, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 11, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 12, 0)) { return 1; } + if (!check_indices (A1a, 10, 5, 12, 0)) { return 1; } + if (!check_indices (B2a, 11, 8, 12, 0)) { return 1; } + + dump_hierarchy (ref->hierarchy); + uint32_t C1 = create_ent (C, "C1"); + dump_hierarchy (ref->hierarchy); + if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; } + + if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; } + if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; } + if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; } + + dump_tree (ref->hierarchy, 0, 0); + Hierarchy_SetTreeMode (ref->hierarchy, true); + //ref->hierarchy->tree_mode = true; + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + if (!check_for_loops (ref->hierarchy)) { return 1; } + if (!check_next_last_indices (ref->hierarchy)) { return 1; } + + create_ent (root, "E"); + create_ent (B1, "B1a"); + create_ent (A2, "A2a"); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + if (!check_for_loops (ref->hierarchy)) { return 1; } + if (!check_next_last_indices (ref->hierarchy)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + + return 0; +} + +static int +test_build_hierarchy2 (void) +{ + printf ("test_build_hierarchy2\n"); + + uint32_t root = create_ent (nullent, "root"); + hierref_t *ref = Ent_GetComponent (root, test_href, test_reg); + Hierarchy_SetTreeMode (ref->hierarchy, true); + uint32_t A = create_ent (root, "A"); + uint32_t A1 = create_ent (A, "A1"); + uint32_t A1a = create_ent (A1, "A1a"); + uint32_t A2 = create_ent (A, "A2"); + uint32_t B = create_ent (root, "B"); + uint32_t B1 = create_ent (B, "B1"); + uint32_t B2 = create_ent (B, "B2"); + uint32_t B2a = create_ent (B2, "B2a"); + uint32_t B3 = create_ent (B, "B3"); + uint32_t C = create_ent (root, "C"); + uint32_t C1 = create_ent (C, "C1"); + uint32_t D = create_ent (root, "D"); + +//check_indices (ent, index, parentIndex, childIndex, childCount) +#define ni nullindex + if (!check_indices (root, 0, ni, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 2, 2)) { return 1; } + if (!check_indices ( A1, 2, 1, 3, 1)) { return 1; } + if (!check_indices (A1a, 3, 2, ni, 0)) { return 1; } + if (!check_indices ( A2, 4, 1, ni, 0)) { return 1; } + if (!check_indices ( B, 5, 0, 6, 3)) { return 1; } + if (!check_indices ( B1, 6, 5, ni, 0)) { return 1; } + if (!check_indices ( B2, 7, 5, 8, 1)) { return 1; } + if (!check_indices (B2a, 8, 7, ni, 0)) { return 1; } + if (!check_indices ( B3, 9, 5, ni, 0)) { return 1; } + if (!check_indices ( C, 10, 0, 11, 1)) { return 1; } + if (!check_indices ( C1, 11, 10, ni, 0)) { return 1; } + if (!check_indices ( D, 12, 0, ni, 0)) { return 1; } + if (!check_next_last_indices (ref->hierarchy)) { return 1; } + if (!check_for_loops (ref->hierarchy)) { return 1; } + + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + Hierarchy_SetTreeMode (ref->hierarchy, true);// shouldn't do anything + if (!check_indices (root, 0, ni, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 2, 2)) { return 1; } + if (!check_indices ( A1, 2, 1, 3, 1)) { return 1; } + if (!check_indices (A1a, 3, 2, ni, 0)) { return 1; } + if (!check_indices ( A2, 4, 1, ni, 0)) { return 1; } + if (!check_indices ( B, 5, 0, 6, 3)) { return 1; } + if (!check_indices ( B1, 6, 5, ni, 0)) { return 1; } + if (!check_indices ( B2, 7, 5, 8, 1)) { return 1; } + if (!check_indices (B2a, 8, 7, ni, 0)) { return 1; } + if (!check_indices ( B3, 9, 5, ni, 0)) { return 1; } + if (!check_indices ( C, 10, 0, 11, 1)) { return 1; } + if (!check_indices ( C1, 11, 10, ni, 0)) { return 1; } + if (!check_indices ( D, 12, 0, ni, 0)) { return 1; } + if (!check_next_last_indices (ref->hierarchy)) { return 1; } + if (!check_for_loops (ref->hierarchy)) { return 1; } + + Hierarchy_SetTreeMode (ref->hierarchy, false); +puts("Hierarchy_SetTreeMode"); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; } + if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; } + if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + + return 0; +} + +int +main (void) +{ + test_reg = ECS_NewRegistry (); + ECS_RegisterComponents (test_reg, test_components, test_num_components); + ECS_CreateComponentPools (test_reg); + + if (test_build_hierarchy ()) { return 1; } + if (test_build_hierarchy2 ()) { return 1; } + + ECS_DelRegistry (test_reg); + + return 0; +} diff --git a/libs/ui/imui.c b/libs/ui/imui.c index 74450422b..59bc93d76 100644 --- a/libs/ui/imui.c +++ b/libs/ui/imui.c @@ -278,6 +278,8 @@ IMUI_BeginFrame (imui_ctx_t *ctx) auto root_size = View_GetLen (ctx->root_view); Ent_RemoveComponent (root_ent, ctx->root_view.comp, ctx->root_view.reg); ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview); + auto ref = View_GetRef (ctx->root_view); + Hierarchy_SetTreeMode (ref->hierarchy, true); View_SetLen (ctx->root_view, root_size.x, root_size.y); ctx->frame_start = Sys_LongTime (); ctx->frame_count++; @@ -583,6 +585,8 @@ void IMUI_Draw (imui_ctx_t *ctx) { ctx->frame_draw = Sys_LongTime (); + auto ref = View_GetRef (ctx->root_view); + Hierarchy_SetTreeMode (ref->hierarchy, false); prune_objects (ctx); layout_objects (ctx);