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.
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.
9 ===Maximum Vertical Step'===
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.
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.
18 This brings up the whittle dialog.
20 > python whittle.py Screw Holder Bottom.stl
21 The whittle tool is parsing the file:
22 Screw Holder Bottom.stl
24 The whittle tool has created the file:
25 .. Screw Holder Bottom_whittle.gcode
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.
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
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'
49 def getCraftedText( fileName, text='', whittleRepository = None ):
50 "Whittle the preface file or text."
51 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), whittleRepository )
53 def getCraftedTextFromText( gcodeText, whittleRepository = None ):
54 "Whittle the preface gcode text."
55 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle'):
57 if whittleRepository == None:
58 whittleRepository = settings.getReadRepository( WhittleRepository() )
59 if not whittleRepository.activateWhittle.value:
61 return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText )
63 def getNewRepository():
65 return WhittleRepository()
67 def writeOutput(fileName, shouldAnalyze=True):
68 "Whittle the carving of a gcode file."
69 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'whittle', shouldAnalyze)
72 class WhittleRepository:
73 "A class to handle the whittle settings."
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'
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:
90 "A class to whittle a skein of extrusions."
92 self.distanceFeedRate = gcodec.DistanceFeedRate()
93 self.layerHeight = 0.3333333333
95 self.movementLines = []
96 self.oldLocation = None
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 :]:
105 return self.distanceFeedRate.output.getvalue()
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 )
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')
125 elif firstWord == '(<layerHeight>':
126 self.setLayerThinknessVerticalDeltas(splitLine)
127 self.distanceFeedRate.addTagBracketedLine('layerStep', self.layerStep )
128 self.distanceFeedRate.addLine(line)
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:
135 firstWord = splitLine[0]
136 if firstWord == 'G1':
137 line = self.getLinearMove( line, splitLine )
138 elif firstWord == 'M103':
140 self.distanceFeedRate.addLine(line)
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 = []
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 )
165 "Display the whittle dialog."
166 if len(sys.argv) > 1:
167 writeOutput(' '.join(sys.argv[1 :]))
169 settings.startMainLoopFromConstructor(getNewRepository())
171 if __name__ == "__main__":