1 from GcodeCommand import GcodeCommand
2 from StringIO import StringIO
3 from collections import OrderedDict
4 from config import config
5 from fabmetheus_utilities.vector3 import Vector3
6 from math import pi
7 from utilities import memory_tracker
8 import gcodes
9 import math
10 import sys
11 import time
12
13
14 _totalExtrusionDistance = 0.0
15 _previousPoint = None
16
18 ''' A Path the tool will follow within a nested ring.'''
19 - def __init__(self, z, runtimeParameters):
20
21 self.z = z
22 self.runtimeParameters = runtimeParameters
23
24 self.type = None
25 self.startPoint = None
26 self.points = []
27 self.gcodeCommands = []
28
29 self.decimalPlaces = self.runtimeParameters.decimalPlaces
30 self.dimensionDecimalPlaces = self.runtimeParameters.dimensionDecimalPlaces
31 self.speedActive = self.runtimeParameters.speedActive
32 self.bridgeFeedRateMinute = self.runtimeParameters.bridgeFeedRateMinute
33 self.perimeterFeedRateMinute = self.runtimeParameters.perimeterFeedRateMinute
34 self.extrusionFeedRateMinute = self.runtimeParameters.extrusionFeedRateMinute
35 self.travelFeedRateMinute = self.runtimeParameters.travelFeedRateMinute
36 self.extrusionUnitsRelative = self.runtimeParameters.extrusionUnitsRelative
37 self.supportFeedRateMinute = self.runtimeParameters.supportFeedRateMinute
38
39 self.dimensionActive = self.runtimeParameters.dimensionActive
40
41 self.oozeRate = self.runtimeParameters.oozeRate
42 self.zDistanceRatio = 5.0
43 self.extruderRetractionSpeedMinute = round(60.0 * self.runtimeParameters.extruderRetractionSpeed, self.dimensionDecimalPlaces)
44
45 self.layerThickness = self.runtimeParameters.layerThickness
46 self.perimeterWidth = self.runtimeParameters.perimeterWidth
47 self.filamentDiameter = self.runtimeParameters.filamentDiameter
48 self.filamentPackingDensity = self.runtimeParameters.filamentPackingDensity
49 self.absolutePositioning = config.getboolean('preface', 'positioning.absolute')
50 self.flowRate = self.runtimeParameters.flowRate
51 self.perimeterFlowRate = self.runtimeParameters.perimeterFlowRate
52 self.bridgeFlowRate = self.runtimeParameters.bridgeFlowRate
53
54 filamentRadius = 0.5 * self.filamentDiameter
55 filamentPackingArea = pi * filamentRadius * filamentRadius * self.filamentPackingDensity
56 self.flowScaleSixty = 60.0 * ((((self.layerThickness + self.perimeterWidth) / 4) ** 2 * pi) / filamentPackingArea)
57
58 self.minimumBridgeFeedRateMultiplier = self.runtimeParameters.minimumBridgeFeedRateMultiplier
59 self.minimumPerimeterFeedRateMultiplier = self.runtimeParameters.minimumPerimeterFeedRateMultiplier
60 self.minimumExtrusionFeedRateMultiplier = self.runtimeParameters.minimumExtrusionFeedRateMultiplier
61 self.minimumTravelFeedRateMultiplier = self.runtimeParameters.minimumTravelFeedRateMultiplier
62 self.minimumLayerFeedRateMinute = self.runtimeParameters.minimumLayerFeedRateMinute
63
65 '''Get the string representation.'''
66 output = StringIO()
67 output.write('%14stype: %s\n' % ('', self.type))
68 output.write('%14sstartPoint: %s\n' % ('', self.startPoint))
69 output.write('%14spoints: %s\n' % ('', self.points))
70 output.write('%14sgcodeCommands:\n' % '')
71 for command in self.gcodeCommands:
72 output.write('%16s%s' % ('', GcodeCommand.printCommand(command)))
73 return output.getvalue()
74
76 '''Returns the time taken to follow the path and the distance'''
77 oldLocation = self.startPoint
78 feedRate = self.travelFeedRateMinute
79 duration = 0.0
80 distance = 0.0
81 for point in self.points:
82 feedRateSecond = feedRate / 60.0
83
84 separationX = point.real - oldLocation.real
85 separationY = point.imag - oldLocation.imag
86 segmentDistance = math.sqrt(separationX ** 2 + separationY ** 2)
87
88 duration += segmentDistance / feedRateSecond
89 distance += segmentDistance
90 oldLocation = point
91 if isinstance(self, BoundaryPerimeter):
92 feedRate = self.perimeterFeedRateMinute
93 else:
94 feedRate = self.extrusionFeedRateMinute
95
96 return (distance, duration)
97
99 return self.startPoint
100
102 if len(self.points) > 0:
103 return self.points[len(self.points) - 1]
104 else:
105 return None
106
108 '''Allows subclasses to override the relevant feedrate method so we don't have to use large if statements.'''
109 return self.extrusionFeedRateMinute
110
111 - def generateGcode(self, lookaheadStartVector=None, feedAndFlowRateMultiplier=1.0):
112 'Transforms paths and points to gcode'
113 global _previousPoint
114 self.gcodeCommands = []
115
116 if _previousPoint == None:
117 _previousPoint = self.startPoint
118
119 for point in self.points:
120
121 gcodeArgs = [('X', round(point.real, self.decimalPlaces)),
122 ('Y', round(point.imag, self.decimalPlaces)),
123 ('Z', round(self.z, self.decimalPlaces))]
124
125 pathFeedRateMinute = self.getFeedRateMinute()
126
127 (pathFeedRateMinute, pathFeedRateMultiplier) = self.getFeedRateAndMultiplier(pathFeedRateMinute, feedAndFlowRateMultiplier)
128
129 if self.speedActive:
130 gcodeArgs.append(('F', pathFeedRateMinute))
131
132 if self.dimensionActive:
133 extrusionDistance = self.getExtrusionDistance(point, self.flowRate * pathFeedRateMultiplier, pathFeedRateMinute)
134 gcodeArgs.append(('E', '%s' % extrusionDistance))
135
136 self.gcodeCommands.append(
137 GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, gcodeArgs))
138
140 'Returns the multiplier that results in either the minimum feed rate or the slowed down feed rate'
141 if (feedRateMultiplier * feedRateMinute) < self.minimumLayerFeedRateMinute:
142 return (self.minimumLayerFeedRateMinute, self.minimumLayerFeedRateMinute / feedRateMinute)
143 else:
144 return (feedRateMinute, feedRateMultiplier)
145
150
151
152 - def getExtrusionDistance(self, point, flowRate, feedRateMinute):
153 global _totalExtrusionDistance
154 global _previousPoint
155 distance = 0.0
156
157 if self.absolutePositioning:
158 if _previousPoint != None:
159 distance = abs(point - _previousPoint)
160 _previousPoint = point
161 else:
162 if _previousPoint == None:
163 logger.warning('There was no absolute location when the G91 command was parsed, so the absolute location will be set to the origin.')
164 _previousPoint = Vector3()
165 distance = abs(point)
166 _previousPoint += point
167
168
169 scaledFlowRate = flowRate * self.flowScaleSixty
170 extrusionDistance = scaledFlowRate / feedRateMinute * distance
171
172 if self.extrusionUnitsRelative:
173 extrusionDistance = round(extrusionDistance, self.dimensionDecimalPlaces)
174 else:
175 _totalExtrusionDistance += extrusionDistance
176 extrusionDistance = round(_totalExtrusionDistance, self.dimensionDecimalPlaces)
177
178 return extrusionDistance
179
181 if self.startPoint != None:
182 self.startPoint = complex(self.startPoint.real + offset.real, self.startPoint.imag + offset.imag)
183 for (index, point) in enumerate(self.points):
184 self.points[index] = complex(point.real + offset.real, point.imag + offset.imag)
185
187 'Add a path to the output.'
188 if len(path) > 0:
189 self.startPoint = path[0]
190 self.points = path[1 :]
191 else:
192 logger.warning('Zero length vertex positions array which was skipped over, this should never happen.')
193 if len(path) < 2:
194 logger.warning('Path of only one point: %s, this should never happen.', path)
195
197 - def __init__(self, z, runtimeParameters):
199
203
207
209 return self.supportFeedRateMinute
210
212 '''Moves from one path to another without extruding. Optionally dodges gaps (comb) and retracts (dimension)'''
213
214 - def __init__(self, z, runtimeParameters, fromLocation, toLocation, combSkein):
215 Path.__init__(self, z, runtimeParameters)
216 self.fromLocation = fromLocation
217 self.toLocation = toLocation
218 self.combSkein = combSkein
219
220 if fromLocation != None:
221 self.startPoint = fromLocation.dropAxis()
222 else:
223 self.startPoint = toLocation.dropAxis()
224
225 self.points.append(toLocation.dropAxis())
226
233
235 output = StringIO()
236 output.write('\n%12sfromLocation: %s\n' % ('', self.fromLocation))
237 output.write('%12stoLocation: %s\n' % ('', self.toLocation))
238 output.write(Path.__str__(self))
239 return output.getvalue()
240
242 '''Adds gcode to move the nozzle to the startpoint of the path.
243 If comb is active the path will dodge all open spaces.
244 '''
245 startPointPath = []
246
247 if self.runtimeParameters.combActive and self.fromLocation != None and self.combSkein != None:
248
249 additionalCommands = self.combSkein.getPathsBetween(self.z, self.fromLocation.dropAxis(), self.toLocation.dropAxis())
250 startPointPath.extend(additionalCommands)
251
252 startPointPath.append(self.toLocation.dropAxis())
253
254 for point in startPointPath:
255 gcodeArgs = [('X', round(point.real, self.decimalPlaces)),
256 ('Y', round(point.imag, self.decimalPlaces)),
257 ('Z', round(self.z, self.decimalPlaces))]
258
259 if self.speedActive:
260 travelFeedRateMinute, travelFeedRateMultiplier = self.getFeedRateAndMultiplier(self.travelFeedRateMinute, feedAndFlowRateMultiplier)
261 gcodeArgs.append(('F', self.travelFeedRateMinute * travelFeedRateMultiplier))
262
263 self.gcodeCommands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, gcodeArgs))
264
265 - def generateGcode(self, lookaheadStartVector=None, feedAndFlowRateMultiplier=1.0):
266 'Transforms paths and points to gcode'
267 lastRetractionExtrusionDistance = 0.0
268
269 if self.dimensionActive:
270
271 if lookaheadStartVector != None and self.fromLocation != None:
272
273 toLocation = lookaheadStartVector
274 locationMinusOld = toLocation - self.fromLocation
275 xyTravel = abs(locationMinusOld.dropAxis())
276 zTravelMultiplied = locationMinusOld.z * self.zDistanceRatio
277 timeToNextThread = math.sqrt(xyTravel * xyTravel + zTravelMultiplied * zTravelMultiplied) / self.extrusionFeedRateMinute * 60
278 retractionExtrusionDistance = timeToNextThread * abs(self.oozeRate) / 60
279 else:
280 retractionExtrusionDistance = 0.0
281
282 self.gcodeCommands.extend(self.getRetractCommands(retractionExtrusionDistance, self.getFeedRateMinute()))
283
284
285 lastRetractionExtrusionDistance = retractionExtrusionDistance
286
287 self.gcodeCommands.append(GcodeCommand(gcodes.TURN_EXTRUDER_OFF))
288
289 self.moveToStartPoint(feedAndFlowRateMultiplier)
290
291 if self.dimensionActive:
292 self.previousPoint = self.startPoint
293 self.gcodeCommands.extend(self.getRetractReverseCommands(lastRetractionExtrusionDistance))
294
295 self.gcodeCommands.append(GcodeCommand(gcodes.TURN_EXTRUDER_ON))
296
310
327
329
330 - def __init__(self, z, runtimeParameters):
331 Path.__init__(self, z, runtimeParameters)
332 self.boundaryPoints = []
333
335 output = StringIO()
336 output.write('%12sboundaryPerimeter:\n' % '')
337 output.write('%14sboundaryPoints: %s\n' % ('', self.boundaryPoints))
338 output.write(Path.__str__(self))
339 return output.getvalue()
340
342 for boundaryPoint in self.boundaryPoints:
343 boundaryPoint.x += offset.real
344 boundaryPoint.y += offset.imag
345 Path.offset(self, offset)
346
348 return self.perimeterFeedRateMinute
349