Skip to content

Commit

Permalink
Feed rate visualization (#2515)
Browse files Browse the repository at this point in the history
* Blends the color for spindle min/max with the color for feed min/max
* Add translation to color settings
* Attempt to fix intermittently failing test
  • Loading branch information
breiler authored May 9, 2024
1 parent 445cd19 commit 963da8a
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 136 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ public class GcodeViewParse {
private final Position max;
private final List<LineSegment> lines;
private double maxSpindleSpeed;
private double maxFeedRate;

public GcodeViewParse() {
maxSpindleSpeed = 0;
maxFeedRate = 0;
min = new Position(UnitUtils.Units.MM);
max = new Position(UnitUtils.Units.MM);
lines = new ArrayList<>();
Expand Down Expand Up @@ -72,6 +74,10 @@ public double getMaxSpindleSpeed() {
return maxSpindleSpeed;
}

public double getMaxFeedRate() {
return maxFeedRate;
}

/**
* Test a point and update min/max coordinates if appropriate.
*/
Expand Down Expand Up @@ -153,6 +159,7 @@ private void recalculateBoundaries() {
testExtremes(lineSegment.getStart());
testExtremes(lineSegment.getEnd());
maxSpindleSpeed = Math.max(lineSegment.getSpindleSpeed(), maxSpindleSpeed);
maxFeedRate = Math.max(lineSegment.getFeedRate(), maxFeedRate);
});
}

Expand Down
6 changes: 4 additions & 2 deletions ugs-core/src/resources/MessagesBundle_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,10 @@ platform.visualizer.dowel.preview.desc = Show dowel preview
platform.visualizer.highlight = Show highlighted lines
platform.visualizer.highlight.desc = Highlights the selected lines from the editor
platform.visualizer.color.highlight = Editor Line Highlight Color
platform.visualizer.color.linear = Linear Movement Color (G1) max speed
platform.visualizer.color.linear.min.speed = Linear Movement Color (G1) min speed
platform.visualizer.color.linear = Linear Movement Color (G1) feed max
platform.visualizer.color.linear.min.speed = Linear Movement Color (G1) feed min
platform.visualizer.color.spindle.max.speed = Linear Movement Color (G1) spindle max
platform.visualizer.color.spindle.min.speed = Linear Movement Color (G1) spindle min
platform.visualizer.color.rapid = Rapid Movement Color (G0)
platform.visualizer.color.arc = Arc Movement Color (G2/G3)
platform.visualizer.color.plunge = Plunge/Raise Movement Color
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.willwinder.universalgcodesender.communicator.event;

import com.willwinder.universalgcodesender.communicator.ICommunicatorListener;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static com.willwinder.universalgcodesender.utils.ThreadHelper.waitUntil;
import org.junit.After;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AsyncCommunicatorEventDispatcherTest {
private AsyncCommunicatorEventDispatcher eventDispatcher;

Expand All @@ -32,13 +31,13 @@ public void tearDown() {
}

@Test
public void dispatchShouldStartWorkerThread() throws TimeoutException, InterruptedException {
public void dispatchShouldStartWorkerThread() throws TimeoutException {
ICommunicatorListener listener = mock(ICommunicatorListener.class);
eventDispatcher.addListener(listener);

eventDispatcher.communicatorPausedOnError();

waitUntil(() -> eventDispatcher.getEventCount() == 0, 1100, TimeUnit.MILLISECONDS);
waitUntil(() -> eventDispatcher.getEventCount() == 0, 2000, TimeUnit.MILLISECONDS);

verify(listener, times(1)).communicatorPausedOnError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,27 @@ This file is part of Universal Gcode Sender (UGS).

import com.google.common.collect.ImmutableList;
import com.willwinder.universalgcodesender.gcode.util.Code;
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 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 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.assertEquals;
import static org.junit.Assert.assertTrue;
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
*/
Expand Down Expand Up @@ -335,6 +338,32 @@ public void overridePositionShouldAddMissingCoordinatesToCommand() {
assertEquals("G0X1Y2Z3A4B5C6", newCommand);
}

@Test
public void updatePointWithCommandShouldSetPositionIfOriginalIsNaN() {
Position position = new Position(Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, MM);
Position newPosition = GcodePreprocessorUtils.updatePointWithCommand(position, 1, 2, 3, 4, 5,6, false);
assertEquals(1, newPosition.getX(), 0.01);
assertEquals(2, newPosition.getY(), 0.01);
assertEquals(3, newPosition.getZ(), 0.01);
assertEquals(4, newPosition.getA(), 0.01);
assertEquals(5, newPosition.getB(), 0.01);
assertEquals(6, newPosition.getC(), 0.01);
assertEquals(MM, newPosition.getUnits());
}

@Test
public void updatePointWithCommandShouldAddToPosition() {
Position position = new Position(1, 2, 3, 4, 5, 6, MM);
Position newPosition = GcodePreprocessorUtils.updatePointWithCommand(position, 1, 2, 3, 4, 5,6, false);
assertEquals(2, newPosition.getX(), 0.01);
assertEquals(4, newPosition.getY(), 0.01);
assertEquals(6, newPosition.getZ(), 0.01);
assertEquals(8, newPosition.getA(), 0.01);
assertEquals(10, newPosition.getB(), 0.01);
assertEquals(12, newPosition.getC(), 0.01);
assertEquals(MM, newPosition.getUnits());
}

static void assertThatPointsAreWithinBoundary(Position start, Position end, double radius, List<Position> points) {
assertTrue(points.stream().allMatch(p -> p.x >= start.x));
assertTrue(points.stream().allMatch(p -> p.x <= end.x));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class VisualizerOptions extends ArrayList<Option<?>> {
public static String VISUALIZER_OPTION_MODEL_DESC = "platform.visualizer.model.desc";
public static String VISUALIZER_OPTION_LINEAR = "platform.visualizer.color.linear";
public static String VISUALIZER_OPTION_LINEAR_MIN_SPEED = "platform.visualizer.color.linear.min.speed";
public static String VISUALIZER_OPTION_SPINDLE_MAX_SPEED = "platform.visualizer.color.spindle.max.speed";
public static String VISUALIZER_OPTION_SPINDLE_MIN_SPEED = "platform.visualizer.color.spindle.min.speed";
public static String VISUALIZER_OPTION_RAPID = "platform.visualizer.color.rapid";
public static String VISUALIZER_OPTION_ARC = "platform.visualizer.color.arc";
public static String VISUALIZER_OPTION_PLUNGE = "platform.visualizer.color.plunge";
Expand Down Expand Up @@ -114,7 +116,9 @@ public VisualizerOptions() {
// GcodeModel renderable
add(getOption(VISUALIZER_OPTION_MODEL, Localization.getString(VISUALIZER_OPTION_MODEL_DESC), true));
add(getOption(VISUALIZER_OPTION_LINEAR, "", new Color(0,0,158)));
add(getOption(VISUALIZER_OPTION_LINEAR_MIN_SPEED, "", new Color(0,0,158, 125)));
add(getOption(VISUALIZER_OPTION_LINEAR_MIN_SPEED, "", new Color(204,255,255)));
add(getOption(VISUALIZER_OPTION_SPINDLE_MAX_SPEED, "", new Color(0,0,158)));
add(getOption(VISUALIZER_OPTION_SPINDLE_MIN_SPEED, "", new Color(204,255,255)));
add(getOption(VISUALIZER_OPTION_RAPID, "", new Color(204,204,0)));
add(getOption(VISUALIZER_OPTION_ARC, "", new Color(178,34,34)));
add(getOption(VISUALIZER_OPTION_PLUNGE, "", new Color(0,100,0)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ This file is part of Universal Gcode Sender (UGS).
package com.willwinder.ugs.nbm.visualizer.renderables;

import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions;
import com.willwinder.universalgcodesender.visualizer.LineSegment;

import java.awt.Color;

import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_ARC;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_COMPLETE;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_LINEAR;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_LINEAR_MIN_SPEED;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_PLUNGE;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_RAPID;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_SPINDLE_MAX_SPEED;
import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_SPINDLE_MIN_SPEED;
import com.willwinder.universalgcodesender.visualizer.LineSegment;

import java.awt.Color;

/**
* Generates a color based on the current line segment
Expand All @@ -37,8 +38,11 @@ This file is part of Universal Gcode Sender (UGS).
*/
public class GcodeLineColorizer {
private double maxSpindleSpeed = 0;
private double maxFeedRate = 0;
private Color feedMinColor = Color.BLACK;
private Color feedMaxColor = Color.BLACK;
private Color spindleMinColor = Color.BLACK;
private Color spindleMaxColor = Color.BLACK;
private Color rapidColor = Color.BLACK;
private Color arcColor = Color.BLACK;
private Color plungeColor = Color.BLACK;
Expand All @@ -47,6 +51,8 @@ public class GcodeLineColorizer {
public void reloadPreferences(VisualizerOptions vo) {
feedMaxColor = vo.getOptionForKey(VISUALIZER_OPTION_LINEAR).value;
feedMinColor = vo.getOptionForKey(VISUALIZER_OPTION_LINEAR_MIN_SPEED).value;
spindleMaxColor = vo.getOptionForKey(VISUALIZER_OPTION_SPINDLE_MAX_SPEED).value;
spindleMinColor = vo.getOptionForKey(VISUALIZER_OPTION_SPINDLE_MIN_SPEED).value;
rapidColor = vo.getOptionForKey(VISUALIZER_OPTION_RAPID).value;
arcColor = vo.getOptionForKey(VISUALIZER_OPTION_ARC).value;
plungeColor = vo.getOptionForKey(VISUALIZER_OPTION_PLUNGE).value;
Expand All @@ -63,26 +69,67 @@ public Color getColor(LineSegment lineSegment, long currentCommandNumber) {
} else if (lineSegment.isZMovement()) {
return plungeColor;
} else {
return getFeedColor(lineSegment.getSpindleSpeed());
return getFeedColor(lineSegment.getFeedRate(), lineSegment.getSpindleSpeed());
}
}

private Color getFeedColor(double spindleSpeed) {
if (maxSpindleSpeed == 0) {
return feedMaxColor;
}

private Color getFeedColor(double feedRate, double spindleSpeed) {
double currentSpindleSpeed = Math.max(spindleSpeed, 0.1);
double currentFeedRate = Math.max(feedRate, 0.1);

double feedRatePercent = currentFeedRate / maxFeedRate;
Color feedColor = maxFeedRate < 0.01 ? feedMaxColor : getColor(feedMinColor, feedMaxColor, feedRatePercent);

double speedPercent = currentSpindleSpeed / maxSpindleSpeed;
int red = Math.min(255, feedMinColor.getRed() + (int) (speedPercent * (feedMaxColor.getRed() - feedMinColor.getRed())));
int green = Math.min(255, feedMinColor.getGreen() + (int) (speedPercent * (feedMaxColor.getGreen() - feedMinColor.getGreen())));
int blue = Math.min(255, feedMinColor.getBlue() + (int) (speedPercent * (feedMaxColor.getBlue() - feedMinColor.getBlue())));
int alpha = Math.min(255, feedMinColor.getAlpha() + (int) (speedPercent * (feedMaxColor.getAlpha() - feedMinColor.getAlpha())));
Color speedColor = maxSpindleSpeed < 0.1 ? spindleMaxColor : getColor(spindleMinColor, spindleMaxColor, speedPercent);
return blend(speedColor, feedColor);
}

/**
* Blends multiple colors with the equal amount to one color
*
* @param colors a list of colors to blend
* @return a blended color
*/
public static Color blend(Color... colors) {
if (colors == null || colors.length == 0) {
return null;
}
float ratio = 1f / (colors.length);

int a = 0;
int r = 0;
int g = 0;
int b = 0;

for (Color color : colors) {
int rgb = color.getRGB();
int a1 = (rgb >> 24 & 0xff);
int r1 = ((rgb & 0xff0000) >> 16);
int g1 = ((rgb & 0xff00) >> 8);
int b1 = (rgb & 0xff);
a += a1 * ratio;
r += r1 * ratio;
g += g1 * ratio;
b += b1 * ratio;
}

return new Color(a << 24 | r << 16 | g << 8 | b);
}

private Color getColor(Color minColor, Color maxColor, double percent) {
int red = Math.min(255, minColor.getRed() + (int) (percent * (maxColor.getRed() - minColor.getRed())));
int green = Math.min(255, minColor.getGreen() + (int) (percent * (maxColor.getGreen() - minColor.getGreen())));
int blue = Math.min(255, minColor.getBlue() + (int) (percent * (maxColor.getBlue() - minColor.getBlue())));
int alpha = Math.min(255, minColor.getAlpha() + (int) (percent * (maxColor.getAlpha() - minColor.getAlpha())));
return new Color(red, green, blue, alpha);
}

public void setMaxSpindleSpeed(double maxSpindleSpeed) {
this.maxSpindleSpeed = maxSpindleSpeed;
}

public void setMaxFeedRate(double maxFeedRate) {
this.maxFeedRate = maxFeedRate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ private boolean generateObject() {
this.objectMin = gcvp.getMinimumExtremes();
this.objectMax = gcvp.getMaximumExtremes();
this.colorizer.setMaxSpindleSpeed(gcvp.getMaxSpindleSpeed());
this.colorizer.setMaxFeedRate(gcvp.getMaxFeedRate());

if (gcodeLineList.isEmpty()) {
return false;
Expand Down

0 comments on commit 963da8a

Please sign in to comment.