buffer non planar, merge paths with zip
This commit is contained in:
parent
af96629b76
commit
c57f843092
2 changed files with 67 additions and 8 deletions
69
__init__.py
69
__init__.py
|
|
@ -13,10 +13,12 @@ bl_info = {
|
||||||
}
|
}
|
||||||
|
|
||||||
from math import floor
|
from math import floor
|
||||||
|
from itertools import zip_longest
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
import bpy
|
import bpy
|
||||||
import os
|
import os
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from mathutils import Euler, Matrix
|
||||||
|
|
||||||
from bpy.types import NodeTree, Operator, Menu
|
from bpy.types import NodeTree, Operator, Menu
|
||||||
from bpy.props import PointerProperty, StringProperty, FloatProperty, IntProperty
|
from bpy.props import PointerProperty, StringProperty, FloatProperty, IntProperty
|
||||||
|
|
@ -110,6 +112,13 @@ class SLICESOCK_String(InputNodeSocket):
|
||||||
value: StringProperty(name="String", default="") # pyright: ignore[reportInvalidTypeForm]
|
value: StringProperty(name="String", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||||
color = (0.4, 0.7, 1.0, 1.0)
|
color = (0.4, 0.7, 1.0, 1.0)
|
||||||
|
|
||||||
|
@register_class
|
||||||
|
class SLICESOCK_Vector(InputNodeSocket):
|
||||||
|
bl_label = "Vector"
|
||||||
|
|
||||||
|
value: bpy.props.FloatVectorProperty(name="Vector", size=3, default=(0.0, 0.0, 0.0), subtype='EULER') # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
color = (0.39, 0.39, 0.78, 1.0)
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICESOCK_Mesh(LabelNodeSocket):
|
class SLICESOCK_Mesh(LabelNodeSocket):
|
||||||
bl_label = "Mesh"
|
bl_label = "Mesh"
|
||||||
|
|
@ -310,6 +319,7 @@ class SLICENODE_BufferShells(SliceNode):
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_Slices")
|
self.add_input("SLICESOCK_Slices")
|
||||||
self.add_input("SLICESOCK_Float", "Distance").value = 0
|
self.add_input("SLICESOCK_Float", "Distance").value = 0
|
||||||
|
self.add_input("SLICESOCK_Vector", "Rotation")
|
||||||
self.add_output("SLICESOCK_Slices")
|
self.add_output("SLICESOCK_Slices")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
|
|
@ -320,7 +330,25 @@ class SLICENODE_BufferShells(SliceNode):
|
||||||
if socket_name != "Slices": return []
|
if socket_name != "Slices": return []
|
||||||
|
|
||||||
slices: list[np.ndarray] = resolve_input(self.inputs.get("Slices"))
|
slices: list[np.ndarray] = resolve_input(self.inputs.get("Slices"))
|
||||||
distance: float = resolve_input(self.inputs.get("Distance"))
|
distance: float = resolve_input(self.inputs.get("Distance")) or 0.0
|
||||||
|
rotation: Any = resolve_input(self.inputs.get("Rotation")) or (0.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
if not slices: return []
|
||||||
|
|
||||||
|
if any(rotation):
|
||||||
|
# De-rotate shells to Z-perpendicular plane
|
||||||
|
mat = Euler(rotation).to_matrix().inverted()
|
||||||
|
mat_np = np.array(mat)
|
||||||
|
de_rotated_slices = [s @ mat_np.T for s in slices]
|
||||||
|
|
||||||
|
# Buffer
|
||||||
|
buffered_slices = slice.buffer_slice(de_rotated_slices, distance, miter=self.shape_option)
|
||||||
|
|
||||||
|
# Re-rotate shells back
|
||||||
|
mat_inv = Euler(rotation).to_matrix()
|
||||||
|
mat_inv_np = np.array(mat_inv)
|
||||||
|
re_rotated_slices = [s @ mat_inv_np.T for s in buffered_slices]
|
||||||
|
return re_rotated_slices
|
||||||
|
|
||||||
buffered_slices = slice.buffer_slice(slices, distance, miter=self.shape_option)
|
buffered_slices = slice.buffer_slice(slices, distance, miter=self.shape_option)
|
||||||
|
|
||||||
|
|
@ -466,15 +494,44 @@ class SLICENODE_JoinPaths(SliceNodeJoiner):
|
||||||
bl_label = "Join Paths"
|
bl_label = "Join Paths"
|
||||||
socket_type = "SLICESOCK_Paths"
|
socket_type = "SLICESOCK_Paths"
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> list[np.ndarray]:
|
mode: bpy.props.EnumProperty(
|
||||||
merged_paths = []
|
name="Mode",
|
||||||
|
items=[
|
||||||
|
('EXTEND', "Extend", "Append paths sequentially"),
|
||||||
|
('ZIP', "Zip", "Interleave paths from all inputs"),
|
||||||
|
],
|
||||||
|
default='EXTEND'
|
||||||
|
) # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
|
||||||
|
def draw_buttons(self, context, layout):
|
||||||
|
assert layout
|
||||||
|
layout.prop(self, "mode", text="")
|
||||||
|
|
||||||
|
def evaluate_output(self, socket_name: str, **_kwargs) -> list[np.ndarray]:
|
||||||
|
inputs_data: list[list[np.ndarray]] = []
|
||||||
for input in self.inputs:
|
for input in self.inputs:
|
||||||
paths = resolve_input(input)
|
paths = resolve_input(input)
|
||||||
if paths:
|
if paths:
|
||||||
merged_paths.extend(paths)
|
inputs_data.append(paths)
|
||||||
|
|
||||||
return merged_paths
|
if not inputs_data:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if self.mode == 'EXTEND':
|
||||||
|
merged_paths = []
|
||||||
|
for paths in inputs_data:
|
||||||
|
merged_paths.extend(paths)
|
||||||
|
return merged_paths
|
||||||
|
|
||||||
|
elif self.mode == 'ZIP':
|
||||||
|
merged_paths = []
|
||||||
|
for group in zip_longest(*inputs_data):
|
||||||
|
for path in group:
|
||||||
|
if path is not None:
|
||||||
|
merged_paths.append(path)
|
||||||
|
return merged_paths
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
### I/O
|
### I/O
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,10 @@ def resolve_input(
|
||||||
assert socket.node
|
assert socket.node
|
||||||
|
|
||||||
if not socket.is_linked:
|
if not socket.is_linked:
|
||||||
if v := getattr(socket, "value", None): return v
|
val = getattr(socket, "value", None)
|
||||||
else: return socket.default
|
if val is not None:
|
||||||
|
return val
|
||||||
|
return socket.default
|
||||||
|
|
||||||
assert socket.is_linked
|
assert socket.is_linked
|
||||||
assert socket.links
|
assert socket.links
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue