buffer non planar, merge paths with zip

This commit is contained in:
Nicolas Predella 2026-05-06 17:45:57 +03:00
parent af96629b76
commit c57f843092
2 changed files with 67 additions and 8 deletions

View file

@ -13,10 +13,12 @@ bl_info = {
}
from math import floor
from itertools import zip_longest
from typing import TYPE_CHECKING, Any
import bpy
import os
import numpy as np
from mathutils import Euler, Matrix
from bpy.types import NodeTree, Operator, Menu
from bpy.props import PointerProperty, StringProperty, FloatProperty, IntProperty
@ -110,6 +112,13 @@ class SLICESOCK_String(InputNodeSocket):
value: StringProperty(name="String", default="") # pyright: ignore[reportInvalidTypeForm]
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
class SLICESOCK_Mesh(LabelNodeSocket):
bl_label = "Mesh"
@ -310,6 +319,7 @@ class SLICENODE_BufferShells(SliceNode):
def init(self, context):
self.add_input("SLICESOCK_Slices")
self.add_input("SLICESOCK_Float", "Distance").value = 0
self.add_input("SLICESOCK_Vector", "Rotation")
self.add_output("SLICESOCK_Slices")
def draw_buttons(self, context, layout):
@ -320,7 +330,25 @@ class SLICENODE_BufferShells(SliceNode):
if socket_name != "Slices": return []
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)
@ -466,16 +494,45 @@ class SLICENODE_JoinPaths(SliceNodeJoiner):
bl_label = "Join Paths"
socket_type = "SLICESOCK_Paths"
def evaluate_output(self, socket_name: str, **_kwargs) -> list[np.ndarray]:
merged_paths = []
mode: bpy.props.EnumProperty(
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:
paths = resolve_input(input)
if paths:
merged_paths.extend(paths)
inputs_data.append(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
@register_class

View file

@ -68,8 +68,10 @@ def resolve_input(
assert socket.node
if not socket.is_linked:
if v := getattr(socket, "value", None): return v
else: return socket.default
val = getattr(socket, "value", None)
if val is not None:
return val
return socket.default
assert socket.is_linked
assert socket.links