feat: more time sequence choice
fix: contrast formulation cause leak of high value fix: empty series_id cause fail to read chore: restructure folder chore!: change layer vars BREAKING CHANGE: not support 4.1 any more
This commit is contained in:
251
bioxelnodes/operators/layer.py
Normal file
251
bioxelnodes/operators/layer.py
Normal file
@@ -0,0 +1,251 @@
|
||||
from pathlib import Path
|
||||
import bpy
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
from ..bioxel.layer import Layer
|
||||
from ..bioxelutils.node import get_nodes_by_type
|
||||
from ..bioxelutils.container import add_layers, get_container_obj
|
||||
from ..bioxelutils.layer import obj_to_layer, get_container_layer_objs, get_layer_obj
|
||||
from .utils import get_preferences, select_object
|
||||
|
||||
|
||||
def get_layer_prop_value(layer_obj: bpy.types.Object, prop: str):
|
||||
try:
|
||||
node_group = layer_obj.modifiers[0].node_group
|
||||
layer_node = get_nodes_by_type(node_group, "BioxelNodes__Layer")[0]
|
||||
return layer_node.inputs[prop].default_value
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_label_layer_selection(self, context):
|
||||
items = [("None", "None", "")]
|
||||
container_obj = get_container_obj(bpy.context.active_object)
|
||||
|
||||
for layer_obj in get_container_layer_objs(container_obj):
|
||||
if get_layer_prop_value(layer_obj, "kind") == "label":
|
||||
items.append((layer_obj.name,
|
||||
layer_obj.name,
|
||||
""))
|
||||
|
||||
return items
|
||||
|
||||
|
||||
class LayerOperator():
|
||||
bl_options = {'UNDO'}
|
||||
base_obj: bpy.types.Object = None
|
||||
|
||||
def add_layer(self, context, layer):
|
||||
preferences = get_preferences(context)
|
||||
cache_dir = Path(preferences.cache_dir, 'VDBs')
|
||||
container_obj = self.base_obj.parent
|
||||
|
||||
add_layers([layer],
|
||||
container_obj,
|
||||
cache_dir)
|
||||
|
||||
select_object(container_obj)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.base_obj = get_layer_obj(bpy.context.active_object)
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
class ScalarOperator(LayerOperator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
label_objs = [obj for obj in context.selected_ids
|
||||
if get_layer_prop_value(obj, "kind") == "scalar"]
|
||||
return True if len(label_objs) > 0 else False
|
||||
|
||||
|
||||
class LabelOperator(LayerOperator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
label_objs = [obj for obj in context.selected_ids
|
||||
if get_layer_prop_value(obj, "kind") == "label"]
|
||||
return True if len(label_objs) > 0 else False
|
||||
|
||||
|
||||
class SignScalar(bpy.types.Operator, ScalarOperator):
|
||||
bl_idname = "bioxelnodes.sign_scalar"
|
||||
bl_label = "Sign Scalar"
|
||||
bl_description = "Sign the scalar value"
|
||||
|
||||
def execute(self, context):
|
||||
base_layer = obj_to_layer(self.base_obj)
|
||||
|
||||
modified_layer = base_layer.copy()
|
||||
modified_layer.data = -base_layer.data
|
||||
modified_layer.name = f"{base_layer.name}_Signed"
|
||||
|
||||
self.add_layer(context, modified_layer)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class FillOperator(ScalarOperator):
|
||||
def invoke(self, context, event):
|
||||
self.base_obj = get_layer_obj(bpy.context.active_object)
|
||||
scalar_min = get_layer_prop_value(self.base_obj, "min")
|
||||
self.fill_value = min(scalar_min, 0)
|
||||
context.window_manager.invoke_props_dialog(self, width=400)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class FillByThreshold(bpy.types.Operator, FillOperator):
|
||||
bl_idname = "bioxelnodes.fill_by_threshold"
|
||||
bl_label = "Fill Value by Threshold"
|
||||
bl_description = "Fill Value by Threshold"
|
||||
|
||||
threshold: bpy.props.FloatProperty(
|
||||
name="Threshold",
|
||||
soft_min=0, soft_max=1024,
|
||||
default=128,
|
||||
) # type: ignore
|
||||
|
||||
fill_value: bpy.props.FloatProperty(
|
||||
name="Fill Value",
|
||||
soft_min=0, soft_max=1024.0,
|
||||
default=0,
|
||||
) # type: ignore
|
||||
|
||||
invert: bpy.props.BoolProperty(
|
||||
name="Invert Area",
|
||||
default=True,
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
base_layer = obj_to_layer(self.base_obj)
|
||||
|
||||
data = np.amax(base_layer.data, -1)
|
||||
mask = data <= self.threshold \
|
||||
if self.invert else data > self.threshold
|
||||
|
||||
modified_layer = base_layer.copy()
|
||||
modified_layer.fill(self.fill_value, mask)
|
||||
modified_layer.name = f"{base_layer.name}_{self.threshold}-Filled"
|
||||
|
||||
self.add_layer(context, modified_layer)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class FillByRange(bpy.types.Operator, FillOperator):
|
||||
bl_idname = "bioxelnodes.fill_by_range"
|
||||
bl_label = "Fill Value by Range"
|
||||
bl_description = "Fill Value by Range"
|
||||
|
||||
from_min: bpy.props.FloatProperty(
|
||||
name="From Min",
|
||||
soft_min=0, soft_max=1024,
|
||||
default=128,
|
||||
) # type: ignore
|
||||
|
||||
from_max: bpy.props.FloatProperty(
|
||||
name="From Max",
|
||||
soft_min=0, soft_max=1024,
|
||||
default=256,
|
||||
) # type: ignore
|
||||
|
||||
fill_value: bpy.props.FloatProperty(
|
||||
name="Fill Value",
|
||||
soft_min=0, soft_max=1024.0,
|
||||
default=0,
|
||||
) # type: ignore
|
||||
|
||||
invert: bpy.props.BoolProperty(
|
||||
name="Invert Area",
|
||||
default=True,
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
base_layer = obj_to_layer(self.base_obj)
|
||||
|
||||
data = np.amax(base_layer.data, -1)
|
||||
mask = (data <= self.from_min) | (data >= self.from_max) if self.invert else \
|
||||
(data > self.from_min) & (data < self.from_max)
|
||||
|
||||
modified_layer = base_layer.copy()
|
||||
modified_layer.fill(self.fill_value, mask)
|
||||
modified_layer.name = f"{base_layer.name}_{self.from_min}-{self.from_max}-Filled"
|
||||
|
||||
self.add_layer(context, modified_layer)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class FillByLabel(bpy.types.Operator, FillOperator):
|
||||
bl_idname = "bioxelnodes.fill_by_label"
|
||||
bl_label = "Fill Value by Label"
|
||||
bl_description = "Fill Value by Label Area"
|
||||
|
||||
label_obj_name: bpy.props.EnumProperty(
|
||||
name="Label Layer",
|
||||
items=get_label_layer_selection
|
||||
) # type: ignore
|
||||
|
||||
fill_value: bpy.props.FloatProperty(
|
||||
name="Fill Value",
|
||||
soft_min=0, soft_max=1024.0,
|
||||
default=0,
|
||||
) # type: ignore
|
||||
|
||||
invert: bpy.props.BoolProperty(
|
||||
name="Invert Label",
|
||||
default=True,
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
label_obj = bpy.data.objects.get(self.label_obj_name)
|
||||
if not label_obj:
|
||||
self.report({"WARNING"}, "Cannot find any label layer.")
|
||||
return {'FINISHED'}
|
||||
|
||||
base_layer = obj_to_layer(self.base_obj)
|
||||
|
||||
label_layer = obj_to_layer(label_obj)
|
||||
label_layer.resize(base_layer.shape)
|
||||
mask = np.amax(label_layer.data, -1)
|
||||
if self.invert:
|
||||
mask = 1 - mask
|
||||
|
||||
modified_layer = base_layer.copy()
|
||||
modified_layer.fill(self.fill_value, mask)
|
||||
modified_layer.name = f"{base_layer.name}_{label_layer.name}-Filled"
|
||||
|
||||
self.add_layer(context, modified_layer)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CombineLabels(bpy.types.Operator, LabelOperator):
|
||||
bl_idname = "bioxelnodes.combine_labels"
|
||||
bl_label = "Combine Labels"
|
||||
bl_description = "Combine all selected labels"
|
||||
|
||||
def execute(self, context):
|
||||
label_objs = [obj for obj in context.selected_ids
|
||||
if get_layer_prop_value(obj, "kind") == "label"]
|
||||
|
||||
if len(label_objs) < 2:
|
||||
self.report({"WARNING"}, "Not enough layers.")
|
||||
return {'FINISHED'}
|
||||
base_obj = label_objs[0]
|
||||
label_objs = label_objs[1:]
|
||||
container_obj = base_obj.parent
|
||||
|
||||
base_layer = obj_to_layer(base_obj)
|
||||
modified_layer = base_layer.copy()
|
||||
label_names = [base_layer.name]
|
||||
|
||||
for label_obj in label_objs:
|
||||
label_layer = obj_to_layer(label_obj)
|
||||
label_layer.resize(base_layer.shape)
|
||||
modified_layer.data = np.maximum(base_layer.data, label_layer.data)
|
||||
label_names.append(label_layer.name)
|
||||
|
||||
modified_layer.name = f"{'-'.join(label_names)}-Combined"
|
||||
|
||||
self.add_layer(context, modified_layer)
|
||||
|
||||
return {'FINISHED'}
|
||||
Reference in New Issue
Block a user