Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import com.willwinder.ugs.designer.io.gcode.path.Segment;
import com.willwinder.ugs.designer.io.gcode.path.SegmentType;
import com.willwinder.ugs.designer.model.Settings;
import static com.willwinder.ugs.designer.utils.GeometryUtils.generateLineString;
import com.willwinder.universalgcodesender.model.PartialPosition;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateXY;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.MultiPoint;

import java.awt.geom.Area;
import java.util.Arrays;
import java.util.List;

public class LaserFillToolPath extends AbstractToolPath {
Expand All @@ -37,30 +37,57 @@ private List<Geometry> getGeometries() {
public void appendGcodePath(GcodePath gcodePath, Settings settings) {
gcodePath.addSegment(new Segment(SegmentType.SEAM, null, null, (int) Math.round(settings.getMaxSpindleSpeed() * (source.getSpindleSpeed() / 100d)), source.getFeedRate()));

double toolPathAngle = source.getToolPathAngle();
List<Geometry> geometries = getGeometries();
geometries.forEach(g -> {
Envelope envelope = g.getEnvelopeInternal();
double[] offsetRange = getOffsetRange(envelope, toolPathAngle);

int currentPass = 0;
while (currentPass < source.getPasses()) {
currentPass++;

boolean reverse = false;
double currentY = envelope.getMinY();
while (currentY <= envelope.getMaxY()) {
LineString lineString = getGeometryFactory().createLineString(new Coordinate[]{
new CoordinateXY(envelope.getMinX(), currentY),
new CoordinateXY(envelope.getMaxX(), currentY),
});

addLineIntersectionSegments(gcodePath, g, lineString, reverse);
currentY += settings.getLaserDiameter();
double currentOffset = offsetRange[0];
while (currentOffset <= offsetRange[1]) {
LineString lineString = generateLineString(envelope, currentOffset, toolPathAngle);
if (lineString != null) {
addLineIntersectionSegments(gcodePath, g, lineString, reverse);
}
currentOffset += settings.getLaserDiameter();
reverse = !reverse;
}
}
});
}

/**
* Returns the range of offsets along the pass normal that covers the whole envelope for the given angle.
* The offset is measured from the envelope's minimum corner, matching {@link com.willwinder.ugs.designer.utils.GeometryUtils#generateLineString}.
*/
private static double[] getOffsetRange(Envelope envelope, double angleInDegrees) {
double radians = Math.toRadians(-angleInDegrees);
double dx = Math.cos(radians);
double dy = Math.sin(radians);

double normalX = -dy;
double normalY = dx;

double width = envelope.getWidth();
double height = envelope.getHeight();

double[] projections = {
0,
width * normalX,
height * normalY,
width * normalX + height * normalY
};

double min = Arrays.stream(projections).min().orElse(0);
double max = Arrays.stream(projections).max().orElse(0);
return new double[]{min, max};
}

private static void addLineIntersectionSegments(GcodePath gcodePath, Geometry geometry, LineString lineString, boolean reverse) {
Geometry intersection = geometry.intersection(lineString);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import static com.willwinder.ugs.designer.io.gcode.path.SegmentType.MOVE;
import static com.willwinder.ugs.designer.io.gcode.path.SegmentType.SEAM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class LaserFillToolPathTest {

Expand Down Expand Up @@ -44,6 +45,42 @@ public void fillToolPathShouldNotConnectThePointEdgesWithALineSegment() {
assertSegment(segments.get(4), LINE, 0.0, 0.2);
}

@Test
public void fillToolPathShouldFillAlongToolPathAngle() {
Path path = new Path();
path.setPasses(1);
path.setToolPathAngle(90);
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 1);
path.lineTo(1, 0);
path.lineTo(0, 0);
path.close();

Settings settings = new Settings();
settings.setMaxSpindleSpeed(10000);

LaserFillToolPath toolPath = new LaserFillToolPath(settings, path);
toolPath.setStartDepth(0);
toolPath.setTargetDepth(0);

GcodePath gcodePath = toolPath.toGcodePath();
List<Segment> segments = gcodePath.getSegments();
assertEquals(SEAM, segments.get(0).type);

List<Segment> lineSegments = segments.stream().filter(s -> s.type == LINE).toList();
assertFalse("Expected the fill to produce line segments", lineSegments.isEmpty());

// A 90 degree angle fills with vertical passes, so every line moves along Y while keeping X constant
lineSegments.forEach(segment ->
assertEquals(segment.getPoint().getX(), findMovePreceding(segments, segment).getPoint().getX(), 0.01));
}

private Segment findMovePreceding(List<Segment> segments, Segment lineSegment) {
int index = segments.indexOf(lineSegment);
return segments.get(index - 1);
}

private void assertSegment(Segment segment, SegmentType segmentType, double x, double y) {
assertEquals(segmentType, segment.getType());
assertEquals(x, segment.getPoint().getX(), 0.01);
Expand Down
Loading