chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / skeinforge_application / skeinforge_plugins / craft_plugins / speed.py
1 """
2 This page is in the table of contents.
3 Speed is a plugin to set the feed rate and flow rate.
4
5 The speed manual page is at:
6 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed
7
8 ==Operation==
9 The default 'Activate Speed' checkbox is on.  When it is on, the functions described below will work, when it is off, nothing will be done.
10
11 ==Settings==
12 ===Add Flow Rate===
13 Default is on.
14
15 When selected, the flow rate will be added to the gcode.
16
17 ===Bridge===
18 ====Bridge Feed Rate Multiplier====
19 Default is one.
20
21 Defines the ratio of the feed rate (head speed) on the bridge layers over the feed rate of the typical non bridge layers.
22
23 ====Bridge Flow Rate Multiplier====
24 Default is one.
25
26 Defines the ratio of the flow rate (extruder speed) on the bridge layers over the flow rate of the typical non bridge layers.
27
28 ===Duty Cyle===
29 ====Duty Cyle at Beginning====
30 Default is one, which will set the extruder motor to full current.
31
32 Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the beginning of the gcode text.  If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string.  To turn off the extruder, set the 'Duty Cyle at Beginning' to zero.
33
34 ====Duty Cyle at Ending====
35 Default is zero, which will turn off the extruder motor.
36
37 Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the ending of the gcode text.  If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string.  To turn off the extruder, set the 'Duty Cyle at Ending' to zero.
38
39 ===Feed Rate===
40 Default is sixteen millimeters per second.
41
42 Defines the operating feed rate, the speed your printing head moves in XY plane, before any modifiers.
43
44 ===Flow Rate Setting===
45 Default is 210.
46
47 Defines the operating flow rate.
48
49 RapMan uses this parameter to define the RPM of the extruder motor.  The extruder motor RPM is flow rate / 10 so if your flow rate is 150.0 that will set the extruder stepper to run at 15 RPM, different printers might read this value differently.
50
51 ===Maximum Z Feed Rate===
52 Default is one millimeter per second.
53
54 Defines the speed of a vertical hop, like the infill hop in skin.  Also, if the Limit plugin is activated, it will limit the maximum speed of the tool head in the z direction to this value.
55
56 ===Object First Layer===
57
58 ====Object First Layer Feed Rate Infill Multiplier====
59 Default is 0.4.
60
61 Defines the object first layer infill feed rate multiplier.  The greater the 'Object First Layer Feed Rate Infill Multiplier, the thinner the infill, the lower the 'Object First Layer Feed Rate Infill Multiplier', the thicker the infill.
62
63 ====Object First Layer Feed Rate Perimeter Multiplier====
64 Default is 0.4.
65
66 Defines the object first layer edge feed rate multiplier.  The greater the 'Object First Layer Feed Rate Perimeter Multiplier, the thinner the edge, the lower the 'Object First Layer Feed Rate Perimeter Multiplier', the thicker the edge.
67
68 ====Object First Layer Flow Rate Infill Multiplier====
69 Default is 0.4.
70
71 Defines the object first layer infill flow rate multiplier.  The greater the 'Object First Layer Flow Rate Infill Multiplier', the thicker the infill, the lower the 'Object First Layer Flow Rate Infill Multiplier, the thinner the infill.
72
73 ====Object First Layer Flow Rate Perimeter Multiplier====
74 Default is 0.4.
75
76 Defines the object first layer edge flow rate multiplier.  The greater the 'Object First Layer Flow Rate Perimeter Multiplier', the thicker the edge, the lower the 'Object First Layer Flow Rate Perimeter Multiplier, the thinner the edge.
77
78 ===Orbital Feed Rate over Operating Feed Rate===
79 Default is 0.5.
80
81 Defines the speed when the head is orbiting compared to the operating extruder speed.  If you want the orbit to be very short, set the "Orbital Feed Rate over Operating Feed Rate" setting to a low value like 0.1.
82
83 ===Perimeter===
84 To have higher build quality on the outside at the expense of slower build speed, a typical setting for the 'Perimeter Feed Rate over Operating Feed Rate' would be 0.5.  To go along with that, if you are using a speed controlled extruder like a stepper extruder, the 'Perimeter Flow Rate over Operating Flow Rate' should also be 0.5.
85
86 A stepper motor is the best way of driving the extruder; however, if you are stuck with a DC motor extruder using Pulse Width Modulation to control the speed, then you'll probably need a slightly higher ratio because there is a minimum voltage 'Flow Rate PWM Setting' required for the extruder motor to turn.  The flow rate PWM ratio would be determined by trial and error, with the first trial being:
87 Perimeter Flow Rate over Operating Flow Rate ~ Perimeter Feed Rate over Operating Feed Rate * (Flow Rate PWM Setting - Minimum Flow Rate PWM Setting) + Minimum Flow Rate PWM Setting
88
89 ====Perimeter Feed Rate Multiplier====
90 Default: 1.0
91
92 Defines the ratio of the feed rate of the edge (outside shell) over the feed rate of the infill.  If you for example set this to 0.8 you will have a "stronger" outside edge than inside extrusion as the outside edge will be printed slower hence better lamination will occur and more filament will be placed there.
93
94 ====Perimeter Flow Rate Multiplier====
95 Default: 1.0
96
97 Defines the ratio of the flow rate of the edge (outside shell) over the flow rate of the infill.  If you want the same thickness of the edge but better lamination you need to compensate for the slower feed rate by slowing down the flow rate, but all combinations are possible for different results.
98
99 ===Travel Feed Rate===
100 Default is sixteen millimeters per second.
101
102 Defines the feed rate when the extruder is off (not printing).  The 'Travel Feed Rate' could be set as high as the extruder can be moved, it is not limited by the maximum extrusion rate.
103
104 ==Examples==
105 The following examples speed the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and speed.py.
106
107 > python speed.py
108 This brings up the speed dialog.
109
110 > python speed.py Screw Holder Bottom.stl
111 The speed tool is parsing the file:
112 Screw Holder Bottom.stl
113 ..
114 The speed tool has created the file:
115 .. Screw Holder Bottom_speed.gcode
116
117 """
118
119 from __future__ import absolute_import
120 #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.
121 import __init__
122
123 from fabmetheus_utilities import archive
124 from fabmetheus_utilities import euclidean
125 from fabmetheus_utilities import gcodec
126 from fabmetheus_utilities import intercircle
127 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
128 from fabmetheus_utilities import settings
129 from skeinforge_application.skeinforge_utilities import skeinforge_craft
130 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
131 from skeinforge_application.skeinforge_utilities import skeinforge_profile
132 import math
133 import sys
134
135
136 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
137 __date__ = '$Date: 2008/21/04 $'
138 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
139
140
141 def getCraftedText( fileName, text='', repository=None):
142         "Speed the file or text."
143         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
144
145 def getCraftedTextFromText(gcodeText, repository=None):
146         "Speed a gcode linear move text."
147         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'speed'):
148                 return gcodeText
149         if repository == None:
150                 repository = settings.getReadRepository( SpeedRepository() )
151         if not repository.activateSpeed.value:
152                 return gcodeText
153         return SpeedSkein().getCraftedGcode(gcodeText, repository)
154
155 def getNewRepository():
156         'Get new repository.'
157         return SpeedRepository()
158
159 def writeOutput(fileName, shouldAnalyze=True):
160         "Speed a gcode linear move file."
161         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'speed', shouldAnalyze)
162
163
164 class SpeedRepository:
165         "A class to handle the speed settings."
166         def __init__(self):
167                 "Set the default settings, execute title & settings fileName."
168                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.speed.html', self )
169                 self.baseNameSynonymDictionary = {
170                         'Object First Layer Feed Rate Infill Multiplier (ratio):' : 'raft.csv',
171                         'Object First Layer Feed Rate Perimeter Multiplier (ratio):' : 'raft.csv',
172                         'Object First Layer Flow Rate Infill Multiplier (ratio):' : 'raft.csv',
173                         'Object First Layer Flow Rate Perimeter Multiplier (ratio):' : 'raft.csv'}
174                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Speed', self, '')
175                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed')
176                 self.activateSpeed = settings.BooleanSetting().getFromValue('Activate Speed', self, True )
177                 self.addFlowRate = settings.BooleanSetting().getFromValue('Add Flow Rate:', self, False )
178                 settings.LabelSeparator().getFromRepository(self)
179                 settings.LabelDisplay().getFromName('- Bridge -', self )
180                 self.bridgeFeedRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Feed Rate Multiplier (ratio):', self, 1.2, 1.0 )
181                 self.bridgeFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Flow Rate Multiplier (ratio):', self, 1.2, 1.0 )
182                 settings.LabelSeparator().getFromRepository(self)
183                 settings.LabelDisplay().getFromName('- Duty Cyle -', self )
184                 self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 )
185                 self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 )
186                 settings.LabelSeparator().getFromRepository(self)
187                 self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 )
188                 self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 )
189                 settings.LabelSeparator().getFromRepository(self)
190                 settings.LabelDisplay().getFromName('- Object First Layers -', self)
191                 self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue(
192                         0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4)
193                 self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue(
194                         0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4)
195                 self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue(
196                         0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4)
197                 self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue(
198                         0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4)
199                 self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue(
200                         0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4)
201                 self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue(
202                         1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3)
203                 settings.LabelSeparator().getFromRepository(self)
204                 self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 )
205                 self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0)
206                 settings.LabelSeparator().getFromRepository(self)
207                 settings.LabelDisplay().getFromName('- Perimeter -', self )
208                 self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0)
209                 self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0)
210                 settings.LabelSeparator().getFromRepository(self)
211                 self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 )
212                 self.executeTitle = 'Speed'
213
214         def execute(self):
215                 "Speed button has been clicked."
216                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
217                 for fileName in fileNames:
218                         writeOutput(fileName)
219
220
221 class SpeedSkein:
222         "A class to speed a skein of extrusions."
223         def __init__(self):
224                 'Initialize.'
225                 self.distanceFeedRate = gcodec.DistanceFeedRate()
226                 self.feedRatePerSecond = 16.0
227                 self.isBridgeLayer = False
228                 self.isEdgePath = False
229                 self.isExtruderActive = False
230                 self.layerIndex = -1
231                 self.lineIndex = 0
232                 self.lines = None
233                 self.oldFlowRate = None
234
235         def addFlowRateLine(self):
236                 "Add flow rate line."
237                 if not self.repository.addFlowRate.value:
238                         return
239                 flowRate = self.repository.flowRateSetting.value
240                 if self.isBridgeLayer:
241                         flowRate *= self.repository.bridgeFlowRateMultiplier.value
242                 if self.isEdgePath:
243                         flowRate *= self.repository.perimeterFlowRateMultiplier.value
244                 if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value:
245                         if self.isEdgePath:
246                                 flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
247                         else:
248                                 flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
249                 if flowRate != self.oldFlowRate:
250                         self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
251                 self.oldFlowRate = flowRate
252
253         def addParameterString( self, firstWord, parameterWord ):
254                 "Add parameter string."
255                 if parameterWord == '':
256                         self.distanceFeedRate.addLine(firstWord)
257                         return
258                 self.distanceFeedRate.addParameter( firstWord, parameterWord )
259
260         def getCraftedGcode(self, gcodeText, repository):
261                 "Parse gcode text and store the speed gcode."
262                 self.repository = repository
263                 self.feedRatePerSecond = repository.feedRatePerSecond.value
264                 self.travelFeedRateMinute = 60.0 * self.repository.travelFeedRatePerSecond.value
265                 self.lines = archive.getTextLines(gcodeText)
266                 self.parseInitialization()
267                 for line in self.lines[self.lineIndex :]:
268                         self.parseLine(line)
269                 self.addParameterString('M113', self.repository.dutyCycleAtEnding.value ) # Set duty cycle .
270                 return self.distanceFeedRate.output.getvalue()
271
272         def getSpeededLine(self, line, splitLine):
273                 'Get gcode line with feed rate.'
274                 if gcodec.getIndexOfStartingWithSecond('F', splitLine) > 0:
275                         return line
276                 feedRateMinute = 60.0 * self.feedRatePerSecond
277                 if self.isBridgeLayer:
278                         feedRateMinute *= self.repository.bridgeFeedRateMultiplier.value
279                 if self.isEdgePath:
280                         feedRateMinute *= self.repository.perimeterFeedRateMultiplier.value
281                 if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value:
282                         if self.isEdgePath:
283                                 feedRateMinute *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
284                         else:
285                                 feedRateMinute *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
286                 self.addFlowRateLine()
287                 if not self.isExtruderActive:
288                         feedRateMinute = self.travelFeedRateMinute
289                         if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value:
290                                 feedRateMinute *= ((self.repository.objectFirstLayerFeedRateTravelMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
291                 return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine)
292
293         def parseInitialization(self):
294                 'Parse gcode initialization and store the parameters.'
295                 for self.lineIndex in xrange(len(self.lines)):
296                         line = self.lines[self.lineIndex]
297                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
298                         firstWord = gcodec.getFirstWord(splitLine)
299                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
300                         if firstWord == '(<layerHeight>':
301                                 self.layerHeight = float(splitLine[1])
302                         elif firstWord == '(</extruderInitialization>)':
303                                 self.distanceFeedRate.addTagBracketedProcedure('speed')
304                                 return
305                         elif firstWord == '(<edgeWidth>':
306                                 self.absoluteEdgeWidth = abs(float(splitLine[1]))
307                                 self.distanceFeedRate.addTagBracketedLine('maximumZFeedRatePerSecond', self.repository.maximumZFeedRatePerSecond.value )
308                                 self.distanceFeedRate.addTagBracketedLine('objectFirstLayerFeedRateInfillMultiplier', self.repository.objectFirstLayerFeedRateInfillMultiplier.value)
309                                 self.distanceFeedRate.addTagBracketedLine('operatingFeedRatePerSecond', self.feedRatePerSecond )
310                                 if self.repository.addFlowRate.value:
311                                         self.distanceFeedRate.addTagBracketedLine('objectFirstLayerFlowRateInfillMultiplier', self.repository.objectFirstLayerFlowRateInfillMultiplier.value)
312                                         self.distanceFeedRate.addTagBracketedLine('operatingFlowRate', self.repository.flowRateSetting.value )
313                                 orbitalFeedRatePerSecond = self.feedRatePerSecond * self.repository.orbitalFeedRateOverOperatingFeedRate.value
314                                 self.distanceFeedRate.addTagBracketedLine('orbitalFeedRatePerSecond', orbitalFeedRatePerSecond )
315                                 self.distanceFeedRate.addTagBracketedLine('travelFeedRatePerSecond', self.repository.travelFeedRatePerSecond.value )
316                         self.distanceFeedRate.addLine(line)
317
318         def parseLine(self, line):
319                 "Parse a gcode line and add it to the speed skein."
320                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
321                 if len(splitLine) < 1:
322                         return
323                 firstWord = splitLine[0]
324                 if firstWord == '(<crafting>)':
325                         self.distanceFeedRate.addLine(line)
326                         self.addParameterString('M113', self.repository.dutyCycleAtBeginning.value ) # Set duty cycle .
327                         return
328                 elif firstWord == 'G1':
329                         line = self.getSpeededLine(line, splitLine)
330                 elif firstWord == 'M101':
331                         self.isExtruderActive = True
332                 elif firstWord == 'M103':
333                         self.isExtruderActive = False
334                 elif firstWord == '(<bridgeRotation>':
335                         self.isBridgeLayer = True
336                 elif firstWord == '(<layer>':
337                         self.layerIndex += 1
338                         settings.printProgress(self.layerIndex, 'speed')
339                         self.isBridgeLayer = False
340                         self.addFlowRateLine()
341                 elif firstWord == '(<edge>' or firstWord == '(<edgePath>)':
342                         self.isEdgePath = True
343                 elif firstWord == '(</edge>)' or firstWord == '(</edgePath>)':
344                         self.isEdgePath = False
345                 self.distanceFeedRate.addLine(line)
346
347
348 def main():
349         "Display the speed dialog."
350         if len(sys.argv) > 1:
351                 writeOutput(' '.join(sys.argv[1 :]))
352         else:
353                 settings.startMainLoopFromConstructor(getNewRepository())
354
355 if __name__ == "__main__":
356         main()