OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux commits / e33a359

Node edits told nobody, so the graph drove nothing
IFSIteratorNode's properties had no update= callback. Editing a node changed the property and notified nothing, so the graph looked connected and had no effect on the fractal. The depsgraph handler in ifs_viewport was meant to carry this and CANNOT: an IFSNodeTree is a free-floating datablock with no scene user - no object, modifier or material references it - so Blender's dependency graph never evaluates it. Measured, with the tree live and a session running, editing a node property produced ZERO depsgraph updates. The handler is left in place because that was established in background mode and a GUI may differ, but it is no longer what the graph relies on. The panel had this right from the start (update=_touch('params') on every field); the nodes simply never got the same treatment. Nine properties now notify - transform, opacity, color_index, color_speed, base_weight, start_weight, xaos, real_values, vec3_values - marking the graph dirty rather than applying inline, because a slider drag fires on every pixel of movement and the repack belongs in the draw handler. apply_tree was never the problem: setting a node's opacity and calling it copied the value onto the document correctly. Splitting the diagnosis into "does the edit propagate" and "does the copy work" is what found this; both had to be tested separately or the working half hides the broken one. Verified end to end with a live session: editing opacity marks the graph dirty and lands on the document (0.25), the xaos matrix path lands too (weight_to[0] = 0.5), and apply_pending_graph flushes an edit through to the bake without waiting on a redraw (0.75). This is the second bug in the same chain. The lazy binding fixed earlier was real - a graph built after starting the viewport was never bound - but fixing it while the notification was missing changed nothing observable, which is why the graph still appeared dead. The lesson is to trace the whole path before fixing the first fault found in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e33a359f71aee24b47b60b60435bfdea1aff8eee
by plankatron <fleeky@gmail.com> · 2026-08-17T22:28:58.000Z

Changed files (1) +41 -9

blender/ifs_nodes.py +41 -9
……@@ -106,6 +106,32 @@ def _transform_items(self, context):
106106 return [(n, n, "") for n in names]
107107
108108
109def _graph_edit(self, context):
110 """
111 Property update callback: tells the live session the graph changed.
112
113 This is the ONLY thing that makes node edits reach the fractal. The depsgraph
114 handler in ifs_viewport was meant to do it, but an IFSNodeTree is a free-floating
115 datablock with no scene user - no object, modifier or material references it - so
116 the dependency graph never evaluates it and never reports an edit. Measured: with a
117 session running, editing a node property produced ZERO depsgraph updates, which is
118 why the graph appeared connected and drove nothing.
119
120 Marking the graph dirty (rather than applying here) matches the panel: a slider drag
121 fires this on every pixel of movement, and the work belongs in the draw handler.
122 """
123 if not ifs_viewport.is_running():
124 return
125 ifs_viewport.session().mark_graph_dirty()
126 window_manager = bpy.context.window_manager
127 if window_manager is None:
128 return
129 for window in window_manager.windows:
130 for area in window.screen.areas:
131 if area.type == 'VIEW_3D':
132 area.tag_redraw()
133
134
109135class IFSIteratorNode(Node):
110136 """One iterator: a transform plus its weights and parameters."""
111137
……@@ -117,23 +143,29 @@ class IFSIteratorNode(Node):
117143 def poll(cls, tree):
118144 return tree.bl_idname == IFSNodeTree.bl_idname
119145
120 transform: EnumProperty(name="Transform", items=_transform_items)
121 opacity: FloatProperty(name="Opacity", default=1.0, min=0.0, max=1.0)
122 color_index: FloatProperty(name="Color", default=0.0, min=0.0, max=1.0)
123 color_speed: FloatProperty(name="Color speed", default=0.5, min=0.0, max=1.0)
124 base_weight: FloatProperty(name="Weight", default=1.0, min=0.0, soft_max=10.0)
146 transform: EnumProperty(name="Transform", items=_transform_items,
147 update=_graph_edit)
148 opacity: FloatProperty(name="Opacity", default=1.0, min=0.0, max=1.0,
149 update=_graph_edit)
150 color_index: FloatProperty(name="Color", default=0.0, min=0.0, max=1.0,
151 update=_graph_edit)
152 color_speed: FloatProperty(name="Color speed", default=0.5, min=0.0, max=1.0,
153 update=_graph_edit)
154 base_weight: FloatProperty(name="Weight", default=1.0, min=0.0, soft_max=10.0,
155 update=_graph_edit)
125156 start_weight: FloatProperty(
126157 name="Start weight", default=1.0, min=0.0, soft_max=10.0,
127158 description="Share of RESET points that land on this iterator. Not the "
128 "same as Weight, which scales its outgoing transitions")
159 "same as Weight, which scales its outgoing transitions",
160 update=_graph_edit)
129161
130162 # Outgoing xaos weights, indexed by DESTINATION iterator. This - not the
131163 # links - is what the fractal reads, because an IFS graph is cyclic and
132164 # Blender invalidates cyclic links.
133165 xaos: FloatVectorProperty(size=MAX_ITERATORS, default=[1.0] * MAX_ITERATORS,
134 min=0.0, soft_max=4.0)
135 real_values: FloatVectorProperty(size=MAX_REAL)
136 vec3_values: FloatVectorProperty(size=MAX_VEC3 * 3)
166 min=0.0, soft_max=4.0, update=_graph_edit)
167 real_values: FloatVectorProperty(size=MAX_REAL, update=_graph_edit)
168 vec3_values: FloatVectorProperty(size=MAX_VEC3 * 3, update=_graph_edit)
137169 real_names: StringProperty(default="")
138170 vec3_names: StringProperty(default="")
139171