66import com .willwinder .ugs .designer .io .gcode .path .Segment ;
77import com .willwinder .ugs .designer .io .gcode .path .SegmentType ;
88import com .willwinder .ugs .designer .model .Settings ;
9+ import static com .willwinder .ugs .designer .utils .GeometryUtils .generateLineString ;
910import com .willwinder .universalgcodesender .model .PartialPosition ;
10- import org .locationtech .jts .geom .Coordinate ;
11- import org .locationtech .jts .geom .CoordinateXY ;
1211import org .locationtech .jts .geom .Envelope ;
1312import org .locationtech .jts .geom .Geometry ;
1413import org .locationtech .jts .geom .LineString ;
1514import org .locationtech .jts .geom .MultiPoint ;
1615
1716import java .awt .geom .Area ;
17+ import java .util .Arrays ;
1818import java .util .List ;
1919
2020public class LaserFillToolPath extends AbstractToolPath {
@@ -37,30 +37,57 @@ private List<Geometry> getGeometries() {
3737 public void appendGcodePath (GcodePath gcodePath , Settings settings ) {
3838 gcodePath .addSegment (new Segment (SegmentType .SEAM , null , null , (int ) Math .round (settings .getMaxSpindleSpeed () * (source .getSpindleSpeed () / 100d )), source .getFeedRate ()));
3939
40+ double toolPathAngle = source .getToolPathAngle ();
4041 List <Geometry > geometries = getGeometries ();
4142 geometries .forEach (g -> {
4243 Envelope envelope = g .getEnvelopeInternal ();
44+ double [] offsetRange = getOffsetRange (envelope , toolPathAngle );
4345
4446 int currentPass = 0 ;
4547 while (currentPass < source .getPasses ()) {
4648 currentPass ++;
4749
4850 boolean reverse = false ;
49- double currentY = envelope .getMinY ();
50- while (currentY <= envelope .getMaxY ()) {
51- LineString lineString = getGeometryFactory ().createLineString (new Coordinate []{
52- new CoordinateXY (envelope .getMinX (), currentY ),
53- new CoordinateXY (envelope .getMaxX (), currentY ),
54- });
55-
56- addLineIntersectionSegments (gcodePath , g , lineString , reverse );
57- currentY += settings .getLaserDiameter ();
51+ double currentOffset = offsetRange [0 ];
52+ while (currentOffset <= offsetRange [1 ]) {
53+ LineString lineString = generateLineString (envelope , currentOffset , toolPathAngle );
54+ if (lineString != null ) {
55+ addLineIntersectionSegments (gcodePath , g , lineString , reverse );
56+ }
57+ currentOffset += settings .getLaserDiameter ();
5858 reverse = !reverse ;
5959 }
6060 }
6161 });
6262 }
6363
64+ /**
65+ * Returns the range of offsets along the pass normal that covers the whole envelope for the given angle.
66+ * The offset is measured from the envelope's minimum corner, matching {@link com.willwinder.ugs.designer.utils.GeometryUtils#generateLineString}.
67+ */
68+ private static double [] getOffsetRange (Envelope envelope , double angleInDegrees ) {
69+ double radians = Math .toRadians (-angleInDegrees );
70+ double dx = Math .cos (radians );
71+ double dy = Math .sin (radians );
72+
73+ double normalX = -dy ;
74+ double normalY = dx ;
75+
76+ double width = envelope .getWidth ();
77+ double height = envelope .getHeight ();
78+
79+ double [] projections = {
80+ 0 ,
81+ width * normalX ,
82+ height * normalY ,
83+ width * normalX + height * normalY
84+ };
85+
86+ double min = Arrays .stream (projections ).min ().orElse (0 );
87+ double max = Arrays .stream (projections ).max ().orElse (0 );
88+ return new double []{min , max };
89+ }
90+
6491 private static void addLineIntersectionSegments (GcodePath gcodePath , Geometry geometry , LineString lineString , boolean reverse ) {
6592 Geometry intersection = geometry .intersection (lineString );
6693
0 commit comments