Skip to content

Commit 6553550

Browse files
authored
Fix problem with arcs with negative radius (#2711)
* Prevent spamming error dialogs when loading gcode with multiple errors. Removed unused parameter for spindle speed. * Added unit tests and fix for handling arcs with negative radius
1 parent 2e2b77a commit 6553550

File tree

8 files changed

+164
-74
lines changed

8 files changed

+164
-74
lines changed

ugs-core/src/com/willwinder/universalgcodesender/gcode/GcodePreprocessorUtils.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This file is part of Universal Gcode Sender (UGS).
3232
import com.willwinder.universalgcodesender.model.PartialPosition;
3333
import com.willwinder.universalgcodesender.model.Position;
3434
import com.willwinder.universalgcodesender.model.UnitUtils;
35+
import static com.willwinder.universalgcodesender.utils.MathUtils.normalizeAngle;
3536

3637
import java.text.DecimalFormat;
3738
import java.util.ArrayList;
@@ -96,14 +97,14 @@ public static String overridePosition(String originalCommand, PartialPosition up
9697
}
9798

9899
/**
99-
* Searches the command string for an 'f' and replaces the speed value
100+
* Searches the command string for an 'f' and replaces the speed value
100101
* between the 'f' and the next space with a percentage of that speed.
101-
* In that way all speed values become a ratio of the provided speed
102+
* In that way all speed values become a ratio of the provided speed
102103
* and don't get overridden with just a fixed speed.
103104
*/
104105
static public String overrideSpeed(String command, double speed) {
105106
String returnString = command;
106-
107+
107108
// Check if command sets feed speed.
108109
Pattern pattern = Pattern.compile("F([0-9.]+)", Pattern.CASE_INSENSITIVE);
109110
Matcher matcher = pattern.matcher(command);
@@ -124,7 +125,7 @@ static public String overrideSpeed(String command, double speed) {
124125
static public String removeComment(String command) {
125126
return COMMENT.matcher(command).replaceAll(EMPTY);
126127
}
127-
128+
128129
/**
129130
* Searches for a comment in the input string and returns the first match.
130131
*/
@@ -135,13 +136,13 @@ static public String parseComment(String command) {
135136
// "(?<=\()[^\(\)]*|(?<=\;)[^;]*"
136137
// "(?<=\\()[^\\(\\)]*|(?<=\\;)[^;]*"
137138
Matcher matcher = COMMENTPARSE.matcher(command);
138-
if (matcher.find()){
139+
if (matcher.find()) {
139140
comment = matcher.group(0);
140141
}
141142

142143
return comment;
143144
}
144-
145+
145146
static public String truncateDecimals(int length, String command) {
146147
if (length != decimalLength) {
147148
//Only build the decimal formatter if the truncation length has changed.
@@ -158,7 +159,7 @@ static public String truncateDecimals(int length, String command) {
158159
matcher.appendReplacement(sb, decimalFormatter.format(d));
159160
}
160161
matcher.appendTail(sb);
161-
162+
162163
// Return new command.
163164
return sb.toString();
164165
}
@@ -196,7 +197,7 @@ private static void updateDecimalFormatter(int length) {
196197
static public List<String> parseCodes(List<String> args, char code) {
197198
List<String> l = new ArrayList<>();
198199
char address = Character.toUpperCase(code);
199-
200+
200201
for (String s : args) {
201202
if (s.length() > 0 && Character.toUpperCase(s.charAt(0)) == address) {
202203
l.add(s.substring(1));
@@ -219,7 +220,7 @@ static public Position updatePointWithCommand(List<String> commandArgs, Position
219220
double c = parseCoord(commandArgs, 'C');
220221

221222
if (Double.isNaN(x) && Double.isNaN(y) && Double.isNaN(z) &&
222-
Double.isNaN(a) && Double.isNaN(b) && Double.isNaN(c)) {
223+
Double.isNaN(a) && Double.isNaN(b) && Double.isNaN(c)) {
223224
return null;
224225
}
225226

@@ -505,6 +506,11 @@ static public List<Position> generatePointsAlongArcBDring(
505506
double endAngle = GcodePreprocessorUtils.getAngle(center, end, plane);
506507
double sweep = GcodePreprocessorUtils.calculateSweep(startAngle, endAngle, clockwise);
507508

509+
// If the radius is negative we need invert the angle
510+
if (radius < 0) {
511+
startAngle = normalizeAngle(startAngle - Math.PI);
512+
}
513+
508514
int numPoints = calculateNumberOfPointsToExpand(r, start.getUnits(), minArcLengthMM, arcSegmentLengthMM, sweep);
509515
if (numPoints == 0) {
510516
return Collections.emptyList();
@@ -523,9 +529,9 @@ static public List<Position> generatePointsAlongArcBDring(
523529
* @param sweep the angle of the arc
524530
* @return the number of segments to split the arc into to achieve the given arc segment length
525531
*/
526-
private static int calculateNumberOfPointsToExpand(double radius, UnitUtils.Units radiusUnits, double minArcLengthMM, double arcSegmentLengthMM, double sweep) {
532+
public static int calculateNumberOfPointsToExpand(double radius, UnitUtils.Units radiusUnits, double minArcLengthMM, double arcSegmentLengthMM, double sweep) {
527533
// Convert units.
528-
double arcLengthMM = sweep * radius * UnitUtils.scaleUnits(radiusUnits, UnitUtils.Units.MM);
534+
double arcLengthMM = Math.abs(sweep * radius * UnitUtils.scaleUnits(radiusUnits, UnitUtils.Units.MM));
529535

530536
// If this arc doesn't meet the minimum threshold, don't expand.
531537
if (minArcLengthMM > 0 && arcLengthMM < minArcLengthMM) {
@@ -620,7 +626,7 @@ public static List<Position> generatePointsAlongArcBDring(
620626
*
621627
* @return the center of rotation between two points with IJK codes.
622628
*/
623-
static private Position convertRToCenter(
629+
public static Position convertRToCenter(
624630
Position start,
625631
Position end,
626632
double radius,
@@ -758,7 +764,7 @@ public static boolean isMotionWord(char character) {
758764
* <p>
759765
* If the code is implicit, like the command "X0Y0", we'll still extract "X0Y0".
760766
* If the code is G0 or G1 and G53 is found, it will also be extracted:
761-
* http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g53
767+
* <a href="http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g53">gcode:g53</a>
762768
*/
763769
public static SplitCommand extractMotion(Code code, String command) {
764770
List<String> args = splitCommand(command);

ugs-core/src/com/willwinder/universalgcodesender/gcode/util/GcodeParserException.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
* @author wwinder
88
*/
99
public class GcodeParserException extends Exception {
10-
public GcodeParserException(String s) {
11-
super(s);
10+
public GcodeParserException(String message) {
11+
super(message);
12+
}
13+
14+
public GcodeParserException(String message, Throwable throwable) {
15+
super(message, throwable);
1216
}
1317
}

ugs-core/src/com/willwinder/universalgcodesender/utils/MathUtils.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class MathUtils {
3939

4040
/**
4141
* To find orientation of ordered triplet (p1, p2, p3).
42-
* https://www.geeksforgeeks.org/orientation-3-ordered-points/
42+
* <a href="https://www.geeksforgeeks.org/orientation-3-ordered-points/">orientation-3-ordered-points</a>
4343
*
4444
* @param p1 a point in a polygon
4545
* @param p2 a point in a polygon
@@ -144,6 +144,21 @@ public static boolean isEqual(double d1, double d2, double delta) {
144144
return Math.abs(d1 - d2) <= delta;
145145
}
146146

147+
/**
148+
* Normalizes an angle given in radians to make sure it is in the interval [0,2π]
149+
*
150+
* @param angle an angle in radians
151+
* @return the normalized angle
152+
*/
153+
public static double normalizeAngle(double angle) {
154+
double twoPi = 2 * Math.PI;
155+
angle = angle % twoPi;
156+
if (angle < 0) {
157+
angle += twoPi;
158+
}
159+
return angle;
160+
}
161+
147162
/**
148163
* Gets the center position given a list of line segments.
149164
*

ugs-core/src/com/willwinder/universalgcodesender/visualizer/GcodeViewParse.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ public List<LineSegment> toObjFromReader(IGcodeStreamReader reader,
132132

133133
// Save the state
134134
Position start = new Position(Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, gp.getCurrentState().getUnits());
135-
double spindleSpeed = 0;
136135

137136
while (reader.getNumRowsRemaining() > 0) {
138137
GcodeCommand commandObject = reader.getNextCommand();
@@ -141,9 +140,8 @@ public List<LineSegment> toObjFromReader(IGcodeStreamReader reader,
141140
List<GcodeMeta> points = gp.addCommand(command, commandObject.getCommandNumber());
142141
for (GcodeMeta meta : points) {
143142
if (meta.point != null) {
144-
VisualizerUtils.addLinesFromPointSegment(start, meta.point, arcSegmentLength, lines, spindleSpeed);
143+
VisualizerUtils.addLinesFromPointSegment(start, meta.point, arcSegmentLength, lines);
145144
start = meta.point.point();
146-
spindleSpeed = meta.point.getSpindleSpeed();
147145
}
148146
}
149147
}
@@ -176,16 +174,14 @@ public List<LineSegment> toObjRedux(List<String> gcode, double arcSegmentLength)
176174

177175
// Save the state
178176
Position start = new Position(Double.NaN, Double.NaN, Double.NaN, gp.getCurrentState().getUnits());
179-
double spindleSpeed = 0;
180177

181178
for (String s : gcode) {
182179
List<String> commands = gp.preprocessCommand(s, gp.getCurrentState());
183180
for (String command : commands) {
184181
List<GcodeMeta> points = gp.addCommand(command);
185182
for (GcodeMeta meta : points) {
186183
if (meta.point != null) {
187-
VisualizerUtils.addLinesFromPointSegment(start, meta.point, arcSegmentLength, lines, spindleSpeed);
188-
spindleSpeed = meta.point.getSpindleSpeed();
184+
VisualizerUtils.addLinesFromPointSegment(start, meta.point, arcSegmentLength, lines);
189185

190186
// if the last set point is in a different or unknown unit, crate a new point-instance with the correct unit set
191187
if (start.getUnits() != UnitUtils.Units.MM && gp.getCurrentState().isMetric) {

ugs-core/src/com/willwinder/universalgcodesender/visualizer/VisualizerUtils.java

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ This file is part of Universal Gcode Sender (UGS).
2222
package com.willwinder.universalgcodesender.visualizer;
2323

2424
import com.willwinder.universalgcodesender.gcode.GcodePreprocessorUtils;
25+
import com.willwinder.universalgcodesender.gcode.util.GcodeParserException;
2526
import com.willwinder.universalgcodesender.gcode.util.PlaneFormatter;
2627
import com.willwinder.universalgcodesender.model.Position;
2728
import com.willwinder.universalgcodesender.types.PointSegment;
28-
import com.willwinder.universalgcodesender.utils.GUIHelpers;
2929

3030
import java.io.BufferedReader;
3131
import java.io.DataInputStream;
@@ -35,14 +35,11 @@ This file is part of Universal Gcode Sender (UGS).
3535
import java.io.InputStreamReader;
3636
import java.util.ArrayList;
3737
import java.util.List;
38-
import java.util.logging.Level;
39-
import java.util.logging.Logger;
4038

4139
/**
4240
* @author wwinder
4341
*/
4442
public class VisualizerUtils {
45-
private static final Logger LOGGER = Logger.getLogger(VisualizerUtils.class.getSimpleName());
4643

4744
/**
4845
* Returns the maximum side dimension of a box containing two points.
@@ -54,24 +51,14 @@ public static double findMaxSide(Position min, Position max) {
5451
return Math.max(x, Math.max(y, z));
5552
}
5653

57-
/**
58-
* Returns the aspect ratio from two points.
59-
*/
60-
public static double findAspectRatio(Position min, Position max) {
61-
double x = Math.abs(min.x) + Math.abs(max.x);
62-
double y = Math.abs(min.y) + Math.abs(max.y);
63-
return x / y;
64-
}
65-
6654
/**
6755
* Returns the center point on a line.
6856
*/
6957
public static Position findCenter(Position min, Position max) {
70-
Position center = new Position(
58+
return new Position(
7159
(min.x + max.x) / 2.0,
7260
(min.y + max.y) / 2.0,
7361
(min.z + max.z) / 2.0);
74-
return center;
7562
}
7663

7764
/**
@@ -132,7 +119,7 @@ public static double getRelativeMovementMultiplier(double objectMin, double obje
132119
/**
133120
* Helper to create a line segment with flags initialized.
134121
*/
135-
private static LineSegment createLineSegment(Position a, Position b, PointSegment meta, double spindleSpeed) {
122+
private static LineSegment createLineSegment(Position a, Position b, PointSegment meta) {
136123
LineSegment ls = new LineSegment(a, b, meta.getLineNumber());
137124
ls.setIsArc(meta.isArc());
138125
ls.setIsFastTraverse(meta.isFastTraverse());
@@ -145,8 +132,10 @@ private static LineSegment createLineSegment(Position a, Position b, PointSegmen
145132

146133
/**
147134
* Turns a point segment into one or more LineSegment. Arcs and rotations around axes are expanded
135+
*
136+
* @throws GcodeParserException if the lines could not be expanded
148137
*/
149-
public static void addLinesFromPointSegment(final Position start, final PointSegment endSegment, double arcSegmentLength, List<LineSegment> ret, double spindleSpeed) {
138+
public static void addLinesFromPointSegment(final Position start, final PointSegment endSegment, double arcSegmentLength, List<LineSegment> ret) throws GcodeParserException {
150139
// For a line segment list ALL arcs must be converted to lines.
151140
double minArcLength = 0;
152141
endSegment.convertToMetric();
@@ -156,22 +145,21 @@ public static void addLinesFromPointSegment(final Position start, final PointSeg
156145
if (start != null) {
157146
// Expand arc for graphics.
158147
if (endSegment.isArc()) {
159-
expandArc(start, endSegment, arcSegmentLength, ret, minArcLength, spindleSpeed);
148+
expandArc(start, endSegment, arcSegmentLength, ret, minArcLength);
160149
} else if (endSegment.isRotation()) {
161-
expandRotationalLineSegment(start, endSegment, ret, spindleSpeed);
150+
expandRotationalLineSegment(start, endSegment, ret);
162151
} else {
163152
// Line
164-
ret.add(createLineSegment(start, endSegment.point(), endSegment, spindleSpeed));
153+
ret.add(createLineSegment(start, endSegment.point(), endSegment));
165154
}
166155
}
167156
} catch (Exception e) {
168-
String message = endSegment.getLineNumber() + ": " + e.getMessage();
169-
GUIHelpers.displayErrorDialog(message, true);
170-
LOGGER.log(Level.SEVERE, message, e);
157+
String message = "Line " + endSegment.getLineNumber() + ": " + e.getMessage();
158+
throw new GcodeParserException(message, e);
171159
}
172160
}
173161

174-
private static void expandArc(Position start, PointSegment endSegment, double arcSegmentLength, List<LineSegment> ret, double minArcLength, double spindleSpeed) {
162+
private static void expandArc(Position start, PointSegment endSegment, double arcSegmentLength, List<LineSegment> ret, double minArcLength) {
175163
List<Position> points =
176164
GcodePreprocessorUtils.generatePointsAlongArcBDring(
177165
start, endSegment.point(), endSegment.center(), endSegment.isClockwise(),
@@ -180,13 +168,13 @@ private static void expandArc(Position start, PointSegment endSegment, double ar
180168
if (!points.isEmpty()) {
181169
Position startPoint = start;
182170
for (Position nextPoint : points) {
183-
ret.add(createLineSegment(startPoint, nextPoint, endSegment, spindleSpeed));
171+
ret.add(createLineSegment(startPoint, nextPoint, endSegment));
184172
startPoint = nextPoint;
185173
}
186174
}
187175
}
188176

189-
public static void expandRotationalLineSegment(Position start, PointSegment endSegment, List<LineSegment> ret, double spindleSpeed) {
177+
public static void expandRotationalLineSegment(Position start, PointSegment endSegment, List<LineSegment> ret) {
190178
double maxDegreesPerStep = 5;
191179
double deltaX = defaultZero(endSegment.point().x) - defaultZero(start.x);
192180
double deltaY = defaultZero(endSegment.point().y) - defaultZero(start.y);
@@ -217,11 +205,11 @@ public static void expandRotationalLineSegment(Position start, PointSegment endS
217205
if (deltaC != 0) {
218206
end.setC(defaultZero(start.c) + ((deltaC / steps) * i));
219207
}
220-
ret.add(createLineSegment(startPoint, end, endSegment, spindleSpeed));
208+
ret.add(createLineSegment(startPoint, end, endSegment));
221209
startPoint = end;
222210
}
223211

224-
ret.add(createLineSegment(startPoint, endSegment.point(), endSegment, spindleSpeed));
212+
ret.add(createLineSegment(startPoint, endSegment.point(), endSegment));
225213
}
226214

227215
/**

0 commit comments

Comments
 (0)