chiark / gitweb /
Move SF into its own directory, to seperate SF and Cura. Rename newui to gui.
[cura.git] / Cura / cura_sf / skeinforge_application / skeinforge_plugins / craft_plugins / oozebane.py
1 """
2 This page is in the table of contents.
3 Oozebane is a script to turn off the extruder before the end of a thread and turn it on before the beginning.
4
5 The oozebane manual page is at:
6 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Oozebane
7
8 After oozebane turns the extruder on, it slows the feed rate down where the thread starts.  Then it speeds it up in steps so in theory the thread will remain at roughly the same thickness from the beginning.
9
10 ==Operation==
11 The default 'Activate Oozebane' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions will not be called.
12
13 ==Settings==
14 ===After Startup Distance===
15 Default is 1.2.
16
17 When oozebane reaches the point where the extruder would of turned on, it slows down so that the thread will be thick at that point.  Afterwards it speeds the extruder back up to operating speed.  The speed up distance is the "After Startup Distance".
18
19 ===Early Shutdown Distance===
20 Default is 1.2.
21
22 Defines the distance before the end of the thread that the extruder will be turned off.  It is the most important oozebane setting.  A higher distance means the extruder will turn off sooner and the end of the line will be thinner.
23
24 ===Early Startup Maximum Distance===
25 Default is 1.2.
26
27 Defines the maximum distance before the thread starts that the extruder will be turned on
28
29 ===Early Startup Distance Constant===
30 Default is twenty.
31
32 The longer the extruder has been off, the earlier the extruder will turn back on, the ratio is one minus one over e to the power of the distance the extruder has been off over the "Early Startup Distance Constant".
33
34 ===First Early Startup Distance===
35 Default is twenty five.
36
37 Defines the distance before the first thread starts that the extruder will be turned off.  This value should be high because, according to Marius, the extruder takes a second or two to extrude when starting for the first time.
38
39 ===Minimum Distance for Early Shutdown===
40 Default is zero.
41
42 Defines the minimum distance that the extruder has to be off after the thread end for the early shutdown feature to activate.
43
44 ===Minimum Distance for Early Startup===
45 Default is zero.
46
47 Defines the minimum distance that the extruder has to be off before the thread begins for the early start up feature to activate.
48
49 ===Slowdown Startup Steps===
50 Default is three.
51
52 When oozebane turns the extruder off, it slows the feed rate down in steps so in theory the thread will remain at roughly the same thickness until the end.  The "Slowdown Startup Steps" setting is the number of steps, the more steps the smaller the size of the step that the feed rate will be decreased and the larger the size of the resulting gcode file.
53
54 ==Examples==
55 The following examples oozebane the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and oozebane.py.
56
57 > python oozebane.py
58 This brings up the oozebane dialog.
59
60 > python oozebane.py Screw Holder Bottom.stl
61 The oozebane tool is parsing the file:
62 Screw Holder Bottom.stl
63 ..
64 The oozebane tool has created the file:
65 .. Screw Holder Bottom_oozebane.gcode
66
67 """
68
69 from __future__ import absolute_import
70 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
71 import __init__
72
73 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
74 from fabmetheus_utilities import archive
75 from fabmetheus_utilities import euclidean
76 from fabmetheus_utilities import gcodec
77 from fabmetheus_utilities import settings
78 from skeinforge_application.skeinforge_utilities import skeinforge_craft
79 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
80 from skeinforge_application.skeinforge_utilities import skeinforge_profile
81 import math
82 import sys
83
84
85 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
86 __date__ = '$Date: 2008/21/04 $'
87 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
88
89
90 def getCraftedText( fileName, text, oozebaneRepository = None ):
91         "Oozebane a gcode linear move file or text."
92         return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), oozebaneRepository )
93
94 def getCraftedTextFromText( gcodeText, oozebaneRepository = None ):
95         "Oozebane a gcode linear move text."
96         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'oozebane'):
97                 return gcodeText
98         if oozebaneRepository == None:
99                 oozebaneRepository = settings.getReadRepository( OozebaneRepository() )
100         if not oozebaneRepository.activateOozebane.value:
101                 return gcodeText
102         return OozebaneSkein().getCraftedGcode( gcodeText, oozebaneRepository )
103
104 def getNewRepository():
105         'Get new repository.'
106         return OozebaneRepository()
107
108 def writeOutput(fileName, shouldAnalyze=True):
109         "Oozebane a gcode linear move file."
110         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'oozebane', shouldAnalyze)
111
112
113 class OozebaneRepository:
114         "A class to handle the oozebane settings."
115         def __init__(self):
116                 "Set the default settings, execute title & settings fileName."
117                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.oozebane.html', self )
118                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Oozebane', self, '')
119                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Oozebane')
120                 self.activateOozebane = settings.BooleanSetting().getFromValue('Activate Oozebane', self, False )
121                 self.afterStartupDistance = settings.FloatSpin().getFromValue( 0.7, 'After Startup Distance (millimeters):', self, 1.7, 1.2 )
122                 self.earlyShutdownDistance = settings.FloatSpin().getFromValue( 0.7, 'Early Shutdown Distance (millimeters):', self, 1.7, 1.2 )
123                 self.earlyStartupDistanceConstant = settings.FloatSpin().getFromValue( 10.0, 'Early Startup Distance Constant (millimeters):', self, 30.0, 20.0 )
124                 self.earlyStartupMaximumDistance = settings.FloatSpin().getFromValue( 0.7, 'Early Startup Maximum Distance (millimeters):', self, 1.7, 1.2 )
125                 self.firstEarlyStartupDistance = settings.FloatSpin().getFromValue( 5.0, 'First Early Startup Distance (millimeters):', self, 45.0, 25.0 )
126                 self.minimumDistanceForEarlyStartup = settings.FloatSpin().getFromValue( 0.0, 'Minimum Distance for Early Startup (millimeters):', self, 10.0, 0.0 )
127                 self.minimumDistanceForEarlyShutdown = settings.FloatSpin().getFromValue( 0.0, 'Minimum Distance for Early Shutdown (millimeters):', self, 10.0, 0.0 )
128                 self.slowdownStartupSteps = settings.IntSpin().getFromValue( 2, 'Slowdown Startup Steps (positive integer):', self, 5, 3 )
129                 self.executeTitle = 'Oozebane'
130
131         def execute(self):
132                 "Oozebane button has been clicked."
133                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
134                 for fileName in fileNames:
135                         writeOutput(fileName)
136
137
138 class OozebaneSkein:
139         "A class to oozebane a skein of extrusions."
140         def __init__(self):
141                 self.distanceFeedRate = gcodec.DistanceFeedRate()
142                 self.distanceFromThreadEndToThreadBeginning = None
143                 self.earlyStartupDistance = None
144                 self.extruderInactiveLongEnough = True
145                 self.feedRateMinute = 961.0
146                 self.isExtruderActive = False
147                 self.isFirstExtrusion = True
148                 self.isShutdownEarly = False
149                 self.isStartupEarly = False
150                 self.lineIndex = 0
151                 self.lines = None
152                 self.oldLocation = None
153                 self.operatingFeedRateMinute = 959.0
154                 self.shutdownStepIndex = 999999999
155                 self.startupStepIndex = 999999999
156
157         def addAfterStartupLine( self, splitLine ):
158                 "Add the after startup lines."
159                 distanceAfterThreadBeginning = self.getDistanceAfterThreadBeginning()
160                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
161                 segment = self.oldLocation - location
162                 segmentLength = segment.magnitude()
163                 distanceBack = distanceAfterThreadBeginning - self.afterStartupDistances[ self.startupStepIndex ]
164                 if segmentLength > 0.0:
165                         locationBack = location + segment * distanceBack / segmentLength
166                         feedRate = self.operatingFeedRateMinute * self.afterStartupFlowRates[ self.startupStepIndex ]
167                         if not self.isCloseToEither( locationBack, location, self.oldLocation ):
168                                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( feedRate, locationBack ) )
169                 self.startupStepIndex += 1
170
171         def addLineSetShutdowns(self, line):
172                 "Add a line and set the shutdown variables."
173                 self.distanceFeedRate.addLine(line)
174                 self.isShutdownEarly = True
175
176         def getActiveFeedRateRatio(self):
177                 "Get the feed rate of the first active move over the operating feed rate."
178                 isSearchExtruderActive = self.isExtruderActive
179                 for afterIndex in xrange(self.lineIndex, len(self.lines)):
180                         line = self.lines[ afterIndex ]
181                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
182                         firstWord = gcodec.getFirstWord(splitLine)
183                         if firstWord == 'G1':
184                                 if isSearchExtruderActive:
185                                         return gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) / self.operatingFeedRateMinute
186                         elif firstWord == 'M101':
187                                 isSearchExtruderActive = True
188                 print('active feed rate ratio was not found in oozebane.')
189                 return 1.0
190
191         def getAddAfterStartupLines(self, line):
192                 "Get and / or add after the startup lines."
193                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
194                 while self.isDistanceAfterThreadBeginningGreater():
195                         self.addAfterStartupLine(splitLine)
196                 if self.startupStepIndex >= len( self.afterStartupDistances ):
197                         self.startupStepIndex = len( self.afterStartupDistances ) + 999999999999
198                         return self.getLinearMoveWithFeedRateSplitLine( self.operatingFeedRateMinute, splitLine )
199                 feedRate = self.operatingFeedRateMinute * self.getStartupFlowRateMultiplier( self.getDistanceAfterThreadBeginning() / self.afterStartupDistance, len( self.afterStartupDistances ) )
200                 return self.getLinearMoveWithFeedRateSplitLine( feedRate, splitLine )
201
202         def getAddBeforeStartupLines(self, line):
203                 "Get and / or add before the startup lines."
204                 distanceThreadBeginning = self.getDistanceToThreadBeginning()
205                 if distanceThreadBeginning == None:
206                         return line
207                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
208                 self.extruderInactiveLongEnough = False
209                 self.isStartupEarly = True
210                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
211                 segment = self.oldLocation - location
212                 segmentLength = segment.magnitude()
213                 distanceBack = self.earlyStartupDistance - distanceThreadBeginning
214                 if segmentLength <= 0.0:
215                         print('This should never happen, segmentLength is zero in getAddBeforeStartupLines in oozebane.')
216                         print(line)
217                         self.extruderInactiveLongEnough = True
218                         self.isStartupEarly = False
219                         return line
220                 locationBack = location + segment * distanceBack / segmentLength
221                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) , locationBack ) )
222                 self.distanceFeedRate.addLine('M101')
223                 if self.isCloseToEither( locationBack, location, self.oldLocation ):
224                         return ''
225                 return self.getLinearMoveWithFeedRate( self.operatingFeedRateMinute, location )
226
227         def getAddShutSlowDownLine(self, line):
228                 "Add the shutdown and slowdown lines."
229                 if self.shutdownStepIndex >= len( self.earlyShutdownDistances ):
230                         self.shutdownStepIndex = len( self.earlyShutdownDistances ) + 99999999
231                         return False
232                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
233                 distanceThreadEnd = self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[ self.shutdownStepIndex ] )
234                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
235                 if distanceThreadEnd == None:
236                         distanceThreadEnd = self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[0] )
237                         if distanceThreadEnd != None:
238                                 shutdownFlowRateMultiplier = self.getShutdownFlowRateMultiplier( 1.0 - distanceThreadEnd / self.earlyShutdownDistance, len( self.earlyShutdownDistances ) )
239                                 line = self.getLinearMoveWithFeedRate( self.feedRateMinute * shutdownFlowRateMultiplier, location )
240                         self.distanceFeedRate.addLine(line)
241                         return False
242                 segment = self.oldLocation - location
243                 segmentLength = segment.magnitude()
244                 distanceBack = self.earlyShutdownDistances[ self.shutdownStepIndex ] - distanceThreadEnd
245                 locationBack = location
246                 if segmentLength > 0.0:
247                         locationBack = location + segment * distanceBack / segmentLength
248                 if self.shutdownStepIndex == 0:
249                         if not self.isCloseToEither( locationBack, location, self.oldLocation ):
250                                 line = self.getLinearMoveWithFeedRate( self.feedRateMinute, locationBack )
251                         self.distanceFeedRate.addLine(line)
252                         self.addLineSetShutdowns('M103')
253                         return True
254                 if self.isClose( locationBack, self.oldLocation ):
255                         return True
256                 feedRate = self.feedRateMinute * self.earlyShutdownFlowRates[ self.shutdownStepIndex ]
257                 line = self.getLinearMoveWithFeedRate( feedRate, locationBack )
258                 if self.isClose( locationBack, location ):
259                         line = self.getLinearMoveWithFeedRate( feedRate, location )
260                 self.distanceFeedRate.addLine(line)
261                 return True
262
263         def getAddShutSlowDownLines(self, line):
264                 "Get and / or add the shutdown and slowdown lines."
265                 while self.getAddShutSlowDownLine(line):
266                         self.shutdownStepIndex += 1
267                 return ''
268
269         def getCraftedGcode( self, gcodeText, oozebaneRepository ):
270                 "Parse gcode text and store the oozebane gcode."
271                 self.lines = archive.getTextLines(gcodeText)
272                 self.oozebaneRepository = oozebaneRepository
273                 self.parseInitialization( oozebaneRepository )
274                 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
275                         line = self.lines[self.lineIndex]
276                         self.parseLine(line)
277                 return self.distanceFeedRate.output.getvalue()
278
279         def getDistanceAfterThreadBeginning(self):
280                 "Get the distance after the beginning of the thread."
281                 line = self.lines[self.lineIndex]
282                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
283                 lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
284                 totalDistance = 0.0
285                 extruderOnReached = False
286                 for beforeIndex in xrange( self.lineIndex - 1, 3, - 1 ):
287                         line = self.lines[ beforeIndex ]
288                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
289                         firstWord = gcodec.getFirstWord(splitLine)
290                         if firstWord == 'G1':
291                                 location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine )
292                                 totalDistance += location.distance( lastThreadLocation )
293                                 lastThreadLocation = location
294                                 if extruderOnReached:
295                                         return totalDistance
296                         elif firstWord == 'M101':
297                                 extruderOnReached = True
298                 return None
299
300         def getDistanceToExtruderOffCommand( self, remainingDistance ):
301                 "Get the distance to the word."
302                 line = self.lines[self.lineIndex]
303                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
304                 lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
305                 totalDistance = 0.0
306                 for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ):
307                         line = self.lines[ afterIndex ]
308                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
309                         firstWord = gcodec.getFirstWord(splitLine)
310                         if firstWord == 'G1':
311                                 location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine )
312                                 totalDistance += location.distance( lastThreadLocation )
313                                 lastThreadLocation = location
314                                 if totalDistance >= remainingDistance:
315                                         return None
316                         elif firstWord == 'M103':
317                                 return totalDistance
318                 return None
319
320         def getDistanceToThreadBeginning(self):
321                 "Get the distance to the beginning of the thread."
322                 if self.earlyStartupDistance == None:
323                         return None
324                 line = self.lines[self.lineIndex]
325                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
326                 lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
327                 totalDistance = 0.0
328                 for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ):
329                         line = self.lines[ afterIndex ]
330                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
331                         firstWord = gcodec.getFirstWord(splitLine)
332                         if firstWord == 'G1':
333                                 location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine )
334                                 totalDistance += location.distance( lastThreadLocation )
335                                 lastThreadLocation = location
336                                 if totalDistance >= self.earlyStartupDistance:
337                                         return None
338                         elif firstWord == 'M101':
339                                 return totalDistance
340                 return None
341
342         def getDistanceToThreadBeginningAfterThreadEnd( self, remainingDistance ):
343                 "Get the distance to the thread beginning after the end of this thread."
344                 extruderOnReached = False
345                 line = self.lines[self.lineIndex]
346                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
347                 lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
348                 threadEndReached = False
349                 totalDistance = 0.0
350                 for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ):
351                         line = self.lines[ afterIndex ]
352                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
353                         firstWord = gcodec.getFirstWord(splitLine)
354                         if firstWord == 'G1':
355                                 location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine )
356                                 if threadEndReached:
357                                         totalDistance += location.distance( lastThreadLocation )
358                                         if totalDistance >= remainingDistance:
359                                                 return None
360                                         if extruderOnReached:
361                                                 return totalDistance
362                                 lastThreadLocation = location
363                         elif firstWord == 'M101':
364                                 extruderOnReached = True
365                         elif firstWord == 'M103':
366                                 threadEndReached = True
367                 return None
368
369         def getDistanceToThreadEnd(self):
370                 "Get the distance to the end of the thread."
371                 if self.shutdownStepIndex >= len( self.earlyShutdownDistances ):
372                         return None
373                 return self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[ self.shutdownStepIndex ] )
374
375         def getLinearMoveWithFeedRate( self, feedRate, location ):
376                 "Get a linear move line with the feed rate."
377                 return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRate, location.dropAxis(), location.z )
378
379         def getLinearMoveWithFeedRateSplitLine( self, feedRate, splitLine ):
380                 "Get a linear move line with the feed rate and split line."
381                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
382                 return self.getLinearMoveWithFeedRate( feedRate, location )
383
384         def getOozebaneLine(self, line):
385                 "Get oozebaned gcode line."
386                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
387                 self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine )
388                 if self.oldLocation == None:
389                         return line
390                 if self.startupStepIndex < len( self.afterStartupDistances ):
391                         return self.getAddAfterStartupLines(line)
392                 if self.extruderInactiveLongEnough:
393                         return self.getAddBeforeStartupLines(line)
394                 if self.shutdownStepIndex < len( self.earlyShutdownDistances ):
395                         return self.getAddShutSlowDownLines(line)
396                 if self.isStartupEarly:
397                         return self.getLinearMoveWithFeedRateSplitLine( self.operatingFeedRateMinute, splitLine )
398                 return line
399
400         def getShutdownFlowRateMultiplier( self, along, numberOfDistances ):
401                 "Get the shut down flow rate multipler."
402                 if numberOfDistances <= 0:
403                         return 1.0
404                 return 1.0 - 0.5 / float( numberOfDistances ) - along * float( numberOfDistances - 1 ) / float( numberOfDistances )
405
406         def getStartupFlowRateMultiplier( self, along, numberOfDistances ):
407                 "Get the startup flow rate multipler."
408                 if numberOfDistances <= 0:
409                         return 1.0
410                 return min( 1.0, 0.5 / float( numberOfDistances ) + along )
411
412         def isClose( self, location, otherLocation ):
413                 "Determine if the location is close to the other location."
414                 return location.distanceSquared( otherLocation ) < self.closeSquared
415
416         def isCloseToEither( self, location, otherLocationFirst, otherLocationSecond ):
417                 "Determine if the location is close to the other locations."
418                 if self.isClose( location, otherLocationFirst ):
419                         return True
420                 return self.isClose( location, otherLocationSecond )
421
422         def isDistanceAfterThreadBeginningGreater(self):
423                 "Determine if the distance after the thread beginning is greater than the step index after startup distance."
424                 if self.startupStepIndex >= len( self.afterStartupDistances ):
425                         return False
426                 return self.getDistanceAfterThreadBeginning() > self.afterStartupDistances[ self.startupStepIndex ]
427
428         def parseInitialization( self, oozebaneRepository ):
429                 'Parse gcode initialization and store the parameters.'
430                 for self.lineIndex in xrange(len(self.lines)):
431                         line = self.lines[self.lineIndex]
432                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
433                         firstWord = gcodec.getFirstWord(splitLine)
434                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
435                         if firstWord == '(</extruderInitialization>)':
436                                 self.distanceFeedRate.addTagBracketedProcedure('oozebane')
437                                 return
438                         elif firstWord == '(<operatingFeedRatePerSecond>':
439                                 self.operatingFeedRateMinute = 60.0 * float(splitLine[1])
440                                 self.feedRateMinute = self.operatingFeedRateMinute
441                         elif firstWord == '(<edgeWidth>':
442                                 self.edgeWidth = float(splitLine[1])
443                                 self.setExtrusionWidth( oozebaneRepository )
444                         self.distanceFeedRate.addLine(line)
445
446         def parseLine(self, line):
447                 "Parse a gcode line and add it to the bevel gcode."
448                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
449                 if len(splitLine) < 1:
450                         return
451                 firstWord = splitLine[0]
452                 if firstWord == 'G1':
453                         self.setEarlyStartupDistance(splitLine)
454                         line = self.getOozebaneLine(line)
455                         self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
456                 elif firstWord == 'M101':
457                         self.isExtruderActive = True
458                         self.extruderInactiveLongEnough = False
459                         if self.getDistanceToExtruderOffCommand( self.earlyShutdownDistance ) == None:
460                                 self.setEarlyShutdown()
461                         if self.getDistanceToExtruderOffCommand( 1.03 * ( self.earlyShutdownDistance + self.afterStartupDistance ) ) == None:
462                                 afterStartupRatio = 1.0
463                                 if self.minimumDistanceForEarlyStartup > 0.0:
464                                         if self.distanceFromThreadEndToThreadBeginning != None:
465                                                 afterStartupRatio = self.distanceFromThreadEndToThreadBeginning / self.minimumDistanceForEarlyStartup
466                                 self.setAfterStartupFlowRates( afterStartupRatio )
467                                 self.startupStepIndex = 9999999999
468                                 if len( self.afterStartupDistances ) > 0:
469                                         self.startupStepIndex = 0
470                         if self.isStartupEarly:
471                                 self.isStartupEarly = False
472                                 return
473                 elif firstWord == 'M103':
474                         self.isExtruderActive = False
475                         self.shutdownStepIndex = 999999999
476                         if self.getDistanceToThreadBeginning() == None:
477                                 self.extruderInactiveLongEnough = True
478                         self.distanceFromThreadEndToThreadBeginning = None
479                         self.earlyStartupDistance = None
480                         if self.isShutdownEarly:
481                                 self.isShutdownEarly = False
482                                 return
483                 self.distanceFeedRate.addLine(line)
484
485         def setAfterStartupFlowRates( self, afterStartupRatio ):
486                 "Set the after startup flow rates."
487                 afterStartupRatio = min( 1.0, afterStartupRatio )
488                 afterStartupRatio = max( 0.0, afterStartupRatio )
489                 self.afterStartupDistance = afterStartupRatio * self.getActiveFeedRateRatio() * self.oozebaneRepository.afterStartupDistance.value
490                 self.afterStartupDistances = []
491                 self.afterStartupFlowRate = 1.0
492                 self.afterStartupFlowRates = []
493                 afterStartupSteps = int( math.floor( afterStartupRatio * float( self.oozebaneRepository.slowdownStartupSteps.value ) ) )
494                 if afterStartupSteps < 1:
495                         return
496                 if afterStartupSteps < 2:
497                         afterStartupSteps = 2
498                 for stepIndex in xrange( afterStartupSteps ):
499                         afterWay = ( stepIndex + 1 ) / float( afterStartupSteps )
500                         afterMiddleWay = self.getStartupFlowRateMultiplier( stepIndex / float( afterStartupSteps ), afterStartupSteps )
501                         self.afterStartupDistances.append( afterWay * self.afterStartupDistance )
502                         if stepIndex == 0:
503                                 self.afterStartupFlowRate = afterMiddleWay
504                         else:
505                                 self.afterStartupFlowRates.append( afterMiddleWay )
506                 if afterStartupSteps > 0:
507                         self.afterStartupFlowRates.append(1.0)
508
509         def setEarlyShutdown(self):
510                 "Set the early shutdown variables."
511                 distanceToThreadBeginning = self.getDistanceToThreadBeginningAfterThreadEnd( self.minimumDistanceForEarlyShutdown )
512                 earlyShutdownRatio = 1.0
513                 if distanceToThreadBeginning != None:
514                         if self.minimumDistanceForEarlyShutdown > 0.0:
515                                 earlyShutdownRatio = distanceToThreadBeginning / self.minimumDistanceForEarlyShutdown
516                 self.setEarlyShutdownFlowRates( earlyShutdownRatio )
517                 if len( self.earlyShutdownDistances ) > 0:
518                         self.shutdownStepIndex = 0
519
520         def setEarlyShutdownFlowRates( self, earlyShutdownRatio ):
521                 "Set the extrusion width."
522                 earlyShutdownRatio = min( 1.0, earlyShutdownRatio )
523                 earlyShutdownRatio = max( 0.0, earlyShutdownRatio )
524                 self.earlyShutdownDistance = earlyShutdownRatio * self.getActiveFeedRateRatio() * self.oozebaneRepository.earlyShutdownDistance.value
525                 self.earlyShutdownDistances = []
526                 self.earlyShutdownFlowRates = []
527                 earlyShutdownSteps = int( math.floor( earlyShutdownRatio * float( self.oozebaneRepository.slowdownStartupSteps.value ) ) )
528                 if earlyShutdownSteps < 2:
529                         earlyShutdownSteps = 0
530                 earlyShutdownStepsMinusOne = float( earlyShutdownSteps ) - 1.0
531                 for stepIndex in xrange( earlyShutdownSteps ):
532                         downMiddleWay = self.getShutdownFlowRateMultiplier( stepIndex / earlyShutdownStepsMinusOne, earlyShutdownSteps )
533                         downWay = 1.0 - stepIndex / earlyShutdownStepsMinusOne
534                         self.earlyShutdownFlowRates.append( downMiddleWay )
535                         self.earlyShutdownDistances.append( downWay * self.earlyShutdownDistance )
536
537         def setEarlyStartupDistance( self, splitLine ):
538                 "Set the early startup distance."
539                 if self.earlyStartupDistance != None:
540                         return
541                 self.distanceFromThreadEndToThreadBeginning = 0.0
542                 lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
543                 if self.oldLocation != None:
544                         self.distanceFromThreadEndToThreadBeginning = lastThreadLocation.distance( self.oldLocation )
545                 for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ):
546                         line = self.lines[ afterIndex ]
547                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
548                         firstWord = gcodec.getFirstWord(splitLine)
549                         if firstWord == 'G1':
550                                 location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine )
551                                 self.distanceFromThreadEndToThreadBeginning += location.distance( lastThreadLocation )
552                                 lastThreadLocation = location
553                         elif firstWord == 'M101':
554                                 distanceConstantRatio = self.distanceFromThreadEndToThreadBeginning / self.earlyStartupDistanceConstant
555                                 earlyStartupOperatingDistance = self.earlyStartupMaximumDistance * ( 1.0 - math.exp( - distanceConstantRatio ) )
556                                 if self.isFirstExtrusion:
557                                         earlyStartupOperatingDistance = self.oozebaneRepository.firstEarlyStartupDistance.value
558                                         self.isFirstExtrusion = False
559                                 self.earlyStartupDistance = earlyStartupOperatingDistance * self.getActiveFeedRateRatio()
560                                 return
561
562         def setExtrusionWidth( self, oozebaneRepository ):
563                 "Set the extrusion width."
564                 self.closeSquared = 0.01 * self.edgeWidth * self.edgeWidth
565                 self.earlyStartupMaximumDistance = oozebaneRepository.earlyStartupMaximumDistance.value
566                 self.earlyStartupDistanceConstant = oozebaneRepository.earlyStartupDistanceConstant.value
567                 self.minimumDistanceForEarlyStartup = oozebaneRepository.minimumDistanceForEarlyStartup.value
568                 self.minimumDistanceForEarlyShutdown = oozebaneRepository.minimumDistanceForEarlyShutdown.value
569                 self.setEarlyShutdownFlowRates(1.0)
570                 self.setAfterStartupFlowRates(1.0)
571
572
573 def main():
574         "Display the oozebane dialog."
575         if len(sys.argv) > 1:
576                 writeOutput(' '.join(sys.argv[1 :]))
577         else:
578                 settings.startMainLoopFromConstructor(getNewRepository())
579
580 if __name__ == "__main__":
581         main()