realtime also when changing parameters
This commit is contained in:
parent
e69a92c9a3
commit
3b6f996724
3 changed files with 80 additions and 45 deletions
|
|
@ -17,6 +17,7 @@ from .. import slice
|
|||
from ..slice import Pather, slice_at_range, SlicePaths
|
||||
from ...infra.blender_mesh_capture import SlicingObject, capture_mesh_edges, SlicingGroup
|
||||
from ...infra.blender_slice_nodes_generic import SliceNode, resolve_input
|
||||
from .tree import notify_tree_changed
|
||||
from ...infra._3mf_writer._3mf import prepare_3mf
|
||||
|
||||
class SliceIntrinsics:
|
||||
|
|
@ -91,6 +92,8 @@ class SLICENODE_PrusaslicerConf(SliceNode):
|
|||
if self.printer_enum != 'NONE':
|
||||
cache.evaluate_compatibility({self.printer_enum}, set(cache.vendors))
|
||||
|
||||
notify_tree_changed(self, context)
|
||||
|
||||
def get_printers(self, context):
|
||||
cache = self.get_cache()
|
||||
items = [('NONE', "None", "No printer selected")]
|
||||
|
|
@ -148,12 +151,14 @@ class SLICENODE_PrusaslicerConf(SliceNode):
|
|||
|
||||
filament_enum: EnumProperty(
|
||||
name="Filament",
|
||||
items=get_filaments
|
||||
items=get_filaments,
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
print_enum: EnumProperty(
|
||||
name="Print",
|
||||
items=get_prints
|
||||
items=get_prints,
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
|
|
@ -186,6 +191,7 @@ class SLICENODE_InputPrinter(SliceNode):
|
|||
|
||||
def update_label(self, context):
|
||||
self.label = self.printer_label
|
||||
notify_tree_changed(self, context)
|
||||
|
||||
printer_label: StringProperty(name="Label", default="Printer", update=update_label) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
|
|
@ -196,7 +202,8 @@ class SLICENODE_InputPrinter(SliceNode):
|
|||
('CREALITY', "Creality", "Creality Cloud API"),
|
||||
('MOONRAKER', "Moonraker", "Moonraker (Klipper) API"),
|
||||
],
|
||||
default='PRUSALINK'
|
||||
default='PRUSALINK',
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
|
|
@ -503,14 +510,16 @@ class SLICENODE_Infill(SliceNode):
|
|||
bl_label = "Infill"
|
||||
|
||||
infill_type: EnumProperty(
|
||||
name="Infill Type",
|
||||
name="Type",
|
||||
items=[
|
||||
('GRID', "Grid", "Cross-hatch grid infill"),
|
||||
('GRID', "Grid", "Cross-hatch grid"),
|
||||
('RECTILINEAR', "Rectilinear", "Alternating parallel lines"),
|
||||
],
|
||||
default='GRID'
|
||||
default='GRID',
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Paths")
|
||||
self.add_input("SLICESOCK_Float", "Density").value = 2.0
|
||||
|
|
@ -562,12 +571,12 @@ class SLICENODE_Infill(SliceNode):
|
|||
class SLICENODE_PathsToGcode(SliceNode):
|
||||
bl_label = "Paths to Gcode"
|
||||
|
||||
use_header: BoolProperty(name="Header", default=True) # pyright: ignore[reportInvalidTypeForm]
|
||||
use_header: BoolProperty(name="Header", default=True, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Paths")
|
||||
self.add_input("SLICESOCK_Intrinsics")
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
self.add_output("SLICESOCK_GcodeRo")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
|
|
@ -593,17 +602,11 @@ class SLICENODE_PathsToGcode(SliceNode):
|
|||
class SLICENODE_GcodeBlock(SliceNode):
|
||||
bl_label = "Gcode Block"
|
||||
|
||||
gcode: StringProperty(name="Gcode", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
layout.prop(self, "gcode", text="")
|
||||
self.add_output("SLICESOCK_Gcode", "Gcode")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||
return self.gcode
|
||||
return self.outputs['Gcode'].value
|
||||
|
||||
@register_class
|
||||
class SLICENODE_GcodeHelper(SliceNode):
|
||||
|
|
@ -644,7 +647,7 @@ class SLICENODE_GcodeHelper(SliceNode):
|
|||
parameters: StringProperty(name="Params", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
self.add_output("SLICESOCK_GcodeRo")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
|
|
@ -675,7 +678,9 @@ class SliceNodeJoiner(SliceNode):
|
|||
|
||||
def update(self) -> None:
|
||||
def is_active(input) -> bool:
|
||||
return input.is_linked or getattr(input, 'value', False)
|
||||
a: bool = input.is_linked or getattr(input, 'value', False)
|
||||
b: bool = getattr(self.inputs[-1], 'value', False)
|
||||
return a or b
|
||||
|
||||
if len(self.inputs) == 0:
|
||||
self.add_input(self.socket_type)
|
||||
|
|
@ -695,6 +700,7 @@ class SliceNodeJoiner(SliceNode):
|
|||
class SLICENODE_JoinGcode(SliceNodeJoiner):
|
||||
bl_label = "Join Gcode"
|
||||
socket_type = "SLICESOCK_Gcode"
|
||||
output_socket_type = "SLICESOCK_GcodeRo"
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||
gcode_input: list[str] = []
|
||||
|
|
@ -841,7 +847,7 @@ class SLICENODE_LoadGcode(SliceNode):
|
|||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_FilePath", "Filepath")
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
self.add_output("SLICESOCK_GcodeRo")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||
path = resolve_input(self.inputs.get("Filepath"))
|
||||
|
|
@ -1053,14 +1059,14 @@ class SLICENODE_PrusaslicerSlice(ExecSliceNode):
|
|||
bl_label = "PrusaSlicer Slice"
|
||||
button_label = "Slice with PrusaSlicer"
|
||||
|
||||
prusaslicer_executable: StringProperty(name="PrusaSlicer Path", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm]
|
||||
prusaslicer_executable: StringProperty(name="PrusaSlicer Path", subtype='FILE_PATH', update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
gcode_result: StringProperty(name="Gcode Result", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self._init_exec()
|
||||
self.add_input("SLICESOCK_Object", "Object")
|
||||
self.add_input("SLICESOCK_PrusaslicerConf", "Config")
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
self.add_output("SLICESOCK_GcodeRo")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
super().draw_buttons(context, layout)
|
||||
|
|
@ -1186,10 +1192,13 @@ class SLICENODE_GcodeToMesh(ExecSliceNode):
|
|||
|
||||
def init(self, context):
|
||||
self._init_exec()
|
||||
self.add_input("SLICESOCK_Gcode")
|
||||
self.add_input("SLICESOCK_Gcode", "Gcode")
|
||||
self.add_input("SLICESOCK_String", "Name")
|
||||
|
||||
def evaluate(self, operator=None):
|
||||
gcode: str = resolve_input(self.inputs.get("Gcode"))
|
||||
gcode: str = self.resolve("Gcode")
|
||||
mesh_name: str = self.resolve("Name")
|
||||
|
||||
if not gcode:
|
||||
return 'empty'
|
||||
|
||||
|
|
@ -1247,7 +1256,6 @@ class SLICENODE_GcodeToMesh(ExecSliceNode):
|
|||
return 'no segments'
|
||||
|
||||
# Create or find "Debug" mesh object
|
||||
mesh_name = "Debug"
|
||||
if mesh_name in bpy.data.objects:
|
||||
obj = bpy.data.objects[mesh_name]
|
||||
mesh = obj.data
|
||||
|
|
@ -1290,7 +1298,7 @@ class SLICENODE_Executor(ExecSliceNode, SliceNodeJoiner):
|
|||
button_label = "Execute All"
|
||||
socket_type = "SLICESOCK_ExecutionLink"
|
||||
|
||||
auto_refresh: bpy.props.BoolProperty(name="Auto-refresh", default=False) # pyright: ignore[reportInvalidTypeForm]
|
||||
auto_refresh: bpy.props.BoolProperty(name="Auto-refresh", default=False, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self._init_exec()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from ...registry import register_class
|
|||
from ...infra.blender_slice_nodes_generic import SliceNodeSocket
|
||||
from ..slice import SlicePaths
|
||||
|
||||
from .tree import notify_tree_changed
|
||||
|
||||
class LabelNodeSocket(SliceNodeSocket):
|
||||
default = ""
|
||||
def draw(self, context: Any, layout: Any, node: Any, text: str | None):
|
||||
|
|
@ -38,6 +40,7 @@ class SLICESOCK_Object(InputNodeSocket):
|
|||
value: PointerProperty(
|
||||
name="Object",
|
||||
type=bpy.types.Object,
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (0.9, 0.7, 0.2, 1.0)
|
||||
|
||||
|
|
@ -45,7 +48,7 @@ class SLICESOCK_Object(InputNodeSocket):
|
|||
class SLICESOCK_Float_Common(InputNodeSocket):
|
||||
bl_label = "Float"
|
||||
|
||||
value: FloatProperty(name="Float", default=0) # pyright: ignore[reportInvalidTypeForm]
|
||||
value: FloatProperty(name="Float", default=0, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (161/255, 161/255, 161/255, 1.0)
|
||||
|
||||
@register_class
|
||||
|
|
@ -57,7 +60,7 @@ class SLICESOCK_Float(SLICESOCK_Float_Common): ...
|
|||
class SLICESOCK_Int_Common(InputNodeSocket):
|
||||
bl_label = "Integer"
|
||||
|
||||
value: IntProperty(name="Int", default=0) # pyright: ignore[reportInvalidTypeForm]
|
||||
value: IntProperty(name="Int", default=0, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (161/255, 161/255, 161/255, 1.0)
|
||||
|
||||
@register_class
|
||||
|
|
@ -70,21 +73,21 @@ class SLICESOCK_IntRo(ReadonlySocket, SLICESOCK_Int_Common): ...
|
|||
class SLICESOCK_String(InputNodeSocket):
|
||||
bl_label = "String"
|
||||
|
||||
value: StringProperty(name="String", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
value: StringProperty(name="String", default="", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (0.4, 0.7, 1.0, 1.0)
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_FilePath(InputNodeSocket):
|
||||
bl_label = "Filepath"
|
||||
|
||||
value: StringProperty(name="Filepath", default="", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm]
|
||||
value: StringProperty(name="Filepath", default="", subtype='FILE_PATH', update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (0.4, 0.7, 1.0, 1.0)
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_VectorRotation_Common(InputNodeSocket):
|
||||
bl_label = "VectorRotation"
|
||||
|
||||
value: bpy.props.FloatVectorProperty(name="Vector", size=3, default=(0.0, 0.0, 0.0), subtype='EULER') # pyright: ignore[reportInvalidTypeForm]
|
||||
value: bpy.props.FloatVectorProperty(name="Vector", size=3, default=(0.0, 0.0, 0.0), subtype='EULER', update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (0.39, 0.39, 0.78, 1.0)
|
||||
@register_class
|
||||
class SLICESOCK_VectorRotation(SLICESOCK_VectorRotation_Common): ...
|
||||
|
|
@ -103,12 +106,16 @@ class SLICESOCK_Paths(LabelNodeSocket):
|
|||
default = SlicePaths()
|
||||
bl_color = (214/255, 0, 120/255, 1.0)
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_Gcode(LabelNodeSocket):
|
||||
class SLICESOCK_Gcode_Common(InputNodeSocket):
|
||||
bl_label = "Gcode"
|
||||
default = ""
|
||||
value: StringProperty(name="Gcode", default="", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
bl_color = (1., 1., 1., 1.)
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_Gcode(SLICESOCK_Gcode_Common): ...
|
||||
@register_class
|
||||
class SLICESOCK_GcodeRo(ReadonlySocket, SLICESOCK_Gcode_Common): ...
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_Intrinsics(LabelNodeSocket):
|
||||
bl_label = "Intrinsics"
|
||||
|
|
@ -131,12 +138,12 @@ class SLICESOCK_ExecutionLink(LabelNodeSocket):
|
|||
class SLICESOCK_Printer(InputNodeSocket):
|
||||
bl_label = "Printer"
|
||||
|
||||
host: StringProperty(name="Host", default="localhost") # pyright: ignore[reportInvalidTypeForm]
|
||||
port: IntProperty(name="Port", default=80, min=1, max=65535) # pyright: ignore[reportInvalidTypeForm]
|
||||
prefix: StringProperty(name="Prefix", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
label: StringProperty(name="Label", default="Printer") # pyright: ignore[reportInvalidTypeForm]
|
||||
username: StringProperty(name="Username", default="") # pyright: ignore[reportInvalidTypeForm]
|
||||
password: StringProperty(name="Password", default="", subtype='PASSWORD') # pyright: ignore[reportInvalidTypeForm]
|
||||
host: StringProperty(name="Host", default="localhost", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
port: IntProperty(name="Port", default=80, min=1, max=65535, update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
prefix: StringProperty(name="Prefix", default="", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
label: StringProperty(name="Label", default="Printer", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
username: StringProperty(name="Username", default="", update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
password: StringProperty(name="Password", default="", subtype='PASSWORD', update=notify_tree_changed) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
host_type: EnumProperty(
|
||||
name="Type",
|
||||
|
|
@ -145,7 +152,8 @@ class SLICESOCK_Printer(InputNodeSocket):
|
|||
('CREALITY', "Creality", "Creality Cloud API"),
|
||||
('MOONRAKER', "Moonraker", "Moonraker (Klipper) API"),
|
||||
],
|
||||
default='PRUSALINK'
|
||||
default='PRUSALINK',
|
||||
update=notify_tree_changed
|
||||
) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
bl_color = (1.0, 0.45, 0.1, 1.0) # Orange
|
||||
|
|
|
|||
|
|
@ -39,9 +39,15 @@ def get_referenced_objects(node, visited=None):
|
|||
return objs
|
||||
|
||||
_is_updating = False
|
||||
_tree_changed = False
|
||||
|
||||
def notify_tree_changed(self, context):
|
||||
global _tree_changed
|
||||
_tree_changed = True
|
||||
|
||||
@persistent
|
||||
def depsgraph_update_handler(scene, depsgraph):
|
||||
global _is_updating
|
||||
global _is_updating, _tree_changed
|
||||
if _is_updating:
|
||||
return
|
||||
|
||||
|
|
@ -55,18 +61,31 @@ def depsgraph_update_handler(scene, depsgraph):
|
|||
if obj.type == 'MESH' and obj.data in updated_meshes:
|
||||
updated_object_names.add(obj.name)
|
||||
|
||||
if not updated_object_names:
|
||||
# Note: custom NodeTree updates often don't appear in depsgraph.updates
|
||||
# so we rely on _tree_changed flag set by property update callbacks.
|
||||
|
||||
if not updated_object_names and not _tree_changed:
|
||||
return
|
||||
|
||||
tree_needs_refresh = _tree_changed
|
||||
_tree_changed = False # reset for next run
|
||||
|
||||
for tree in bpy.data.node_groups:
|
||||
if tree.bl_idname != "SLICETREE_NodeTree":
|
||||
continue
|
||||
|
||||
for node in tree.nodes:
|
||||
if node.bl_idname == "SLICENODE_Executor" and getattr(node, "auto_refresh", False):
|
||||
referenced = get_referenced_objects(node)
|
||||
referenced_names = {obj.name for obj in referenced if obj}
|
||||
if updated_object_names & referenced_names:
|
||||
# Trigger if the tree changed OR if a referenced object changed
|
||||
should_refresh = tree_needs_refresh
|
||||
|
||||
if not should_refresh:
|
||||
referenced = get_referenced_objects(node)
|
||||
referenced_names = {obj.name for obj in referenced if obj}
|
||||
if updated_object_names & referenced_names:
|
||||
should_refresh = True
|
||||
|
||||
if should_refresh:
|
||||
_is_updating = True
|
||||
try:
|
||||
node.evaluate()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue