chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / cura_sf / skeinforge_application / skeinforge_plugins / craft_plugins / wipe.py
1 """
2 This page is in the table of contents.
3 At the beginning of a layer, depending on the settings, wipe will move the nozzle with the extruder off to the arrival point, then to the wipe point, then to the departure point, then back to the layer.
4
5 The wipe path is machine specific, so you'll probably have to change all the default locations.
6
7 The wipe manual page is at:
8 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Wipe
9
10 ==Operation==
11 The default 'Activate Wipe' checkbox is off.  When it is on, the functions described below will work, when it is off, nothing will be done.
12
13 ==Settings==
14 ===Arrival Location===
15 ====Arrival X====
16 Default is minus seventy millimeters.
17
18 Defines the x coordinate of the arrival location.
19
20 ====Arrival Y====
21 Default is minus fifty millimeters.
22
23 Defines the y coordinate of the arrival location.
24
25 ====Arrival Z====
26 Default is fifty millimeters.
27
28 Defines the z coordinate of the arrival location.
29
30 ===Departure Location===
31 ====Departure X====
32 Default is minus seventy millimeters.
33
34 Defines the x coordinate of the departure location.
35
36 ====Departure Y====
37 Default is minus forty millimeters.
38
39 Defines the y coordinate of the departure location.
40
41 ====Departure Z====
42 Default is fifty millimeters.
43
44 Defines the z coordinate of the departure location.
45
46 ===Wipe Location===
47 ====Wipe X====
48 Default is minus seventy millimeters.
49
50 Defines the x coordinate of the wipe location.
51
52 ====Wipe Y====
53 Default is minus seventy millimeters.
54
55 Defines the y coordinate of the wipe location.
56
57 ====Wipe Z====
58 Default is fifty millimeters.
59
60 Defines the z coordinate of the wipe location.
61
62 ===Wipe Period===
63 Default is three.
64
65 Defines the number of layers between wipes.  Wipe will always wipe just before layer zero, afterwards it will wipe every "Wipe Period" layers.  With the default of three, wipe will wipe just before layer zero, layer three, layer six and so on.
66
67 ==Examples==
68 The following examples wipe the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and wipe.py.
69
70 > python wipe.py
71 This brings up the wipe dialog.
72
73 > python wipe.py Screw Holder Bottom.stl
74 The wipe tool is parsing the file:
75 Screw Holder Bottom.stl
76 ..
77 The wipe tool has created the file:
78 .. Screw Holder Bottom_wipe.gcode
79
80 """
81
82 from __future__ import absolute_import
83 #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.
84 import __init__
85
86 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
87 from fabmetheus_utilities.vector3 import Vector3
88 from fabmetheus_utilities import archive
89 from fabmetheus_utilities import euclidean
90 from fabmetheus_utilities import gcodec
91 from fabmetheus_utilities import settings
92 from skeinforge_application.skeinforge_utilities import skeinforge_craft
93 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
94 from skeinforge_application.skeinforge_utilities import skeinforge_profile
95 import math
96 import sys
97
98
99 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
100 __date__ = '$Date: 2008/21/04 $'
101 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
102
103
104 def getCraftedText( fileName, text, wipeRepository = None ):
105         "Wipe a gcode linear move text."
106         return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), wipeRepository )
107
108 def getCraftedTextFromText( gcodeText, wipeRepository = None ):
109         "Wipe a gcode linear move text."
110         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'wipe'):
111                 return gcodeText
112         if wipeRepository == None:
113                 wipeRepository = settings.getReadRepository( WipeRepository() )
114         if not wipeRepository.activateWipe.value:
115                 return gcodeText
116         return WipeSkein().getCraftedGcode( gcodeText, wipeRepository )
117
118 def getNewRepository():
119         'Get new repository.'
120         return WipeRepository()
121
122 def writeOutput(fileName, shouldAnalyze=True):
123         "Wipe a gcode linear move file."
124         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'wipe', shouldAnalyze)
125
126
127 class WipeRepository:
128         "A class to handle the wipe settings."
129         def __init__(self):
130                 "Set the default settings, execute title & settings fileName."
131                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.wipe.html', self)
132                 self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Wipe', self, '')
133                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Wipe')
134                 self.activateWipe = settings.BooleanSetting().getFromValue('Activate Wipe', self, False)
135                 settings.LabelSeparator().getFromRepository(self)
136                 settings.LabelDisplay().getFromName('- Arrival Location -', self)
137                 self.locationArrivalX = settings.FloatSpin().getFromValue(-100.0, 'Arrival X (mm):', self, 100.0, -70.0)
138                 self.locationArrivalY = settings.FloatSpin().getFromValue(-100.0, 'Arrival Y (mm):', self, 100.0, -50.0)
139                 self.locationArrivalZ = settings.FloatSpin().getFromValue(-100.0, 'Arrival Z (mm):', self, 100.0, 50.0)
140                 settings.LabelSeparator().getFromRepository(self)
141                 settings.LabelDisplay().getFromName('- Departure Location -', self)
142                 self.locationDepartureX = settings.FloatSpin().getFromValue(-100.0, 'Departure X (mm):', self, 100.0, -70.0)
143                 self.locationDepartureY = settings.FloatSpin().getFromValue(-100.0, 'Departure Y (mm):', self, 100.0, -40.0)
144                 self.locationDepartureZ = settings.FloatSpin().getFromValue(-100.0, 'Departure Z (mm):', self, 100.0, 50.0)
145                 settings.LabelSeparator().getFromRepository(self)
146                 settings.LabelDisplay().getFromName('- Wipe Location -', self)
147                 self.locationWipeX = settings.FloatSpin().getFromValue(-100.0, 'Wipe X (mm):', self, 100.0, -70.0)
148                 self.locationWipeY = settings.FloatSpin().getFromValue(-100.0, 'Wipe Y (mm):', self, 100.0, -70.0)
149                 self.locationWipeZ = settings.FloatSpin().getFromValue(-100.0, 'Wipe Z (mm):', self, 100.0, 50.0)
150                 settings.LabelSeparator().getFromRepository(self)
151                 self.wipePeriod = settings.IntSpin().getFromValue(1, 'Wipe Period (layers):', self, 5, 3)
152                 self.executeTitle = 'Wipe'
153
154         def execute(self):
155                 "Wipe button has been clicked."
156                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
157                 for fileName in fileNames:
158                         writeOutput(fileName)
159
160
161 class WipeSkein:
162         "A class to wipe a skein of extrusions."
163         def __init__(self):
164                 self.distanceFeedRate = gcodec.DistanceFeedRate()
165                 self.extruderActive = False
166                 self.highestZ = None
167                 self.layerIndex = -1
168                 self.lineIndex = 0
169                 self.lines = None
170                 self.oldLocation = None
171                 self.shouldWipe = False
172                 self.travelFeedRateMinute = 957.0
173
174         def addHop( self, begin, end ):
175                 "Add hop to highest point."
176                 beginEndDistance = begin.distance(end)
177                 if beginEndDistance < 3.0 * self.absoluteEdgeWidth:
178                         return
179                 alongWay = self.absoluteEdgeWidth / beginEndDistance
180                 closeToOldLocation = euclidean.getIntermediateLocation( alongWay, begin, end )
181                 closeToOldLocation.z = self.highestZ
182                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, closeToOldLocation ) )
183                 closeToOldArrival = euclidean.getIntermediateLocation( alongWay, end, begin )
184                 closeToOldArrival.z = self.highestZ
185                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, closeToOldArrival ) )
186
187         def addWipeTravel( self, splitLine ):
188                 "Add the wipe travel gcode."
189                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
190                 self.highestZ = max( self.highestZ, location.z )
191                 if not self.shouldWipe:
192                         return
193                 self.shouldWipe = False
194                 if self.extruderActive:
195                         self.distanceFeedRate.addLine('M103')
196                 if self.oldLocation != None:
197                         self.addHop( self.oldLocation, self.locationArrival )
198                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationArrival ) )
199                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationWipe ) )
200                 self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationDeparture ) )
201                 self.addHop( self.locationDeparture, location )
202                 if self.extruderActive:
203                         self.distanceFeedRate.addLine('M101')
204
205         def getCraftedGcode( self, gcodeText, wipeRepository ):
206                 "Parse gcode text and store the wipe gcode."
207                 self.lines = archive.getTextLines(gcodeText)
208                 self.wipePeriod = wipeRepository.wipePeriod.value
209                 self.parseInitialization( wipeRepository )
210                 self.locationArrival = Vector3( wipeRepository.locationArrivalX.value, wipeRepository.locationArrivalY.value, wipeRepository.locationArrivalZ.value )
211                 self.locationDeparture = Vector3( wipeRepository.locationDepartureX.value, wipeRepository.locationDepartureY.value, wipeRepository.locationDepartureZ.value )
212                 self.locationWipe = Vector3( wipeRepository.locationWipeX.value, wipeRepository.locationWipeY.value, wipeRepository.locationWipeZ.value )
213                 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
214                         line = self.lines[self.lineIndex]
215                         self.parseLine(line)
216                 return self.distanceFeedRate.output.getvalue()
217
218         def getLinearMoveWithFeedRate( self, feedRate, location ):
219                 "Get a linear move line with the feedRate."
220                 return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRate, location.dropAxis(), location.z )
221
222         def parseInitialization( self, wipeRepository ):
223                 'Parse gcode initialization and store the parameters.'
224                 for self.lineIndex in xrange(len(self.lines)):
225                         line = self.lines[self.lineIndex]
226                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
227                         firstWord = gcodec.getFirstWord(splitLine)
228                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
229                         if firstWord == '(</extruderInitialization>)':
230                                 self.distanceFeedRate.addTagBracketedProcedure('wipe')
231                                 return
232                         elif firstWord == '(<edgeWidth>':
233                                 self.absoluteEdgeWidth = abs(float(splitLine[1]))
234                         elif firstWord == '(<travelFeedRatePerSecond>':
235                                 self.travelFeedRateMinute = 60.0 * float(splitLine[1])
236                         self.distanceFeedRate.addLine(line)
237
238         def parseLine(self, line):
239                 "Parse a gcode line and add it to the bevel gcode."
240                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
241                 if len(splitLine) < 1:
242                         return
243                 firstWord = splitLine[0]
244                 if firstWord == 'G1':
245                         self.addWipeTravel(splitLine)
246                         self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
247                 elif firstWord == '(<layer>':
248                         self.layerIndex += 1
249                         settings.printProgress(self.layerIndex, 'wipe')
250                         if self.layerIndex % self.wipePeriod == 0:
251                                 self.shouldWipe = True
252                 elif firstWord == 'M101':
253                         self.extruderActive = True
254                 elif firstWord == 'M103':
255                         self.extruderActive = False
256                 self.distanceFeedRate.addLine(line)
257
258
259 def main():
260         "Display the wipe dialog."
261         if len(sys.argv) > 1:
262                 writeOutput(' '.join(sys.argv[1 :]))
263         else:
264                 settings.startMainLoopFromConstructor(getNewRepository())
265
266 if __name__ == "__main__":
267         main()