separate by attribute
This commit is contained in:
parent
90a2c61fb4
commit
0cf055876c
3 changed files with 100 additions and 4 deletions
|
|
@ -2,9 +2,9 @@ from __future__ import annotations
|
|||
from typing import Any
|
||||
import numpy as np
|
||||
|
||||
from ..bpy_ns_types import NfBoolProperty
|
||||
from ..bpy_ns_types import NfBoolProperty, NfEnumProperty, notify_tree_changed
|
||||
from ....registry import register_class
|
||||
from .base import SliceNode, SlicingCollection, get_all_children, SliceNodeJoiner, SlicingGroup
|
||||
from .base import SliceNode, SlicingCollection, get_all_children, SliceNodeJoiner, SlicingGroup, SlicingObject
|
||||
|
||||
@register_class
|
||||
class SLICENODE_GetMesh(SliceNode):
|
||||
|
|
@ -63,3 +63,97 @@ class SLICENODE_JoinMeshGroups(SliceNodeJoiner):
|
|||
group.collections[coll.objects[0].name] = coll
|
||||
|
||||
return group
|
||||
|
||||
@register_class
|
||||
class SLICENODE_SeparateMesh(SliceNode):
|
||||
bl_label = "Separate Mesh"
|
||||
|
||||
comparison: NfEnumProperty(
|
||||
name="Comparison",
|
||||
items=[
|
||||
('EQUAL', "Equal", "=="),
|
||||
('NOT_EQUAL', "Not Equal", "!="),
|
||||
('GREATER_THAN', "Greater Than", ">"),
|
||||
('LESS_THAN', "Less Than", "<"),
|
||||
('GREATER_EQUAL', "Greater Equal", ">="),
|
||||
('LESS_EQUAL', "Less Equal", "<="),
|
||||
],
|
||||
default='EQUAL',
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Mesh")
|
||||
self.add_input("SLICESOCK_String", "Attribute")
|
||||
self.add_input("SLICESOCK_Float", "Value")
|
||||
self.add_output("SLICESOCK_Mesh", "Selection")
|
||||
self.add_output("SLICESOCK_Mesh", "Inverted")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
layout.prop(self, "comparison", text="")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicingCollection | None:
|
||||
coll: SlicingCollection | None = self.resolve("Mesh")
|
||||
attr_name: str = self.resolve("Attribute")
|
||||
value: float = self.resolve("Value")
|
||||
|
||||
if not coll: return None
|
||||
|
||||
res_selection = SlicingCollection([], coll.name + "_selection")
|
||||
res_inverted = SlicingCollection([], coll.name + "_inverted")
|
||||
|
||||
for so in coll.objects:
|
||||
if attr_name not in so.attributes:
|
||||
# If attribute missing, entire object goes to inverted
|
||||
res_inverted.objects.append(so)
|
||||
continue
|
||||
|
||||
attr = so.attributes[attr_name]
|
||||
vals = attr["values"]
|
||||
|
||||
mask = np.zeros(len(so.mesh), dtype=bool)
|
||||
if self.comparison == 'EQUAL': mask = vals == value
|
||||
elif self.comparison == 'NOT_EQUAL': mask = vals != value
|
||||
elif self.comparison == 'GREATER_THAN': mask = vals > value
|
||||
elif self.comparison == 'LESS_THAN': mask = vals < value
|
||||
elif self.comparison == 'GREATER_EQUAL': mask = vals >= value
|
||||
elif self.comparison == 'LESS_EQUAL': mask = vals <= value
|
||||
|
||||
if np.all(mask):
|
||||
res_selection.objects.append(so)
|
||||
elif not np.any(mask):
|
||||
res_inverted.objects.append(so)
|
||||
else:
|
||||
# Split the object
|
||||
so_sel = SlicingObject()
|
||||
so_sel.name = so.name + "_sel"
|
||||
so_sel.parent = so.parent
|
||||
so_sel.object_type = so.object_type
|
||||
so_sel.extruder = so.extruder
|
||||
so_sel.mesh = so.mesh[mask]
|
||||
for k, v in so.attributes.items():
|
||||
so_sel.attributes[k] = {
|
||||
"domain": v["domain"],
|
||||
"type": v["type"],
|
||||
"values": v["values"][mask]
|
||||
}
|
||||
res_selection.objects.append(so_sel)
|
||||
|
||||
so_inv = SlicingObject()
|
||||
so_inv.name = so.name + "_inv"
|
||||
so_inv.parent = so.parent
|
||||
so_inv.object_type = so.object_type
|
||||
so_inv.extruder = so.extruder
|
||||
so_inv.mesh = so.mesh[~mask]
|
||||
for k, v in so.attributes.items():
|
||||
so_inv.attributes[k] = {
|
||||
"domain": v["domain"],
|
||||
"type": v["type"],
|
||||
"values": v["values"][~mask]
|
||||
}
|
||||
res_inverted.objects.append(so_inv)
|
||||
|
||||
if socket_name == "Selection":
|
||||
return res_selection
|
||||
return res_inverted
|
||||
|
|
|
|||
|
|
@ -496,6 +496,7 @@ class SLICENODE_MT_Geometry(Menu):
|
|||
layout = self.layout
|
||||
assert layout
|
||||
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="Get Paths").type = "SLICENODE_GetPaths"
|
||||
layout.separator()
|
||||
layout.operator("node.add_node", text="Slice Shells").type = "SLICENODE_SliceShells"
|
||||
|
|
|
|||
|
|
@ -29,15 +29,16 @@ class SlicingObject():
|
|||
def __init__(self, obj: Object | None = None, parent: str = "") -> None:
|
||||
if not bpy.context.scene: raise Exception('No scene currently open!')
|
||||
|
||||
if obj: self.name = str(obj.name)
|
||||
self.name = str(obj.name) if obj else ""
|
||||
self.parent = parent
|
||||
self.attributes = {}
|
||||
self.modifiers = []
|
||||
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
assert bpy.context.scene.unit_settings
|
||||
scene_scale: float = bpy.context.scene.unit_settings.scale_length
|
||||
|
||||
if ns := getattr(obj, 'nodeslicer', None):
|
||||
if obj and (ns := getattr(obj, 'nodeslicer', None)):
|
||||
self.object_type = ns.object_type
|
||||
self.extruder = ns.extruder
|
||||
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue