chiark / gitweb /
1827bd478ee7cfbf4d39520b80fbfebd7d2c5349
[cura.git] / Cura / skeinforge_application / skeinforge_plugins / craft_plugins / unpause.py
1 """
2 This page is in the table of contents.
3 The unpause plugin is based on the Shane Hathaway's patch to speed up a line segment to compensate for the delay of the microprocessor.  The description is at:
4 http://shane.willowrise.com/archives/delay-compensation-in-firmware/
5
6 The unpause manual page is at:
7 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Unpause
8
9 ==Operation==
10 The default 'Activate Unpause' checkbox is off.  When it is on, the functions described below will work, when it is off, nothing will be done.
11
12 ==Settings==
13 ===Delay===
14 Default is 28 milliseconds, which Shane found for the Arduino.
15
16 Defines the delay on the microprocessor that will be at least partially compensated for.
17
18 ===Maximum Speed===
19 Default is 1.3.
20
21 Defines the maximum amount that the feed rate will be sped up to, compared to the original feed rate.
22
23 ==Examples==
24 The following examples unpause the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and unpause.py.
25
26 > python unpause.py
27 This brings up the unpause dialog.
28
29 > python unpause.py Screw Holder Bottom.stl
30 The unpause tool is parsing the file:
31 Screw Holder Bottom.stl
32 ..
33 The unpause tool has created the file:
34 .. Screw Holder Bottom_unpause.gcode
35
36 """
37
38 from __future__ import absolute_import
39 #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.
40 import __init__
41
42 from fabmetheus_utilities import archive
43 from fabmetheus_utilities import euclidean
44 from fabmetheus_utilities import gcodec
45 from fabmetheus_utilities import intercircle
46 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
47 from fabmetheus_utilities import settings
48 from skeinforge_application.skeinforge_utilities import skeinforge_craft
49 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
50 from skeinforge_application.skeinforge_utilities import skeinforge_profile
51 import math
52 import sys
53
54
55 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
56 __date__ = '$Date: 2008/21/04 $'
57 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
58
59
60 def getCraftedText( fileName, gcodeText, repository=None):
61         "Unpause a gcode linear move file or text."
62         return getCraftedTextFromText( archive.getTextIfEmpty( fileName, gcodeText ), repository )
63
64 def getCraftedTextFromText(gcodeText, repository=None):
65         "Unpause a gcode linear move text."
66         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'unpause'):
67                 return gcodeText
68         if repository == None:
69                 repository = settings.getReadRepository( UnpauseRepository() )
70         if not repository.activateUnpause.value:
71                 return gcodeText
72         return UnpauseSkein().getCraftedGcode(gcodeText, repository)
73
74 def getNewRepository():
75         'Get new repository.'
76         return UnpauseRepository()
77
78 def getSelectedPlugin(repository):
79         "Get the selected plugin."
80         for plugin in repository.unpausePlugins:
81                 if plugin.value:
82                         return plugin
83         return None
84
85 def writeOutput(fileName, shouldAnalyze=True):
86         "Unpause a gcode linear move file."
87         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'unpause', shouldAnalyze)
88
89
90 class UnpauseRepository:
91         "A class to handle the unpause settings."
92         def __init__(self):
93                 "Set the default settings, execute title & settings fileName."
94                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.unpause.html', self)
95                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Unpause', self, '')
96                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Unpause')
97                 self.activateUnpause = settings.BooleanSetting().getFromValue('Activate Unpause', self, False )
98                 self.delay = settings.FloatSpin().getFromValue( 2.0, 'Delay (milliseconds):', self, 42.0, 28.0 )
99                 self.maximumSpeed = settings.FloatSpin().getFromValue( 1.1, 'Maximum Speed (ratio):', self, 1.9, 1.3 )
100                 self.executeTitle = 'Unpause'
101
102         def execute(self):
103                 "Unpause button has been clicked."
104                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
105                 for fileName in fileNames:
106                         writeOutput(fileName)
107
108
109 class UnpauseSkein:
110         "A class to unpause a skein of extrusions."
111         def __init__(self):
112                 self.distanceFeedRate = gcodec.DistanceFeedRate()
113                 self.feedRateMinute = 959.0
114                 self.lineIndex = 0
115                 self.lines = None
116                 self.oldLocation = None
117
118         def getCraftedGcode(self, gcodeText, repository):
119                 "Parse gcode text and store the unpause gcode."
120                 self.delaySecond = repository.delay.value * 0.001
121                 self.maximumSpeed = repository.maximumSpeed.value
122                 self.minimumSpeedUpReciprocal = 1.0 / self.maximumSpeed
123                 self.repository = repository
124                 self.lines = archive.getTextLines(gcodeText)
125                 self.parseInitialization()
126                 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
127                         line = self.lines[self.lineIndex]
128                         self.parseLine(line)
129                 return self.distanceFeedRate.output.getvalue()
130
131         def getUnpausedArcMovement( self, line, splitLine ):
132                 "Get an unpaused arc movement."
133                 self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine )
134                 if self.oldLocation == None:
135                         return line
136                 relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
137                 self.oldLocation += relativeLocation
138                 distance = gcodec.getArcDistance(relativeLocation, splitLine)
139                 return self.getUnpausedMovement(distance, line, splitLine)
140
141         def getUnpausedLinearMovement( self, line, splitLine ):
142                 "Get an unpaused linear movement."
143                 self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine )
144                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
145                 if self.oldLocation == None:
146                         self.oldLocation = location
147                         return line
148                 distance = abs(self.oldLocation - location)
149                 self.oldLocation = location
150                 return self.getUnpausedMovement(distance, line, splitLine)
151
152         def getUnpausedMovement(self, distance, line, splitLine):
153                 "Get an unpaused movement."
154                 if distance <= 0.0:
155                         return line
156                 resultantReciprocal = 1.0 - self.delaySecond / distance * self.feedRateMinute / 60.0
157                 resultantReciprocal = max(self.minimumSpeedUpReciprocal, resultantReciprocal)
158                 return self.distanceFeedRate.getLineWithFeedRate(self.feedRateMinute / resultantReciprocal, line, splitLine)
159
160         def parseInitialization(self):
161                 'Parse gcode initialization and store the parameters.'
162                 for self.lineIndex in xrange(len(self.lines)):
163                         line = self.lines[self.lineIndex]
164                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
165                         firstWord = gcodec.getFirstWord(splitLine)
166                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
167                         if firstWord == '(</extruderInitialization>)':
168                                 self.distanceFeedRate.addTagBracketedProcedure('unpause')
169                                 return
170                         self.distanceFeedRate.addLine(line)
171
172         def parseLine(self, line):
173                 "Parse a gcode line."
174                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
175                 if len(splitLine) < 1:
176                         return
177                 firstWord = splitLine[0]
178                 if firstWord == 'G1':
179                         line = self.getUnpausedLinearMovement( line, splitLine )
180                 if firstWord == 'G2' or firstWord == 'G3':
181                         line = self.getUnpausedArcMovement( line, splitLine )
182                 self.distanceFeedRate.addLine(line)
183
184
185 def main():
186         "Display the unpause dialog."
187         if len(sys.argv) > 1:
188                 writeOutput(' '.join(sys.argv[1 :]))
189         else:
190                 settings.startMainLoopFromConstructor(getNewRepository())
191
192 if __name__ == "__main__":
193         main()