separate by range
This commit is contained in:
parent
0cf055876c
commit
9c144a7465
2 changed files with 90 additions and 1 deletions
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
from typing import Any
|
from typing import Any
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from ..bpy_ns_types import NfBoolProperty, NfEnumProperty, notify_tree_changed
|
from ..bpy_ns_types import NfBoolProperty, NfEnumProperty, NfIntProperty, notify_tree_changed
|
||||||
from ....registry import register_class
|
from ....registry import register_class
|
||||||
from .base import SliceNode, SlicingCollection, get_all_children, SliceNodeJoiner, SlicingGroup, SlicingObject
|
from .base import SliceNode, SlicingCollection, get_all_children, SliceNodeJoiner, SlicingGroup, SlicingObject
|
||||||
|
|
||||||
|
|
@ -157,3 +157,91 @@ class SLICENODE_SeparateMesh(SliceNode):
|
||||||
if socket_name == "Selection":
|
if socket_name == "Selection":
|
||||||
return res_selection
|
return res_selection
|
||||||
return res_inverted
|
return res_inverted
|
||||||
|
|
||||||
|
@register_class
|
||||||
|
class SLICENODE_SeparateMeshByRange(SliceNode):
|
||||||
|
bl_label = "Separate Mesh by Range"
|
||||||
|
|
||||||
|
start_val: NfIntProperty(name="Start", default=0, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
end_val: NfIntProperty(name="End", default=1, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
self.add_input("SLICESOCK_Mesh")
|
||||||
|
self.add_input("SLICESOCK_String", "Attribute")
|
||||||
|
self._update_outputs()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
super().update()
|
||||||
|
self._update_outputs()
|
||||||
|
|
||||||
|
def _update_outputs(self):
|
||||||
|
# Ensure end >= start
|
||||||
|
start = self.start_val
|
||||||
|
end = max(start, self.end_val)
|
||||||
|
expected_ids = [str(i) for i in range(start, end + 1)]
|
||||||
|
|
||||||
|
# Remove outputs that are no longer in the range
|
||||||
|
# We use a list to avoid modification-during-iteration issues
|
||||||
|
to_remove = [o for o in self.outputs if o.identifier not in expected_ids]
|
||||||
|
for o in to_remove:
|
||||||
|
self.outputs.remove(o)
|
||||||
|
|
||||||
|
# Add new outputs
|
||||||
|
for id_str in expected_ids:
|
||||||
|
if id_str not in self.outputs:
|
||||||
|
self.add_output("SLICESOCK_Mesh", id_str, identifier=id_str)
|
||||||
|
|
||||||
|
# Re-order to match range
|
||||||
|
for i, id_str in enumerate(expected_ids):
|
||||||
|
current_index = self.outputs.find(id_str)
|
||||||
|
if current_index != -1 and current_index != i:
|
||||||
|
self.outputs.move(current_index, i)
|
||||||
|
|
||||||
|
def draw_buttons(self, context, layout):
|
||||||
|
col = layout.column(align=True)
|
||||||
|
col.prop(self, "start_val")
|
||||||
|
col.prop(self, "end_val")
|
||||||
|
|
||||||
|
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicingCollection | None:
|
||||||
|
coll: SlicingCollection | None = self.resolve("Mesh")
|
||||||
|
attr_name: str = self.resolve("Attribute")
|
||||||
|
|
||||||
|
if not coll: return None
|
||||||
|
|
||||||
|
# Check if we can parse the socket name as an integer
|
||||||
|
try:
|
||||||
|
target_val = int(socket_name)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = SlicingCollection([], f"{coll.name}_{target_val}")
|
||||||
|
|
||||||
|
for so in coll.objects:
|
||||||
|
if attr_name not in so.attributes:
|
||||||
|
continue
|
||||||
|
|
||||||
|
attr = so.attributes[attr_name]
|
||||||
|
vals = attr["values"]
|
||||||
|
|
||||||
|
# Attribute values might be floats or ints, we check for equality
|
||||||
|
mask = vals == target_val
|
||||||
|
|
||||||
|
if np.all(mask):
|
||||||
|
res.objects.append(so)
|
||||||
|
elif np.any(mask):
|
||||||
|
# Split the object
|
||||||
|
so_new = SlicingObject()
|
||||||
|
so_new.name = f"{so.name}_{target_val}"
|
||||||
|
so_new.parent = so.parent
|
||||||
|
so_new.object_type = so.object_type
|
||||||
|
so_new.extruder = so.extruder
|
||||||
|
so_new.mesh = so.mesh[mask]
|
||||||
|
for k, v in so.attributes.items():
|
||||||
|
so_new.attributes[k] = {
|
||||||
|
"domain": v["domain"],
|
||||||
|
"type": v["type"],
|
||||||
|
"values": v["values"][mask]
|
||||||
|
}
|
||||||
|
res.objects.append(so_new)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
|
||||||
|
|
@ -497,6 +497,7 @@ class SLICENODE_MT_Geometry(Menu):
|
||||||
assert layout
|
assert layout
|
||||||
layout.operator("node.add_node", text="Get Mesh").type = "SLICENODE_GetMesh"
|
layout.operator("node.add_node", text="Get Mesh").type = "SLICENODE_GetMesh"
|
||||||
layout.operator("node.add_node", text="Separate Mesh").type = "SLICENODE_SeparateMesh"
|
layout.operator("node.add_node", text="Separate Mesh").type = "SLICENODE_SeparateMesh"
|
||||||
|
layout.operator("node.add_node", text="Separate Mesh by Range").type = "SLICENODE_SeparateMeshByRange"
|
||||||
layout.operator("node.add_node", text="Get Paths").type = "SLICENODE_GetPaths"
|
layout.operator("node.add_node", text="Get Paths").type = "SLICENODE_GetPaths"
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("node.add_node", text="Slice Shells").type = "SLICENODE_SliceShells"
|
layout.operator("node.add_node", text="Slice Shells").type = "SLICENODE_SliceShells"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue