Package entities :: Module paths
[hide private]
[frames] | no frames]

Source Code for Module entities.paths

  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  # globals used as an easy way to maintain state between layer changes 
 14  _totalExtrusionDistance = 0.0 
 15  _previousPoint = None 
 16   
17 -class Path:
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
64 - def __str__(self):
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
75 - def getDistanceAndDuration(self):
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
98 - def getStartPoint(self):
99 return self.startPoint
100
101 - def getEndPoint(self):
102 if len(self.points) > 0: 103 return self.points[len(self.points) - 1] 104 else: 105 return None
106
107 - def getFeedRateMinute(self):
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
139 - def getFeedRateAndMultiplier(self, feedRateMinute, feedRateMultiplier):
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
180 - def offset(self, offset):
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
186 - def addPath(self, path):
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
196 -class Loop(Path):
197 - def __init__(self, z, runtimeParameters):
198 Path.__init__(self, z, runtimeParameters)
199
200 -class InfillPath(Path):
201 - def __init__(self, z, runtimeParameters):
202 Path.__init__(self, z, runtimeParameters)
203
204 -class SupportPath(Path):
205 - def __init__(self, z, runtimeParameters):
206 Path.__init__(self, z, runtimeParameters)
207
208 - def getFeedRateMinute(self):
209 return self.supportFeedRateMinute
210
211 -class TravelPath(Path):
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
227 - def offset(self, offset):
228 self.fromLocation.x += offset.real 229 self.fromLocation.y += offset.imag 230 self.toLocation.x += offset.real 231 self.toLocation.y += offset.imag 232 Path.offset(self, offset)
233
234 - def __str__(self):
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
241 - def moveToStartPoint(self, feedAndFlowRateMultiplier):
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 #Store for reverse retraction 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
297 - def getRetractCommands(self, extrusionDistance, resumingSpeed):
298 global _totalExtrusionDistance 299 commands = [] 300 if self.extrusionUnitsRelative: 301 retractDistance = round(extrusionDistance, self.dimensionDecimalPlaces) 302 else: 303 _totalExtrusionDistance -= extrusionDistance 304 retractDistance = round(_totalExtrusionDistance, self.dimensionDecimalPlaces) 305 306 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('F', '%s' % self.extruderRetractionSpeedMinute)])) 307 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('E', '%s' % retractDistance)])) 308 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('F', '%s' % resumingSpeed)])) 309 return commands
310
311 - def getRetractReverseCommands(self, extrusionDistance):
312 global _totalExtrusionDistance 313 commands = [] 314 if self.extrusionUnitsRelative: 315 retractDistance = round(extrusionDistance, self.dimensionDecimalPlaces) 316 else: 317 _totalExtrusionDistance += extrusionDistance 318 retractDistance = round(_totalExtrusionDistance, self.dimensionDecimalPlaces) 319 320 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('F', '%s' % self.extruderRetractionSpeedMinute)])) 321 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('E', '%s' % retractDistance)])) 322 commands.append(GcodeCommand(gcodes.LINEAR_GCODE_MOVEMENT, [('F', '%s' % self.travelFeedRateMinute)])) 323 324 if not self.extrusionUnitsRelative: 325 commands.append(self.getResetExtruderDistanceCommand()) 326 return commands
327
328 -class BoundaryPerimeter(Path):
329
330 - def __init__(self, z, runtimeParameters):
331 Path.__init__(self, z, runtimeParameters) 332 self.boundaryPoints = []
333
334 - def __str__(self):
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
341 - def offset(self, offset):
342 for boundaryPoint in self.boundaryPoints: 343 boundaryPoint.x += offset.real 344 boundaryPoint.y += offset.imag 345 Path.offset(self, offset)
346
347 - def getFeedRateMinute(self):
348 return self.perimeterFeedRateMinute
349