Skip to content

Commit d624707

Browse files
authored
Merge pull request #167 from jgphilpott/copilot/fix-dome-slice-infill-issue
Fix concentric infill generating inside holes (dome/cavity cross-sections)
2 parents 3a64932 + 3edad65 commit d624707

File tree

2 files changed

+250
-80
lines changed

2 files changed

+250
-80
lines changed
Lines changed: 151 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Concentric infill pattern implementation for Polyslice.
22

33
coders = require('../../gcode/coders')
4+
clipping = require('../../utils/clipping')
45
paths = require('../../utils/paths')
56
combing = require('../../geometry/combing')
6-
primitives = require('../../utils/primitives')
77

88
module.exports =
99

@@ -50,93 +50,187 @@ module.exports =
5050

5151
continue if currentLoop.length < 3
5252

53-
# Check if this loop should be skipped because it's inside a hole.
54-
# Sample multiple points on the loop to ensure accurate detection.
55-
skipLoop = false
56-
5753
if holeInnerWalls.length > 0
5854

59-
# Sample points evenly distributed around the loop.
60-
sampleCount = Math.min(8, currentLoop.length)
55+
# Clip each edge of the loop against hole walls to prevent
56+
# infill from being generated inside holes.
57+
validSegments = []
6158

62-
pointsInHoles = 0
59+
for i in [0...currentLoop.length]
6360

64-
for sampleIdx in [0...sampleCount]
61+
segStart = currentLoop[i]
62+
segEnd = currentLoop[(i + 1) % currentLoop.length]
6563

66-
# Distribute samples evenly across the loop's length.
67-
pointIdx = Math.floor(sampleIdx * currentLoop.length / sampleCount)
68-
testPoint = currentLoop[pointIdx]
64+
clippedSegs = clipping.clipLineWithHoles(segStart, segEnd, infillBoundary, holeInnerWalls)
6965

70-
# Check if this point is inside any hole.
71-
for holeWall in holeInnerWalls
66+
for seg in clippedSegs
7267

73-
if holeWall.length >= 3 and primitives.pointInPolygon(testPoint, holeWall)
68+
validSegments.push(seg)
7469

75-
pointsInHoles++
76-
break
70+
continue if validSegments.length is 0
7771

78-
# Skip loop only if majority of sampled points are inside holes.
79-
# This prevents skipping loops that merely pass near holes.
80-
if pointsInHoles > sampleCount / 2
72+
# Group consecutive segments into polylines to minimize travel moves.
73+
polylines = []
74+
currentPolyline = [validSegments[0].start, validSegments[0].end]
8175

82-
skipLoop = true
76+
for segIdx in [1...validSegments.length]
8377

84-
continue if skipLoop
78+
seg = validSegments[segIdx]
79+
prevEnd = currentPolyline[currentPolyline.length - 1]
8580

86-
# Find optimal start point on this loop if we have a last position.
87-
startIndex = 0
81+
dx = seg.start.x - prevEnd.x
82+
dy = seg.start.y - prevEnd.y
8883

89-
if lastEndPoint?
84+
if dx * dx + dy * dy < 0.001 * 0.001
9085

91-
minDistSq = Infinity
86+
currentPolyline.push(seg.end)
9287

93-
for i in [0...currentLoop.length]
88+
else
89+
90+
polylines.push(currentPolyline)
91+
currentPolyline = [seg.start, seg.end]
92+
93+
polylines.push(currentPolyline)
94+
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()
136+
137+
continue if polyline.length < 2
138+
139+
startPoint = polyline[0]
140+
141+
combingPath = combing.findCombingPath(lastEndPoint or startPoint, startPoint, holeOuterWalls, infillBoundary, nozzleDiameter)
142+
143+
for i in [0...combingPath.length - 1]
144+
145+
waypoint = combingPath[i + 1]
146+
offsetWaypointX = waypoint.x + centerOffsetX
147+
offsetWaypointY = waypoint.y + centerOffsetY
148+
149+
slicer.gcode += coders.codeLinearMovement(slicer, offsetWaypointX, offsetWaypointY, z, null, travelSpeedMmMin).replace(slicer.newline, (if verbose then "; Moving to concentric loop" + slicer.newline else slicer.newline))
150+
151+
prevPoint = polyline[0]
152+
153+
for ptIdx in [1...polyline.length]
154+
155+
point = polyline[ptIdx]
156+
157+
dx = point.x - prevPoint.x
158+
dy = point.y - prevPoint.y
159+
160+
distance = Math.sqrt(dx * dx + dy * dy)
161+
162+
if distance > 0.001
163+
164+
extrusionDelta = slicer.calculateExtrusion(distance, nozzleDiameter)
165+
slicer.cumulativeE += extrusionDelta
166+
167+
offsetX = point.x + centerOffsetX
168+
offsetY = point.y + centerOffsetY
169+
170+
slicer.gcode += coders.codeLinearMovement(slicer, offsetX, offsetY, z, slicer.cumulativeE, infillSpeedMmMin)
171+
172+
prevPoint = point
173+
174+
lastEndPoint = prevPoint
175+
176+
else
177+
178+
# No holes: use optimized full-loop rendering with start point selection.
179+
180+
# Find optimal start point on this loop if we have a last position.
181+
startIndex = 0
182+
183+
if lastEndPoint?
184+
185+
minDistSq = Infinity
186+
187+
for i in [0...currentLoop.length]
94188

