intrinsics implementation
This commit is contained in:
parent
c57f843092
commit
77bab165af
3 changed files with 100 additions and 5 deletions
82
__init__.py
82
__init__.py
|
|
@ -32,6 +32,18 @@ from .infra.blender_slice_nodes_generic import SliceNode, SliceNodeSocket, resol
|
|||
if TYPE_CHECKING:
|
||||
from bpy.stub_internal.rna_enums import OperatorReturnItems
|
||||
|
||||
class SliceIntrinsics:
|
||||
bed_x: float
|
||||
bed_y: float
|
||||
feedrate: float
|
||||
extrusion_rate: float
|
||||
|
||||
def __init__(self, bed_x=250.0, bed_y=250.0, feedrate=1200.0, extrusion_rate=0.017):
|
||||
self.bed_x = bed_x
|
||||
self.bed_y = bed_y
|
||||
self.feedrate = feedrate
|
||||
self.extrusion_rate = extrusion_rate
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Node tree
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
@ -143,6 +155,12 @@ class SLICESOCK_Gcode(LabelNodeSocket):
|
|||
default = ""
|
||||
color = (1., 1., 1., 1.)
|
||||
|
||||
@register_class
|
||||
class SLICESOCK_Intrinsics(LabelNodeSocket):
|
||||
bl_label = "Intrinsics"
|
||||
default = None
|
||||
color = (0.2, 0.2, 0.2, 1.0)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Nodes
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
@ -179,6 +197,33 @@ class SLICENODE_InputObject(InputNode):
|
|||
input_type = "Object"
|
||||
bl_icon = "OBJECT_DATA"
|
||||
|
||||
@register_class
|
||||
class SLICENODE_InputIntrinsics(SliceNode):
|
||||
bl_label = "Printer Intrinsics"
|
||||
|
||||
bed_x: FloatProperty(name="Bed X", default=250.0) # pyright: ignore[reportInvalidTypeForm]
|
||||
bed_y: FloatProperty(name="Bed Y", default=250.0) # pyright: ignore[reportInvalidTypeForm]
|
||||
feedrate: FloatProperty(name="Feedrate", default=1200.0) # pyright: ignore[reportInvalidTypeForm]
|
||||
extrusion_rate: FloatProperty(name="Extrusion Rate", default=0.017, precision=4) # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_output("SLICESOCK_Intrinsics")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
layout.prop(self, "bed_x")
|
||||
layout.prop(self, "bed_y")
|
||||
layout.prop(self, "feedrate")
|
||||
layout.prop(self, "extrusion_rate")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> SliceIntrinsics:
|
||||
return SliceIntrinsics(
|
||||
bed_x=self.bed_x,
|
||||
bed_y=self.bed_y,
|
||||
feedrate=self.feedrate,
|
||||
extrusion_rate=self.extrusion_rate
|
||||
)
|
||||
|
||||
# Math
|
||||
|
||||
@register_class
|
||||
|
|
@ -372,11 +417,22 @@ class SLICENODE_PathsToGcode(SliceNode):
|
|||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Paths")
|
||||
self.add_input("SLICESOCK_Intrinsics")
|
||||
self.add_output("SLICESOCK_Gcode")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||
paths: list[np.ndarray] = resolve_input(self.inputs.get("Paths"))
|
||||
intrinsics: SliceIntrinsics | None = resolve_input(self.inputs.get("Intrinsics"))
|
||||
|
||||
pather: Pather = Pather(paths)
|
||||
if intrinsics:
|
||||
# Note: Pather currently uses bed_center_x/y as its internal attributes.
|
||||
# We map the bed size center to these attributes if provided.
|
||||
pather.bed_center_x = intrinsics.bed_x / 2.0
|
||||
pather.bed_center_y = intrinsics.bed_y / 2.0
|
||||
pather.feedrate = intrinsics.feedrate
|
||||
pather.extrusion_rate = intrinsics.extrusion_rate
|
||||
|
||||
gcode: str = pather.to_gcode()
|
||||
return gcode
|
||||
|
||||
|
|
@ -598,12 +654,24 @@ class SLICENODE_GcodeToMesh(ExecSliceNode):
|
|||
segments = []
|
||||
last_x, last_y, last_z = 0.0, 0.0, 0.0
|
||||
last_e = 0.0
|
||||
|
||||
offset_x, offset_y = 0.0, 0.0
|
||||
|
||||
for line in gcode.splitlines():
|
||||
line = line.split(';')[0].strip()
|
||||
if not line: continue
|
||||
# Parse metadata from comments
|
||||
if ';' in line:
|
||||
comment = line.split(';', 1)[1].strip()
|
||||
if comment.startswith("OFFSET_X="):
|
||||
try: offset_x = float(comment.split('=')[1])
|
||||
except ValueError: pass
|
||||
elif comment.startswith("OFFSET_Y="):
|
||||
try: offset_y = float(comment.split('=')[1])
|
||||
except ValueError: pass
|
||||
|
||||
line_no_comment = line.split(';')[0].strip()
|
||||
if not line_no_comment: continue
|
||||
|
||||
parts = line.split()
|
||||
parts = line_no_comment.split()
|
||||
cmd = parts[0].upper()
|
||||
|
||||
params = {}
|
||||
|
|
@ -626,7 +694,12 @@ class SLICENODE_GcodeToMesh(ExecSliceNode):
|
|||
is_extruding = new_e > 0
|
||||
|
||||
if is_extruding:
|
||||
segments.append(((last_x, last_y, last_z), (new_x, new_y, new_z)))
|
||||
# Subtract offsets (scaled to Blender units) to return to original space
|
||||
ox, oy = offset_x * 0.001, offset_y * 0.001
|
||||
segments.append((
|
||||
(last_x - ox, last_y - oy, last_z),
|
||||
(new_x - ox, new_y - oy, new_z)
|
||||
))
|
||||
|
||||
last_x, last_y, last_z = new_x, new_y, new_z
|
||||
last_e = new_e
|
||||
|
|
@ -707,6 +780,7 @@ class SLICENODE_MT_Menu(Menu):
|
|||
layout.operator("node.add_node", text="Integer").type = "SLICENODE_InputInt"
|
||||
layout.operator("node.add_node", text="Integer Math").type = "SLICENODE_IntMath"
|
||||
layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject"
|
||||
layout.operator("node.add_node", text="Printer Intrinsics").type = "SLICENODE_InputIntrinsics"
|
||||
layout.operator("node.add_node", text="Test").type = "SLICENODE_Test"
|
||||
layout.operator("node.add_node", text="Get Mesh").type = "SLICENODE_GetMesh"
|
||||
layout.operator("node.add_node", text="Get Paths").type = "SLICENODE_GetPaths"
|
||||
|
|
|
|||
|
|
@ -313,6 +313,23 @@ class Pather(object):
|
|||
if not self.paths:
|
||||
return ""
|
||||
|
||||
# Centering logic using NumPy
|
||||
all_pts = np.concatenate(self.paths)
|
||||
min_xy = all_pts[:, :2].min(axis=0)
|
||||
max_xy = all_pts[:, :2].max(axis=0)
|
||||
center_xy = (min_xy + max_xy) / 2.0
|
||||
|
||||
offset_x = self.bed_center_x - center_xy[0]
|
||||
offset_y = self.bed_center_y - center_xy[1]
|
||||
|
||||
# Apply offset to all paths
|
||||
centered_paths = []
|
||||
for path in self.paths:
|
||||
new_path = path.copy()
|
||||
new_path[:, 0] += offset_x
|
||||
new_path[:, 1] += offset_y
|
||||
centered_paths.append(new_path)
|
||||
|
||||
def segment_length_3d(p1, p2) -> float:
|
||||
dx = p2[0] - p1[0]
|
||||
dy = p2[1] - p1[1]
|
||||
|
|
@ -324,6 +341,8 @@ class Pather(object):
|
|||
f";TYPE:Custom",
|
||||
f";FEEDRATE={self.feedrate}",
|
||||
f";EXTRUSION_RATE={self.extrusion_rate}",
|
||||
f";OFFSET_X={offset_x:.6f}",
|
||||
f";OFFSET_Y={offset_y:.6f}",
|
||||
"G21 ; set units to mm",
|
||||
"G90 ; absolute XYZ positioning",
|
||||
"M83 ; relative extrusion mode",
|
||||
|
|
@ -333,7 +352,7 @@ class Pather(object):
|
|||
last_z = -1.0
|
||||
max_z = 0
|
||||
|
||||
for line in self.paths:
|
||||
for line in centered_paths:
|
||||
coords = line.tolist()
|
||||
if len(coords) < 2:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -364,6 +364,8 @@ def capture_mesh_edges(obj: Object) -> list[NDArray[float64]]:
|
|||
return []
|
||||
|
||||
# 1. Scaling & Matrix Setup
|
||||
assert bpy.context.scene
|
||||
assert bpy.context.scene.unit_settings
|
||||
scale = 1000.0 * bpy.context.scene.unit_settings.scale_length
|
||||
world_mat = np.array(obj.matrix_world)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue