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
|
from typing import Any
|
||||||
import numpy as np
|
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 ....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
|
@register_class
|
||||||
class SLICENODE_GetMesh(SliceNode):
|
class SLICENODE_GetMesh(SliceNode):
|
||||||
|
|
@ -63,3 +63,97 @@ class SLICENODE_JoinMeshGroups(SliceNodeJoiner):
|
||||||
group.collections[coll.objects[0].name] = coll
|
group.collections[coll.objects[0].name] = coll
|
||||||
|
|
||||||
return group
|
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
|
layout = self.layout
|
||||||
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="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"
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,16 @@ class SlicingObject():
|
||||||
def __init__(self, obj: Object | None = None, parent: str = "") -> None:
|
def __init__(self, obj: Object | None = None, parent: str = "") -> None:
|
||||||
if not bpy.context.scene: raise Exception('No scene currently open!')
|
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.parent = parent
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
|
self.modifiers = []
|
||||||
|
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
assert bpy.context.scene.unit_settings
|
assert bpy.context.scene.unit_settings
|
||||||
scene_scale: float = bpy.context.scene.unit_settings.scale_length
|
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.object_type = ns.object_type
|
||||||
self.extruder = ns.extruder
|
self.extruder = ns.extruder
|
||||||
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue