chiark / gitweb /
Add back the ultimaker platform, and made the platform mesh simpler.
[cura.git] / Cura / slice / cura_sf / 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
121 from fabmetheus_utilities import archive
122 from fabmetheus_utilities import euclidean
123 from fabmetheus_utilities import gcodec
124 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
125 from fabmetheus_utilities import settings
126 from skeinforge_application.skeinforge_utilities import skeinforge_craft
127 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
128 from skeinforge_application.skeinforge_utilities import skeinforge_profile
129 import sys
130
131
132 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
133 __date__ = '$Date: 2008/21/04 $'
134 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
135
136
137 def getCraftedText( fileName, text='', repository=None):
138         "Speed the file or text."
139         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
140
141 def getCraftedTextFromText(gcodeText, repository=None):
142         "Speed a gcode linear move text."
143         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'speed'):
144                 return gcodeText
145         if repository == None:
146                 repository = settings.getReadRepository( SpeedRepository() )
147         if not repository.activateSpeed.value:
148                 return gcodeText
149         return SpeedSkein().getCraftedGcode(gcodeText, repository)
150
151 def getNewRepository():
152         'Get new repository.'
153         return SpeedRepository()
154
155 def writeOutput(fileName, shouldAnalyze=True):
156         "Speed a gcode linear move file."
157         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'speed', shouldAnalyze)
158
159
160 class SpeedRepository(object):
161         "A class to handle the speed settings."
162         def __init__(self):
163                 "Set the default settings, execute title & settings fileName."
164                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.speed.html', self )
165                 self.baseNameSynonymDictionary = {
166                         'Object First Layer Feed Rate Infill Multiplier (ratio):' : 'raft.csv',
167                         'Object First Layer Feed Rate Perimeter Multiplier (ratio):' : 'raft.csv',
168                         'Object First Layer Flow Rate Infill Multiplier (ratio):' : 'raft.csv',
169                         'Object First Layer Flow Rate Perimeter Multiplier (ratio):' : 'raft.csv'}
170                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Speed', self, '')
171                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed')
172                 self.activateSpeed = settings.BooleanSetting().getFromValue('Activate Speed', self, True )
173                 self.addFlowRate = settings.BooleanSetting().getFromValue('Add Flow Rate:', self, False )
174                 settings.LabelSeparator().getFromRepository(self)
175                 settings.LabelDisplay().getFromName('- Bridge -', self )
176                 self.bridgeFeedRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Feed Rate Multiplier (ratio):', self, 1.2, 1.0 )
177                 self.bridgeFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Flow Rate Multiplier (ratio):', self, 1.2, 1.0 )
178                 settings.LabelSeparator().getFromRepository(self)
179                 settings.LabelDisplay().getFromName('- Duty Cyle -', self )
180                 self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 )
181                 self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 )
182                 settings.LabelSeparator().getFromRepository(self)
183                 self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 )
184                 self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 )
185                 settings.LabelSeparator().getFromRepository(self)
186                 settings.LabelDisplay().getFromName('- Object First Layers -', self)
187                 self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue(
188                         0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4)
189                 self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue(
190                         0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4)
191                 self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue(
192                         0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4)
193                 self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue(
194                         0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4)
195                 self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue(
196                         0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4)
197                 self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue(
198                         1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3)
199                 settings.LabelSeparator().getFromRepository(self)
200                 self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 )
201                 self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0)
202                 settings.LabelSeparator().getFromRepository(self)
203                 settings.LabelDisplay().getFromName('- Perimeter -', self )
204                 self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0)
205                 self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0)
206                 settings.LabelSeparator().getFromRepository(self)
207                 self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 )
208                 self.executeTitle = 'Speed'
209                 
210                 self.bottomLayerFlowRateMultiplier = settings.FloatSpin().getFromValue(0.0, 'Bottom layer flow rate (ratio):', self, 10.0, 1.0)
211
212         def execute(self):
213                 "Speed button has been clicked."
214                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
215                 for fileName in fileNames:
216                         writeOutput(fileName)
217
218
219 class SpeedSkein(object):
220         "A class to speed a skein of extrusions."
221         def __init__(self):
222                 'Initialize.'
223                 self.distanceFeedRate = gcodec.DistanceFeedRate()
224                 self.feedRatePerSecond = 16.0
225                 self.isBridgeLayer = False
226                 self.isEdgePath = False
227                 self.isExtruderActive = False
228                 self.layerIndex = -1
229                 self.lineIndex = 0
230                 self.lines = None
231                 self.oldFlowRate = None
232
233         def addFlowRateLine(self):
234                 "Add flow rate line."
235                 if not self.repository.addFlowRate.value:
236                         return
237                 flowRate = self.repository.flowRateSetting.value
238                 if self.isBridgeLayer:
239                         flowRate *= self.repository.bridgeFlowRateMultiplier.value
240                 if self.isEdgePath:
241                         flowRate *= self.repository.perimeterFlowRateMultiplier.value
242                 if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value:
243                         if self.isEdgePath:
244                                 flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
245                         else:
246                                 flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value
247                 if self.layerIndex == 0:
248                         flowRate *= self.repository.bottomLayerFlowRateMultiplier.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 * self.repository.bottomLayerFlowRateMultiplier.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()