implement add/remove to node dictionary default tip

Tue, 07 Apr 2026 20:33:29 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 07 Apr 2026 20:33:29 +0200
changeset 305
67a590cf2d15
parent 304
afe83f4642cc

implement add/remove to node dictionary

src/scene.c file | annotate | diff | comparison | revisions
src/scene_node.c file | annotate | diff | comparison | revisions
--- a/src/scene.c	Tue Apr 07 20:18:39 2026 +0200
+++ b/src/scene.c	Tue Apr 07 20:33:29 2026 +0200
@@ -63,8 +63,9 @@
         cxListFree(scene->internal.render_groups[i]);
         scene->internal.render_groups[i] = NULL;
     }
+    asc_scene_node_free(scene->root);
     asc_scene_node_dict_free(scene->nodes_dict);
-    asc_scene_node_free(scene->root);
+    scene->nodes_dict = NULL;
     scene->root = NULL;
     asc_dprintf("Destroyed scene %"CX_PRIstr, CX_SFMT(scene->name));
     cx_strfree(&scene->name);
--- a/src/scene_node.c	Tue Apr 07 20:18:39 2026 +0200
+++ b/src/scene_node.c	Tue Apr 07 20:33:29 2026 +0200
@@ -45,17 +45,86 @@
 }
 
 static void asc_scene_node_dict_add(AscSceneNodeDict *dict, AscSceneNode *node) {
-    if (dict == NULL || !asc_test_flag(node->flags, ASC_SCENE_NODE_HAS_NAME)) {
+    if (dict == NULL) return;
+
+    // set a reference to the dict in the node
+    node->dict = dict;
+
+    // also add child nodes which have a dedicated name
+    AscSceneNode *child = node->children;
+    while (child != NULL) {
+        asc_scene_node_dict_add(dict, child);
+        child = child->next;
+    }
+
+    if (!asc_test_flag(node->flags, ASC_SCENE_NODE_HAS_NAME)) return;
+
+    AscSceneNodeDictEntry *entry = cxMapGet(dict, node->name);
+    if (entry == NULL) {
+        entry = cxMapEmplace(dict, node->name);
+        entry->count = 1;
+        entry->node = cxMallocDefault(sizeof(AscSceneNode*));
+        entry->node[0] = node;
+        asc_dprintf("Dict add: %" CX_PRIstr,
+                CX_SFMT(node->name));
         return;
     }
-    // TODO: add a new dict entry or add the node the the list if it's not already present
+    if (cxReallocateArrayDefault(&entry->node, entry->count + 1, sizeof(AscSceneNode*))) {
+        asc_error("Reallocating dict entry for node %" CX_PRIstr " failed.",
+                CX_SFMT(node->name));
+    }
+    entry->node[entry->count++] = node;
+    asc_dprintf("Dict add: %" CX_PRIstr " (now %z entries).",
+        CX_SFMT(node->name), entry->count);
 }
 
-static void asc_scene_node_dict_remove(AscSceneNodeDict *dict, AscSceneNode *node) {
-    if (dict == NULL || !asc_test_flag(node->flags, ASC_SCENE_NODE_HAS_NAME)) {
+static void asc_scene_node_dict_remove(AscSceneNode *node) {
+    // fast exit, node (and its children) do not belong to a dict
+    if (node->dict == NULL) return;
+
+    // remove the children, first (use standard recursion, depth will be manageable)
+    // TODO: think about adding a flag for indicating if ANY child has a name, so that we can skip subtrees here
+    AscSceneNode *child = node->children;
+    while (child != NULL) {
+        asc_scene_node_dict_remove(child);
+        child = child->next;
+    }
+
+    if (!asc_test_flag(node->flags, ASC_SCENE_NODE_HAS_NAME)) return;
+
+    AscSceneNodeDictEntry *entry = cxMapGet(node->dict, node->name);
+    if (entry == NULL) return;
+
+    // last node with that name, remove node
+    if (entry->count == 1) {
+        if (*entry->node == node) {
+            cxMapRemove(node->dict, node->name);
+            asc_dprintf("Dict remove: %" CX_PRIstr,
+                CX_SFMT(node->name));
+        } else {
+            asc_error("BUG: Node %" CX_PRIstr " not found in its own dict.",
+                CX_SFMT(node->name));
+        }
         return;
     }
-    // TODO: find entries for node's name and remove it from the list - erase the entry when it was the last
+
+    // find index of the particular node
+    unsigned idx;
+    for (idx = 0 ; idx < entry->count ; idx++) {
+        if (entry->node[idx] == node) break;
+    }
+    if (idx == entry->count) {
+        asc_error("BUG: Node %" CX_PRIstr " not found in its own dict.",
+                CX_SFMT(node->name));
+        return;
+    }
+    entry->node[idx] = entry->node[--entry->count];
+    if (cxReallocateArrayDefault(&entry->node, entry->count, sizeof(AscSceneNode*))) {
+        asc_error("Shrinking dict for node %" CX_PRIstr " failed.",
+                CX_SFMT(node->name));
+    }
+    asc_dprintf("Dict remove: %" CX_PRIstr " (%z others left).",
+                CX_SFMT(node->name), entry->count);
 }
 
 static CxTreeIterator asc_scene_node_iterator(
@@ -166,7 +235,7 @@
 }
 
 void asc_scene_node_name(AscSceneNode *node, const char *name) {
-    asc_scene_node_dict_remove(node->dict, node);
+    asc_scene_node_dict_remove(node);
     cx_strfree(&node->name);
     if (name == NULL) {
         asc_clear_flag(node->flags, ASC_SCENE_NODE_HAS_NAME);
@@ -209,7 +278,7 @@
 }
 
 void asc_scene_node_remove(AscSceneNode *node) {
-    asc_scene_node_dict_remove(node->dict, node);
+    asc_scene_node_dict_remove(node);
     cx_tree_remove(
             node,
             offsetof(AscSceneNode, parent),

mercurial