From 2543f82b7028baab477983e8a87fb2f6db3281a6 Mon Sep 17 00:00:00 2001 From: Joacim Breiler Date: Fri, 11 Aug 2023 19:31:35 +0200 Subject: [PATCH] Fixes problems in autoleveler with scaling between metric and imperial units (#2273) * Replaced the setting for handling units with the global preferred unit setting * Show the color difference for high and low based on the min/max value of the probed values * Fixed problems with unit scaling between imperial and metric. Now uses the units from the positions instead of and explicit one. --- .../gcode/processors/MeshLeveler.java | 106 ++--- .../gcode/processors/TranslateProcessor.java | 2 +- .../universalgcodesender/model/Position.java | 5 + .../universalgcodesender/utils/Settings.java | 12 + .../GcodePreprocessorUtilsTest.java | 320 ------------- .../gcode/GcodeParserTest.java | 3 +- .../gcode/GcodePreprocessorUtilsTest.java | 275 +++++++++++ .../gcode/fixtures/FixturesTest.java | 3 +- .../gcode/processors/MeshLevelerTest.java | 285 ++++++++--- .../model/PositionTest.java | 159 +++++-- .../surfacescanner/AutoLevelPreview.java | 151 +++--- .../AutoLevelerTopComponent.form | 449 ------------------ .../AutoLevelerTopComponent.java | 135 ++---- .../surfacescanner/SurfaceScanner.java | 103 ++-- 14 files changed, 846 insertions(+), 1162 deletions(-) delete mode 100644 ugs-core/test/com/willwinder/universalgcodesender/GcodePreprocessorUtilsTest.java delete mode 100644 ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.form diff --git a/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/MeshLeveler.java b/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/MeshLeveler.java index ed456a065..4c11dc102 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/MeshLeveler.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/MeshLeveler.java @@ -28,6 +28,7 @@ This file is part of Universal Gcode Sender (UGS). import com.willwinder.universalgcodesender.model.Position; import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.model.UnitUtils.Units; + import java.util.Collections; import java.util.List; @@ -37,36 +38,46 @@ This file is part of Universal Gcode Sender (UGS). * @author wwinder */ public class MeshLeveler implements CommandProcessor { - final private double materialSurfaceHeight; - final private Position[][] surfaceMesh; - final private Position lowerLeft; - final private int xLen, yLen; - final private double resolution; - - private Units units; - - public final static String ERROR_MESH_SHAPE= "Surface mesh must be a rectangular 2D array."; + public final static String ERROR_MESH_SHAPE = "Surface mesh must be a rectangular 2D array."; public final static String ERROR_NOT_ENOUGH_SAMPLES = "Need at least 2 samples along each axis."; public final static String ERROR_X_ALIGNMENT = "Unaligned x coordinate in surface grid."; public final static String ERROR_Y_ALIGNMENT = "Unaligned y coordinate in surface grid."; public final static String ERROR_Y_ASCENTION = "Found a y coordinate that isn't ascending."; public final static String ERROR_X_ASCENTION = "Found a x coordinate that isn't ascending."; - public final static String ERROR_UNEXPECTED_ARC = "The mesh leveler cannot process arcs. Enable the arc expander."; public final static String ERROR_MISSING_POINT_DATA = "Internal parser error: missing data. "; + final private double materialSurfaceHeightMM; + final private Position[][] surfaceMesh; + final private Position lowerLeft; + final private int xLen, yLen; + final private double resolution; + private Units surfaceMeshUnits; /** * @param materialSurfaceHeightMM Z height used in offset. - * @param surfaceMesh 2D array in the format Position[x][y] + * @param surfaceMesh 2D array in the format Position[x][y] */ - public MeshLeveler(double materialSurfaceHeightMM, Position[][] surfaceMesh, Units units) { + public MeshLeveler(double materialSurfaceHeightMM, Position[][] surfaceMesh) { if (surfaceMesh == null) { throw new IllegalArgumentException("Surface mesh is required."); } - // Validate that points form a rectangular 2D array. + this.materialSurfaceHeightMM = materialSurfaceHeightMM; this.yLen = surfaceMesh[0].length; this.xLen = surfaceMesh.length; + + validateMesh(surfaceMesh); + this.surfaceMesh = surfaceMesh; + this.surfaceMeshUnits = surfaceMesh[0][0].getUnits(); + this.resolution = Math.max( + surfaceMesh[1][0].x - surfaceMesh[0][0].x, + surfaceMesh[0][1].y - surfaceMesh[0][0].y); + + this.lowerLeft = surfaceMesh[0][0]; + } + + private void validateMesh(Position[][] surfaceMesh) { + // Validate that points form a rectangular 2D array. for (Position[] arr : surfaceMesh) { if (arr.length != yLen) { throw new IllegalArgumentException(ERROR_MESH_SHAPE); @@ -84,11 +95,11 @@ public MeshLeveler(double materialSurfaceHeightMM, Position[][] surfaceMesh, Uni double yCoord = surfaceMesh[xIdx][0].y; for (int yIdx = 0; yIdx < yLen; yIdx++) { if (surfaceMesh[xIdx][yIdx].x != xCoord) { - String err = "@ " + xIdx + ", " + yIdx + ": ("+xCoord+" != "+surfaceMesh[xIdx][yIdx].x+")"; + String err = "@ " + xIdx + ", " + yIdx + ": (" + xCoord + " != " + surfaceMesh[xIdx][yIdx].x + ")"; throw new IllegalArgumentException(ERROR_X_ALIGNMENT + err); } if (yCoord > surfaceMesh[xIdx][yIdx].y) { - String err = "@ " + xIdx + ", " + yIdx + ": ("+yCoord+" !<= "+surfaceMesh[xIdx][yIdx].y+")"; + String err = "@ " + xIdx + ", " + yIdx + ": (" + yCoord + " !<= " + surfaceMesh[xIdx][yIdx].y + ")"; throw new IllegalArgumentException(ERROR_Y_ASCENTION + err); } yCoord = surfaceMesh[xIdx][yIdx].y; @@ -101,32 +112,23 @@ public MeshLeveler(double materialSurfaceHeightMM, Position[][] surfaceMesh, Uni double yCoord = surfaceMesh[0][yIdx].y; for (int xIdx = 0; xIdx < xLen; xIdx++) { if (surfaceMesh[xIdx][yIdx].y != yCoord) { - String err = "@ " + xIdx + ", " + yIdx + ": ("+yCoord+" != "+surfaceMesh[xIdx][yIdx].y+")"; + String err = "@ " + xIdx + ", " + yIdx + ": (" + yCoord + " != " + surfaceMesh[xIdx][yIdx].y + ")"; throw new IllegalArgumentException(ERROR_Y_ALIGNMENT + err); } if (xCoord > surfaceMesh[xIdx][yIdx].x) { - String err = "@ " + xIdx + ", " + yIdx + ": ("+xCoord+" !<= "+surfaceMesh[xIdx][yIdx].x+")"; + String err = "@ " + xIdx + ", " + yIdx + ": (" + xCoord + " !<= " + surfaceMesh[xIdx][yIdx].x + ")"; throw new IllegalArgumentException(ERROR_X_ASCENTION + err); } xCoord = surfaceMesh[xIdx][yIdx].x; } } - - this.units = units; - this.materialSurfaceHeight = materialSurfaceHeightMM; - this.surfaceMesh = surfaceMesh; - this.resolution = Math.max( - surfaceMesh[1][0].x-surfaceMesh[0][0].x, - surfaceMesh[0][1].y-surfaceMesh[0][0].y); - - this.lowerLeft = surfaceMesh[0][0]; } private boolean ensureJustLines(List commands) throws GcodeParserException { if (commands == null) return false; boolean hasLine = false; for (GcodeMeta command : commands) { - switch(command.code) { + switch (command.code) { case G0: case G1: hasLine = true; @@ -163,26 +165,28 @@ public List processCommand(final String commandString, GcodeState state) // Get offset relative to the expected surface height. // Visualizer normalizes everything to MM but probe mesh might be INCH - double probeScaleFactor = UnitUtils.scaleUnits(UnitUtils.Units.MM, this.units); - double zScaleFactor = UnitUtils.scaleUnits(UnitUtils.Units.MM, state.isMetric ? Units.MM : Units.INCH); - double zPointOffset; if (state.inAbsoluteMode) { - zPointOffset = surfaceHeightAt(end.x, end.y, zScaleFactor) - (this.materialSurfaceHeight / probeScaleFactor); + Position position = end.getPositionIn(surfaceMeshUnits); + double materialSurfaceHeight = this.materialSurfaceHeightMM * UnitUtils.scaleUnits(Units.MM, surfaceMeshUnits); + zPointOffset = (surfaceHeightAt(position.x, position.y) - materialSurfaceHeight) * UnitUtils.scaleUnits(surfaceMeshUnits, end.getUnits()); } else { // TODO: If the first move in the gcode file is relative it won't properly take the materialSurfaceHeight // into account. To fix the CommandProcessor needs to inject an adjustment before that first relative move // happens. Until that happens the user must make sure the materialSurfaceHeight is zero. // In relative mode we only need to adjust by the z delta between the starting and ending point - zPointOffset = surfaceHeightAt(end.x, end.y, zScaleFactor) - surfaceHeightAt(start.x, start.y, zScaleFactor); + Position startPositionInMeshUnits = start.getPositionIn(surfaceMeshUnits); + double startHeight = surfaceHeightAt(startPositionInMeshUnits.x, startPositionInMeshUnits.y); + Position endPositionInMeshUnits = end.getPositionIn(surfaceMeshUnits); + double endHeight = surfaceHeightAt(endPositionInMeshUnits.x, endPositionInMeshUnits.y); + zPointOffset = (endHeight - startHeight) * UnitUtils.scaleUnits(surfaceMeshUnits, end.getUnits()); } - zPointOffset *= zScaleFactor; // Update z coordinate. double newZ = end.getZ() + zPointOffset; - PartialPosition.Builder overrideZ = PartialPosition.builder(units); + PartialPosition.Builder overrideZ = PartialPosition.builder(end.getUnits()); if (command.state.inAbsoluteMode) { overrideZ.setZ(newZ); } else { @@ -195,13 +199,7 @@ public List processCommand(final String commandString, GcodeState state) return adjustedCommands.build(); } - protected Position[][] findBoundingArea(double x, double y) throws GcodeParserException { - /* - if (x < this.lowerLeft.x || x > this.upperRight.x || y < this.lowerLeft.y || y > this.upperRight.y) { - throw new GcodeParserException("Coordinate out of bounds."); - } - */ - + protected Position[][] findBoundingArea(double x, double y) { double xOffset = x - this.lowerLeft.x; double yOffset = y - this.lowerLeft.y; @@ -214,19 +212,19 @@ protected Position[][] findBoundingArea(double x, double y) throws GcodeParserEx xIdx = Math.max(xIdx, 0); yIdx = Math.max(yIdx, 0); - return new Position[][] { - {this.surfaceMesh[xIdx ][yIdx], this.surfaceMesh[xIdx ][yIdx+1]}, - {this.surfaceMesh[xIdx+1][yIdx], this.surfaceMesh[xIdx+1][yIdx+1]} + return new Position[][]{ + {this.surfaceMesh[xIdx][yIdx], this.surfaceMesh[xIdx][yIdx + 1]}, + {this.surfaceMesh[xIdx + 1][yIdx], this.surfaceMesh[xIdx + 1][yIdx + 1]} }; } /** + * Get the surface height from the mesh and returns it in the surface mesh units. + *

* Bilinear interpolation: * http://supercomputingblog.com/graphics/coding-bilinear-interpolation/ */ - protected double surfaceHeightAt(double unscaledX, double unscaledY, double zScaleFactor) throws GcodeParserException { - double x = unscaledX / zScaleFactor; - double y = unscaledY / zScaleFactor; + protected double surfaceHeightAt(double x, double y) { Position[][] q = findBoundingArea(x, y); Position Q11 = q[0][0]; @@ -234,23 +232,15 @@ protected double surfaceHeightAt(double unscaledX, double unscaledY, double zSca Position Q12 = q[0][1]; Position Q22 = q[1][1]; - /* - // This check doesn't work properly because I chose to clamp bounds - if (Q11.x > x || Q12.x > x || Q21.x < x || Q22.x < x || - Q12.y < y || Q22.y < y || Q11.y > y || Q21.y > y) { - throw new GcodeParserException("Problem detected getting surface height. Please submit file to github for analysis."); - } - */ - double x1 = Q11.x; double x2 = Q21.x; double y1 = Q11.y; double y2 = Q12.y; - double R1 = ((x2 - x)/(x2 - x1)) * Q11.z + ((x - x1)/(x2 - x1)) * Q21.z; - double R2 = ((x2 - x)/(x2 - x1)) * Q12.z + ((x - x1)/(x2 - x1)) * Q22.z; + double R1 = ((x2 - x) / (x2 - x1)) * Q11.z + ((x - x1) / (x2 - x1)) * Q21.z; + double R2 = ((x2 - x) / (x2 - x1)) * Q12.z + ((x - x1) / (x2 - x1)) * Q22.z; - return ((y2 - y)/(y2 - y1)) * R1 + ((y - y1)/(y2 - y1)) * R2; + return ((y2 - y) / (y2 - y1)) * R1 + ((y - y1) / (y2 - y1)) * R2; } @Override diff --git a/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/TranslateProcessor.java b/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/TranslateProcessor.java index 5d25031ad..9ae71f083 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/TranslateProcessor.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/gcode/processors/TranslateProcessor.java @@ -101,7 +101,7 @@ private List translateCoordinates(String command, GcodeState state) thro Position end = gcodeMeta.point.point() .getPositionIn(currentUnits); - end.add(offset.getPositionIn(currentUnits)); + end = end.add(offset.getPositionIn(currentUnits)); String adjustedCommand = GcodePreprocessorUtils.generateLineFromPoints( gcodeMeta.code, start, end, gcodeMeta.state.inAbsoluteMode, null); diff --git a/ugs-core/src/com/willwinder/universalgcodesender/model/Position.java b/ugs-core/src/com/willwinder/universalgcodesender/model/Position.java index 432c649df..a5709b2ec 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/model/Position.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/model/Position.java @@ -188,4 +188,9 @@ public Position rotate(Position center, double radians) { z, units); } + + public Position add(Position position) { + Position p = position.getPositionIn(units); + return new Position(x + p.x, y + p.y, z + p.z, a + p.a, b + p.b, c + p.c, units); + } } diff --git a/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java b/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java index c919905a5..319b8438e 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java @@ -557,8 +557,20 @@ public static class AutoLevelSettings { // Setting window public double autoLevelProbeZeroHeight = 0; public Position autoLevelProbeOffset = new Position(0, 0, 0, Units.MM); + + /** + * How long the arcs segments should be expanded in millimeters + */ public double autoLevelArcSliceLength = 0.01; + + /** + * The fast probe scan rate in mm/min + */ public double probeScanFeedRate = 1000; + + /** + * Probe speed in mm/min + */ public double probeSpeed = 10; // Main window diff --git a/ugs-core/test/com/willwinder/universalgcodesender/GcodePreprocessorUtilsTest.java b/ugs-core/test/com/willwinder/universalgcodesender/GcodePreprocessorUtilsTest.java deleted file mode 100644 index 9c6ed4382..000000000 --- a/ugs-core/test/com/willwinder/universalgcodesender/GcodePreprocessorUtilsTest.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - Copyright 2018-2020 Will Winder - - This file is part of Universal Gcode Sender (UGS). - - UGS is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - UGS is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with UGS. If not, see . - */ -package com.willwinder.universalgcodesender; - -import com.google.common.collect.ImmutableList; -import com.willwinder.universalgcodesender.gcode.GcodePreprocessorUtils; -import com.willwinder.universalgcodesender.gcode.GcodeState; -import com.willwinder.universalgcodesender.gcode.util.Code; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import static com.willwinder.universalgcodesender.gcode.util.Code.G1; -import static com.willwinder.universalgcodesender.gcode.util.Code.G2; -import static com.willwinder.universalgcodesender.gcode.util.Code.G3; -import static com.willwinder.universalgcodesender.gcode.util.Code.G38_2; -import static com.willwinder.universalgcodesender.gcode.util.Code.G92_1; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; - -/** - * - * @author wwinder - */ -public class GcodePreprocessorUtilsTest { - - public GcodePreprocessorUtilsTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } - - /** - * Test of overrideSpeed method, of class CommUtils. - */ - @Test - public void testOverrideSpeed() { - System.out.println("overrideSpeed"); - String command; - double speed; - String expResult; - String result; - - - command = "some command F100 blah blah blah"; - speed = 22.5; - expResult = "some command F22.5 blah blah blah"; - result = GcodePreprocessorUtils.overrideSpeed(command, speed); - assertEquals(expResult, result); - - command = "some command F100.0 blah blah blah"; - result = GcodePreprocessorUtils.overrideSpeed(command, speed); - assertEquals(expResult, result); - } - - /** - * Test of parseComment method, of class GrblUtils. - */ - @Test - public void testParseComment() { - System.out.println("parseComment"); - String command; - String expResult; - String result; - - command = "some command ;comment"; - expResult = "comment"; - result = GcodePreprocessorUtils.parseComment(command); - assertEquals(expResult, result); - - command = "some (comment here) command ;comment"; - expResult = "comment here"; - result = GcodePreprocessorUtils.parseComment(command); - assertEquals(expResult, result); - } - - /** - * Test of truncateDecimals method, of class GcodePreprocessorUtils. - */ - @Test - public void testTruncateDecimals() { - System.out.println("truncateDecimals"); - int length; - String command; - String result; - String expResult; - - // Length tests. - length = 0; - command = "G1 X0.11111111111111111111"; - expResult = "G1 X0"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - length = 8; - expResult = "G1 X0.11111111"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - length = 800; - expResult = command; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - // Rounding tests. - length = 3; - command = "G1 X1.5555555"; - expResult = "G1 X1.556"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - length = 0; - expResult = "G1 X2"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - length = 5; - command = "G1 X1.99999999"; - expResult = "G1 X2"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - - // Multiple hits. - length = 3; - command = "G1 X1.23456 Y9.87654 Z104.49443"; - expResult = "G1 X1.235 Y9.877 Z104.494"; - result = GcodePreprocessorUtils.truncateDecimals(length, command); - assertEquals(expResult, result); - } - - @Test - public void testParseCodes() { - System.out.println("parseCodes"); - - // Basic case, find one gcode. - List sl = new ArrayList<>(); - sl.add("G0"); - sl.add("X7"); - sl.add("Y5.235235"); - List l = GcodePreprocessorUtils.parseCodes(sl, 'G'); - assertEquals(1, l.size()); - assertEquals("0", l.get(0)); - - // Find two gcodes. - sl.add("G20"); - l = GcodePreprocessorUtils.parseCodes(sl, 'G'); - assertEquals(2, l.size()); - assertEquals("0", l.get(0)); - assertEquals("20", l.get(1)); - - // Find X, mismatched case. - sl.add("G20"); - l = GcodePreprocessorUtils.parseCodes(sl, 'x'); - assertEquals(1, l.size()); - assertEquals("7", l.get(0)); - } - - @Test - public void parseCoord() throws Exception { - List args = ImmutableList.of("G10", "G3", "X100", "y-.5", "Z0.25"); - assertThat(GcodePreprocessorUtils.parseCoord(args, 'x')).isEqualTo(100); - assertThat(GcodePreprocessorUtils.parseCoord(args, 'y')).isEqualTo(-0.5); - assertThat(GcodePreprocessorUtils.parseCoord(args, 'z')).isEqualTo(0.25); - - assertThat(GcodePreprocessorUtils.parseCoord(args, 'X')).isEqualTo(100); - assertThat(GcodePreprocessorUtils.parseCoord(args, 'Y')).isEqualTo(-0.5); - assertThat(GcodePreprocessorUtils.parseCoord(args, 'Z')).isEqualTo(0.25); - } - - @Test - public void extractWord() throws Exception { - List args = ImmutableList.of("G10", "G3", "X100", "y-.5", "Z0.25"); - assertThat(GcodePreprocessorUtils.extractWord(args, 'x')).isEqualTo("X100"); - assertThat(GcodePreprocessorUtils.extractWord(args, 'y')).isEqualTo("y-.5"); - assertThat(GcodePreprocessorUtils.extractWord(args, 'z')).isEqualTo("Z0.25"); - - assertThat(GcodePreprocessorUtils.extractWord(args, 'X')).isEqualTo("X100"); - assertThat(GcodePreprocessorUtils.extractWord(args, 'Y')).isEqualTo("y-.5"); - assertThat(GcodePreprocessorUtils.extractWord(args, 'Z')).isEqualTo("Z0.25"); - } - - @Test - public void testGetGcodes() throws Exception { - List args = ImmutableList.of("F100", "M30", "G1", "G2", "F100", "G3", "G92.1", "G38.2", "S1300"); - Set codes = GcodePreprocessorUtils.getGCodes(args); - assertThat(codes).containsExactly(G1, G2, G3, G92_1, G38_2); - } - - @Test - public void testExtractMotion() throws Exception { - assertThat(GcodePreprocessorUtils.extractMotion(G3, "G17 G03 X0 Y12 I0.25 J-0.25 K1.99 F100")) - .hasFieldOrPropertyWithValue("extracted", "G03X0Y12I0.25J-0.25K1.99") - .hasFieldOrPropertyWithValue("remainder", "G17F100"); - - assertThat(GcodePreprocessorUtils.extractMotion(G1, "G17 G03 X0 Y12 I0.25 J-0.25 K1.99 F100")) - .isNull(); - - assertThat(GcodePreprocessorUtils.extractMotion(G1, "")) - .isNull(); - - assertThat(GcodePreprocessorUtils.extractMotion(G1, "G53 G0 X0")) - .isNull(); - - assertThat(GcodePreprocessorUtils.extractMotion(G1, "G53 G01 X0 F100 S1300")) - .hasFieldOrPropertyWithValue("extracted", "G53G01X0") - .hasFieldOrPropertyWithValue("remainder", "F100S1300"); - - assertThat(GcodePreprocessorUtils.extractMotion(G3, "G53 G03 X0 F100 S1300")) - .hasFieldOrPropertyWithValue("extracted", "G03X0") - .hasFieldOrPropertyWithValue("remainder", "G53F100S1300"); - - assertThat(GcodePreprocessorUtils.extractMotion(G1, "X0 Y0 Z1 F100 S1300")) - .hasFieldOrPropertyWithValue("extracted", "X0Y0Z1") - .hasFieldOrPropertyWithValue("remainder", "F100S1300"); - } - - @Test - public void processSleepCommand() { - String command = "$SLP"; - List args = GcodePreprocessorUtils.splitCommand(command); - assertEquals(1, args.size()); - assertEquals("$SLP", args.get(0)); - } - - @Test - public void testSplitCommand() { - List splitted = GcodePreprocessorUtils.splitCommand("G53F100S1300"); - assertEquals(3, splitted.size()); - assertEquals("G53", splitted.get(0)); - assertEquals("F100", splitted.get(1)); - assertEquals("S1300", splitted.get(2)); - - splitted = GcodePreprocessorUtils.splitCommand("G53 F 100 S 1300"); - assertEquals(3, splitted.size()); - assertEquals("G53", splitted.get(0)); - assertEquals("F100", splitted.get(1)); - assertEquals("S1300", splitted.get(2)); - - splitted = GcodePreprocessorUtils.splitCommand("G53G90.1S1300"); - assertEquals(3, splitted.size()); - assertEquals("G90.1", splitted.get(1)); - - splitted = GcodePreprocessorUtils.splitCommand("G53G90_1S1300"); - assertEquals(4, splitted.size()); - assertEquals("G90", splitted.get(1)); - assertEquals("1", splitted.get(2)); - } - - @Test - public void splitCommandWithComments() { - List splitted = GcodePreprocessorUtils.splitCommand("(comment)G1X10"); - assertEquals(3, splitted.size()); - - splitted = GcodePreprocessorUtils.splitCommand("(comment)G1X10(comment)"); - assertEquals(4, splitted.size()); - - splitted = GcodePreprocessorUtils.splitCommand(";commentG1X10(comment)"); - assertEquals(1, splitted.size()); - } - - @Test - public void processCommandWithBlockComments() throws Exception { - List splitted = GcodePreprocessorUtils.splitCommand("(hello world)G3"); - assertThat(splitted.size()).isEqualTo(2); - - splitted = GcodePreprocessorUtils.splitCommand("(1)(2)G3(3)"); - assertThat(splitted.size()).isEqualTo(4); - } - - @Test - public void normalizeCommand() throws Exception { - GcodeState state = new GcodeState(); - - // Add state to a complete command, ignoring stale motion mode. - assertThat(GcodePreprocessorUtils.normalizeCommand("G1X0Y0", state)) - .isEqualTo("F0.0S0.0G1X0Y0"); - - state.currentMotionMode = Code.G1; - state.feedRate = 12.5; - - // Add state and insert implicit motion mode. - assertThat(GcodePreprocessorUtils.normalizeCommand("X0Y0", state)) - .isEqualTo("F12.5S0.0G1X0Y0"); - } -} diff --git a/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodeParserTest.java b/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodeParserTest.java index 565959746..d6151c046 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodeParserTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodeParserTest.java @@ -34,7 +34,6 @@ This file is part of Universal Gcode Sender (UGS). import com.willwinder.universalgcodesender.gcode.util.Plane; import com.willwinder.universalgcodesender.i18n.Localization; import com.willwinder.universalgcodesender.model.Position; -import com.willwinder.universalgcodesender.model.UnitUtils.Units; import com.willwinder.universalgcodesender.types.GcodeCommand; import com.willwinder.universalgcodesender.types.PointSegment; import com.willwinder.universalgcodesender.utils.GcodeStreamReader; @@ -291,7 +290,7 @@ public void autoLevelerProcessorSet() throws Exception { { new Position(-5,-5,0, MM), new Position(-5,35,0, MM) }, { new Position(35,-5,0, MM), new Position(35,35,0, MM) } }; - gcp.addCommandProcessor(new MeshLeveler(0, grid, Units.MM)); + gcp.addCommandProcessor(new MeshLeveler(0, grid)); Path output = Files.createTempFile("autoleveler_processor_set_test.nc", ""); diff --git a/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodePreprocessorUtilsTest.java b/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodePreprocessorUtilsTest.java index 716c5b0bf..5481f4e70 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodePreprocessorUtilsTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/gcode/GcodePreprocessorUtilsTest.java @@ -18,22 +18,283 @@ This file is part of Universal Gcode Sender (UGS). */ package com.willwinder.universalgcodesender.gcode; +import com.google.common.collect.ImmutableList; +import com.willwinder.universalgcodesender.gcode.util.Code; import com.willwinder.universalgcodesender.gcode.util.Plane; import com.willwinder.universalgcodesender.gcode.util.PlaneFormatter; +import com.willwinder.universalgcodesender.model.PartialPosition; import com.willwinder.universalgcodesender.model.Position; import com.willwinder.universalgcodesender.model.UnitUtils; import org.junit.Test; +import java.util.ArrayList; import java.util.List; +import java.util.Set; +import static com.willwinder.universalgcodesender.gcode.util.Code.*; +import static com.willwinder.universalgcodesender.gcode.util.Code.G1; import static com.willwinder.universalgcodesender.model.UnitUtils.Units.INCH; import static com.willwinder.universalgcodesender.model.UnitUtils.Units.MM; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; /** * @author Joacim Breiler */ public class GcodePreprocessorUtilsTest { + /** + * Test of overrideSpeed method, of class CommUtils. + */ + @Test + public void testOverrideSpeed() { + System.out.println("overrideSpeed"); + String command; + double speed; + String expResult; + String result; + + + command = "some command F100 blah blah blah"; + speed = 22.5; + expResult = "some command F22.5 blah blah blah"; + result = GcodePreprocessorUtils.overrideSpeed(command, speed); + assertEquals(expResult, result); + + command = "some command F100.0 blah blah blah"; + result = GcodePreprocessorUtils.overrideSpeed(command, speed); + assertEquals(expResult, result); + } + + /** + * Test of parseComment method, of class GrblUtils. + */ + @Test + public void testParseComment() { + System.out.println("parseComment"); + String command; + String expResult; + String result; + + command = "some command ;comment"; + expResult = "comment"; + result = GcodePreprocessorUtils.parseComment(command); + assertEquals(expResult, result); + + command = "some (comment here) command ;comment"; + expResult = "comment here"; + result = GcodePreprocessorUtils.parseComment(command); + assertEquals(expResult, result); + } + + /** + * Test of truncateDecimals method, of class GcodePreprocessorUtils. + */ + @Test + public void testTruncateDecimals() { + System.out.println("truncateDecimals"); + int length; + String command; + String result; + String expResult; + + // Length tests. + length = 0; + command = "G1 X0.11111111111111111111"; + expResult = "G1 X0"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + length = 8; + expResult = "G1 X0.11111111"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + length = 800; + expResult = command; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + // Rounding tests. + length = 3; + command = "G1 X1.5555555"; + expResult = "G1 X1.556"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + length = 0; + expResult = "G1 X2"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + length = 5; + command = "G1 X1.99999999"; + expResult = "G1 X2"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + + // Multiple hits. + length = 3; + command = "G1 X1.23456 Y9.87654 Z104.49443"; + expResult = "G1 X1.235 Y9.877 Z104.494"; + result = GcodePreprocessorUtils.truncateDecimals(length, command); + assertEquals(expResult, result); + } + + @Test + public void testParseCodes() { + System.out.println("parseCodes"); + + // Basic case, find one gcode. + List sl = new ArrayList<>(); + sl.add("G0"); + sl.add("X7"); + sl.add("Y5.235235"); + List l = GcodePreprocessorUtils.parseCodes(sl, 'G'); + assertEquals(1, l.size()); + assertEquals("0", l.get(0)); + + // Find two gcodes. + sl.add("G20"); + l = GcodePreprocessorUtils.parseCodes(sl, 'G'); + assertEquals(2, l.size()); + assertEquals("0", l.get(0)); + assertEquals("20", l.get(1)); + + // Find X, mismatched case. + sl.add("G20"); + l = GcodePreprocessorUtils.parseCodes(sl, 'x'); + assertEquals(1, l.size()); + assertEquals("7", l.get(0)); + } + + @Test + public void parseCoord() throws Exception { + List args = ImmutableList.of("G10", "G3", "X100", "y-.5", "Z0.25"); + assertThat(GcodePreprocessorUtils.parseCoord(args, 'x')).isEqualTo(100); + assertThat(GcodePreprocessorUtils.parseCoord(args, 'y')).isEqualTo(-0.5); + assertThat(GcodePreprocessorUtils.parseCoord(args, 'z')).isEqualTo(0.25); + + assertThat(GcodePreprocessorUtils.parseCoord(args, 'X')).isEqualTo(100); + assertThat(GcodePreprocessorUtils.parseCoord(args, 'Y')).isEqualTo(-0.5); + assertThat(GcodePreprocessorUtils.parseCoord(args, 'Z')).isEqualTo(0.25); + } + + @Test + public void extractWord() throws Exception { + List args = ImmutableList.of("G10", "G3", "X100", "y-.5", "Z0.25"); + assertThat(GcodePreprocessorUtils.extractWord(args, 'x')).isEqualTo("X100"); + assertThat(GcodePreprocessorUtils.extractWord(args, 'y')).isEqualTo("y-.5"); + assertThat(GcodePreprocessorUtils.extractWord(args, 'z')).isEqualTo("Z0.25"); + + assertThat(GcodePreprocessorUtils.extractWord(args, 'X')).isEqualTo("X100"); + assertThat(GcodePreprocessorUtils.extractWord(args, 'Y')).isEqualTo("y-.5"); + assertThat(GcodePreprocessorUtils.extractWord(args, 'Z')).isEqualTo("Z0.25"); + } + + @Test + public void testGetGcodes() throws Exception { + List args = ImmutableList.of("F100", "M30", "G1", "G2", "F100", "G3", "G92.1", "G38.2", "S1300"); + Set codes = GcodePreprocessorUtils.getGCodes(args); + assertThat(codes).containsExactly(G1, G2, G3, G92_1, G38_2); + } + + @Test + public void testExtractMotion() throws Exception { + assertThat(GcodePreprocessorUtils.extractMotion(G3, "G17 G03 X0 Y12 I0.25 J-0.25 K1.99 F100")) + .hasFieldOrPropertyWithValue("extracted", "G03X0Y12I0.25J-0.25K1.99") + .hasFieldOrPropertyWithValue("remainder", "G17F100"); + + assertThat(GcodePreprocessorUtils.extractMotion(G1, "G17 G03 X0 Y12 I0.25 J-0.25 K1.99 F100")) + .isNull(); + + assertThat(GcodePreprocessorUtils.extractMotion(G1, "")) + .isNull(); + + assertThat(GcodePreprocessorUtils.extractMotion(G1, "G53 G0 X0")) + .isNull(); + + assertThat(GcodePreprocessorUtils.extractMotion(G1, "G53 G01 X0 F100 S1300")) + .hasFieldOrPropertyWithValue("extracted", "G53G01X0") + .hasFieldOrPropertyWithValue("remainder", "F100S1300"); + + assertThat(GcodePreprocessorUtils.extractMotion(G3, "G53 G03 X0 F100 S1300")) + .hasFieldOrPropertyWithValue("extracted", "G03X0") + .hasFieldOrPropertyWithValue("remainder", "G53F100S1300"); + + assertThat(GcodePreprocessorUtils.extractMotion(G1, "X0 Y0 Z1 F100 S1300")) + .hasFieldOrPropertyWithValue("extracted", "X0Y0Z1") + .hasFieldOrPropertyWithValue("remainder", "F100S1300"); + } + + @Test + public void processSleepCommand() { + String command = "$SLP"; + List args = GcodePreprocessorUtils.splitCommand(command); + assertEquals(1, args.size()); + assertEquals("$SLP", args.get(0)); + } + + @Test + public void testSplitCommand() { + List splitted = GcodePreprocessorUtils.splitCommand("G53F100S1300"); + assertEquals(3, splitted.size()); + assertEquals("G53", splitted.get(0)); + assertEquals("F100", splitted.get(1)); + assertEquals("S1300", splitted.get(2)); + + splitted = GcodePreprocessorUtils.splitCommand("G53 F 100 S 1300"); + assertEquals(3, splitted.size()); + assertEquals("G53", splitted.get(0)); + assertEquals("F100", splitted.get(1)); + assertEquals("S1300", splitted.get(2)); + + splitted = GcodePreprocessorUtils.splitCommand("G53G90.1S1300"); + assertEquals(3, splitted.size()); + assertEquals("G90.1", splitted.get(1)); + + splitted = GcodePreprocessorUtils.splitCommand("G53G90_1S1300"); + assertEquals(4, splitted.size()); + assertEquals("G90", splitted.get(1)); + assertEquals("1", splitted.get(2)); + } + + @Test + public void splitCommandWithComments() { + List splitted = GcodePreprocessorUtils.splitCommand("(comment)G1X10"); + assertEquals(3, splitted.size()); + + splitted = GcodePreprocessorUtils.splitCommand("(comment)G1X10(comment)"); + assertEquals(4, splitted.size()); + + splitted = GcodePreprocessorUtils.splitCommand(";commentG1X10(comment)"); + assertEquals(1, splitted.size()); + } + + @Test + public void processCommandWithBlockComments() throws Exception { + List splitted = GcodePreprocessorUtils.splitCommand("(hello world)G3"); + assertThat(splitted.size()).isEqualTo(2); + + splitted = GcodePreprocessorUtils.splitCommand("(1)(2)G3(3)"); + assertThat(splitted.size()).isEqualTo(4); + } + + @Test + public void normalizeCommand() throws Exception { + GcodeState state = new GcodeState(); + + // Add state to a complete command, ignoring stale motion mode. + assertThat(GcodePreprocessorUtils.normalizeCommand("G1X0Y0", state)) + .isEqualTo("F0.0S0.0G1X0Y0"); + + state.currentMotionMode = Code.G1; + state.feedRate = 12.5; + + // Add state and insert implicit motion mode. + assertThat(GcodePreprocessorUtils.normalizeCommand("X0Y0", state)) + .isEqualTo("F12.5S0.0G1X0Y0"); + } + @Test public void generatePointsAlongArcBDringShouldGenerateAnArc() { Position start = new Position(0, 0, 0, MM); @@ -60,6 +321,20 @@ public void generatePointsAlongArcBDringShouldGenerateAnArcWhenPositionsAreInInc assertTrue("Coordinates are generated in wrong units", points.stream().allMatch(p -> p.getUnits() == INCH)); } + @Test + public void overridePositionShouldUpdateTheCoordinatesInCommand() { + PartialPosition position = PartialPosition.builder(MM).setX(1d).setY(2d).setZ(3d).setA(4d).setB(5d).setC(6d).build(); + String newCommand = GcodePreprocessorUtils.overridePosition("G0X0Y0Z0A0B0C0", position); + assertEquals("G0X1Y2Z3A4B5C6", newCommand); + } + + @Test + public void overridePositionShouldAddMissingCoordinatesToCommand() { + PartialPosition position = PartialPosition.builder(MM).setX(1d).setY(2d).setZ(3d).setA(4d).setB(5d).setC(6d).build(); + String newCommand = GcodePreprocessorUtils.overridePosition("G0", position); + assertEquals("G0X1Y2Z3A4B5C6", newCommand); + } + static void assertThatPointsAreWithinBoundary(Position start, Position end, double radius, List points) { assertTrue(points.stream().allMatch(p -> p.x >= start.x)); assertTrue(points.stream().allMatch(p -> p.x <= end.x)); diff --git a/ugs-core/test/com/willwinder/universalgcodesender/gcode/fixtures/FixturesTest.java b/ugs-core/test/com/willwinder/universalgcodesender/gcode/fixtures/FixturesTest.java index 6f1ccb6e9..b1e4055bb 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/gcode/fixtures/FixturesTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/gcode/fixtures/FixturesTest.java @@ -7,7 +7,6 @@ import com.willwinder.universalgcodesender.gcode.util.GcodeParserUtils; import com.willwinder.universalgcodesender.i18n.Localization; import com.willwinder.universalgcodesender.model.Position; -import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.types.GcodeCommand; import com.willwinder.universalgcodesender.utils.GcodeStreamReader; import com.willwinder.universalgcodesender.utils.GcodeStreamWriter; @@ -78,7 +77,7 @@ public void testArcWithMeshLevelerFixtures() throws Exception { { new Position(-5,-5,0, MM), new Position(-5,35,0, MM) }, { new Position(35,-5,0, MM), new Position(35,35,0, MM) } }; - gcp.addCommandProcessor(new MeshLeveler(0, grid, UnitUtils.Units.MM)); + gcp.addCommandProcessor(new MeshLeveler(0, grid)); return gcp; }); diff --git a/ugs-core/test/com/willwinder/universalgcodesender/gcode/processors/MeshLevelerTest.java b/ugs-core/test/com/willwinder/universalgcodesender/gcode/processors/MeshLevelerTest.java index 435962849..9bc6c8c0e 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/gcode/processors/MeshLevelerTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/gcode/processors/MeshLevelerTest.java @@ -1,5 +1,5 @@ /* - Copyright 2017 Will Winder + Copyright 2017-2023 Will Winder This file is part of Universal Gcode Sender (UGS). @@ -19,60 +19,86 @@ This file is part of Universal Gcode Sender (UGS). package com.willwinder.universalgcodesender.gcode.processors; import com.willwinder.universalgcodesender.gcode.GcodeState; +import com.willwinder.universalgcodesender.gcode.util.Code; import com.willwinder.universalgcodesender.gcode.util.GcodeParserException; -import com.willwinder.universalgcodesender.i18n.Localization; import com.willwinder.universalgcodesender.model.Position; +import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.model.UnitUtils.Units; -import static com.willwinder.universalgcodesender.model.UnitUtils.Units.MM; -import java.util.Collections; -import java.util.List; - import org.assertj.core.util.Lists; import org.junit.Assert; -import org.junit.Test; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; +import java.util.List; + +import static com.willwinder.universalgcodesender.model.UnitUtils.Units.INCH; +import static com.willwinder.universalgcodesender.model.UnitUtils.Units.MM; +import static org.junit.Assert.assertEquals; + /** - * * @author wwinder */ public class MeshLevelerTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - private static final Position[][] BIG_FLAT_GRID_Z0 = { - {new Position(0,0,0,MM), new Position(0, 10, 0, MM)}, - {new Position(10,0,0,MM), new Position(10, 10, 0, MM)} + {new Position(0, 0, 0, MM), new Position(0, 10, 0, MM)}, + {new Position(10, 0, 0, MM), new Position(10, 10, 0, MM)} }; private static final Position[][] BIG_FLAT_GRID_Z1 = { - {new Position(0,0,1,MM), new Position(0, 10, 1,MM)}, - {new Position(10,0,1,MM), new Position(10, 10, 1,MM)} + {new Position(0, 0, 1, MM), new Position(0, 10, 1, MM)}, + {new Position(10, 0, 1, MM), new Position(10, 10, 1, MM)} }; - + @Rule + public ExpectedException expectedEx = ExpectedException.none(); private static void sendCommandExpectResult(MeshLeveler ml, GcodeState state, String command, String... result) { try { List expected = Lists.newArrayList(result); List results = ml.processCommand(command, state); - Assert.assertEquals(expected, results); + assertEquals(expected, results); } catch (GcodeParserException ex) { Assert.fail("Unexpected exception."); } } + /** + * Helper method for generating a sloped mesh in the following format where V is given through the parameter "value" + * in the supplied units + * + *

+     *                         V
+     *       z=-V  *           |           * z=V
+     *                         |
+     *                         |
+     *            -V --------------------- V
+     *                         |
+     *                         |
+     *       z=-V  *           |           * z=V
+     *                        -V
+     * 
+ * + * @param value the min/max value for each axis + * @param units the units in which the value is given in + * @return a Position grid + */ + private static Position[][] generateSlopeMesh(double value, Units units) { + return new Position[][]{ + {new Position(-value, -value, -value, units), new Position(-value, value, -value, units)}, + {new Position(value, -value, value, units), new Position(value, value, value, units)} + }; + } + @Test public void testNotEnoughPoints() { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage(MeshLeveler.ERROR_NOT_ENOUGH_SAMPLES); Position[][] grid = { - {new Position(MM)}, - {new Position(MM)} + {new Position(MM)}, + {new Position(MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -81,11 +107,11 @@ public void testNonRectangularGrid() { expectedEx.expectMessage(MeshLeveler.ERROR_MESH_SHAPE); Position[][] grid = { - {new Position(MM), new Position(MM)}, - {new Position(MM), new Position(MM), new Position(MM)} + {new Position(MM), new Position(MM)}, + {new Position(MM), new Position(MM), new Position(MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -94,11 +120,11 @@ public void testXAlignment() { expectedEx.expectMessage(MeshLeveler.ERROR_X_ALIGNMENT); Position[][] grid = { - {new Position(0,0,0, MM), new Position(1, 0, 0, MM)}, - {new Position(10,1,0, MM), new Position(1, 1, 0, MM)} + {new Position(0, 0, 0, MM), new Position(1, 0, 0, MM)}, + {new Position(10, 1, 0, MM), new Position(1, 1, 0, MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -107,11 +133,11 @@ public void testYAlignment() { expectedEx.expectMessage(MeshLeveler.ERROR_Y_ALIGNMENT); Position[][] grid = { - {new Position(0,0,0, MM), new Position(0, 0, 0, MM)}, - {new Position(1,0,0, MM), new Position(1, 1, 0, MM)} + {new Position(0, 0, 0, MM), new Position(0, 0, 0, MM)}, + {new Position(1, 0, 0, MM), new Position(1, 1, 0, MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -120,11 +146,11 @@ public void testXAscention() { expectedEx.expectMessage(MeshLeveler.ERROR_X_ASCENTION); Position[][] grid = { - {new Position(10,0,0, MM), new Position(10, 10, 0, MM)}, - {new Position(0,0,0, MM), new Position(0, 10, 0, MM)} + {new Position(10, 0, 0, MM), new Position(10, 10, 0, MM)}, + {new Position(0, 0, 0, MM), new Position(0, 10, 0, MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -133,11 +159,11 @@ public void testYAscention() { expectedEx.expectMessage(MeshLeveler.ERROR_Y_ASCENTION); Position[][] grid = { - {new Position(0,10,0, MM), new Position(0, 0, 0, MM)}, - {new Position(10,0,0, MM), new Position(10, 10, 0, MM)} + {new Position(0, 10, 0, MM), new Position(0, 0, 0, MM)}, + {new Position(10, 0, 0, MM), new Position(10, 10, 0, MM)} }; - new MeshLeveler(0.0, grid, Units.MM); + new MeshLeveler(0.0, grid); } @Test @@ -145,7 +171,7 @@ public void testUnexpectedArc() throws GcodeParserException { expectedEx.expect(GcodeParserException.class); expectedEx.expectMessage(MeshLeveler.ERROR_UNEXPECTED_ARC); - MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0, Units.MM); + MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0); GcodeState state = new GcodeState(); state.currentPoint = new Position(0, 0, 0, MM); @@ -156,7 +182,7 @@ public void testUnexpectedArc() throws GcodeParserException { @Test public void testMultipleCommandsWithLine() { - MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0, Units.MM); + MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0); GcodeState state = new GcodeState(); state.currentPoint = new Position(0, 0, 0, MM); @@ -166,8 +192,8 @@ public void testMultipleCommandsWithLine() { } @Test - public void testNoZChangesWithFlatMeshOnSurface() throws GcodeParserException { - MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0, Units.MM); + public void testNoZChangesWithFlatMeshOnSurface() { + MeshLeveler ml = new MeshLeveler(0.0, BIG_FLAT_GRID_Z0); GcodeState state = new GcodeState(); state.currentPoint = new Position(0, 0, 0, MM); @@ -177,8 +203,8 @@ public void testNoZChangesWithFlatMeshOnSurface() throws GcodeParserException { } @Test - public void testFlatMeshOnSurfaceOffSurface() throws GcodeParserException { - MeshLeveler ml = new MeshLeveler(1.0, BIG_FLAT_GRID_Z1, Units.MM); + public void testFlatMeshOnSurfaceOffSurface() { + MeshLeveler ml = new MeshLeveler(1.0, BIG_FLAT_GRID_Z1); GcodeState state = new GcodeState(); state.currentPoint = new Position(0, 0, 0, MM); @@ -188,9 +214,9 @@ public void testFlatMeshOnSurfaceOffSurface() throws GcodeParserException { } @Test - public void testNegativeOffset() throws GcodeParserException { + public void testNegativeOffset() { // The probe will be at 1.0 instead of 0.9 which means the end point needs to be raised 0.1 - MeshLeveler ml = new MeshLeveler(0.9, BIG_FLAT_GRID_Z1, Units.MM); + MeshLeveler ml = new MeshLeveler(0.9, BIG_FLAT_GRID_Z1); GcodeState state = new GcodeState(); state.currentPoint = new Position(0, 0, 0, MM); @@ -200,27 +226,166 @@ public void testNegativeOffset() throws GcodeParserException { } @Test - public void testUnevenSurface() throws GcodeParserException { - /* - 10 - z=-10 * | * z=10 - | - | - -10 --------------------- 10 - | - | - z=-10 * | * z=10 - -10 - */ + public void testProbedPointsInMMWithAbsoluteGcodeInInches() { + Position[][] grid = generateSlopeMesh(25.4, MM); + MeshLeveler ml = new MeshLeveler(0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G20; + state.currentPoint = new Position(-1, -1, 0, INCH); + state.inAbsoluteMode = true; + + // Moving along Y axis on flat line + sendCommandExpectResult(ml, state, "G1Y-1", "G1Y-1Z-1"); + sendCommandExpectResult(ml, state, "G1Y0", "G1Y0Z-1"); + sendCommandExpectResult(ml, state, "G1Y1", "G1Y1Z-1"); + + // Moving along X axis up slope + sendCommandExpectResult(ml, state, "G1X-0.5", "G1X-0.5Z-0.5"); + sendCommandExpectResult(ml, state, "G1X0", "G1X0Z0"); + sendCommandExpectResult(ml, state, "G1X0.5", "G1X0.5Z0.5"); + sendCommandExpectResult(ml, state, "G1X1", "G1X1Z1"); + + // Moving up slope along X/Y + sendCommandExpectResult(ml, state, "G1X-0.5Y-0.5", "G1X-0.5Y-0.5Z-0.5"); + sendCommandExpectResult(ml, state, "G1X0Y0", "G1X0Y0Z0"); + sendCommandExpectResult(ml, state, "G1X0.5Y0.5", "G1X0.5Y0.5Z0.5"); + sendCommandExpectResult(ml, state, "G1X1Y1", "G1X1Y1Z1"); + } + + @Test + public void testProbedPointsInInchesWithAbsoluteGcodeInMM() { + Position[][] grid = generateSlopeMesh(1, INCH); + MeshLeveler ml = new MeshLeveler(0.0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G21; + state.currentPoint = new Position(-25.4, -25.4, 0, MM); + state.inAbsoluteMode = true; + + // Moving along Y axis on flat line + sendCommandExpectResult(ml, state, "G1Y-25.4", "G1Y-25.4Z-25.4"); + sendCommandExpectResult(ml, state, "G1Y0", "G1Y0Z-25.4"); + sendCommandExpectResult(ml, state, "G1Y25.4", "G1Y25.4Z-25.4"); + + // Moving along X axis up slope + sendCommandExpectResult(ml, state, "G1X-12.7", "G1X-12.7Z-12.7"); + sendCommandExpectResult(ml, state, "G1X0", "G1X0Z0"); + sendCommandExpectResult(ml, state, "G1X12.7", "G1X12.7Z12.7"); + sendCommandExpectResult(ml, state, "G1X25.4", "G1X25.4Z25.4"); + + // Moving up slope along X/Y + sendCommandExpectResult(ml, state, "G1X-12.7Y-12.7", "G1X-12.7Y-12.7Z-12.7"); + sendCommandExpectResult(ml, state, "G1X0Y0", "G1X0Y0Z0"); + sendCommandExpectResult(ml, state, "G1X12.7Y12.7", "G1X12.7Y12.7Z12.7"); + sendCommandExpectResult(ml, state, "G1X25.4Y25.4", "G1X25.4Y25.4Z25.4"); + } + + @Test + public void testProbedPointsInMMWithRelativeGcodeInMM() { + Position[][] grid = generateSlopeMesh(10, MM); + MeshLeveler ml = new MeshLeveler(0.0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G21; + state.currentPoint = new Position( -10, -10, -10, MM); + state.inAbsoluteMode = false; + + // We are not updating the state, so all start positions are from the "currentPoint" + sendCommandExpectResult(ml, state, "G1Y10", "G1Y10Z0"); + sendCommandExpectResult(ml, state, "G1Y20", "G1Y20Z0"); + sendCommandExpectResult(ml, state, "G1X10Y10", "G1X10Y10Z10"); + sendCommandExpectResult(ml, state, "G1X20Y20", "G1X20Y20Z20"); + sendCommandExpectResult(ml, state, "G1X10", "G1X10Z10"); + sendCommandExpectResult(ml, state, "G1X20", "G1X20Z20"); + } + + @Test + public void testProbedPointsInMMWithRelativeGcodeInInches() { + Position[][] grid = generateSlopeMesh(25.4, MM); + MeshLeveler ml = new MeshLeveler(0.0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G20; + state.currentPoint = new Position(-1, -1, -1, INCH); + state.inAbsoluteMode = false; + + // We are not updating the state, so all start positions are from the "currentPoint" + sendCommandExpectResult(ml, state, "G1Y1", "G1Y1Z0"); + sendCommandExpectResult(ml, state, "G1Y2", "G1Y2Z0"); + sendCommandExpectResult(ml, state, "G1X1Y1", "G1X1Y1Z1"); + sendCommandExpectResult(ml, state, "G1X2Y2", "G1X2Y2Z2"); + sendCommandExpectResult(ml, state, "G1X1", "G1X1Z1"); + sendCommandExpectResult(ml, state, "G1X2", "G1X2Z2"); + } + + @Test + public void testProbedPointsInInchesWithRelativeGcodeInMM() { + Position[][] grid = generateSlopeMesh(1, INCH); + MeshLeveler ml = new MeshLeveler(0.0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G21; + state.currentPoint = new Position(-25.4, -25.4, 0, MM); + state.inAbsoluteMode = false; + + // We are not updating the state, so all start positions are from the "currentPoint" + sendCommandExpectResult(ml, state, "G1Y25.4", "G1Y25.4Z0"); + sendCommandExpectResult(ml, state, "G1Y50.8", "G1Y50.8Z0"); + sendCommandExpectResult(ml, state, "G1X25.4Y25.4", "G1X25.4Y25.4Z25.4"); + sendCommandExpectResult(ml, state, "G1X50.8Y50.8", "G1X50.8Y50.8Z50.8"); + sendCommandExpectResult(ml, state, "G1X25.4", "G1X25.4Z25.4"); + sendCommandExpectResult(ml, state, "G1X50.8", "G1X50.8Z50.8"); + } + + + private double toInches(int valueInMM) { + return valueInMM * UnitUtils.scaleUnits(MM, INCH); + } + + @Test + public void testUnevenSurfaceMillimeters() { + Position[][] grid = generateSlopeMesh(10, MM); + + MeshLeveler ml = new MeshLeveler(0.0, grid); + + GcodeState state = new GcodeState(); + state.units = Code.G21; + state.currentPoint = new Position(-10, -10, 0, MM); + state.inAbsoluteMode = true; + + // Moving along Y axis on flat line + sendCommandExpectResult(ml, state, "G1Y-5", "G1Y-5Z-10"); + sendCommandExpectResult(ml, state, "G1Y0", "G1Y0Z-10"); + sendCommandExpectResult(ml, state, "G1Y5", "G1Y5Z-10"); + sendCommandExpectResult(ml, state, "G1Y10", "G1Y10Z-10"); + + // Moving along X axis up slope + sendCommandExpectResult(ml, state, "G1X-5", "G1X-5Z-5"); + sendCommandExpectResult(ml, state, "G1X0", "G1X0Z0"); + sendCommandExpectResult(ml, state, "G1X5", "G1X5Z5"); + sendCommandExpectResult(ml, state, "G1X10", "G1X10Z10"); + + // Moving up slope along X/Y + sendCommandExpectResult(ml, state, "G1X-5Y-5", "G1X-5Y-5Z-5"); + sendCommandExpectResult(ml, state, "G1X0Y0", "G1X0Y0Z0"); + sendCommandExpectResult(ml, state, "G1X5Y5", "G1X5Y5Z5"); + sendCommandExpectResult(ml, state, "G1X10Y10", "G1X10Y10Z10"); + } + + + @Test + public void testUnevenSurfaceInches() { Position[][] grid = { - {new Position(-10,-10,-10, MM), new Position(-10, 10, -10, MM)}, - {new Position(10,-10,10, MM), new Position(10, 10, 10, MM)} + {new Position(-10, -10, -10, INCH), new Position(-10, 10, -10, INCH)}, + {new Position(10, -10, 10, INCH), new Position(10, 10, 10, INCH)} }; - MeshLeveler ml = new MeshLeveler(0.0, grid, Units.MM); + MeshLeveler ml = new MeshLeveler(0.0, grid); GcodeState state = new GcodeState(); - state.currentPoint = new Position(-10, -10, 0, MM); + state.units = Code.G20; + state.currentPoint = new Position(-10, -10, 0, INCH); state.inAbsoluteMode = true; // Moving along Y axis on flat line diff --git a/ugs-core/test/com/willwinder/universalgcodesender/model/PositionTest.java b/ugs-core/test/com/willwinder/universalgcodesender/model/PositionTest.java index 90758f8d7..afbe61fe4 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/model/PositionTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/model/PositionTest.java @@ -19,60 +19,125 @@ This file is part of Universal Gcode Sender (UGS). package com.willwinder.universalgcodesender.model; import com.willwinder.universalgcodesender.model.UnitUtils.Units; -import java.time.Instant; import org.assertj.core.api.Assertions; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; /** - * * @author wwinder * @author Joacim Breiler */ public class PositionTest { - - /** - * Test of equals method, of class Position. - */ - @Test - public void testEquals_Object() { - System.out.println("equals"); - Position instance = new Position(1, 2, 3, Units.MM); - - Assertions.assertThat(instance.equals(new Position(1, 2, 3, Units.MM))) - .isTrue(); - Assertions.assertThat(instance.equals(new Position(1, 2, 3, Units.UNKNOWN))) - .isFalse(); - - Assertions.assertThat(instance.equals((Object) new Position(1, 2, 3, Units.MM))) - .isTrue(); - Assertions.assertThat(instance.equals((Object) new Position(1, 2, 3, Units.UNKNOWN))) - .isFalse(); - } - - /** - * Test of isSamePositionIgnoreUnits method, of class Position. - */ - @Test - public void testIsSamePositionIgnoreUnits() { - System.out.println("isSamePositionIgnoreUnits"); - Position instance = new Position(1, 2, 3, Units.MM); - - Assertions.assertThat(instance.isSamePositionIgnoreUnits(new Position(1, 2, 3, Units.MM))) - .isTrue(); - Assertions.assertThat(instance.isSamePositionIgnoreUnits(new Position(1, 2, 3, Units.UNKNOWN))) - .isTrue(); - } - - @Test - public void getPositionInUnitsShouldConvert() { - Position position = new Position(1.0, 1.0, 1.0, UnitUtils.Units.INCH); - - Position positionInMM = position.getPositionIn(UnitUtils.Units.MM); - assertEquals(Double.valueOf(25.4), Double.valueOf(positionInMM.get(Axis.X))); - - Position positionInInches = positionInMM.getPositionIn(UnitUtils.Units.INCH); - assertEquals(1, Math.round(positionInInches.get(Axis.X))); - } + + /** + * Test of equals method, of class Position. + */ + @Test + public void testEquals_Object() { + Position instance = new Position(1, 2, 3, Units.MM); + + Assertions.assertThat(instance.equals(new Position(1, 2, 3, Units.MM))) + .isTrue(); + Assertions.assertThat(instance.equals(new Position(1, 2, 3, Units.UNKNOWN))) + .isFalse(); + + Assertions.assertThat(instance.equals((Object) new Position(1, 2, 3, Units.MM))) + .isTrue(); + Assertions.assertThat(instance.equals((Object) new Position(1, 2, 3, Units.UNKNOWN))) + .isFalse(); + } + + /** + * Test of isSamePositionIgnoreUnits method, of class Position. + */ + @Test + public void testIsSamePositionIgnoreUnits() { + Position instance = new Position(1, 2, 3, Units.MM); + + Assertions.assertThat(instance.isSamePositionIgnoreUnits(new Position(1, 2, 3, Units.MM))) + .isTrue(); + Assertions.assertThat(instance.isSamePositionIgnoreUnits(new Position(1, 2, 3, Units.UNKNOWN))) + .isTrue(); + } + + @Test + public void getPositionInUnitsShouldConvert() { + Position position = new Position(1.0, 1.0, 1.0, UnitUtils.Units.INCH); + + Position positionInMM = position.getPositionIn(UnitUtils.Units.MM); + assertEquals(Double.valueOf(25.4), Double.valueOf(positionInMM.get(Axis.X))); + + Position positionInInches = positionInMM.getPositionIn(UnitUtils.Units.INCH); + assertEquals(1, Math.round(positionInInches.get(Axis.X))); + } + + @Test + public void addShouldAddXYZPosition() { + Position position1 = new Position(1, 2, 3, Units.MM); + Position position2 = new Position(3, 2, 1, Units.MM); + + Position result = position1.add(position2); + assertEquals(4, result.getX(), 0.1); + assertEquals(4, result.getY(), 0.1); + assertEquals(4, result.getZ(), 0.1); + assertEquals(Double.NaN, result.getA(), 0.1); + assertEquals(Double.NaN, result.getB(), 0.1); + assertEquals(Double.NaN, result.getC(), 0.1); + assertEquals(Units.MM, result.getUnits()); + } + + @Test + public void addShouldAddXYZABCPosition() { + Position position1 = new Position(1, 2, 3, 4, 5, 6, Units.MM); + Position position2 = new Position(6, 5, 4, 3, 2, 1, Units.MM); + + Position result = position1.add(position2); + assertEquals(7, result.getX(), 0.1); + assertEquals(7, result.getY(), 0.1); + assertEquals(7, result.getZ(), 0.1); + assertEquals(7, result.getA(), 0.1); + assertEquals(7, result.getB(), 0.1); + assertEquals(7, result.getC(), 0.1); + assertEquals(Units.MM, result.getUnits()); + } + + @Test + public void addShouldAddXYZABCPositionInDifferetUnits() { + Position position1 = new Position(1, 2, 3, 4, 5, 6, Units.MM); + Position position2 = new Position(6, 5, 4, 3, 2, 1, Units.INCH); + + Position result = position1.add(position2); + assertEquals(153.4, result.getX(), 0.1); + assertEquals(129, result.getY(), 0.1); + assertEquals(104.6, result.getZ(), 0.1); + assertEquals("Rotational axes should not be converted to inches", 7, result.getA(), 0.1); + assertEquals("Rotational axes should not be converted to inches", 7, result.getB(), 0.1); + assertEquals("Rotational axes should not be converted to inches", 7, result.getC(), 0.1); + assertEquals(Units.MM, result.getUnits()); + } + + @Test + public void addShouldCreateNewInstance() { + Position position1 = new Position(1, 2, 3, 4, 5, 6, Units.MM); + Position position2 = new Position(6, 5, 4, 3, 2, 1, Units.MM); + + Position result = position1.add(position2); + assertNotEquals(position1.getX(), result.getX(), 0.1); + assertNotEquals(position1.getY(), result.getY(), 0.1); + assertNotEquals(position1.getZ(), result.getZ(), 0.1); + assertNotEquals(position1.getA(), result.getA(), 0.1); + assertNotEquals(position1.getB(), result.getB(), 0.1); + assertNotEquals(position1.getC(), result.getC(), 0.1); + + assertNotEquals(position2.getX(), result.getX(), 0.1); + assertNotEquals(position2.getY(), result.getY(), 0.1); + assertNotEquals(position2.getZ(), result.getZ(), 0.1); + assertNotEquals(position2.getA(), result.getA(), 0.1); + assertNotEquals(position2.getB(), result.getB(), 0.1); + assertNotEquals(position2.getC(), result.getC(), 0.1); + + assertEquals(Units.MM, result.getUnits()); + } } diff --git a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelPreview.java b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelPreview.java index 08da392a7..5e81511c9 100644 --- a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelPreview.java +++ b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelPreview.java @@ -25,14 +25,17 @@ This file is part of Universal Gcode Sender (UGS). import com.jogamp.opengl.util.gl2.GLUT; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; import com.willwinder.ugs.nbm.visualizer.shared.Renderable; +import com.willwinder.universalgcodesender.model.CNCPoint; import com.willwinder.universalgcodesender.model.Position; import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.model.UnitUtils.Units; +import java.util.Arrays; +import java.util.stream.DoubleStream; + import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_AUTOLEVEL_PREVIEW; /** - * * @author wwinder */ public class AutoLevelPreview extends Renderable { @@ -42,11 +45,12 @@ public class AutoLevelPreview extends Renderable { private Position[][] grid = null; // The maximum distance of a probe used for coloring. - private double maxZ, minZ; + private double maxZ; + private double minZ; + + private float[] high = {0, 255, 0}; // green + private float[] low = {255, 0, 0}; // red - private float high[] = {0, 255, 0}; // green - private float low[] = {255, 0, 0}; // red - public AutoLevelPreview(String title) { super(10, title); @@ -77,18 +81,10 @@ public final void reloadPreferences(VisualizerOptions vo) { public void updateSettings( ImmutableCollection positions, - final Position[][] grid, - Position max, - Position min) { + final Position[][] grid) { if (positions != null && !positions.isEmpty()) { this.positions = positions; this.grid = grid; - if (max != null) { - this.maxZ = max.z; - } - if (min != null) { - this.minZ = min.z; - } } } @@ -105,61 +101,62 @@ public void draw(GLAutoDrawable drawable, boolean idle, Position machineCoord, P double objectX = objectMax.x - objectMin.x; double objectY = objectMax.y - objectMin.y; - double diameter = Math.max(objectX*0.005, objectY*0.005); - - double minx, miny, minz; - double maxx, maxy, maxz; + double diameter = Math.max(objectX * 0.005, objectY * 0.005); + + double minx, miny; + double maxx, maxy; + double minz, maxz; minx = maxx = first.x; miny = maxy = first.y; minz = maxz = first.z; - + GL2 gl = drawable.getGL().getGL2(); gl.glPushMatrix(); - // Scale inch to mm if needed - double scale = UnitUtils.scaleUnits(unit, Units.MM); - if (unit != Units.MM) { - gl.glScaled(scale, scale, scale); - } - - // Balls indicating the probe start locations. - gl.glColor4fv(new float[]{0.1f, 0.1f, 0.1f, 1.0f}, 0); - for (Position p : positions) { - gl.glPushMatrix(); - gl.glTranslated(p.x, p.y, p.z); - glut.glutSolidSphere(diameter/scale, 7, 7); - - // update min/max - minx = Math.min(minx, p.x); - maxx = Math.max(maxx, p.x); - miny = Math.min(miny, p.y); - maxz = Math.max(maxz, p.z); - minz = Math.min(minz, p.z); - maxy = Math.max(maxy, p.y); - gl.glPopMatrix(); - } + // Scale inch to mm if needed + double scale = UnitUtils.scaleUnits(unit, Units.MM); + if (unit != Units.MM) { + gl.glScaled(scale, scale, scale); + } - // Outline of probe area + // Balls indicating the probe start locations. + gl.glColor4fv(new float[]{0.1f, 0.1f, 0.1f, 1.0f}, 0); + for (Position p : positions) { gl.glPushMatrix(); - gl.glTranslated( - (minx+maxx)/2, - (miny+maxy)/2, - (minz+maxz)/2); - gl.glScaled(maxx-minx, maxy-miny, maxz-minz); - gl.glColor4fv(new float[]{0.3f, 0, 0, 0.1f}, 0); - glut.glutWireCube((float) 1.); + gl.glTranslated(p.x, p.y, p.z); + glut.glutSolidSphere(diameter / scale, 7, 7); + + // update min/max + minx = Math.min(minx, p.x); + maxx = Math.max(maxx, p.x); + miny = Math.min(miny, p.y); + maxz = Math.max(maxz, p.z); + minz = Math.min(minz, p.z); + maxy = Math.max(maxy, p.y); gl.glPopMatrix(); + } - drawProbedSurface(gl); + // Outline of probe area + gl.glPushMatrix(); + gl.glTranslated( + (minx + maxx) / 2, + (miny + maxy) / 2, + (minz + maxz) / 2); + gl.glScaled(maxx - minx, maxy - miny, maxz - minz); + gl.glColor4fv(new float[]{0.3f, 0, 0, 0.1f}, 0); + glut.glutWireCube((float) 1.); + gl.glPopMatrix(); + + drawProbedSurface(gl); gl.glPopMatrix(); } private void setColorForZ(GL2 gl, double zPos) { float ratio = (float) ((zPos - minZ) / (maxZ - minZ)); - float r = ratio * high[0] + (1-ratio) * low[0]; - float g = ratio * high[1] + (1-ratio) * low[1]; - float b = ratio * high[2] + (1-ratio) * low[2]; - float a = ratio * high[3] + (1-ratio) * low[3]; + float r = ratio * high[0] + (1 - ratio) * low[0]; + float g = ratio * high[1] + (1 - ratio) * low[1]; + float b = ratio * high[2] + (1 - ratio) * low[2]; + float a = ratio * high[3] + (1 - ratio) * low[3]; gl.glColor4f(r, g, b, a); } @@ -169,6 +166,7 @@ private void drawProbedSurface(GL2 gl) { return; } + updateMinMaxZ(); /* 0,5 ? ? ? ? ? 5,5 @@ -182,40 +180,40 @@ private void drawProbedSurface(GL2 gl) { */ - gl.glBegin(GL2.GL_TRIANGLES); + gl.glBegin(GL2.GL_TRIANGLES); int xLen = this.grid.length; - for(int x = 0; x < xLen - 1; x++) { - if (this.grid[x] == null || this.grid[x+1] == null) { + for (int x = 0; x < xLen - 1; x++) { + if (this.grid[x] == null || this.grid[x + 1] == null) { continue; } for (int y = 0; y < this.grid[x].length - 1; y++) { Position pos1 = this.grid[x][y]; - Position pos2 = this.grid[x+1][y]; - Position pos3 = this.grid[x][y+1]; - Position pos4 = this.grid[x+1][y+1]; + Position pos2 = this.grid[x + 1][y]; + Position pos3 = this.grid[x][y + 1]; + Position pos4 = this.grid[x + 1][y + 1]; // Bottom left of quad if (pos1 != null && pos2 != null && pos3 != null) { setColorForZ(gl, pos1.z); - gl.glVertex3d( pos1.x, pos1.y, pos1.z ); // Left Of Triangle (Front) + gl.glVertex3d(pos1.x, pos1.y, pos1.z); // Left Of Triangle (Front) setColorForZ(gl, pos3.z); - gl.glVertex3d( pos3.x, pos3.y, pos3.z ); // Top Of Triangle (Front) + gl.glVertex3d(pos3.x, pos3.y, pos3.z); // Top Of Triangle (Front) setColorForZ(gl, pos2.z); - gl.glVertex3d( pos2.x, pos2.y, pos2.z ); // Right Of Triangle (Front) + gl.glVertex3d(pos2.x, pos2.y, pos2.z); // Right Of Triangle (Front) } // Top right of quad if (pos2 != null && pos3 != null && pos4 != null) { setColorForZ(gl, pos4.z); - gl.glVertex3d( pos4.x, pos4.y, pos4.z ); // Right Of Triangle (Front) - + gl.glVertex3d(pos4.x, pos4.y, pos4.z); // Right Of Triangle (Front) + setColorForZ(gl, pos3.z); - gl.glVertex3d( pos3.x, pos3.y, pos3.z ); // Top Of Triangle (Front) - + gl.glVertex3d(pos3.x, pos3.y, pos3.z); // Top Of Triangle (Front) + setColorForZ(gl, pos2.z); - gl.glVertex3d( pos2.x, pos2.y, pos2.z ); // Left Of Triangle (Front) + gl.glVertex3d(pos2.x, pos2.y, pos2.z); // Left Of Triangle (Front) } } } @@ -223,6 +221,23 @@ private void drawProbedSurface(GL2 gl) { gl.glEnd(); } + private void updateMinMaxZ() { + // Figure out the min/max scanned points + minZ = getGridZStream() + .min() + .orElse(0); + + maxZ = getGridZStream() + .max() + .orElse(0); + } + + private DoubleStream getGridZStream() { + return Arrays.stream(grid).flatMap(Arrays::stream) + .mapToDouble(CNCPoint::getZ) + .filter(z -> !Double.isNaN(z)); + } + @Override public boolean isEnabled() { return VisualizerOptions.getBooleanOption(VISUALIZER_OPTION_AUTOLEVEL_PREVIEW, true); diff --git a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.form b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.form deleted file mode 100644 index 047253698..000000000 --- a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.form +++ /dev/null @@ -1,449 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.java b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.java index f95c6ec5c..ed78c5c8b 100644 --- a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.java +++ b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/AutoLevelerTopComponent.java @@ -37,7 +37,6 @@ This file is part of Universal Gcode Sender (UGS). import com.willwinder.universalgcodesender.model.BackendAPI; import com.willwinder.universalgcodesender.model.Position; import com.willwinder.universalgcodesender.model.UGSEvent; -import com.willwinder.universalgcodesender.model.UnitUtils.Units; import com.willwinder.universalgcodesender.model.events.FileState; import com.willwinder.universalgcodesender.model.events.FileStateEvent; import com.willwinder.universalgcodesender.model.events.ProbeEvent; @@ -68,11 +67,7 @@ This file is part of Universal Gcode Sender (UGS). /** * Top component which displays something. */ -@TopComponent.Description( - preferredID = "AutoLevelerTopComponent", - //iconBase="SET/PATH/TO/ICON/HERE", - persistenceType = TopComponent.PERSISTENCE_ALWAYS -) +@TopComponent.Description(preferredID = "AutoLevelerTopComponent") @TopComponent.Registration(mode = Mode.OUTPUT, openAtStartup = false) @ActionID(category = AutoLevelerTopComponent.AutoLevelerCategory, id = AutoLevelerTopComponent.AutoLevelerActionId) @ActionReference(path = LocalizingService.MENU_WINDOW_PLUGIN) @@ -126,15 +121,11 @@ public AutoLevelerTopComponent() { zMin.addChangeListener(cl); zMax.addChangeListener(cl); zSurface.addChangeListener(cl); - unitInch.addItemListener(this); - unitMM.addItemListener(this); // Localize... this.useLoadedFile.setText(Localization.getString("autoleveler.panel.use-file")); this.scanSurfaceButton.setText(Localization.getString("autoleveler.panel.scan-surface")); this.applyToGcode.setText(Localization.getString("autoleveler.panel.apply")); - this.unitMM.setText("mm"); - this.unitInch.setText("inch"); this.minLabel.setText(Localization.getString("autoleveler.panel.min")); this.maxLabel.setText(Localization.getString("autoleveler.panel.max")); this.xLabel.setText(Localization.getString("machineStatus.pin.x") + ':'); @@ -152,13 +143,6 @@ public AutoLevelerTopComponent() { private void updateSettings() { this.bulkChanges = true; - // Only set units radio button the first time. - if (!this.unitInch.isSelected() && !this.unitMM.isSelected()) { - Units u = settings.getPreferredUnits(); - this.unitInch.setSelected(u == Units.INCH); - this.unitMM.setSelected(u == Units.MM); - } - boolean isSettingChange = false; AutoLevelSettings als = settings.getAutoLevelSettings(); if (getValue(this.stepResolution) != als.stepResolution) { @@ -225,7 +209,6 @@ private void updateScanner() { } } - Units units = this.unitMM.isSelected() ? Units.MM : Units.INCH;; Settings.AutoLevelSettings autoLevelerSettings = this.settings.getAutoLevelSettings(); autoLevelerSettings.stepResolution = getValue(this.stepResolution); autoLevelerSettings.zRetract = getValue(this.zRetract); @@ -234,17 +217,16 @@ private void updateScanner() { double xOff = autoLevelerSettings.autoLevelProbeOffset.x; double yOff = autoLevelerSettings.autoLevelProbeOffset.y; - Position corner1 = new Position(getValue(xMin) + xOff, getValue(yMin) + yOff, getValue(zMin), units); - Position corner2 = new Position(getValue(xMax) + xOff, getValue(yMax) + yOff, getValue(zMax), units); + Position corner1 = new Position(getValue(xMin) + xOff, getValue(yMin) + yOff, getValue(zMin), backend.getSettings().getPreferredUnits()); + Position corner2 = new Position(getValue(xMax) + xOff, getValue(yMax) + yOff, getValue(zMax), backend.getSettings().getPreferredUnits()); - scanner.update(corner1, corner2, units); + scanner.update(corner1, corner2); if (autoLevelPreview != null) { autoLevelPreview.updateSettings( scanner.getProbeStartPositions(), - scanner.getProbePositionGrid(), - scanner.getMaxXYZ(), - scanner.getMinXYZ()); + scanner.getProbePositionGrid() + ); } updateMeshLeveler(); } @@ -306,8 +288,7 @@ private void updateMeshLeveler() { // Step 3: Adjust Z heights codes based on mesh offsets. commandProcessors.add( new MeshLeveler(getValue(this.zSurface), - scanner.getProbePositionGrid(), - scanner.getUnits())); + scanner.getProbePositionGrid())); activeCommandProcessors = commandProcessors.build(); for(CommandProcessor p : activeCommandProcessors) { @@ -327,10 +308,8 @@ private void updateMeshLeveler() { * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The * content of this method is always regenerated by the Form Editor. */ - // //GEN-BEGIN:initComponents private void initComponents() { - unitGroup = new javax.swing.ButtonGroup(); jPanel1 = new javax.swing.JPanel(); minLabel = new javax.swing.JLabel(); xLabel = new javax.swing.JLabel(); @@ -357,8 +336,6 @@ private void initComponents() { zRetract = new javax.swing.JSpinner(); jPanel3 = new javax.swing.JPanel(); scanSurfaceButton = new javax.swing.JButton(); - unitMM = new javax.swing.JRadioButton(); - unitInch = new javax.swing.JRadioButton(); applyToGcode = new javax.swing.JCheckBox(); activeLabel = new javax.swing.JLabel(); @@ -386,18 +363,10 @@ private void initComponents() { visibleAutoLeveler.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(visibleAutoLeveler, "Visible AutoLeveler"); - visibleAutoLeveler.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - visibleAutoLevelerActionPerformed(evt); - } - }); + visibleAutoLeveler.addActionListener(this::visibleAutoLevelerActionPerformed); org.openide.awt.Mnemonics.setLocalizedText(useLoadedFile, "Use Loaded File"); - useLoadedFile.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - useLoadedFileActionPerformed(evt); - } - }); + useLoadedFile.addActionListener(this::useLoadedFileActionPerformed); javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); @@ -483,25 +452,13 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { zSurface.setModel(new javax.swing.SpinnerNumberModel(0.0d, null, null, 1.0d)); org.openide.awt.Mnemonics.setLocalizedText(dataViewer, "View Data"); - dataViewer.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - dataViewerActionPerformed(evt); - } - }); + dataViewer.addActionListener(this::dataViewerActionPerformed); org.openide.awt.Mnemonics.setLocalizedText(settingsButton, "Settings"); - settingsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - settingsButtonActionPerformed(evt); - } - }); + settingsButton.addActionListener(this::settingsButtonActionPerformed); org.openide.awt.Mnemonics.setLocalizedText(generateTestDataButton, "Generate Test Data"); - generateTestDataButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - generateTestDataButtonActionPerformed(evt); - } - }); + generateTestDataButton.addActionListener(this::generateTestDataButtonActionPerformed); org.openide.awt.Mnemonics.setLocalizedText(zRetractLabel, "Z Resolution:"); @@ -560,27 +517,12 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { ); org.openide.awt.Mnemonics.setLocalizedText(scanSurfaceButton, "Scan Surface"); - scanSurfaceButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - scanSurfaceButtonActionPerformed(evt); - } - }); - - unitGroup.add(unitMM); - unitMM.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(unitMM, "mm"); - - unitGroup.add(unitInch); - org.openide.awt.Mnemonics.setLocalizedText(unitInch, "inch"); + scanSurfaceButton.addActionListener(this::scanSurfaceButtonActionPerformed); applyToGcode.setSelected(true); applyToGcode.setActionCommand("applyToGcode"); applyToGcode.setLabel("Apply to Gcode"); - applyToGcode.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - applyToGcodeActionPerformed(evt); - } - }); + applyToGcode.addActionListener(this::applyToGcodeActionPerformed); org.openide.awt.Mnemonics.setLocalizedText(activeLabel, "INACTIVE"); @@ -593,11 +535,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(scanSurfaceButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(applyToGcode, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(jPanel3Layout.createSequentialGroup() - .addComponent(unitMM) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(unitInch) - .addGap(0, 0, Short.MAX_VALUE)) .addComponent(activeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); @@ -605,9 +542,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() .addContainerGap() - .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(unitMM) - .addComponent(unitInch)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(scanSurfaceButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -640,14 +574,14 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); - }// //GEN-END:initComponents + } - private void scanSurfaceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scanSurfaceButtonActionPerformed + private void scanSurfaceButtonActionPerformed(java.awt.event.ActionEvent evt) { updateScanner(); scanner.scan(); - }//GEN-LAST:event_scanSurfaceButtonActionPerformed + } - private void dataViewerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataViewerActionPerformed + private void dataViewerActionPerformed(java.awt.event.ActionEvent evt) { List> probeData = new ArrayList<>(); // Collect data from grid. @@ -668,22 +602,20 @@ private void dataViewerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI JTextArea ta = new JTextArea(15, 30); ta.setText(GSON.toJson(probeData)); JOptionPane.showMessageDialog(null, new JScrollPane(ta)); - }//GEN-LAST:event_dataViewerActionPerformed + } - private void settingsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsButtonActionPerformed + private void settingsButtonActionPerformed(java.awt.event.ActionEvent evt) { OptionsDisplayer.getDefault().open("UGS/autoleveler"); - }//GEN-LAST:event_settingsButtonActionPerformed + } - private void useLoadedFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useLoadedFileActionPerformed + private void useLoadedFileActionPerformed(java.awt.event.ActionEvent evt) { if (backend.getProcessedGcodeFile() == null) { return; } FileStats fs = backend.getSettings().getFileStats(); - - Units u = this.unitMM.isSelected() ? Units.MM : Units.INCH; - Position min = fs.minCoordinate.getPositionIn(u); - Position max = fs.maxCoordinate.getPositionIn(u); + Position min = fs.minCoordinate.getPositionIn(backend.getSettings().getPreferredUnits()); + Position max = fs.maxCoordinate.getPositionIn(backend.getSettings().getPreferredUnits()); this.xMin.setValue(min.x); this.yMin.setValue(min.y); @@ -692,23 +624,22 @@ private void useLoadedFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN this.xMax.setValue(max.x); this.yMax.setValue(max.y); this.zMax.setValue(max.z); - }//GEN-LAST:event_useLoadedFileActionPerformed + } - private void generateTestDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_generateTestDataButtonActionPerformed + private void generateTestDataButtonActionPerformed(java.awt.event.ActionEvent evt) { updateScanner(); scanner.scanRandomData(); updateMeshLeveler(); - }//GEN-LAST:event_generateTestDataButtonActionPerformed + } - private void visibleAutoLevelerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_visibleAutoLevelerActionPerformed + private void visibleAutoLevelerActionPerformed(java.awt.event.ActionEvent evt) { autoLevelPreview.setEnabled(visibleAutoLeveler.isSelected()); - }//GEN-LAST:event_visibleAutoLevelerActionPerformed + } - private void applyToGcodeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyToGcodeActionPerformed + private void applyToGcodeActionPerformed(java.awt.event.ActionEvent evt) { updateMeshLeveler(); - }//GEN-LAST:event_applyToGcodeActionPerformed + } - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel activeLabel; private javax.swing.JCheckBox applyToGcode; private javax.swing.JButton dataViewer; @@ -722,9 +653,6 @@ private void applyToGcodeActionPerformed(java.awt.event.ActionEvent evt) {//GEN- private javax.swing.JButton scanSurfaceButton; private javax.swing.JButton settingsButton; private javax.swing.JSpinner stepResolution; - private javax.swing.ButtonGroup unitGroup; - private javax.swing.JRadioButton unitInch; - private javax.swing.JRadioButton unitMM; private javax.swing.JButton useLoadedFile; private javax.swing.JCheckBox visibleAutoLeveler; private javax.swing.JLabel xLabel; @@ -740,7 +668,6 @@ private void applyToGcodeActionPerformed(java.awt.event.ActionEvent evt) {//GEN- private javax.swing.JLabel zRetractLabel; private javax.swing.JSpinner zSurface; private javax.swing.JLabel zSurfaceLabel; - // End of variables declaration//GEN-END:variables @Override public void componentOpened() { diff --git a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/SurfaceScanner.java b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/SurfaceScanner.java index f1df3a336..f9f9697ce 100644 --- a/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/SurfaceScanner.java +++ b/ugs-platform/ugs-platform-surfacescanner/src/main/java/com/willwinder/ugs/platform/surfacescanner/SurfaceScanner.java @@ -1,5 +1,5 @@ /* - Copyright 2017 Will Winder + Copyright 2017-2023 Will Winder This file is part of Universal Gcode Sender (UGS). @@ -23,6 +23,7 @@ This file is part of Universal Gcode Sender (UGS). import com.willwinder.universalgcodesender.model.BackendAPI; import com.willwinder.universalgcodesender.model.PartialPosition; import com.willwinder.universalgcodesender.model.Position; +import com.willwinder.universalgcodesender.model.UnitUtils; import com.willwinder.universalgcodesender.model.UnitUtils.Units; import com.willwinder.universalgcodesender.model.events.ProbeEvent; import com.willwinder.universalgcodesender.utils.Settings; @@ -46,8 +47,6 @@ public class SurfaceScanner { private final Settings.AutoLevelSettings settings; private Position[][] probePositionGrid = new Position[0][0]; private Deque pendingPositions = new LinkedList<>(); - - private Units units = Units.UNKNOWN; private Position minXYZ = Position.ZERO; private Position maxXYZ = Position.ZERO; private Position machineWorkOffset = new Position(Units.MM); @@ -60,11 +59,10 @@ public SurfaceScanner(BackendAPI backend) { /** * Provides two points of the scanners bounding box and the number of points to sample in the X/Y directions. */ - public void update(final Position corner1, final Position corner2, Units units) { + public void update(final Position corner1, final Position corner2) { if (corner1.getUnits() != corner2.getUnits()) { throw new IllegalArgumentException("Provide same unit for both measures."); } - this.units = units; double xMin = Math.min(corner1.x, corner2.x); double xMax = Math.max(corner1.x, corner2.x); @@ -73,8 +71,8 @@ public void update(final Position corner1, final Position corner2, Units units) double zMin = Math.min(corner1.z, corner2.z); double zMax = Math.max(corner1.z, corner2.z); - Position newMin = new Position(xMin, yMin, zMin, units); - Position newMax = new Position(xMax, yMax, zMax, units); + Position newMin = new Position(xMin, yMin, zMin, corner1.getUnits()); + Position newMax = new Position(xMax, yMax, zMax, corner1.getUnits()); if (newMin.getX() == newMax.getX() || newMin.getY() == newMax.getY() || newMin.getZ() == newMax.getZ()) { // If we're 0 in any dimension there is nothing we can do yet. @@ -96,7 +94,7 @@ public void update(final Position corner1, final Position corner2, Units units) xMin + Math.min(xMax - xMin, x * resolution), yMin + Math.min(yMax - yMin, y * resolution), Double.NaN, - units); + corner1.getUnits()); probePositionGrid[x][y] = p; } } @@ -127,27 +125,31 @@ public void handleEvent(ProbeEvent evt) { } if (probeMachinePosition.getUnits() == Units.UNKNOWN) { - System.out.println("Unknown units in autoleveler receiving probe. Assuming " + this.units); + logger.warning("Unknown units in autoleveler receiving probe. Assuming " + getPreferredUnits()); } - probeMachinePosition = probeMachinePosition.getPositionIn(units); - - Position probePosition = new Position( - probeMachinePosition.getX() + machineWorkOffset.x, - probeMachinePosition.getY() + machineWorkOffset.y, - probeMachinePosition.getZ() + machineWorkOffset.z, - probeMachinePosition.getUnits()); + probeMachinePosition = probeMachinePosition.getPositionIn(getPreferredUnits()); + Position probePosition = probeMachinePosition.add(machineWorkOffset); logger.log(Level.INFO, "Record ({0}, {1}, {2})", new Object[] {probePosition.getX(), probePosition.getY(), probePosition.getZ()}); probeEvent(probePosition); - if (!pendingPositions.isEmpty()) { - probeNextPoint(probePosition.getZ()); + try { + double retractedZ = retract(probePosition.getZ()); + if (!pendingPositions.isEmpty()) { + probeNextPoint(retractedZ); + } + } catch (Exception e) { + throw new RuntimeException(e); } } + private Units getPreferredUnits() { + return this.backend.getSettings().getPreferredUnits(); + } + private void reset() { - update(minXYZ, maxXYZ, units); + update(minXYZ, maxXYZ); } public void probeEvent(final Position p) { @@ -156,7 +158,7 @@ public void probeEvent(final Position p) { reset(); throw new RuntimeException(String.format("Unexpected probe location, expected %s to be %s", p, probePosition)); } - Position settingsOffset = settings.autoLevelProbeOffset.getPositionIn(units); + Position settingsOffset = settings.autoLevelProbeOffset.getPositionIn(getPreferredUnits()); probePosition.setX(probePosition.getX() + settingsOffset.getX()); probePosition.setY(probePosition.getY() + settingsOffset.getY()); @@ -175,42 +177,53 @@ public void scan() { probeNextPoint(work.getZ()); } - private void probeNextPoint(Double zLast) { + private void probeNextPoint(Double zBackoff) { try { Position p = this.pendingPositions.peek(); - double zRetract = settings.zRetract; - if (zRetract <= 0) { - zRetract = maxXYZ.getZ() - minXYZ.getZ(); - } - - // Start by backing off the current position - double zBackoff = Math.min(zLast + zRetract, maxXYZ.getZ()); - PartialPosition safeZ = PartialPosition.builder(maxXYZ.getUnits()).setZ(zBackoff).build(); - String cmd = GcodeUtils.generateMoveCommand( - "G90G0", - settings.probeScanFeedRate, - safeZ); - logger.log(Level.INFO, "MoveTo {0} {1}", new Object[] {safeZ, cmd}); - backend.sendGcodeCommand(true, cmd); - // Position over next probe position PartialPosition startPos = PartialPosition.builder(p).clearZ().build(); + String cmd = GcodeUtils.generateMoveCommand( + "G90G0", getProbeScanFeedRate(), startPos); logger.log(Level.INFO, "MoveTo {0} {1}", new Object[] {startPos, cmd}); - cmd = GcodeUtils.generateMoveCommand( - "G90G0", settings.probeScanFeedRate, startPos); backend.sendGcodeCommand(true, cmd); // Send probe command, probing down to zMin double probeDistance = minXYZ.getZ() - zBackoff; logger.log(Level.INFO, "Probe {0}", probeDistance); - backend.probe("Z", settings.probeSpeed, probeDistance, units); + backend.probe("Z", getProbeSpeed(), probeDistance, getPreferredUnits()); } catch (Exception e) { reset(); throw new RuntimeException(e); } } + private double getProbeSpeed() { + return settings.probeSpeed * UnitUtils.scaleUnits(Units.MM, getPreferredUnits()); + } + + private double getProbeScanFeedRate() { + return settings.probeScanFeedRate * UnitUtils.scaleUnits(Units.MM, getPreferredUnits()); + } + + private double retract(Double zLast) throws Exception { + double zRetract = settings.zRetract; + if (zRetract <= 0) { + zRetract = maxXYZ.getZ() - minXYZ.getZ(); + } + + // Start by backing off the current position + double zBackoff = Math.min(zLast + zRetract, maxXYZ.getZ()); + PartialPosition safeZ = PartialPosition.builder(maxXYZ.getUnits()).setZ(zBackoff).build(); + String retractCommand = GcodeUtils.generateMoveCommand( + "G90G0", + getProbeScanFeedRate(), + safeZ); + logger.log(Level.INFO, "MoveTo {0} {1}", new Object[] {safeZ, retractCommand}); + backend.sendGcodeCommand(true, retractCommand); + return zBackoff; + } + public void scanRandomData(){ machineWorkOffset.x = 0; machineWorkOffset.y = 0; @@ -241,18 +254,6 @@ public final Position[][] getProbePositionGrid() { return this.probePositionGrid; } - public final Position getMaxXYZ() { - return this.maxXYZ; - } - - public final Position getMinXYZ() { - return this.minXYZ; - } - - public final Units getUnits() { - return units; - } - public boolean isValid() { return probePositionGrid.length > 0 && pendingPositions.isEmpty(); }