diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/params.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/params.py index 52e4237..fe3453c 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/params.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/params.py @@ -56,6 +56,7 @@ class ParamsFlow: msg = f"Symbols in {symbol_values} don't perfectly match the ParamsFlow symbols {self.symbols}" raise ValueError(msg) + ## TODO: MutableDenseMatrix causes error with 'in' check bc it isn't hashable. return [ spux.scale_to_unit_system(arg, unit_system, use_jax_array=True) if arg not in symbol_values diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/math/operate_math.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/math/operate_math.py index 7a52579..437be45 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/math/operate_math.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/math/operate_math.py @@ -30,6 +30,7 @@ from ... import base, events log = logger.get(__name__) FUNCS = { + # Number | Number 'ADD': lambda exprs: exprs[0] + exprs[1], 'SUB': lambda exprs: exprs[0] - exprs[1], 'MUL': lambda exprs: exprs[0] * exprs[1], diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py index 2cc41a6..60cdf79 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py @@ -974,16 +974,19 @@ class MaxwellSimNode(bpy.types.Node, bl_instance.BLInstance): Notes: Run by Blender when a new instance of a node is added to a tree. """ - # Initialize Sockets - ## -> Ensures the availability of static sockets before items/methods. - ## -> Ensures the availability of static sockets before items/methods. - self._sync_sockets() - # Initialize Instance ID ## -> This is used by various caches from 'bl_cache'. ## -> Also generates (first-time) the various enums. self.reset_instance_id() + # Initialize Sockets + ## -> Ensures the availability of static sockets before dynamic fields. + self._sync_sockets() + + # Initialize Dynamic Field Persistance + ## -> Ensures the availability of enum items for subsequent setters. + self.regenerate_dynamic_field_persistance() + # Initialize Name ## -> Ensures the availability of sim_node_name immediately. self.sim_node_name = self.name diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index a657c34..1ab5ede 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -51,6 +51,7 @@ class SocketDef(pyd.BaseModel, abc.ABC): bl_socket: The Blender node socket to alter using data from this SocketDef. """ bl_socket.reset_instance_id() + bl_socket.regenerate_dynamic_field_persistance() def postinit(self, bl_socket: bpy.types.NodeSocket) -> None: """Pre-initialize a real Blender node socket from this socket definition. @@ -205,11 +206,12 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance): """ ## TODO: Evaluate this properly if self.is_initializing: - log.debug( - '%s: Rejected on_prop_changed("%s") while initializing', - self.bl_label, - prop_name, - ) + pass + # log.debug( + # '%s: Rejected on_prop_changed("%s") while initializing', + # self.bl_label, + # prop_name, + # ) elif hasattr(self, prop_name): # Property Callbacks: Active Kind if prop_name == 'active_kind': diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py index 0b3644c..7a57d0c 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py @@ -214,9 +214,7 @@ class ExprBLSocket(base.MaxwellSimSocket): return None - @bl_cache.cached_bl_property() - def prev_unit(self) -> spux.Unit | None: - return self.unit + prev_unit: str | None = bl_cache.BLField(None) #################### # - Prop-Change Callback @@ -231,19 +229,21 @@ class ExprBLSocket(base.MaxwellSimSocket): ## -> 1. "Laggy" unit must be different than new unit. ## -> 2. Unit-conversion of value only within same physical_type ## -> 3. Never unit-convert expressions w/symbolic variables - ## No matter what, prev_unit is always re-armed. + ## No matter what, prev_unit is always regenerated. + prev_unit = ( + spux.unit_str_to_unit(self.prev_unit) + if self.prev_unit is not None + else None + ) if ( - self.prev_unit != self.unit - and self.prev_unit in self.physical_type.valid_units + prev_unit != self.unit + and prev_unit in self.physical_type.valid_units and not self.symbols ): - log.critical(self.value, self.prev_unit, self.unit) - self.value = spu.convert_to(self.value, self.prev_unit) - log.critical(self.value, self.prev_unit, self.unit) - self.lazy_array_range = self.lazy_array_range.rescale_to_unit( - self.prev_unit - ) - self.prev_unit = bl_cache.Signal.InvalidateCache + self.value = self.value.subs({self.unit: prev_unit}) + self.lazy_array_range = self.lazy_array_range.correct_unit(prev_unit) + + self.prev_unit = self.active_unit #################### # - Value Utilities @@ -378,18 +378,22 @@ class ExprBLSocket(base.MaxwellSimSocket): ), }, NS.Vec2: { - MT_Z: lambda: sp.Matrix([Z(i) for i in self.raw_value_int2]), - MT_Q: lambda: sp.Matrix([Q(q[0], q[1]) for q in self.raw_value_rat2]), - MT_R: lambda: sp.Matrix([R(r) for r in self.raw_value_float2]), - MT_C: lambda: sp.Matrix( + MT_Z: lambda: sp.ImmutableMatrix([Z(i) for i in self.raw_value_int2]), + MT_Q: lambda: sp.ImmutableMatrix( + [Q(q[0], q[1]) for q in self.raw_value_rat2] + ), + MT_R: lambda: sp.ImmutableMatrix([R(r) for r in self.raw_value_float2]), + MT_C: lambda: sp.ImmutableMatrix( [c[0] + sp.I * c[1] for c in self.raw_value_complex2] ), }, NS.Vec3: { - MT_Z: lambda: sp.Matrix([Z(i) for i in self.raw_value_int3]), - MT_Q: lambda: sp.Matrix([Q(q[0], q[1]) for q in self.raw_value_rat3]), - MT_R: lambda: sp.Matrix([R(r) for r in self.raw_value_float3]), - MT_C: lambda: sp.Matrix( + MT_Z: lambda: sp.ImmutableMatrix([Z(i) for i in self.raw_value_int3]), + MT_Q: lambda: sp.ImmutableMatrix( + [Q(q[0], q[1]) for q in self.raw_value_rat3] + ), + MT_R: lambda: sp.ImmutableMatrix([R(r) for r in self.raw_value_float3]), + MT_C: lambda: sp.ImmutableMatrix( [c[0] + sp.I * c[1] for c in self.raw_value_complex3] ), }, @@ -1185,11 +1189,13 @@ class ExprSocketDef(base.SocketDef): # FlowKind.Value ## -> We must take units into account when setting bl_socket.value if self.physical_type is not spux.PhysicalType.NonPhysical: - self.active_unit = sp.sstr(self.default_unit) + bl_socket.active_unit = sp.sstr(self.default_unit) bl_socket.value = self.default_value * self.default_unit else: bl_socket.value = self.default_value + bl_socket.prev_unit = bl_socket.active_unit + # FlowKind.LazyArrayRange ## -> We can directly pass None to unit. bl_socket.lazy_array_range = ct.LazyArrayRangeFlow( diff --git a/src/blender_maxwell/utils/bl_cache/cached_bl_property.py b/src/blender_maxwell/utils/bl_cache/cached_bl_property.py index cc60a5d..48f9309 100644 --- a/src/blender_maxwell/utils/bl_cache/cached_bl_property.py +++ b/src/blender_maxwell/utils/bl_cache/cached_bl_property.py @@ -75,6 +75,9 @@ class CachedBLProperty: self.decode_type: type = inspect.signature(getter_method).return_annotation + # Write Suppressing + self.suppress_write: dict[str, bool] = {} + # Check Non-Empty Type Annotation ## For now, just presume that all types can be encoded/decoded. if self.decode_type is inspect.Signature.empty: @@ -122,6 +125,10 @@ class CachedBLProperty: return Signal.CacheNotReady return cached_value + def suppress_next_write(self, bl_instance) -> None: + self.suppress_write[bl_instance.instance_id] = True + ## TODO: Make it a context manager to prevent the worst of surprises + def __set__( self, bl_instance: bl_instance.BLInstance | None, value: typ.Any ) -> None: @@ -144,7 +151,10 @@ class CachedBLProperty: # Fill Caches ## -> persist: Fill Persist and Non-Persist Cache ## -> else: Fill Non-Persist Cache - if self.persist: + if self.persist and not self.suppress_write.get( + bl_instance.instance_id + ): + self.suppress_next_write(bl_instance) self.bl_prop.write(bl_instance, self.getter_method(bl_instance)) else: @@ -162,7 +172,7 @@ class CachedBLProperty: self.setter_method(bl_instance, value) # Fill Non-Persistant (and maybe Persistent) Cache - if self.persist: + if self.persist and not self.suppress_write.get(bl_instance.instance_id): self.bl_prop.write(bl_instance, self.getter_method(bl_instance)) else: diff --git a/src/blender_maxwell/utils/bl_cache/managed_cache.py b/src/blender_maxwell/utils/bl_cache/managed_cache.py index ae9f6ea..c7f5fbf 100644 --- a/src/blender_maxwell/utils/bl_cache/managed_cache.py +++ b/src/blender_maxwell/utils/bl_cache/managed_cache.py @@ -20,7 +20,6 @@ import typing as typ -from blender_maxwell import contracts as ct from blender_maxwell.utils import bl_instance, logger from .signal import Signal @@ -94,8 +93,9 @@ def read( # Check if Instance ID is Available if not bl_instance.instance_id: log.debug( - "Can't Get CachedBLProperty: Instance ID not (yet) defined on bl_instance.BLInstance %s", + '%s (Non-Persist): Tried read() (key=%s), but Instance ID not (yet) defined', str(bl_instance), + str(key), ) return Signal.CacheNotReady diff --git a/src/blender_maxwell/utils/bl_instance.py b/src/blender_maxwell/utils/bl_instance.py index 2c8e40d..bafb6c9 100644 --- a/src/blender_maxwell/utils/bl_instance.py +++ b/src/blender_maxwell/utils/bl_instance.py @@ -74,7 +74,6 @@ class BLInstance: The Instance ID is a `UUID4`, which is globally unique, negating the need for extraneous overlap-checks. """ self.instance_id = str(uuid.uuid4()) - self.regenerate_dynamic_field_persistance() @classmethod def assert_attrs_valid(cls, mandatory_props: set[str]) -> None: