Skip to content

Commit 3edad65

Browse files
Copilotjgphilpott
andcommitted
Address PR review feedback: DRY test helpers, shared tolerance constant, nearest-neighbour polyline ordering
Co-authored-by: jgphilpott <4128208+jgphilpott@users.noreply.github.com>
1 parent 33096c4 commit 3edad65

File tree

2 files changed

+77
-44
lines changed

2 files changed

+77
-44
lines changed

src/slicer/infill/patterns/concentric.coffee

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,47 @@ module.exports =
9292

9393
polylines.push(currentPolyline)
9494

95-
# Render each polyline with combing travel and extrusion.
96-
for polyline in polylines
95+
# Select and render polylines in nearest-neighbour order to minimize travel.
96+
remainingPolylines = polylines.slice()
97+
98+
while remainingPolylines.length > 0
99+
100+
if lastEndPoint?
101+
102+
# Find the polyline start/end closest to the current position.
103+
bestIdx = 0
104+
bestFlipped = false
105+
minDistSq = Infinity
106+
107+
for plIdx in [0...remainingPolylines.length]
108+
109+
pl = remainingPolylines[plIdx]
110+
continue if pl.length < 2
111+
112+
startDistSq = (pl[0].x - lastEndPoint.x) ** 2 + (pl[0].y - lastEndPoint.y) ** 2
113+
endDistSq = (pl[pl.length - 1].x - lastEndPoint.x) ** 2 + (pl[pl.length - 1].y - lastEndPoint.y) ** 2
114+
115+
if startDistSq < minDistSq
116+
117+
minDistSq = startDistSq
118+
bestIdx = plIdx
119+
bestFlipped = false
120+
121+
if endDistSq < minDistSq
122+
123+
minDistSq = endDistSq
124+
bestIdx = plIdx
125+
bestFlipped = true
126+
127+
polyline = remainingPolylines.splice(bestIdx, 1)[0]
128+
129+
if bestFlipped
130+
131+
polyline = polyline.slice().reverse()
132+
133+
else
134+
135+
polyline = remainingPolylines.shift()
97136

98137
continue if polyline.length < 2
99138

src/slicer/infill/patterns/concentric.test.coffee

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@ describe 'Concentric Infill Generation', ->
1212

1313
slicer = new Polyslice({progressCallback: null})
1414

15+
# Parse X/Y coordinates from all infill extrusion (G1 ... E) lines in a G-code string.
16+
parseFillExtrusionCoords = (gcode) ->
17+
18+
lines = gcode.split('\n')
19+
inFill = false
20+
coords = []
21+
22+
for line in lines
23+
24+
if line.includes('; TYPE: FILL')
25+
inFill = true
26+
else if line.includes('; TYPE:') and not line.includes('; TYPE: FILL')
27+
inFill = false
28+
29+
if inFill and line.includes('G1') and line.includes('E')
30+
31+
xMatch = line.match(/X([\d.-]+)/)
32+
yMatch = line.match(/Y([\d.-]+)/)
33+
34+
if xMatch and yMatch
35+
coords.push({ x: parseFloat(xMatch[1]), y: parseFloat(yMatch[1]) })
36+
37+
return coords
38+
39+
# Tolerance (mm) applied to hole-radius assertions to absorb polygon approximation
40+
# of circular hole boundaries (sliced as polygons, not true circles).
41+
HOLE_TOLERANCE = 0.5
42+
1543
describe 'Pattern Generation Tests', ->
1644

1745
test 'should generate infill for middle layers', ->
@@ -199,35 +227,16 @@ describe 'Concentric Infill Generation', ->
199227

200228
# Verify that infill doesn't generate in the hole area.
201229
# Extract coordinates from infill sections.
202-
inFill = false
203-
fillCoords = []
204-
205-
for line in lines
206-
207-
if line.includes('; TYPE: FILL')
208-
inFill = true
209-
else if line.includes('; TYPE:') and not line.includes('; TYPE: FILL')
210-
inFill = false
211-
212-
if inFill and line.includes('G1') and line.includes('E')
213-
214-
# Parse X and Y coordinates from G-code.
215-
# Regex captures numeric value after X or Y (e.g., "X110.5" → match[1] = "110.5").
216-
xMatch = line.match(/X([\d.-]+)/)
217-
yMatch = line.match(/Y([\d.-]+)/)
218-
219-
if xMatch and yMatch
220-
fillCoords.push({ x: parseFloat(xMatch[1]), y: parseFloat(yMatch[1]) })
230+
fillCoords = parseFillExtrusionCoords(result)
221231

222232
# Check that no infill points are in the center hole area.
223233
# Derive build plate center from slicer configuration to avoid hardcoding.
224234
centerX = slicer.getBuildPlateWidth() / 2
225235
centerY = slicer.getBuildPlateLength() / 2
226236

227237
# For a torus with radius 5 and tube 2, the hole radius is approximately 3mm.
238+
# Use HOLE_TOLERANCE to absorb polygon approximation of the circular hole.
228239
holeRadius = 3
229-
230-
# Count how many infill points are near the hole center.
231240
pointsNearHole = 0
232241

233242
for coord in fillCoords
@@ -236,7 +245,7 @@ describe 'Concentric Infill Generation', ->
236245
dy = coord.y - centerY
237246
distToCenter = Math.sqrt(dx * dx + dy * dy)
238247

239-
if distToCenter < holeRadius
248+
if distToCenter < holeRadius - HOLE_TOLERANCE
240249
pointsNearHole++
241250

242251
# Should have no points in the hole area after the fix.
@@ -286,29 +295,14 @@ describe 'Concentric Infill Generation', ->
286295

287296
lines = result.split('\n')
288297

289-
# Extract infill extrusion G-code coordinates.
290-
inFill = false
291-
fillCoords = []
292-
293-
for line in lines
294-
295-
if line.includes('; TYPE: FILL')
296-
inFill = true
297-
else if line.includes('; TYPE:') and not line.includes('; TYPE: FILL')
298-
inFill = false
299-
300-
if inFill and line.includes('G1') and line.includes('E')
301-
302-
xMatch = line.match(/X([\d.-]+)/)
303-
yMatch = line.match(/Y([\d.-]+)/)
304-
305-
if xMatch and yMatch
306-
fillCoords.push({ x: parseFloat(xMatch[1]), y: parseFloat(yMatch[1]) })
298+
# Extract infill extrusion G-code coordinates using shared helper.
299+
fillCoords = parseFillExtrusionCoords(result)
307300

308301
centerX = slicer.getBuildPlateWidth() / 2
309302
centerY = slicer.getBuildPlateLength() / 2
310303

311-
# The circular hole has radius 6mm — no infill point should fall inside it.
304+
# The circular hole has radius 6mm. Use HOLE_TOLERANCE to absorb
305+
# polygon approximation of the circular hole boundary.
312306
holeRadius = 6
313307
pointsNearHole = 0
314308

@@ -318,7 +312,7 @@ describe 'Concentric Infill Generation', ->
318312
dy = coord.y - centerY
319313
distToCenter = Math.sqrt(dx * dx + dy * dy)
320314

321-
if distToCenter < holeRadius
315+
if distToCenter < holeRadius - HOLE_TOLERANCE
322316
pointsNearHole++
323317

324318
# No infill should be generated inside the circular hole.

0 commit comments

Comments
 (0)