chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / skeinforge_application / skeinforge_plugins / craft_plugins / whittle.py
1 """
2 This page is in the table of contents.
3 Whittle will convert each polygon of a gcode file into a helix which has a vertical step down on each rotation.
4
5 ==Operation==
6 The default 'Activate Whittle' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions will not be called.  If the cutting tool can cut the slab in one cut, the 'Activate Whittle' checkbox should be off, the default is off.
7
8 ==Settings==
9 ===Maximum Vertical Step'===
10 Default is 0.1 mm.
11
12 Defines the maximum distance that the helix will step down on each rotation.  The number of steps in the helix will be the layer height divided by the 'Maximum Vertical Step', rounded up.  The amount the helix will step down is the layer height divided by the number of steps.  The thinner the 'Maximum Vertical Step', the more times the cutting tool will circle around on its way to the bottom of the slab.
13
14 ==Examples==
15 The following examples whittle the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and whittle.py.
16
17 > python whittle.py
18 This brings up the whittle dialog.
19
20 > python whittle.py Screw Holder Bottom.stl
21 The whittle tool is parsing the file:
22 Screw Holder Bottom.stl
23 ..
24 The whittle tool has created the file:
25 .. Screw Holder Bottom_whittle.gcode
26
27 """
28
29 from __future__ import absolute_import
30 #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.
31 import __init__
32
33 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
34 from fabmetheus_utilities import archive
35 from fabmetheus_utilities import gcodec
36 from fabmetheus_utilities import settings
37 from skeinforge_application.skeinforge_utilities import skeinforge_craft
38 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
39 from skeinforge_application.skeinforge_utilities import skeinforge_profile
40 import math
41 import sys
42
43
44 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
45 __date__ = '$Date: 2008/02/05 $'
46 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
47
48
49 def getCraftedText( fileName, text='', whittleRepository = None ):
50         "Whittle the preface file or text."
51         return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), whittleRepository )
52
53 def getCraftedTextFromText( gcodeText, whittleRepository = None ):
54         "Whittle the preface gcode text."
55         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle'):
56                 return gcodeText
57         if whittleRepository == None:
58                 whittleRepository = settings.getReadRepository( WhittleRepository() )
59         if not whittleRepository.activateWhittle.value:
60                 return gcodeText
61         return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText )
62
63 def getNewRepository():
64         'Get new repository.'
65         return WhittleRepository()
66
67 def writeOutput(fileName, shouldAnalyze=True):
68         "Whittle the carving of a gcode file."
69         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'whittle', shouldAnalyze)
70
71
72 class WhittleRepository:
73         "A class to handle the whittle settings."
74         def __init__(self):
75                 "Set the default settings, execute title & settings fileName."
76                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html', self )
77                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Whittled', self, '')
78                 self.activateWhittle = settings.BooleanSetting().getFromValue('Activate Whittle', self, False )
79                 self.maximumVerticalStep = settings.FloatSpin().getFromValue( 0.02, 'Maximum Vertical Step (mm):', self, 0.42, 0.1 )
80                 self.executeTitle = 'Whittle'
81
82         def execute(self):
83                 "Whittle button has been clicked."
84                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
85                 for fileName in fileNames:
86                         writeOutput(fileName)
87
88
89 class WhittleSkein:
90         "A class to whittle a skein of extrusions."
91         def __init__(self):
92                 self.distanceFeedRate = gcodec.DistanceFeedRate()
93                 self.layerHeight = 0.3333333333
94                 self.lineIndex = 0
95                 self.movementLines = []
96                 self.oldLocation = None
97
98         def getCraftedGcode( self, whittleRepository, gcodeText ):
99                 "Parse gcode text and store the whittle gcode."
100                 self.whittleRepository = whittleRepository
101                 self.lines = archive.getTextLines(gcodeText)
102                 self.parseInitialization()
103                 for line in self.lines[self.lineIndex :]:
104                         self.parseLine(line)
105                 return self.distanceFeedRate.output.getvalue()
106
107         def getLinearMove( self, line, splitLine ):
108                 "Get the linear move."
109                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
110                 self.movementLines.append(line)
111                 z = location.z + self.layerDeltas[0]
112                 self.oldLocation = location
113                 return self.distanceFeedRate.getLineWithZ( line, splitLine, z )
114
115         def parseInitialization(self):
116                 'Parse gcode initialization and store the parameters.'
117                 for self.lineIndex in xrange(len(self.lines)):
118                         line = self.lines[self.lineIndex].lstrip()
119                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
120                         firstWord = gcodec.getFirstWord(splitLine)
121                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
122                         if firstWord == '(</extruderInitialization>)':
123                                 self.distanceFeedRate.addTagBracketedProcedure('whittle')
124                                 return
125                         elif firstWord == '(<layerHeight>':
126                                 self.setLayerThinknessVerticalDeltas(splitLine)
127                                 self.distanceFeedRate.addTagBracketedLine('layerStep', self.layerStep )
128                         self.distanceFeedRate.addLine(line)
129
130         def parseLine(self, line):
131                 "Parse a gcode line and add it to the whittle skein."
132                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
133                 if len(splitLine) < 1:
134                         return
135                 firstWord = splitLine[0]
136                 if firstWord == 'G1':
137                         line = self.getLinearMove( line, splitLine )
138                 elif firstWord == 'M103':
139                         self.repeatLines()
140                 self.distanceFeedRate.addLine(line)
141
142         def repeatLines(self):
143                 "Repeat the lines at decreasing altitude."
144                 for layerDelta in self.layerDeltas[1 :]:
145                         for movementLine in self.movementLines:
146                                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(movementLine)
147                                 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
148                                 z = location.z + layerDelta
149                                 self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( movementLine, splitLine, z ) )
150                 self.movementLines = []
151
152         def setLayerThinknessVerticalDeltas( self, splitLine ):
153                 "Set the layer height and the vertical deltas."
154                 self.layerHeight = float(splitLine[1])
155                 numberOfSteps = int( math.ceil( self.layerHeight / self.whittleRepository.maximumVerticalStep.value ) )
156                 self.layerStep = self.layerHeight / float( numberOfSteps )
157                 self.layerDeltas = []
158                 halfDeltaMinusHalfTop = 0.5 * self.layerStep * ( 1.0 - numberOfSteps )
159                 for layerDeltaIndex in xrange( numberOfSteps - 1, - 1, - 1 ):
160                         layerDelta = layerDeltaIndex * self.layerStep + halfDeltaMinusHalfTop
161                         self.layerDeltas.append( layerDelta )
162
163
164 def main():
165         "Display the whittle dialog."
166         if len(sys.argv) > 1:
167                 writeOutput(' '.join(sys.argv[1 :]))
168         else:
169                 settings.startMainLoopFromConstructor(getNewRepository())
170
171 if __name__ == "__main__":
172         main()