up
This commit is contained in:
parent
562dc221a2
commit
31d34577e7
8 changed files with 123 additions and 83 deletions
|
|
@ -251,7 +251,7 @@ class SLICENODE_SeparateMeshByRange(SliceNode):
|
||||||
so_new.mesh = so.mesh[mask]
|
so_new.mesh = so.mesh[mask]
|
||||||
so_new.transforms = [dict(t) for t in getattr(so, "transforms", [])]
|
so_new.transforms = [dict(t) for t in getattr(so, "transforms", [])]
|
||||||
so_new.pdf_properties = copy.deepcopy(so.pdf_properties) if hasattr(so, "pdf_properties") and so.pdf_properties else PdfProperties()
|
so_new.pdf_properties = copy.deepcopy(so.pdf_properties) if hasattr(so, "pdf_properties") and so.pdf_properties else PdfProperties()
|
||||||
so_new.object_properties = list(getattr(so, "object_properties", []))
|
so_new.pdf_object_properties = list(getattr(so, "pdf_object_properties", []))
|
||||||
so_new.modifiers = so.modifiers.copy() if so.modifiers else []
|
so_new.modifiers = so.modifiers.copy() if so.modifiers else []
|
||||||
for k, v in so.attributes.items():
|
for k, v in so.attributes.items():
|
||||||
so_new.attributes[k] = {
|
so_new.attributes[k] = {
|
||||||
|
|
@ -436,7 +436,7 @@ def _copy_slicing_object(so: SlicingObject) -> SlicingObject:
|
||||||
new_so.extruder = so.extruder
|
new_so.extruder = so.extruder
|
||||||
new_so.transforms = [dict(t) for t in getattr(so, "transforms", [])]
|
new_so.transforms = [dict(t) for t in getattr(so, "transforms", [])]
|
||||||
new_so.pdf_properties = copy.deepcopy(so.pdf_properties) if hasattr(so, "pdf_properties") and so.pdf_properties else PdfProperties()
|
new_so.pdf_properties = copy.deepcopy(so.pdf_properties) if hasattr(so, "pdf_properties") and so.pdf_properties else PdfProperties()
|
||||||
new_so.object_properties = list(getattr(so, "object_properties", []))
|
new_so.pdf_object_properties = list(getattr(so, "pdf_object_properties", []))
|
||||||
return new_so
|
return new_so
|
||||||
|
|
||||||
def _copy_slicing_collection(sc: SlicingCollection) -> SlicingCollection:
|
def _copy_slicing_collection(sc: SlicingCollection) -> SlicingCollection:
|
||||||
|
|
@ -778,7 +778,7 @@ def _assign_properties_to_item(geom: Any, props: list[tuple[str, str]]) -> Any:
|
||||||
return None
|
return None
|
||||||
elif isinstance(geom, SlicePaths):
|
elif isinstance(geom, SlicePaths):
|
||||||
copied = geom.copy_with(paths=list(geom))
|
copied = geom.copy_with(paths=list(geom))
|
||||||
copied.object_properties = resolve_object_properties(copied, props)
|
copied.pdf_object_properties = resolve_object_properties(copied, props)
|
||||||
return copied
|
return copied
|
||||||
elif isinstance(geom, (list, tuple)):
|
elif isinstance(geom, (list, tuple)):
|
||||||
return [_assign_properties_to_item(item, props) for item in geom]
|
return [_assign_properties_to_item(item, props) for item in geom]
|
||||||
|
|
@ -786,23 +786,23 @@ def _assign_properties_to_item(geom: Any, props: list[tuple[str, str]]) -> Any:
|
||||||
copied = _copy_slicing_group(geom)
|
copied = _copy_slicing_group(geom)
|
||||||
for coll in copied.collections.values():
|
for coll in copied.collections.values():
|
||||||
for obj in coll.objects:
|
for obj in coll.objects:
|
||||||
obj.object_properties = resolve_object_properties(obj, props)
|
obj.pdf_object_properties = resolve_object_properties(obj, props)
|
||||||
return copied
|
return copied
|
||||||
elif isinstance(geom, SlicingCollection):
|
elif isinstance(geom, SlicingCollection):
|
||||||
copied = _copy_slicing_collection(geom)
|
copied = _copy_slicing_collection(geom)
|
||||||
for obj in copied.objects:
|
for obj in copied.objects:
|
||||||
obj.object_properties = resolve_object_properties(obj, props)
|
obj.pdf_object_properties = resolve_object_properties(obj, props)
|
||||||
return copied
|
return copied
|
||||||
elif isinstance(geom, SlicingObject):
|
elif isinstance(geom, SlicingObject):
|
||||||
copied = _copy_slicing_object(geom)
|
copied = _copy_slicing_object(geom)
|
||||||
copied.object_properties = resolve_object_properties(copied, props)
|
copied.pdf_object_properties = resolve_object_properties(copied, props)
|
||||||
return copied
|
return copied
|
||||||
return geom
|
return geom
|
||||||
|
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_AssignPropertyToObject(SliceNode):
|
class SLICENODE_AssignPropertyToObject(SliceNode):
|
||||||
bl_label = "Assign Property to Object"
|
bl_label = "Assign PDF Object Properties"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_Mesh", "Geometry")
|
self.add_input("SLICESOCK_Mesh", "Geometry")
|
||||||
|
|
|
||||||
|
|
@ -497,9 +497,6 @@ class SLICENODE_MT_DataInput(Menu):
|
||||||
layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject"
|
layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject"
|
||||||
layout.operator("node.add_node", text="Collection Objects").type = "SLICENODE_CollectionObjects"
|
layout.operator("node.add_node", text="Collection Objects").type = "SLICENODE_CollectionObjects"
|
||||||
layout.operator("node.add_node", text="Settings").type = "SLICENODE_InputSettings"
|
layout.operator("node.add_node", text="Settings").type = "SLICENODE_InputSettings"
|
||||||
layout.operator("node.add_node", text="Wipe Tower").type = "SLICENODE_WipeTower"
|
|
||||||
layout.operator("node.add_node", text="Offset Object").type = "SLICENODE_OffsetObject"
|
|
||||||
layout.operator("node.add_node", text="PrusaSlicer Config").type = "SLICENODE_PrusaslicerConf"
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("node.add_node", text="Printer").type = "SLICENODE_InputPrinter"
|
layout.operator("node.add_node", text="Printer").type = "SLICENODE_InputPrinter"
|
||||||
layout.operator("node.add_node", text="Printer List").type = "SLICENODE_PrinterList"
|
layout.operator("node.add_node", text="Printer List").type = "SLICENODE_PrinterList"
|
||||||
|
|
@ -515,9 +512,6 @@ class SLICENODE_MT_Geometry(Menu):
|
||||||
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="Rename Mesh").type = "SLICENODE_RenameMesh"
|
layout.operator("node.add_node", text="Rename Mesh").type = "SLICENODE_RenameMesh"
|
||||||
layout.operator("node.add_node", text="Assign PDF Properties").type = "SLICENODE_AssignPdfProperties"
|
|
||||||
layout.operator("node.add_node", text="Assign Property to Object").type = "SLICENODE_AssignPropertyToObject"
|
|
||||||
layout.operator("node.add_node", text="Assign Modifiers").type = "SLICENODE_AssignModifiers"
|
|
||||||
layout.operator("node.add_node", text="Transform Geometry").type = "SLICENODE_TransformGeometry"
|
layout.operator("node.add_node", text="Transform Geometry").type = "SLICENODE_TransformGeometry"
|
||||||
layout.operator("node.add_node", text="Drop Geometry").type = "SLICENODE_DropGeometry"
|
layout.operator("node.add_node", text="Drop Geometry").type = "SLICENODE_DropGeometry"
|
||||||
layout.operator("node.add_node", text="Separate Mesh").type = "SLICENODE_SeparateMesh"
|
layout.operator("node.add_node", text="Separate Mesh").type = "SLICENODE_SeparateMesh"
|
||||||
|
|
@ -541,6 +535,35 @@ class SLICENODE_MT_Geometry(Menu):
|
||||||
layout.operator("node.add_node", text="Math").type = "SLICENODE_FloatMath"
|
layout.operator("node.add_node", text="Math").type = "SLICENODE_FloatMath"
|
||||||
layout.operator("node.add_node", text="Integer Math").type = "SLICENODE_IntMath"
|
layout.operator("node.add_node", text="Integer Math").type = "SLICENODE_IntMath"
|
||||||
|
|
||||||
|
@register_class
|
||||||
|
class SLICENODE_MT_PrusaSlicer(Menu):
|
||||||
|
bl_idname = "SLICENODE_MT_PrusaSlicer"
|
||||||
|
bl_label = "PrusaSlicer"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
assert layout
|
||||||
|
layout.operator("node.add_node", text="PrusaSlicer Config").type = "SLICENODE_PrusaslicerConf"
|
||||||
|
layout.operator("node.add_node", text="Assign Modifiers").type = "SLICENODE_AssignModifiers"
|
||||||
|
layout.operator("node.add_node", text="Wipe Tower").type = "SLICENODE_WipeTower"
|
||||||
|
layout.operator("node.add_node", text="Offset Object").type = "SLICENODE_OffsetObject"
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("node.add_node", text="PrusaSlicer Slice").type = "SLICENODE_PrusaslicerSlice"
|
||||||
|
layout.operator("node.add_node", text="PrusaSlicer Preview").type = "SLICENODE_PrusaslicerPreview"
|
||||||
|
|
||||||
|
@register_class
|
||||||
|
class SLICENODE_MT_PdfExporter(Menu):
|
||||||
|
bl_idname = "SLICENODE_MT_PdfExporter"
|
||||||
|
bl_label = "PDF Exporter"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
assert layout
|
||||||
|
layout.operator("node.add_node", text="Assign PDF Properties").type = "SLICENODE_AssignPdfProperties"
|
||||||
|
layout.operator("node.add_node", text="Assign PDF Object Properties").type = "SLICENODE_AssignPropertyToObject"
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("node.add_node", text="Mesh to PDF").type = "SLICENODE_MeshToPdf"
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_MT_Gcode(Menu):
|
class SLICENODE_MT_Gcode(Menu):
|
||||||
bl_idname = "SLICENODE_MT_Gcode"
|
bl_idname = "SLICENODE_MT_Gcode"
|
||||||
|
|
@ -580,7 +603,6 @@ class SLICENODE_MT_ActionIO(Menu):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
assert layout
|
assert layout
|
||||||
layout.operator("node.add_node", text="Gcode to File").type = "SLICENODE_GcodeToFile"
|
layout.operator("node.add_node", text="Gcode to File").type = "SLICENODE_GcodeToFile"
|
||||||
layout.operator("node.add_node", text="Mesh to PDF").type = "SLICENODE_MeshToPdf"
|
|
||||||
layout.operator("node.add_node", text="Gcode to Text Block").type = "SLICENODE_GcodeToTextBlock"
|
layout.operator("node.add_node", text="Gcode to Text Block").type = "SLICENODE_GcodeToTextBlock"
|
||||||
layout.operator("node.add_node", text="Start Print").type = "SLICENODE_StartPrint"
|
layout.operator("node.add_node", text="Start Print").type = "SLICENODE_StartPrint"
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
@ -588,8 +610,6 @@ class SLICENODE_MT_ActionIO(Menu):
|
||||||
layout.operator("node.add_node", text="Airtable Submit").type = "SLICENODE_AirtableSubmit"
|
layout.operator("node.add_node", text="Airtable Submit").type = "SLICENODE_AirtableSubmit"
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("node.add_node", text="Gcode to Preview Mesh").type = "SLICENODE_GcodeToMesh"
|
layout.operator("node.add_node", text="Gcode to Preview Mesh").type = "SLICENODE_GcodeToMesh"
|
||||||
layout.operator("node.add_node", text="PrusaSlicer Preview").type = "SLICENODE_PrusaslicerPreview"
|
|
||||||
layout.operator("node.add_node", text="PrusaSlicer Slice").type = "SLICENODE_PrusaslicerSlice"
|
|
||||||
layout.operator("node.add_node", text="Executor").type = "SLICENODE_Executor"
|
layout.operator("node.add_node", text="Executor").type = "SLICENODE_Executor"
|
||||||
layout.operator("node.add_node", text="Test").type = "SLICENODE_Test"
|
layout.operator("node.add_node", text="Test").type = "SLICENODE_Test"
|
||||||
|
|
||||||
|
|
@ -620,6 +640,8 @@ class SLICENODE_MT_Menu(Menu):
|
||||||
assert layout
|
assert layout
|
||||||
layout.menu("SLICENODE_MT_DataInput")
|
layout.menu("SLICENODE_MT_DataInput")
|
||||||
layout.menu("SLICENODE_MT_Geometry")
|
layout.menu("SLICENODE_MT_Geometry")
|
||||||
|
layout.menu("SLICENODE_MT_PrusaSlicer")
|
||||||
|
layout.menu("SLICENODE_MT_PdfExporter")
|
||||||
layout.menu("SLICENODE_MT_Gcode")
|
layout.menu("SLICENODE_MT_Gcode")
|
||||||
layout.menu("SLICENODE_MT_Joiners")
|
layout.menu("SLICENODE_MT_Joiners")
|
||||||
layout.menu("SLICENODE_MT_ActionIO")
|
layout.menu("SLICENODE_MT_ActionIO")
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ def render_object_properties_table(
|
||||||
"""
|
"""
|
||||||
objs_with_props = [
|
objs_with_props = [
|
||||||
so for so in slicing_objs
|
so for so in slicing_objs
|
||||||
if getattr(so, "object_properties", None) is not None and len(getattr(so, "object_properties", [])) > 0
|
if getattr(so, "pdf_object_properties", getattr(so, "object_properties", None)) is not None and len(getattr(so, "pdf_object_properties", getattr(so, "object_properties", []))) > 0
|
||||||
]
|
]
|
||||||
if not objs_with_props:
|
if not objs_with_props:
|
||||||
return
|
return
|
||||||
|
|
@ -203,7 +203,7 @@ def render_object_properties_table(
|
||||||
label_color = (0.35, 0.35, 0.35)
|
label_color = (0.35, 0.35, 0.35)
|
||||||
|
|
||||||
for so in objs_with_props:
|
for so in objs_with_props:
|
||||||
raw_props = getattr(so, "object_properties", [])
|
raw_props = getattr(so, "pdf_object_properties", getattr(so, "object_properties", []))
|
||||||
props = resolve_object_properties(so, raw_props)
|
props = resolve_object_properties(so, raw_props)
|
||||||
if not props:
|
if not props:
|
||||||
continue
|
continue
|
||||||
|
|
@ -999,7 +999,7 @@ class export_mesh_to_pdf:
|
||||||
|
|
||||||
objs_with_props = [
|
objs_with_props = [
|
||||||
so for so in self.slicing_objs
|
so for so in self.slicing_objs
|
||||||
if getattr(so, "object_properties", None) is not None and len(getattr(so, "object_properties", [])) > 0
|
if getattr(so, "pdf_object_properties", getattr(so, "object_properties", None)) is not None and len(getattr(so, "pdf_object_properties", getattr(so, "object_properties", []))) > 0
|
||||||
]
|
]
|
||||||
|
|
||||||
w_table_mm = 0.0
|
w_table_mm = 0.0
|
||||||
|
|
@ -1015,7 +1015,7 @@ class export_mesh_to_pdf:
|
||||||
if objs_with_props:
|
if objs_with_props:
|
||||||
w_table_mm = (self.font_size_pt / MM_TO_PT) * 25.0
|
w_table_mm = (self.font_size_pt / MM_TO_PT) * 25.0
|
||||||
h_props_total_mm = sum(
|
h_props_total_mm = sum(
|
||||||
(1 + len(getattr(so, "object_properties", []))) * cell_height_mm
|
(1 + len(getattr(so, "pdf_object_properties", getattr(so, "object_properties", [])))) * cell_height_mm
|
||||||
for so in objs_with_props
|
for so in objs_with_props
|
||||||
) + len(objs_with_props) * gap_mm
|
) + len(objs_with_props) * gap_mm
|
||||||
if h_table_col_mm > 0.0:
|
if h_table_col_mm > 0.0:
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class SlicePaths(list[np.ndarray]):
|
||||||
layer_height: float
|
layer_height: float
|
||||||
first_layer_height: float | None
|
first_layer_height: float | None
|
||||||
transforms: list[dict[str, Any]]
|
transforms: list[dict[str, Any]]
|
||||||
object_properties: list[tuple[str, str]]
|
pdf_object_properties: list[tuple[str, str]]
|
||||||
pdf_properties: PdfProperties
|
pdf_properties: PdfProperties
|
||||||
name: str
|
name: str
|
||||||
objects_info: dict[str, Any] | None
|
objects_info: dict[str, Any] | None
|
||||||
|
|
|
||||||
124
doc/NODES.md
124
doc/NODES.md
|
|
@ -43,41 +43,21 @@ A standard Python string containing formatted G-code.
|
||||||
|
|
||||||
All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** category.
|
All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** category.
|
||||||
|
|
||||||
### Data & Input
|
#### Data & Input
|
||||||
- **Object**: Blender object picker.
|
- **Object**: Blender object picker.
|
||||||
- **Float**: Single float input.
|
- **Float**: Single float input.
|
||||||
- **Integer**: Single integer input.
|
- **Integer**: Single integer input.
|
||||||
- **String**: Single string/path input.
|
- **String**: Single string/path input.
|
||||||
- **Settings**: Read-only/write configuration settings dictionary.
|
- **Settings**: Read-only/write configuration settings dictionary.
|
||||||
- **Wipe Tower**: Configures the Wipe Tower printbed position (`wipe_tower_position`) passed to 3MF metadata (`Prusa_Slicer_wipe_tower_information.xml`) without injecting into profile INI settings.
|
- **Printer**: Printer name picker.
|
||||||
- **Position X**: X coordinate of the wipe tower on the printbed.
|
- **Printer List**: List of configured printers.
|
||||||
- **Position Y**: Y coordinate of the wipe tower on the printbed.
|
|
||||||
- **Offset Object**: Configures the object placement transform offset (`offset_object`) applied to `<item transform="...">` inside 3MF model build data (`3D/3dmodel.model`) without injecting into profile INI settings.
|
|
||||||
- **Offset X**: X translation offset for the build item.
|
|
||||||
- **Offset Y**: Y translation offset for the build item.
|
|
||||||
- **Offset Z**: Z translation offset for the build item.
|
|
||||||
- **Printer Intrinsics**: Configures bed size, extrusion/travel rates, retraction distance & feedrate, and Z-lift.
|
- **Printer Intrinsics**: Configures bed size, extrusion/travel rates, retraction distance & feedrate, and Z-lift.
|
||||||
- **Gcode Block**: Manual text area for entering raw G-code.
|
|
||||||
|
|
||||||
### Geometry Processing
|
### Geometry Processing
|
||||||
- **Get Mesh**: Converts a Blender Object into internal `SlicingObject` format.
|
- **Get Mesh**: Converts a Blender Object into internal `SlicingObject` format.
|
||||||
- **Separate Mesh**: Splits a `SlicingCollection` into two collections (`Selection` and `Inverted`) by comparing a specified mesh attribute against a target value with comparison operators (`==`, `!=`, `>`, `<`, `>=`, `<=`).
|
|
||||||
- **Separate Mesh by Range**: Splits a `SlicingCollection` into multiple output sockets across an integer range (`Start` to `End`), matching a specified integer attribute. Objects in each output socket are renamed with a zero-indexed padded suffix (`f"{base_mesh_name}{i:0{num_digits}d}"` where `num_digits = max(2, len(str(total_parts)))`).
|
|
||||||
- **Rename Mesh**: Overrides the internal metadata name of a `SlicingObject`, `SlicingCollection`, or `SlicingGroup`. Supports `{AttributeName}` attribute placeholders (e.g., `Rebar-L{Rebar Length}M`) that evaluate to index zero of the target attribute for each object, as well as `{Name}` to include the item's previous name (e.g., `{Name}_T{Thickness}mm`).
|
- **Rename Mesh**: Overrides the internal metadata name of a `SlicingObject`, `SlicingCollection`, or `SlicingGroup`. Supports `{AttributeName}` attribute placeholders (e.g., `Rebar-L{Rebar Length}M`) that evaluate to index zero of the target attribute for each object, as well as `{Name}` to include the item's previous name (e.g., `{Name}_T{Thickness}mm`).
|
||||||
- **Geometry**: Input geometry data.
|
- **Geometry**: Input geometry data.
|
||||||
- **Name**: String input socket or property for the new name template.
|
- **Name**: String input socket or property for the new name template.
|
||||||
- **Assign PDF Properties**: Assigns line width (mm) and line color PDF export properties to geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`).
|
|
||||||
- **Geometry**: Generic geometry input socket (accepts `SLICESOCK_Mesh`, `SLICESOCK_Paths`, or `SLICESOCK_Geometry`).
|
|
||||||
- **Line Width**: Input socket / float property for line width in mm.
|
|
||||||
- **Line Color**: Color property for stroke color.
|
|
||||||
- **Assign Property to Object**: Assigns custom settings/properties to geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`) for rendering in PDF properties tables.
|
|
||||||
- **Geometry**: Generic geometry input socket (accepts `SLICESOCK_Mesh`, `SLICESOCK_Paths`, or `SLICESOCK_Geometry`).
|
|
||||||
- **Settings**: Input socket accepting configuration settings or property items.
|
|
||||||
- **Assign Modifiers**: Assigns PrusaSlicer part/volume types (`ModelPart`, `ParameterModifier`, `NegativeVolume`, `SupportBlocker`, `SupportEnforcer`, `WipeTower`), extruder assignments (`0` to `5`), and config parameter modifiers directly to geometry (`SlicingObject`, `SlicingCollection`, or `SlicingGroup`) for 3MF export.
|
|
||||||
- **Geometry**: Generic geometry input socket (`SLICESOCK_Mesh`).
|
|
||||||
- **Modifiers**: Optional input socket accepting configuration settings dictionary (`SLICESOCK_Settings`).
|
|
||||||
- **Part Type**: Dropdown property specifying target part/volume type (`Keep Original`, `Model Part`, `Modifier`, `Negative Volume`, `Support Blocker`, `Support Enforcer`, `Wipe Tower`).
|
|
||||||
- **Extruder**: Dropdown property specifying target extruder assignment (`Keep Original`, `Default Extruder`, `Extruder 1-5`).
|
|
||||||
- **Transform Geometry**: Moves, rotates, and scales geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`) relative to the origin.
|
- **Transform Geometry**: Moves, rotates, and scales geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`) relative to the origin.
|
||||||
- **Geometry**: Input geometry data.
|
- **Geometry**: Input geometry data.
|
||||||
- **Translation**: 3D vector specifying the translation offset.
|
- **Translation**: 3D vector specifying the translation offset.
|
||||||
|
|
@ -88,16 +68,20 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
||||||
- **Z**: Target minimum Z coordinate (defaults to `0.0`).
|
- **Z**: Target minimum Z coordinate (defaults to `0.0`).
|
||||||
- **Mesh**: Output dropped mesh data.
|
- **Mesh**: Output dropped mesh data.
|
||||||
- **Translation**: Output translation vector (X, Y, Z) applied to the geometry.
|
- **Translation**: Output translation vector (X, Y, Z) applied to the geometry.
|
||||||
|
- **Separate Mesh**: Splits a `SlicingCollection` into two collections (`Selection` and `Inverted`) by comparing a specified mesh attribute against a target value with comparison operators (`==`, `!=`, `>`, `<`, `>=`, `<=`).
|
||||||
|
- **Separate Mesh by Range**: Splits a `SlicingCollection` into multiple output sockets across an integer range (`Start` to `End`), matching a specified integer attribute. Objects in each output socket are renamed with a zero-indexed padded suffix (`f"{base_mesh_name}{i:0{num_digits}d}"` where `num_digits = max(2, len(str(total_parts)))`).
|
||||||
|
- **Arrange Slicing Group**: Arranges the slicing collections in a slicing group on the build plate using the `libnest2d` bin-packing library. It nests their 2D bounding boxes to prevent collisions.
|
||||||
|
- **Method**: Switch between **NFP** (No-Fit Polygon placer + First-Fit selection), **Bottom-Left** (Bottom-Left placer + First-Fit selection), and **DJD Heuristic** (No-Fit Polygon placer + Dyckhoff's heuristic).
|
||||||
|
- **Allow Rotations**: Toggle whether to allow rotating objects (by 90-degree steps) during nesting.
|
||||||
|
- **Bed X / Y**: The build plate dimensions in mm.
|
||||||
|
- **Spacing**: The minimum distance between arranged objects in mm.
|
||||||
- **Get Paths**: Extracts existing edges from a mesh as paths.
|
- **Get Paths**: Extracts existing edges from a mesh as paths.
|
||||||
- **Extrusion Multiplier Attribute**: Mesh attribute to use for per-segment extrusion rates.
|
- **Extrusion Multiplier Attribute**: Mesh attribute to use for per-segment extrusion rates.
|
||||||
- **Feedrate Attribute**: Mesh attribute to use for per-segment feedrates.
|
- **Feedrate Attribute**: Mesh attribute to use for per-segment feedrates.
|
||||||
- **Extra Attributes**: Dictionary mapping mesh attribute names to custom G-code prefixes (e.g., `opening_size` -> `S`).
|
- **Extra Attributes**: Dictionary mapping mesh attribute names to custom G-code prefixes (e.g., `opening_size` -> `S`).
|
||||||
- **Path Offset**: Offsets `SlicePaths` by a 3D vector.
|
|
||||||
- **Rotate Path**: Rotates `SlicePaths` around the Z-axis.
|
|
||||||
- **Angle**: Rotation angle in degrees.
|
|
||||||
- **Center**: Optional center of rotation. If not provided, it defaults to the midpoint of the first layer.
|
|
||||||
- **Slice Shells**: Slices a `SlicingObject` into 2D `SlicePaths` at a given layer height. Automatically aligns path seams to the sharpest concave angle (inner corner) of each layer. Supports rotation for slicing at an angle.
|
- **Slice Shells**: Slices a `SlicingObject` into 2D `SlicePaths` at a given layer height. Automatically aligns path seams to the sharpest concave angle (inner corner) of each layer. Supports rotation for slicing at an angle.
|
||||||
- **Buffer Shells**: Offsets/buffers 2D `SlicePaths`. Supports rotated offsetting and multiple miter types.
|
- **Buffer Shells**: Offsets/buffers 2D `SlicePaths`. Supports rotated offsetting and multiple miter types.
|
||||||
|
- **Buffer Shells Multiple**: Offsets 2D `SlicePaths` with multiple buffer passes.
|
||||||
- **Buffer Curve**: Buffers open/closed curves using Shapely, allowing open path offsetting.
|
- **Buffer Curve**: Buffers open/closed curves using Shapely, allowing open path offsetting.
|
||||||
- **Cap Style**: Style of the curve ends (Round, Flat, Square).
|
- **Cap Style**: Style of the curve ends (Round, Flat, Square).
|
||||||
- **Join Style**: Style of corner joins (Round, Mitre, Bevel).
|
- **Join Style**: Style of corner joins (Round, Mitre, Bevel).
|
||||||
|
|
@ -108,6 +92,11 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
||||||
- **Join Style**: Style of corner joins (Round, Mitre, Bevel).
|
- **Join Style**: Style of corner joins (Round, Mitre, Bevel).
|
||||||
- **Distance**: The offset distance (positive for left side, negative for right side).
|
- **Distance**: The offset distance (positive for left side, negative for right side).
|
||||||
- **Rotation**: Optional Euler rotation for non-planar offsetting.
|
- **Rotation**: Optional Euler rotation for non-planar offsetting.
|
||||||
|
- **Path Offset**: Offsets `SlicePaths` by a 3D vector.
|
||||||
|
- **Rotate Path**: Rotates `SlicePaths` around the Z-axis.
|
||||||
|
- **Angle**: Rotation angle in degrees.
|
||||||
|
- **Center**: Optional center of rotation. If not provided, it defaults to the midpoint of the first layer.
|
||||||
|
- **Simplify Paths**: Simplifies `SlicePaths` geometry using the Douglas-Peucker algorithm.
|
||||||
- **Infill**: Generates a cross-hatch grid or parallel lines clipped by boundary paths.
|
- **Infill**: Generates a cross-hatch grid or parallel lines clipped by boundary paths.
|
||||||
- **Infill Type**: Switch between **Grid** (cross-hatch), **Rectilinear** (alternating parallel lines), and **Lines** (parallel lines in one direction).
|
- **Infill Type**: Switch between **Grid** (cross-hatch), **Rectilinear** (alternating parallel lines), and **Lines** (parallel lines in one direction).
|
||||||
- **Density**: Distance between lines in units.
|
- **Density**: Distance between lines in units.
|
||||||
|
|
@ -116,25 +105,40 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
||||||
- **Get Infill Mask**: Computes the intersection of all closed paths in each group (layer) using Shapely.
|
- **Get Infill Mask**: Computes the intersection of all closed paths in each group (layer) using Shapely.
|
||||||
- **Overhang Mask**: Computes the accumulated overhang mask for each layer, propagating support requirements from top to bottom.
|
- **Overhang Mask**: Computes the accumulated overhang mask for each layer, propagating support requirements from top to bottom.
|
||||||
- **Boolean Paths**: Performs 2D boolean operations (`UNION`, `INTERSECTION`, `DIFFERENCE`, `XOR`) on multiple slice sets.
|
- **Boolean Paths**: Performs 2D boolean operations (`UNION`, `INTERSECTION`, `DIFFERENCE`, `XOR`) on multiple slice sets.
|
||||||
- **Arrange Slicing Group**: Arranges the slicing collections in a slicing group on the build plate using the `libnest2d` bin-packing library. It nests their 2D bounding boxes to prevent collisions.
|
- **Math**: Float math operations.
|
||||||
- **Method**: Switch between **NFP** (No-Fit Polygon placer + First-Fit selection), **Bottom-Left** (Bottom-Left placer + First-Fit selection), and **DJD Heuristic** (No-Fit Polygon placer + Dyckhoff's heuristic).
|
|
||||||
- **Allow Rotations**: Toggle whether to allow rotating objects (by 90-degree steps) during nesting.
|
|
||||||
- **Bed X / Y**: The build plate dimensions in mm.
|
|
||||||
- **Spacing**: The minimum distance between arranged objects in mm.
|
|
||||||
|
|
||||||
### Utility & Joiners
|
|
||||||
- **Join Mesh**: Merges multiple `SlicingObject` instances into one.
|
|
||||||
- **Join Paths**: Combines multiple `SlicePaths`. Supports Sequential or Interleaved modes.
|
|
||||||
- **Join Gcode**: Concatenates multiple G-code strings.
|
|
||||||
- **Join Settings**: Sequentially merges multiple override dictionaries. Later inputs overwrite earlier ones for matching keys.
|
|
||||||
- **Integer Math**: Simple integer operations (Add, Multiply, etc.).
|
- **Integer Math**: Simple integer operations (Add, Multiply, etc.).
|
||||||
|
|
||||||
### Action & I/O
|
### PrusaSlicer
|
||||||
- **Paths to Gcode**: Converts `SlicePaths` into G-code blocks.
|
- **PrusaSlicer Config**: Configures print, filament, and printer profiles from PrusaSlicer INI bundles.
|
||||||
- **Header**: Toggles the generation of standard NodeSlicer G-code headers and units setup.
|
- **Assign Modifiers**: Assigns PrusaSlicer part/volume types (`ModelPart`, `ParameterModifier`, `NegativeVolume`, `SupportBlocker`, `SupportEnforcer`, `WipeTower`), extruder assignments (`0` to `5`), and config parameter modifiers directly to geometry (`SlicingObject`, `SlicingCollection`, or `SlicingGroup`) for 3MF export.
|
||||||
- **Load Gcode**: Reads a G-code file from disk.
|
- **Geometry**: Generic geometry input socket (`SLICESOCK_Mesh`).
|
||||||
- **Gcode to File**: Writes G-code string to a file on disk.
|
- **Modifiers**: Optional input socket accepting configuration settings dictionary (`SLICESOCK_Settings`).
|
||||||
- **Automatic Binary Conversion**: If the G-code contains the comment `; binary_gcode = 1`, the node automatically converts the output to Prusa's binary G-code format (`.bgcode`) using MeatPack encoding and Heatshrink compression.
|
- **Part Type**: Dropdown property specifying target part/volume type (`Keep Original`, `Model Part`, `Modifier`, `Negative Volume`, `Support Blocker`, `Support Enforcer`, `Wipe Tower`).
|
||||||
|
- **Extruder**: Dropdown property specifying target extruder assignment (`Keep Original`, `Default Extruder`, `Extruder 1-5`).
|
||||||
|
- **Wipe Tower**: Configures the Wipe Tower printbed position (`wipe_tower_position`) passed to 3MF metadata (`Prusa_Slicer_wipe_tower_information.xml`) without injecting into profile INI settings.
|
||||||
|
- **Position X**: X coordinate of the wipe tower on the printbed.
|
||||||
|
- **Position Y**: Y coordinate of the wipe tower on the printbed.
|
||||||
|
- **Offset Object**: Configures the object placement transform offset (`offset_object`) applied to `<item transform="...">` inside 3MF model build data (`3D/3dmodel.model`) without injecting into profile INI settings.
|
||||||
|
- **Offset X**: X translation offset for the build item.
|
||||||
|
- **Offset Y**: Y translation offset for the build item.
|
||||||
|
- **Offset Z**: Z translation offset for the build item.
|
||||||
|
- **PrusaSlicer Slice**: Exports the current mesh and configuration to a 3MF file and calls PrusaSlicer to generate G-code.
|
||||||
|
- **Executable**: Path to the PrusaSlicer executable.
|
||||||
|
- **Object**: The Blender object to slice.
|
||||||
|
- **Config**: PrusaSlicer configuration (from the **PrusaSlicer Config** node).
|
||||||
|
- **Gcode**: Output socket providing the generated G-code string.
|
||||||
|
- **PrusaSlicer Preview**: Launches PrusaSlicer's built-in G-code viewer to visualize a G-code string.
|
||||||
|
- **Executable**: Path to the PrusaSlicer executable (default: `flatpak run com.prusa3d.PrusaSlicer`).
|
||||||
|
- **Gcode**: Input socket for the G-code string to preview.
|
||||||
|
|
||||||
|
### PDF Exporter
|
||||||
|
- **Assign PDF Properties**: Assigns line width (mm) and line color PDF export properties to geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`).
|
||||||
|
- **Geometry**: Generic geometry input socket (accepts `SLICESOCK_Mesh`, `SLICESOCK_Paths`, or `SLICESOCK_Geometry`).
|
||||||
|
- **Line Width**: Input socket / float property for line width in mm.
|
||||||
|
- **Line Color**: Color property for stroke color.
|
||||||
|
- **Assign PDF Object Properties**: Assigns custom settings/properties to geometry (`SlicingObject`, `SlicingCollection`, `SlicingGroup`, or `SlicePaths`) for rendering in PDF properties tables and object callout labels.
|
||||||
|
- **Geometry**: Generic geometry input socket (accepts `SLICESOCK_Mesh`, `SLICESOCK_Paths`, or `SLICESOCK_Geometry`).
|
||||||
|
- **Settings**: Input socket accepting configuration settings or property items.
|
||||||
- **Mesh to PDF**: Generates a 1:1 true-scale 2D top-view (+Z orthographic) vector PDF from a `SlicingGroup` or `SlicingCollection` with a custom grid and background color. Subpanel menus are collapsed by default.
|
- **Mesh to PDF**: Generates a 1:1 true-scale 2D top-view (+Z orthographic) vector PDF from a `SlicingGroup` or `SlicingCollection` with a custom grid and background color. Subpanel menus are collapsed by default.
|
||||||
- **Technical Drawing Header**: Collapsible menu containing string inputs for technical drawing info (Company, Part Name, Drawing No., Author, Revision, Material, Tolerance). If Part Name is left empty, it automatically defaults to the name of the first object being rendered. Automatically computes ISO date and scale length (`1:1`). Renders an ISO/DIN technical drawing header table on the bottom-right of the drawing.
|
- **Technical Drawing Header**: Collapsible menu containing string inputs for technical drawing info (Company, Part Name, Drawing No., Author, Revision, Material, Tolerance). If Part Name is left empty, it automatically defaults to the name of the first object being rendered. Automatically computes ISO date and scale length (`1:1`). Renders an ISO/DIN technical drawing header table on the bottom-right of the drawing.
|
||||||
- **Geometry**: Input socket accepting a single geometry (`SlicingGroup`, `SlicingCollection`, or `SlicingObject`). Join multiple geometries using Join Geometry nodes.
|
- **Geometry**: Input socket accepting a single geometry (`SlicingGroup`, `SlicingCollection`, or `SlicingObject`). Join multiple geometries using Join Geometry nodes.
|
||||||
|
|
@ -156,6 +160,28 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
||||||
- **Margin (mm)**: Margin around the geometry grid in mm.
|
- **Margin (mm)**: Margin around the geometry grid in mm.
|
||||||
- **Background**: Color picker for the canvas background.
|
- **Background**: Color picker for the canvas background.
|
||||||
- **Wireframe**: Color picker for the wireframe lines and grid.
|
- **Wireframe**: Color picker for the wireframe lines and grid.
|
||||||
|
|
||||||
|
### Gcode Processing
|
||||||
|
- **Paths to Gcode**: Converts `SlicePaths` into G-code blocks.
|
||||||
|
- **Header**: Toggles the generation of standard NodeSlicer G-code headers and units setup.
|
||||||
|
- **Gcode Block**: Manual text area for entering raw G-code.
|
||||||
|
- **Gcode Regex Replace**: Performs regex search and replace on G-code.
|
||||||
|
- **Assign Metadata**: Injects custom metadata comments into G-code.
|
||||||
|
- **Gcode Helper**: Utility helper functions for G-code stream modifications.
|
||||||
|
- **Load Gcode**: Reads a G-code file from disk.
|
||||||
|
|
||||||
|
### Utility & Joiners
|
||||||
|
- **Join Mesh**: Merges multiple `SlicingObject` instances into one.
|
||||||
|
- **Join Paths**: Combines multiple `SlicePaths`. Supports Sequential or Interleaved modes.
|
||||||
|
- **Join Gcode**: Concatenates multiple G-code strings.
|
||||||
|
- **Join Settings**: Sequentially merges multiple override dictionaries. Later inputs overwrite earlier ones for matching keys.
|
||||||
|
- **Join Execution**: Synchronizes sequential execution flows.
|
||||||
|
|
||||||
|
### Action & I/O
|
||||||
|
- **Gcode to File**: Writes G-code string to a file on disk.
|
||||||
|
- **Automatic Binary Conversion**: If the G-code contains the comment `; binary_gcode = 1`, the node automatically converts the output to Prusa's binary G-code format (`.bgcode`) using MeatPack encoding and Heatshrink compression.
|
||||||
|
- **Gcode to Text Block**: Creates a new Blender text block containing the G-code and opens it in any active text editors.
|
||||||
|
- **Start Print**: Sends G-code directly to a network printer.
|
||||||
- **Baserow Submit**: Sends G-code and metadata to a Baserow database.
|
- **Baserow Submit**: Sends G-code and metadata to a Baserow database.
|
||||||
- **Host**: Baserow host URL.
|
- **Host**: Baserow host URL.
|
||||||
- **Token File**: Path to a file containing the Baserow API token.
|
- **Token File**: Path to a file containing the Baserow API token.
|
||||||
|
|
@ -168,14 +194,6 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
||||||
- **Table ID**: The ID or name of the target Airtable table.
|
- **Table ID**: The ID or name of the target Airtable table.
|
||||||
- **Record ID**: Optional ID of an existing record to update.
|
- **Record ID**: Optional ID of an existing record to update.
|
||||||
- **Gcode Field**: The name of the attachment field in Airtable.
|
- **Gcode Field**: The name of the attachment field in Airtable.
|
||||||
- **PrusaSlicer Slice**: Exports the current mesh and configuration to a 3MF file and calls PrusaSlicer to generate G-code.
|
|
||||||
- **Executable**: Path to the PrusaSlicer executable.
|
|
||||||
- **Object**: The Blender object to slice.
|
|
||||||
- **Config**: PrusaSlicer configuration (from the **PrusaSlicer Config** node).
|
|
||||||
- **Gcode**: Output socket providing the generated G-code string.
|
|
||||||
- **Gcode to Text Block**: Creates a new Blender text block containing the G-code and opens it in any active text editors.
|
|
||||||
- **Gcode to Preview Mesh**: Decodes G-code and creates a Blender mesh ("Debug" object) for visualization.
|
- **Gcode to Preview Mesh**: Decodes G-code and creates a Blender mesh ("Debug" object) for visualization.
|
||||||
- **PrusaSlicer Preview**: Launches PrusaSlicer's built-in G-code viewer to visualize a G-code string.
|
- **Executor**: Executes sub-tree node chains.
|
||||||
- **Executable**: Path to the PrusaSlicer executable (default: `flatpak run com.prusa3d.PrusaSlicer`).
|
|
||||||
- **Gcode**: Input socket for the G-code string to preview.
|
|
||||||
- **Test**: Triggers evaluation for debugging purposes.
|
- **Test**: Triggers evaluation for debugging purposes.
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ class SlicingObject():
|
||||||
modifiers: list[dict[str, Any]] = []
|
modifiers: list[dict[str, Any]] = []
|
||||||
extruder: int = 1
|
extruder: int = 1
|
||||||
pdf_properties: PdfProperties
|
pdf_properties: PdfProperties
|
||||||
object_properties: list[tuple[str, str]]
|
pdf_object_properties: list[tuple[str, str]]
|
||||||
|
|
||||||
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!')
|
||||||
|
|
@ -123,7 +123,7 @@ class SlicingObject():
|
||||||
self.modifiers = []
|
self.modifiers = []
|
||||||
self.transforms = []
|
self.transforms = []
|
||||||
self.pdf_properties = PdfProperties()
|
self.pdf_properties = PdfProperties()
|
||||||
self.object_properties = []
|
self.pdf_object_properties = []
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -150,7 +150,7 @@ class SlicingObject():
|
||||||
merged.object_type = objects[0].object_type
|
merged.object_type = objects[0].object_type
|
||||||
merged.extruder = objects[0].extruder
|
merged.extruder = objects[0].extruder
|
||||||
merged.pdf_properties = copy.deepcopy(objects[0].pdf_properties) if hasattr(objects[0], "pdf_properties") and objects[0].pdf_properties else PdfProperties()
|
merged.pdf_properties = copy.deepcopy(objects[0].pdf_properties) if hasattr(objects[0], "pdf_properties") and objects[0].pdf_properties else PdfProperties()
|
||||||
merged.object_properties = list(getattr(objects[0], "object_properties", []))
|
merged.pdf_object_properties = list(getattr(objects[0], "pdf_object_properties", []))
|
||||||
merged.transforms = []
|
merged.transforms = []
|
||||||
for o in objects:
|
for o in objects:
|
||||||
for t in getattr(o, "transforms", []):
|
for t in getattr(o, "transforms", []):
|
||||||
|
|
|
||||||
|
|
@ -1119,13 +1119,13 @@ class TestPdfExporter(unittest.TestCase):
|
||||||
|
|
||||||
for coll in updated_sg.collections.values():
|
for coll in updated_sg.collections.values():
|
||||||
for obj in coll.objects:
|
for obj in coll.objects:
|
||||||
self.assertEqual(obj.object_properties, props)
|
self.assertEqual(obj.pdf_object_properties, props)
|
||||||
|
|
||||||
# Also verify SlicePaths support
|
# Also verify SlicePaths support
|
||||||
from NodeSlicer.core.slice import SlicePaths
|
from NodeSlicer.core.slice import SlicePaths
|
||||||
sp = SlicePaths([np.array([[0, 0, 0], [10, 10, 0]], dtype=np.float64)])
|
sp = SlicePaths([np.array([[0, 0, 0], [10, 10, 0]], dtype=np.float64)])
|
||||||
updated_sp = _assign_properties_to_item(sp, props)
|
updated_sp = _assign_properties_to_item(sp, props)
|
||||||
self.assertEqual(updated_sp.object_properties, props)
|
self.assertEqual(updated_sp.pdf_object_properties, props)
|
||||||
|
|
||||||
# Verify dynamic socket color logic
|
# Verify dynamic socket color logic
|
||||||
from NodeSlicer.core.nodeslicer.sockets import SLICESOCK_Geometry, SLICESOCK_Paths, SLICESOCK_Mesh
|
from NodeSlicer.core.nodeslicer.sockets import SLICESOCK_Geometry, SLICESOCK_Paths, SLICESOCK_Mesh
|
||||||
|
|
@ -1180,7 +1180,7 @@ class TestPdfExporter(unittest.TestCase):
|
||||||
sg.collections = {"Coll1": sc1, "Coll2": sc2}
|
sg.collections = {"Coll1": sc1, "Coll2": sc2}
|
||||||
|
|
||||||
# Assign properties to so1 only
|
# Assign properties to so1 only
|
||||||
so1.object_properties = [("", "Material: PETG"), ("Tolerance", "ISO 2768-f")]
|
so1.pdf_object_properties = [("", "Material: PETG"), ("Tolerance", "ISO 2768-f")]
|
||||||
|
|
||||||
edges_2d = extract_unique_top_view_edges(sg)
|
edges_2d = extract_unique_top_view_edges(sg)
|
||||||
|
|
||||||
|
|
@ -1236,7 +1236,7 @@ class TestPdfExporter(unittest.TestCase):
|
||||||
|
|
||||||
res_obj = updated_sg.collections["Coll"].objects[0]
|
res_obj = updated_sg.collections["Coll"].objects[0]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
res_obj.object_properties,
|
res_obj.pdf_object_properties,
|
||||||
[("infill_density", "15.5"), ("material", "ABS"), ("custom_setting", "fixed_value")]
|
[("infill_density", "15.5"), ("material", "ABS"), ("custom_setting", "fixed_value")]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class TestAssignModifiersNode(unittest.TestCase):
|
||||||
result_so = node.evaluate_output("Geometry")
|
result_so = node.evaluate_output("Geometry")
|
||||||
self.assertIsNotNone(result_so)
|
self.assertIsNotNone(result_so)
|
||||||
self.assertEqual(result_so.object_type, "ParameterModifier")
|
self.assertEqual(result_so.object_type, "ParameterModifier")
|
||||||
self.assertEqual(result_so.extruder, "3")
|
self.assertEqual(result_so.extruder, 3)
|
||||||
|
|
||||||
mod_dict = {m['param_id']: m['param_value'] for m in result_so.modifiers}
|
mod_dict = {m['param_id']: m['param_value'] for m in result_so.modifiers}
|
||||||
self.assertEqual(mod_dict["initial_param"], "123")
|
self.assertEqual(mod_dict["initial_param"], "123")
|
||||||
|
|
@ -129,7 +129,7 @@ class TestAssignModifiersNode(unittest.TestCase):
|
||||||
self.assertIsNotNone(result_sg)
|
self.assertIsNotNone(result_sg)
|
||||||
res_so = result_sg.collections["TestColl"].objects[0]
|
res_so = result_sg.collections["TestColl"].objects[0]
|
||||||
self.assertEqual(res_so.object_type, "NegativeVolume")
|
self.assertEqual(res_so.object_type, "NegativeVolume")
|
||||||
self.assertEqual(res_so.extruder, "2")
|
self.assertEqual(res_so.extruder, 2)
|
||||||
|
|
||||||
mod_dict = {m['param_id']: m['param_value'] for m in res_so.modifiers}
|
mod_dict = {m['param_id']: m['param_value'] for m in res_so.modifiers}
|
||||||
self.assertEqual(mod_dict["perimeters"], "4")
|
self.assertEqual(mod_dict["perimeters"], "4")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue