aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Mirsky <andrew@mirsky.net>2022-11-22 23:22:46 -0500
committerKevinOConnor <kevin@koconnor.net>2022-12-11 20:55:59 -0500
commit0c5c87d7c08c2b8aa2f248d81dd1003c5c7d5a7c (patch)
tree25310c0bb94995633aad96c67f96848478481f36
parentc7f323e863eb73f3f50e28729b04f302bf8e3c6a (diff)
downloadkutter-0c5c87d7c08c2b8aa2f248d81dd1003c5c7d5a7c.tar.gz
kutter-0c5c87d7c08c2b8aa2f248d81dd1003c5c7d5a7c.tar.xz
kutter-0c5c87d7c08c2b8aa2f248d81dd1003c5c7d5a7c.zip
gcode_arcs: support XY, XZ and YZ planes
add G17, G18 and G19 commands to select arc planes enhance G2/G3 to support arc moves in XY, XZ and YZ planes Signed-off-by: Andrew Mirsky <andrew@mirsky.net>
-rw-r--r--docs/G-Codes.md5
-rw-r--r--klippy/extras/gcode_arcs.py101
-rw-r--r--test/klippy/gcode_arcs.test23
3 files changed, 98 insertions, 31 deletions
diff --git a/docs/G-Codes.md b/docs/G-Codes.md
index d9cefd80..137d14ba 100644
--- a/docs/G-Codes.md
+++ b/docs/G-Codes.md
@@ -549,8 +549,9 @@ clears any error state from the micro-controller.
The following standard G-Code commands are available if a
[gcode_arcs config section](Config_Reference.md#gcode_arcs) is
enabled:
-- Controlled Arc Move (G2 or G3): `G2 [X<pos>] [Y<pos>] [Z<pos>]
- [E<pos>] [F<speed>] I<value> J<value>`
+- Arc Move Clockwise (G2), Arc Move Counter-clockwise (G3): `G2|G3 [X<pos>] [Y<pos>] [Z<pos>]
+ [E<pos>] [F<speed>] I<value> J<value>|I<value> K<value>|J<value> K<value>`
+- Arc Plane Select: G17 (XY plane), G18 (XZ plane), G19 (YZ plane)
### [gcode_macro]
diff --git a/klippy/extras/gcode_arcs.py b/klippy/extras/gcode_arcs.py
index 61fa7234..eac89050 100644
--- a/klippy/extras/gcode_arcs.py
+++ b/klippy/extras/gcode_arcs.py
@@ -7,12 +7,25 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import math
-
+from gcode import Coord
# Coordinates created by this are converted into G1 commands.
#
-# note: only IJ version available
+# supports XY, XZ & YZ planes with remaining axis as helical
+
+# Enum
+ARC_PLANE_X_Y = 0
+ARC_PLANE_X_Z = 1
+ARC_PLANE_Y_Z = 2
+
+# Enum
+X_AXIS = 0
+Y_AXIS = 1
+Z_AXIS = 2
+E_AXIS = 3
+
class ArcSupport:
+
def __init__(self, config):
self.printer = config.get_printer()
self.mm_per_arc_segment = config.getfloat('resolution', 1., above=0.0)
@@ -22,12 +35,28 @@ class ArcSupport:
self.gcode.register_command("G2", self.cmd_G2)
self.gcode.register_command("G3", self.cmd_G3)
+ self.gcode.register_command("G17", self.cmd_G17)
+ self.gcode.register_command("G18", self.cmd_G18)
+ self.gcode.register_command("G19", self.cmd_G19)
+
+ # backwards compatibility, prior implementation only supported XY
+ self.plane = ARC_PLANE_X_Y
+
def cmd_G2(self, gcmd):
self._cmd_inner(gcmd, True)
def cmd_G3(self, gcmd):
self._cmd_inner(gcmd, False)
+ def cmd_G17(self, gcmd):
+ self.plane = ARC_PLANE_X_Y
+
+ def cmd_G18(self, gcmd):
+ self.plane = ARC_PLANE_X_Z
+
+ def cmd_G19(self, gcmd):
+ self.plane = ARC_PLANE_Y_Z
+
def _cmd_inner(self, gcmd, clockwise):
gcodestatus = self.gcode_move.get_status()
if not gcodestatus['absolute_coordinates']:
@@ -35,21 +64,33 @@ class ArcSupport:
currentPos = gcodestatus['gcode_position']
# Parse parameters
- asX = gcmd.get_float("X", currentPos[0])
- asY = gcmd.get_float("Y", currentPos[1])
- asZ = gcmd.get_float("Z", currentPos[2])
+ asTarget = Coord(x=gcmd.get_float("X", currentPos[0]),
+ y=gcmd.get_float("Y", currentPos[1]),
+ z=gcmd.get_float("Z", currentPos[2]),
+ e=None)
+
if gcmd.get_float("R", None) is not None:
raise gcmd.error("G2/G3 does not support R moves")
- asI = gcmd.get_float("I", 0.)
- asJ = gcmd.get_float("J", 0.)
- if not asI and not asJ:
- raise gcmd.error("G2/G3 neither I nor J given")
+
+ # determine the plane coordinates and the helical axis
+ asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IJ') ]
+ axes = (X_AXIS, Y_AXIS, Z_AXIS)
+ if self.plane == ARC_PLANE_X_Z:
+ asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IK') ]
+ axes = (X_AXIS, Z_AXIS, Y_AXIS)
+ elif self.plane == ARC_PLANE_Y_Z:
+ asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('JK') ]
+ axes = (Y_AXIS, Z_AXIS, X_AXIS)
+
+ if not asPlanar[0] or not asPlanar[1]:
+ raise gcmd.error("G2/G3 requires IJ, IK or JK parameters")
+
asE = gcmd.get_float("E", None)
asF = gcmd.get_float("F", None)
- # Build list of linear coordinates to move to
- coords = self.planArc(currentPos, [asX, asY, asZ], [asI, asJ],
- clockwise)
+ # Build list of linear coordinates to move
+ coords = self.planArc(currentPos, asTarget, asPlanar,
+ clockwise, *axes)
e_per_move = e_base = 0.
if asE is not None:
if gcodestatus['absolute_extrude']:
@@ -74,37 +115,37 @@ class ArcSupport:
# The arc is approximated by generating many small linear segments.
# The length of each segment is configured in MM_PER_ARC_SEGMENT
# Arcs smaller then this value, will be a Line only
- def planArc(self, currentPos, targetPos, offset, clockwise):
+ #
+ # alpha and beta axes are the current plane, helical axis is linear travel
+ def planArc(self, currentPos, targetPos, offset, clockwise,
+ alpha_axis, beta_axis, helical_axis):
# todo: sometimes produces full circles
- X_AXIS = 0
- Y_AXIS = 1
- Z_AXIS = 2
# Radius vector from center to current location
r_P = -offset[0]
r_Q = -offset[1]
# Determine angular travel
- center_P = currentPos[X_AXIS] - r_P
- center_Q = currentPos[Y_AXIS] - r_Q
- rt_X = targetPos[X_AXIS] - center_P
- rt_Y = targetPos[Y_AXIS] - center_Q
- angular_travel = math.atan2(r_P * rt_Y - r_Q * rt_X,
- r_P * rt_X + r_Q * rt_Y)
+ center_P = currentPos[alpha_axis] - r_P
+ center_Q = currentPos[beta_axis] - r_Q
+ rt_Alpha = targetPos[alpha_axis] - center_P
+ rt_Beta = targetPos[beta_axis] - center_Q
+ angular_travel = math.atan2(r_P * rt_Beta - r_Q * rt_Alpha,
+ r_P * rt_Alpha + r_Q * rt_Beta)
if angular_travel < 0.:
angular_travel += 2. * math.pi
if clockwise:
angular_travel -= 2. * math.pi
if (angular_travel == 0.
- and currentPos[X_AXIS] == targetPos[X_AXIS]
- and currentPos[Y_AXIS] == targetPos[Y_AXIS]):
+ and currentPos[alpha_axis] == targetPos[alpha_axis]
+ and currentPos[beta_axis] == targetPos[beta_axis]):
# Make a circle if the angular rotation is 0 and the
# target is current position
angular_travel = 2. * math.pi
# Determine number of segments
- linear_travel = targetPos[Z_AXIS] - currentPos[Z_AXIS]
+ linear_travel = targetPos[helical_axis] - currentPos[helical_axis]
radius = math.hypot(r_P, r_Q)
flat_mm = radius * angular_travel
if linear_travel:
@@ -118,14 +159,18 @@ class ArcSupport:
linear_per_segment = linear_travel / segments
coords = []
for i in range(1, int(segments)):
- dist_Z = i * linear_per_segment
+ dist_Helical = i * linear_per_segment
cos_Ti = math.cos(i * theta_per_segment)
sin_Ti = math.sin(i * theta_per_segment)
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti
- c = [center_P + r_P, center_Q + r_Q, currentPos[Z_AXIS] + dist_Z]
- coords.append(c)
+ # Coord doesn't support index assignment, create list
+ c = [None, None, None, None]
+ c[alpha_axis] = center_P + r_P
+ c[beta_axis] = center_Q + r_Q
+ c[helical_axis] = currentPos[helical_axis] + dist_Helical
+ coords.append(Coord(*c))
coords.append(targetPos)
return coords
diff --git a/test/klippy/gcode_arcs.test b/test/klippy/gcode_arcs.test
index 2be4efc9..6f9a7e3a 100644
--- a/test/klippy/gcode_arcs.test
+++ b/test/klippy/gcode_arcs.test
@@ -2,10 +2,31 @@
DICTIONARY atmega2560.dict
CONFIG gcode_arcs.cfg
-# Home and move in arcs
+# Home and move in XY arc
G28
+G90
G1 X20 Y20 Z20
G2 X125 Y32 Z20 E1 I10.5 J10.5
# XY+Z arc move
G2 X20 Y20 Z10 E1 I10.5 J10.5
+
+# Home and move in XZ arc
+G28
+G90
+G1 X20 Y20 Z20
+G18
+G2 X125 Y20 Z32 E1 I10.5 K10.5
+
+# XZ+Y arc move
+G2 X20 Y10 Z20 E1 I10.5 K10.5
+
+# Home and move in YZ arc
+G28
+G90
+G1 X20 Y20 Z20
+G19
+G2 X20 Y125 Z32 E1 J10.5 K10.5
+
+# YZ+X arc move
+G2 X10 Y20 Z20 E1 J10.5 K10.5