fix: BLFields in FilterMath, bug fixes.
parent
44d61b5639
commit
f09b58e0e7
|
@ -24,7 +24,7 @@ class FlowEvent(enum.StrEnum):
|
||||||
ShowPlot: Indicates that the node/socket should enable its plotted preview.
|
ShowPlot: Indicates that the node/socket should enable its plotted preview.
|
||||||
This should generally be used if the node is rendering to an image, for viewing through the Blender image editor.
|
This should generally be used if the node is rendering to an image, for viewing through the Blender image editor.
|
||||||
LinkChanged: Indicates that a link to a node/socket was added/removed.
|
LinkChanged: Indicates that a link to a node/socket was added/removed.
|
||||||
In nodes, this is accompanied by a `socket_name` to indicate which socket it is that had its links altered.
|
Is translated to `DataChanged` on sockets before propagation.
|
||||||
DataChanged: Indicates that data flowing through a node/socket was altered.
|
DataChanged: Indicates that data flowing through a node/socket was altered.
|
||||||
In nodes, this event is accompanied by a `socket_name` or `prop_name`, to indicate which socket/property it is that was changed.
|
In nodes, this event is accompanied by a `socket_name` or `prop_name`, to indicate which socket/property it is that was changed.
|
||||||
**This event is essential**, as it invalidates all input/output socket caches along its path.
|
**This event is essential**, as it invalidates all input/output socket caches along its path.
|
||||||
|
|
|
@ -195,7 +195,7 @@ class ExtractDataNode(base.MaxwellSimNode):
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Data',
|
'Data',
|
||||||
kind=ct.FlowKind.Value,
|
kind=ct.FlowKind.Array,
|
||||||
props={'extract_filter'},
|
props={'extract_filter'},
|
||||||
input_sockets={'Monitor Data'},
|
input_sockets={'Monitor Data'},
|
||||||
)
|
)
|
||||||
|
@ -210,7 +210,7 @@ class ExtractDataNode(base.MaxwellSimNode):
|
||||||
'Data',
|
'Data',
|
||||||
kind=ct.FlowKind.LazyValueFunc,
|
kind=ct.FlowKind.LazyValueFunc,
|
||||||
output_sockets={'Data'},
|
output_sockets={'Data'},
|
||||||
output_socket_kinds={'Data': ct.FlowKind.Value},
|
output_socket_kinds={'Data': ct.FlowKind.Array},
|
||||||
)
|
)
|
||||||
def compute_extracted_data_lazy(self, output_sockets: dict) -> ct.LazyValueFuncFlow:
|
def compute_extracted_data_lazy(self, output_sockets: dict) -> ct.LazyValueFuncFlow:
|
||||||
return ct.LazyValueFuncFlow(
|
return ct.LazyValueFuncFlow(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import enum
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
@ -14,6 +15,13 @@ log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FilterMathNode(base.MaxwellSimNode):
|
class FilterMathNode(base.MaxwellSimNode):
|
||||||
|
"""Reduces the dimensionality of data.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
operation: Operation to apply to the input.
|
||||||
|
dim: Dims to use when filtering data
|
||||||
|
"""
|
||||||
|
|
||||||
node_type = ct.NodeType.FilterMath
|
node_type = ct.NodeType.FilterMath
|
||||||
bl_label = 'Filter Math'
|
bl_label = 'Filter Math'
|
||||||
|
|
||||||
|
@ -31,32 +39,17 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
####################
|
####################
|
||||||
# - Properties
|
# - Properties
|
||||||
####################
|
####################
|
||||||
operation: bpy.props.EnumProperty(
|
operation: enum.Enum = bl_cache.BLField(
|
||||||
name='Op',
|
prop_ui=True, enum_cb=lambda self, _: self.search_operations()
|
||||||
description='Operation to filter with',
|
|
||||||
items=lambda self, _: self.search_operations(),
|
|
||||||
update=lambda self, context: self.on_prop_changed('operation', context),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
dim: bpy.props.StringProperty(
|
dim: str = bl_cache.BLField(
|
||||||
name='Dim',
|
'', prop_ui=True, str_cb=lambda self, _, edit_text: self.search_dims(edit_text)
|
||||||
description='Dims to use when filtering data',
|
|
||||||
default='',
|
|
||||||
search=lambda self, _, edit_text: self.search_dims(edit_text),
|
|
||||||
update=lambda self, context: self.on_prop_changed('dim', context),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
dim_names: list[str] = bl_cache.BLField([])
|
dim_names: list[str] = bl_cache.BLField([])
|
||||||
dim_lens: dict[str, int] = bl_cache.BLField({})
|
dim_lens: dict[str, int] = bl_cache.BLField({})
|
||||||
|
|
||||||
@property
|
|
||||||
def has_dim(self) -> bool:
|
|
||||||
return (
|
|
||||||
self.active_socket_set in ['By Dim', 'By Dim Value']
|
|
||||||
and self.inputs['Data'].is_linked
|
|
||||||
and self.dim_names
|
|
||||||
)
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Operation Search
|
# - Operation Search
|
||||||
####################
|
####################
|
||||||
|
@ -77,7 +70,7 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
# - Dim Search
|
# - Dim Search
|
||||||
####################
|
####################
|
||||||
def search_dims(self, edit_text: str) -> list[tuple[str, str, str]]:
|
def search_dims(self, edit_text: str) -> list[tuple[str, str, str]]:
|
||||||
if self.has_dim:
|
if self.dim_names:
|
||||||
dims = [
|
dims = [
|
||||||
(dim_name, dim_name)
|
(dim_name, dim_name)
|
||||||
for dim_name in self.dim_names
|
for dim_name in self.dim_names
|
||||||
|
@ -94,17 +87,23 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
# - UI
|
# - UI
|
||||||
####################
|
####################
|
||||||
def draw_props(self, _: bpy.types.Context, layout: bpy.types.UILayout) -> None:
|
def draw_props(self, _: bpy.types.Context, layout: bpy.types.UILayout) -> None:
|
||||||
layout.prop(self, 'operation', text='')
|
layout.prop(self, self.blfields['operation'], text='')
|
||||||
if self.has_dim:
|
if self.dim_names:
|
||||||
layout.prop(self, 'dim', text='')
|
layout.prop(self, self.blfields['dim'], text='')
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Events
|
# - Events
|
||||||
####################
|
####################
|
||||||
|
@events.on_value_changed(
|
||||||
|
prop_name='active_socket_set',
|
||||||
|
)
|
||||||
|
def on_socket_set_changed(self):
|
||||||
|
self.operation = bl_cache.Signal.ResetEnumItems
|
||||||
|
|
||||||
@events.on_value_changed(
|
@events.on_value_changed(
|
||||||
socket_name={'Data'},
|
socket_name={'Data'},
|
||||||
prop_name={'active_socket_set', 'dim'},
|
prop_name={'active_socket_set'},
|
||||||
props={'active_socket_set', 'dim'},
|
props={'active_socket_set'},
|
||||||
input_sockets={'Data'},
|
input_sockets={'Data'},
|
||||||
input_socket_kinds={'Data': ct.FlowKind.Info},
|
input_socket_kinds={'Data': ct.FlowKind.Info},
|
||||||
input_sockets_optional={'Data': True},
|
input_sockets_optional={'Data': True},
|
||||||
|
@ -121,21 +120,32 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
self.dim_names = []
|
self.dim_names = []
|
||||||
self.dim_lens = {}
|
self.dim_lens = {}
|
||||||
|
|
||||||
# Add Input Value w/Unit from InfoFlow
|
# Reset String Searcher
|
||||||
## Socket Type is determined from the Unit
|
self.dim = bl_cache.Signal.ResetStrSearch
|
||||||
|
|
||||||
|
@events.on_value_changed(
|
||||||
|
prop_name='dim',
|
||||||
|
props={'active_socket_set', 'dim'},
|
||||||
|
input_sockets={'Data'},
|
||||||
|
input_socket_kinds={'Data': ct.FlowKind.Info},
|
||||||
|
input_sockets_optional={'Data': True},
|
||||||
|
)
|
||||||
|
def on_dim_change(self, props: dict, input_sockets: dict):
|
||||||
|
# Add/Remove Input Socket "Value"
|
||||||
if (
|
if (
|
||||||
props['active_socket_set'] == 'By Dim Value'
|
props['active_socket_set'] == 'By Dim Value'
|
||||||
and props['dim'] != ''
|
|
||||||
and props['dim'] in input_sockets['Data'].dim_names
|
and props['dim'] in input_sockets['Data'].dim_names
|
||||||
):
|
):
|
||||||
socket_def = sockets.SOCKET_DEFS[
|
# Get Current and Wanted Socket Defs
|
||||||
|
current_socket_def = self.loose_input_sockets.get('Value')
|
||||||
|
wanted_socket_def = sockets.SOCKET_DEFS[
|
||||||
ct.unit_to_socket_type(input_sockets['Data'].dim_idx[props['dim']].unit)
|
ct.unit_to_socket_type(input_sockets['Data'].dim_idx[props['dim']].unit)
|
||||||
]
|
]
|
||||||
if (
|
|
||||||
_val_socket_def := self.loose_input_sockets.get('Value')
|
# Determine Whether to Declare New Loose Input SOcket
|
||||||
) is None or _val_socket_def != socket_def:
|
if current_socket_def is None or current_socket_def != wanted_socket_def:
|
||||||
self.loose_input_sockets = {
|
self.loose_input_sockets = {
|
||||||
'Value': socket_def(),
|
'Value': wanted_socket_def(),
|
||||||
}
|
}
|
||||||
elif self.loose_input_sockets:
|
elif self.loose_input_sockets:
|
||||||
self.loose_input_sockets = {}
|
self.loose_input_sockets = {}
|
||||||
|
@ -151,18 +161,18 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
input_socket_kinds={'Data': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Info}},
|
input_socket_kinds={'Data': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Info}},
|
||||||
)
|
)
|
||||||
def compute_data(self, props: dict, input_sockets: dict):
|
def compute_data(self, props: dict, input_sockets: dict):
|
||||||
|
# Retrieve Inputs
|
||||||
lazy_value_func = input_sockets['Data'][ct.FlowKind.LazyValueFunc]
|
lazy_value_func = input_sockets['Data'][ct.FlowKind.LazyValueFunc]
|
||||||
info = input_sockets['Data'][ct.FlowKind.Info]
|
info = input_sockets['Data'][ct.FlowKind.Info]
|
||||||
|
|
||||||
# Determine Bound/Free Parameters
|
# Compute Bound/Free Parameters
|
||||||
if props['dim'] in info.dim_names:
|
func_args = [int] if props['active_socket_set'] == 'By Dim Value' else []
|
||||||
|
if props['dim']:
|
||||||
axis = info.dim_names.index(props['dim'])
|
axis = info.dim_names.index(props['dim'])
|
||||||
else:
|
else:
|
||||||
msg = 'Dimension invalid'
|
msg = 'Dimension cannot be empty'
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
func_args = [int] if props['active_socket_set'] == 'By Dim Value' else []
|
|
||||||
|
|
||||||
# Select Function
|
# Select Function
|
||||||
filter_func: typ.Callable[[jax.Array], jax.Array] = {
|
filter_func: typ.Callable[[jax.Array], jax.Array] = {
|
||||||
'By Dim': {'SQUEEZE': lambda data: jnp.squeeze(data, axis)},
|
'By Dim': {'SQUEEZE': lambda data: jnp.squeeze(data, axis)},
|
||||||
|
@ -189,8 +199,11 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_array(self, output_sockets: dict) -> ct.ArrayFlow:
|
def compute_array(self, output_sockets: dict) -> ct.ArrayFlow:
|
||||||
|
# Retrieve Inputs
|
||||||
lazy_value_func = output_sockets['Data'][ct.FlowKind.LazyValueFunc]
|
lazy_value_func = output_sockets['Data'][ct.FlowKind.LazyValueFunc]
|
||||||
params = output_sockets['Data'][ct.FlowKind.Params]
|
params = output_sockets['Data'][ct.FlowKind.Params]
|
||||||
|
|
||||||
|
# Compute Array
|
||||||
return ct.ArrayFlow(
|
return ct.ArrayFlow(
|
||||||
values=lazy_value_func.func_jax(*params.func_args, **params.func_kwargs),
|
values=lazy_value_func.func_jax(*params.func_args, **params.func_kwargs),
|
||||||
unit=None, ## TODO: Unit Propagation
|
unit=None, ## TODO: Unit Propagation
|
||||||
|
@ -207,14 +220,18 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
input_socket_kinds={'Data': ct.FlowKind.Info},
|
input_socket_kinds={'Data': ct.FlowKind.Info},
|
||||||
)
|
)
|
||||||
def compute_data_info(self, props: dict, input_sockets: dict) -> ct.InfoFlow:
|
def compute_data_info(self, props: dict, input_sockets: dict) -> ct.InfoFlow:
|
||||||
|
# Retrieve Inputs
|
||||||
info = input_sockets['Data']
|
info = input_sockets['Data']
|
||||||
|
|
||||||
if props['dim'] in info.dim_names:
|
# Compute Bound/Free Parameters
|
||||||
|
## Empty Dimension -> Empty InfoFlow
|
||||||
|
if props['dim']:
|
||||||
axis = info.dim_names.index(props['dim'])
|
axis = info.dim_names.index(props['dim'])
|
||||||
else:
|
else:
|
||||||
return ct.InfoFlow()
|
return ct.InfoFlow()
|
||||||
|
|
||||||
# Compute Axis
|
# Compute Information
|
||||||
|
## Compute Info w/By-Operation Change to Dimensions
|
||||||
if (props['active_socket_set'], props['operation']) in [
|
if (props['active_socket_set'], props['operation']) in [
|
||||||
('By Dim', 'SQUEEZE'),
|
('By Dim', 'SQUEEZE'),
|
||||||
('By Dim Value', 'FIX'),
|
('By Dim Value', 'FIX'),
|
||||||
|
@ -228,6 +245,7 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fallback to Empty InfoFlow
|
||||||
return ct.InfoFlow()
|
return ct.InfoFlow()
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
|
@ -238,20 +256,30 @@ class FilterMathNode(base.MaxwellSimNode):
|
||||||
input_socket_kinds={'Data': {ct.FlowKind.Info, ct.FlowKind.Params}},
|
input_socket_kinds={'Data': {ct.FlowKind.Info, ct.FlowKind.Params}},
|
||||||
input_sockets_optional={'Value': True},
|
input_sockets_optional={'Value': True},
|
||||||
)
|
)
|
||||||
def compute_data_params(self, props: dict, input_sockets: dict) -> ct.ParamsFlow:
|
def compute_composed_params(
|
||||||
|
self, props: dict, input_sockets: dict
|
||||||
|
) -> ct.ParamsFlow:
|
||||||
|
# Retrieve Inputs
|
||||||
info = input_sockets['Data'][ct.FlowKind.Info]
|
info = input_sockets['Data'][ct.FlowKind.Info]
|
||||||
params = input_sockets['Data'][ct.FlowKind.Params]
|
params = input_sockets['Data'][ct.FlowKind.Params]
|
||||||
|
|
||||||
|
# Compute Composed Parameters
|
||||||
|
## -> Only operations that add parameters.
|
||||||
|
## -> A dimension must be selected.
|
||||||
|
## -> There must be an input value.
|
||||||
if (
|
if (
|
||||||
(props['active_socket_set'], props['operation'])
|
(props['active_socket_set'], props['operation'])
|
||||||
in [
|
in [
|
||||||
('By Dim Value', 'FIX'),
|
('By Dim Value', 'FIX'),
|
||||||
]
|
]
|
||||||
and props['dim'] in info.dim_names
|
and props['dim']
|
||||||
and input_sockets['Value'] is not None
|
and input_sockets['Value'] is not None
|
||||||
):
|
):
|
||||||
# Compute IDX Corresponding to Value
|
# Compute IDX Corresponding to Coordinate Value
|
||||||
## Aka. "indexing by a float"
|
## -> Each dimension declares a unit-aware real number at each index.
|
||||||
|
## -> "Value" is a unit-aware real number from loose input socket.
|
||||||
|
## -> This finds the dimensional index closest to "Value".
|
||||||
|
## Total Effect: Indexing by a unit-aware real number.
|
||||||
nearest_idx_to_value = info.dim_idx[props['dim']].nearest_idx_of(
|
nearest_idx_to_value = info.dim_idx[props['dim']].nearest_idx_of(
|
||||||
input_sockets['Value'], require_sorted=True
|
input_sockets['Value'], require_sorted=True
|
||||||
)
|
)
|
||||||
|
@ -269,6 +297,3 @@ BL_REGISTER = [
|
||||||
FilterMathNode,
|
FilterMathNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {ct.NodeType.FilterMath: (ct.NodeCategory.MAXWELLSIM_ANALYSIS_MATH)}
|
BL_NODES = {ct.NodeType.FilterMath: (ct.NodeCategory.MAXWELLSIM_ANALYSIS_MATH)}
|
||||||
|
|
||||||
|
|
||||||
## TODO TODO Okay so just like, Value needs to be a Loose socket, events needs to be able to handle sets of kinds, the invalidator needs to be able to handle sets of kinds too. Given all that, we only need to propagate the output array unit; given all all that, we are 100% goddamn ready to fix that goddamn coordinate.
|
|
||||||
|
|
|
@ -753,8 +753,8 @@ class MaxwellSimNode(bpy.types.Node):
|
||||||
Notes:
|
Notes:
|
||||||
This can be an unpredictably heavy function, depending on the node graph topology.
|
This can be an unpredictably heavy function, depending on the node graph topology.
|
||||||
|
|
||||||
Doesn't currently accept `LinkChanged` (->Output) events; rather, these propagate as `DataChanged` events.
|
Doesn't accept `LinkChanged` events; they are translated to `DataChanged` on the socket.
|
||||||
**This may change** if it becomes important for the node to differentiate between "change in data" and "change in link".
|
This is on purpose: It seems to be a bad idea to try and differentiate between "changes in data" and "changes in linkage".
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
event: The event to report forwards/backwards along the node tree.
|
event: The event to report forwards/backwards along the node tree.
|
||||||
|
@ -762,10 +762,12 @@ class MaxwellSimNode(bpy.types.Node):
|
||||||
pop_name: The property that was altered, if any, in order to trigger this event.
|
pop_name: The property that was altered, if any, in order to trigger this event.
|
||||||
"""
|
"""
|
||||||
# Outflow Socket Kinds
|
# Outflow Socket Kinds
|
||||||
## Something has happened, that much is for sure.
|
## -> Something has happened!
|
||||||
## Output methods might require invalidation of (outsck, FlowKind)s.
|
## -> The effect is yet to be determined...
|
||||||
## Whichever FlowKinds we do happen to invalidate, we should mark.
|
## -> We will watch for which kinds actually invalidate.
|
||||||
## This way, each FlowKind gets its own invalidation chain.
|
## -> ...Then ONLY propagate kinds that have an invalidated outsck.
|
||||||
|
## -> This way, kinds get "their own" invalidation chains.
|
||||||
|
## -> ...While still respecting "crossovers".
|
||||||
altered_socket_kinds = set()
|
altered_socket_kinds = set()
|
||||||
|
|
||||||
# Invalidate Caches on DataChanged
|
# Invalidate Caches on DataChanged
|
||||||
|
@ -869,7 +871,6 @@ class MaxwellSimNode(bpy.types.Node):
|
||||||
"""
|
"""
|
||||||
if hasattr(self, prop_name):
|
if hasattr(self, prop_name):
|
||||||
# Invalidate UI BLField Caches
|
# Invalidate UI BLField Caches
|
||||||
log.critical((prop_name, self.ui_blfields))
|
|
||||||
if prop_name in self.ui_blfields:
|
if prop_name in self.ui_blfields:
|
||||||
setattr(self, prop_name, bl_cache.Signal.InvalidateCache)
|
setattr(self, prop_name, bl_cache.Signal.InvalidateCache)
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ class ViewerNode(base.MaxwellSimNode):
|
||||||
props={'auto_plot'},
|
props={'auto_plot'},
|
||||||
)
|
)
|
||||||
def on_changed_plot_preview(self, props):
|
def on_changed_plot_preview(self, props):
|
||||||
if self.inputs['Any'].is_linked and props['auto_plot']:
|
if props['auto_plot']:
|
||||||
self.trigger_event(ct.FlowEvent.ShowPlot)
|
self.trigger_event(ct.FlowEvent.ShowPlot)
|
||||||
|
|
||||||
@events.on_value_changed(
|
@events.on_value_changed(
|
||||||
|
@ -150,7 +150,7 @@ class ViewerNode(base.MaxwellSimNode):
|
||||||
|
|
||||||
# Remove Non-Repreviewed Previews on Close
|
# Remove Non-Repreviewed Previews on Close
|
||||||
with node_tree.repreview_all():
|
with node_tree.repreview_all():
|
||||||
if self.inputs['Any'].is_linked and props['auto_3d_preview']:
|
if props['auto_3d_preview']:
|
||||||
self.trigger_event(ct.FlowEvent.ShowPreview)
|
self.trigger_event(ct.FlowEvent.ShowPreview)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -712,9 +712,11 @@ class BLField:
|
||||||
kwargs_prop['options'].add('SKIP_SAVE')
|
kwargs_prop['options'].add('SKIP_SAVE')
|
||||||
|
|
||||||
if self._str_cb is not None:
|
if self._str_cb is not None:
|
||||||
kwargs_prop |= lambda _self, context, edit_text: self._safe_str_cb(
|
kwargs_prop |= {
|
||||||
|
'search': lambda _self, context, edit_text: self._safe_str_cb(
|
||||||
_self, context, edit_text
|
_self, context, edit_text
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
## Path
|
## Path
|
||||||
elif AttrType is Path:
|
elif AttrType is Path:
|
||||||
|
@ -845,6 +847,16 @@ class BLField:
|
||||||
self._cached_bl_property.__set__(bl_instance, Signal.InvalidateCache)
|
self._cached_bl_property.__set__(bl_instance, Signal.InvalidateCache)
|
||||||
|
|
||||||
elif value == Signal.ResetStrSearch:
|
elif value == Signal.ResetStrSearch:
|
||||||
|
# Set String to ''
|
||||||
|
## Prevents the presence of an invalid value not in the new search.
|
||||||
|
## -> Infinite recursion if we don't check current value for ''.
|
||||||
|
## -> May cause a hiccup (chains will trigger twice)
|
||||||
|
current_value = self._cached_bl_property.__get__(
|
||||||
|
bl_instance, bl_instance.__class__
|
||||||
|
)
|
||||||
|
if current_value != '':
|
||||||
|
self._cached_bl_property.__set__(bl_instance, '')
|
||||||
|
|
||||||
# Pop the Cached String Search Items
|
# Pop the Cached String Search Items
|
||||||
## The next time Blender does a str search, it'll update.
|
## The next time Blender does a str search, it'll update.
|
||||||
self._str_cb_cache.pop(bl_instance.instance_id, None)
|
self._str_cb_cache.pop(bl_instance.instance_id, None)
|
||||||
|
|
Loading…
Reference in New Issue