95-
point = currentLoop[i]
96-
distSq = (point.x - lastEndPoint.x) ** 2 + (point.y - lastEndPoint.y) ** 2
189+
point = currentLoop[i]
190+
distSq = (point.x - lastEndPoint.x) ** 2 + (point.y - lastEndPoint.y) ** 2
97191

98-
if distSq < minDistSq
192+
if distSq < minDistSq
99193

100-
minDistSq = distSq
101-
startIndex = i
194+
minDistSq = distSq
195+
startIndex = i
102196

103-
# Travel to start point with combing.
104-
firstPoint = currentLoop[startIndex]
197+
# Travel to start point with combing.
198+
firstPoint = currentLoop[startIndex]
105199

106-
combingPath = combing.findCombingPath(lastEndPoint or firstPoint, firstPoint, holeOuterWalls, infillBoundary, nozzleDiameter)
200+
combingPath = combing.findCombingPath(lastEndPoint or firstPoint, firstPoint, holeOuterWalls, infillBoundary, nozzleDiameter)
107201

108-
for i in [0...combingPath.length - 1]
202+
for i in [0...combingPath.length - 1]
109203

110-
waypoint = combingPath[i + 1]
111-
offsetWaypointX = waypoint.x + centerOffsetX
112-
offsetWaypointY = waypoint.y + centerOffsetY
204+
waypoint = combingPath[i + 1]
205+
offsetWaypointX = waypoint.x + centerOffsetX
206+
offsetWaypointY = waypoint.y + centerOffsetY
113207

114-
slicer.gcode += coders.codeLinearMovement(slicer, offsetWaypointX, offsetWaypointY, z, null, travelSpeedMmMin).replace(slicer.newline, (if verbose then "; Moving to concentric loop" + slicer.newline else slicer.newline))
208+
slicer.gcode += coders.codeLinearMovement(slicer, offsetWaypointX, offsetWaypointY, z, null, travelSpeedMmMin).replace(slicer.newline, (if verbose then "; Moving to concentric loop" + slicer.newline else slicer.newline))
115209

116-
# Print the loop starting from startIndex.
117-
prevPoint = currentLoop[startIndex]
210+
# Print the loop starting from startIndex.
211+
prevPoint = currentLoop[startIndex]
118212

119-
for i in [1..currentLoop.length]
213+
for i in [1..currentLoop.length]
120214

121-
currentIndex = (startIndex + i) % currentLoop.length
122-
point = currentLoop[currentIndex]
215+
currentIndex = (startIndex + i) % currentLoop.length
216+
point = currentLoop[currentIndex]
123217

124-
dx = point.x - prevPoint.x
125-
dy = point.y - prevPoint.y
218+
dx = point.x - prevPoint.x
219+
dy = point.y - prevPoint.y
126220

127-
distance = Math.sqrt(dx * dx + dy * dy)
221+
distance = Math.sqrt(dx * dx + dy * dy)
128222

129-
if distance > 0.001
223+
if distance > 0.001
130224

131-
extrusionDelta = slicer.calculateExtrusion(distance, nozzleDiameter)
132-
slicer.cumulativeE += extrusionDelta
225+
extrusionDelta = slicer.calculateExtrusion(distance, nozzleDiameter)
226+
slicer.cumulativeE += extrusionDelta
133227

134-
offsetX = point.x + centerOffsetX
135-
offsetY = point.y + centerOffsetY
228+
offsetX = point.x + centerOffsetX
229+
offsetY = point.y + centerOffsetY
136230

137-
slicer.gcode += coders.codeLinearMovement(slicer, offsetX, offsetY, z, slicer.cumulativeE, infillSpeedMmMin)
231+
slicer.gcode += coders.codeLinearMovement(slicer, offsetX, offsetY, z, slicer.cumulativeE, infillSpeedMmMin)
138232

139-
prevPoint = point
233+
prevPoint = point
140234

141-
# Update last end point for next loop.
142-
lastEndPoint = prevPoint
235+
# Update last end point for next loop.
236+
lastEndPoint = prevPoint

0 commit comments

Comments
 (0)