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 / lift.py
1 """
2 This page is in the table of contents.
3 Lift will change the altitude of the cutting tool when it is on so that it will cut through the slab at the correct altitude.  It will also lift the gcode when the tool is off so that the cutting tool will clear the top of the slab.
4
5 ==Operation==
6 The default 'Activate Lift' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions will not be called.
7
8 ==Settings==
9 ===Cutting Lift over Layer Step===
10 Default is minus 0.5, because the end mill is the more common tool.
11
12 Defines the ratio of the amount the cutting tool will be lifted over the layer step.  If whittle is off the layer step will be the layer height, if it is on, it will be the layer step from the whittle gcode.  If the cutting tool is like an end mill, where the cutting happens until the end of the tool, then the 'Cutting Lift over Layer Step' should be minus 0.5, so that the end mill cuts to the bottom of the slab.  If the cutting tool is like a laser, where the cutting happens around the focal point. the 'Cutting Lift over Layer Step' should be zero, so that the cutting action will be focused in the middle of the slab.
13
14 ===Clearance above Top===
15 Default is 5 millimeters.
16
17 Defines the distance above the top of the slab the cutting tool will be lifted when will tool is off so that the cutting tool will clear the top of the slab.
18
19 ==Examples==
20 The following examples lift the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and lift.py.
21
22 > python lift.py
23 This brings up the lift dialog.
24
25 > python lift.py Screw Holder Bottom.stl
26 The lift tool is parsing the file:
27 Screw Holder Bottom.stl
28 ..
29 The lift tool has created the file:
30 .. Screw Holder Bottom_lift.gcode
31
32 """
33
34 from __future__ import absolute_import
35 #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.
36 import __init__
37
38 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
39 from fabmetheus_utilities import archive
40 from fabmetheus_utilities import gcodec
41 from fabmetheus_utilities import settings
42 from skeinforge_application.skeinforge_utilities import skeinforge_craft
43 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
44 from skeinforge_application.skeinforge_utilities import skeinforge_profile
45 import sys
46
47
48 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
49 __date__ = '$Date: 2008/02/05 $'
50 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
51
52
53 def getCraftedText( fileName, text='', liftRepository = None ):
54         "Lift the preface file or text."
55         return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), liftRepository )
56
57 def getCraftedTextFromText( gcodeText, liftRepository = None ):
58         "Lift the preface gcode text."
59         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'lift'):
60                 return gcodeText
61         if liftRepository == None:
62                 liftRepository = settings.getReadRepository( LiftRepository() )
63         if not liftRepository.activateLift.value:
64                 return gcodeText
65         return LiftSkein().getCraftedGcode( liftRepository, gcodeText )
66
67 def getNewRepository():
68         'Get new repository.'
69         return LiftRepository()
70
71 def writeOutput(fileName, shouldAnalyze=True):
72         "Lift the carving of a gcode file."
73         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'lift', shouldAnalyze)
74
75
76 class LiftRepository:
77         "A class to handle the lift settings."
78         def __init__(self):
79                 "Set the default settings, execute title & settings fileName."
80                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.lift.html', self )
81                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Lifted', self, '')
82                 self.activateLift = settings.BooleanSetting().getFromValue('Activate Lift', self, True )
83                 self.cuttingLiftOverLayerStep = settings.FloatSpin().getFromValue( - 1.0, 'Cutting Lift over Layer Step (ratio):', self, 1.0, - 0.5 )
84                 self.clearanceAboveTop = settings.FloatSpin().getFromValue( 0.0, 'Clearance above Top (mm):', self, 10.0, 5.0 )
85                 self.executeTitle = 'Lift'
86
87         def execute(self):
88                 "Lift button has been clicked."
89                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
90                 for fileName in fileNames:
91                         writeOutput(fileName)
92
93
94 class LiftSkein:
95         "A class to lift a skein of extrusions."
96         def __init__(self):
97                 self.distanceFeedRate = gcodec.DistanceFeedRate()
98                 self.extruderActive = False
99                 self.layerStep = None
100                 self.layerHeight = 0.3333333333
101                 self.lineIndex = 0
102                 self.maximumZ = - 912345678.0
103                 self.oldLocation = None
104                 self.previousActiveMovementLine = None
105                 self.previousInactiveMovementLine = None
106
107         def addPreviousInactiveMovementLineIfNecessary(self):
108                 "Add the previous inactive movement line if necessary."
109                 if self.previousInactiveMovementLine != None:
110                         self.distanceFeedRate.addLine( self.previousInactiveMovementLine )
111                         self.previousInactiveMovementLine = None
112
113         def getCraftedGcode( self, liftRepository, gcodeText ):
114                 "Parse gcode text and store the lift gcode."
115                 self.liftRepository = liftRepository
116                 self.lines = archive.getTextLines(gcodeText)
117                 self.parseInitialization()
118                 self.oldLocation = None
119                 if self.layerStep == None:
120                         self.layerStep = self.layerHeight
121                 self.cuttingLift = self.layerStep * liftRepository.cuttingLiftOverLayerStep.value
122                 self.setMaximumZ()
123                 self.travelZ = self.maximumZ + 0.5 * self.layerStep + liftRepository.clearanceAboveTop.value
124                 for line in self.lines[self.lineIndex :]:
125                         self.parseLine(line)
126                 return self.distanceFeedRate.output.getvalue()
127
128         def getLinearMove( self, line, location, splitLine ):
129                 "Get the linear move."
130                 if self.extruderActive:
131                         z = location.z + self.cuttingLift
132                         return self.distanceFeedRate.getLineWithZ( line, splitLine, z )
133                 if self.previousActiveMovementLine != None:
134                         previousActiveMovementLineSplit = self.previousActiveMovementLine.split()
135                         self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( self.previousActiveMovementLine, previousActiveMovementLineSplit, self.travelZ ) )
136                         self.previousActiveMovementLine = None
137                 self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( line, splitLine, self.travelZ ) )
138                 self.previousInactiveMovementLine = line
139                 return ''
140
141         def parseInitialization(self):
142                 'Parse gcode initialization and store the parameters.'
143                 for self.lineIndex in xrange(len(self.lines)):
144                         line = self.lines[self.lineIndex].lstrip()
145                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
146                         firstWord = gcodec.getFirstWord(splitLine)
147                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
148                         if firstWord == '(</extruderInitialization>)':
149                                 self.distanceFeedRate.addTagBracketedProcedure('lift')
150                                 return
151                         elif firstWord == '(<layerHeight>':
152                                 self.layerHeight = float(splitLine[1])
153                         elif firstWord == '(<layerStep>':
154                                 self.layerStep = float(splitLine[1])
155                         self.distanceFeedRate.addLine(line)
156
157         def parseLine(self, line):
158                 "Parse a gcode line and add it to the lift skein."
159                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
160                 if len(splitLine) < 1:
161                         return
162                 firstWord = splitLine[0]
163                 if firstWord == 'G1':
164                         location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
165                         line = self.getLinearMove( line, location, splitLine )
166                         self.previousActiveMovementLine = line
167                         self.oldLocation = location
168                 elif firstWord == 'M101':
169                         self.addPreviousInactiveMovementLineIfNecessary()
170                         self.extruderActive = True
171                 elif firstWord == 'M103':
172                         self.extruderActive = False
173                 self.distanceFeedRate.addLine(line)
174
175         def setMaximumZ(self):
176                 "Set maximum  z."
177                 localOldLocation = None
178                 for line in self.lines[self.lineIndex :]:
179                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
180                         firstWord = gcodec.getFirstWord(splitLine)
181                         if firstWord == 'G1':
182                                 location = gcodec.getLocationFromSplitLine( localOldLocation, splitLine )
183                                 self.maximumZ = max( self.maximumZ, location.z )
184                                 localOldLocation = location
185
186
187 def main():
188         "Display the lift dialog."
189         if len(sys.argv) > 1:
190                 writeOutput(' '.join(sys.argv[1 :]))
191         else:
192                 settings.startMainLoopFromConstructor(getNewRepository())
193
194 if __name__ == "__main__":
195         main()