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
31 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
32 from fabmetheus_utilities import archive
33 from fabmetheus_utilities import gcodec
34 from fabmetheus_utilities import settings
35 from skeinforge_application.skeinforge_utilities import skeinforge_craft
36 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
37 from skeinforge_application.skeinforge_utilities import skeinforge_profile
42 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
43 __date__ = '$Date: 2008/02/05 $'
44 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
47 def getCraftedText( fileName, text='', whittleRepository = None ):
48 "Whittle the preface file or text."
49 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), whittleRepository )
51 def getCraftedTextFromText( gcodeText, whittleRepository = None ):
52 "Whittle the preface gcode text."
53 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle'):
55 if whittleRepository == None:
56 whittleRepository = settings.getReadRepository( WhittleRepository() )
57 if not whittleRepository.activateWhittle.value:
59 return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText )
61 def getNewRepository():
63 return WhittleRepository()
65 def writeOutput(fileName, shouldAnalyze=True):
66 "Whittle the carving of a gcode file."
67 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'whittle', shouldAnalyze)
70 class WhittleRepository(object):
71 "A class to handle the whittle settings."
73 "Set the default settings, execute title & settings fileName."
74 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html', self )
75 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Whittled', self, '')
76 self.activateWhittle = settings.BooleanSetting().getFromValue('Activate Whittle', self, False )
77 self.maximumVerticalStep = settings.FloatSpin().getFromValue( 0.02, 'Maximum Vertical Step (mm):', self, 0.42, 0.1 )
78 self.executeTitle = 'Whittle'
81 "Whittle button has been clicked."
82 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
83 for fileName in fileNames:
87 class WhittleSkein(object):
88 "A class to whittle a skein of extrusions."
90 self.distanceFeedRate = gcodec.DistanceFeedRate()
91 self.layerHeight = 0.3333333333
93 self.movementLines = []
94 self.oldLocation = None
96 def getCraftedGcode( self, whittleRepository, gcodeText ):
97 "Parse gcode text and store the whittle gcode."
98 self.whittleRepository = whittleRepository
99 self.lines = archive.getTextLines(gcodeText)
100 self.parseInitialization()
101 for line in self.lines[self.lineIndex :]:
103 return self.distanceFeedRate.output.getvalue()
105 def getLinearMove( self, line, splitLine ):
106 "Get the linear move."
107 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
108 self.movementLines.append(line)
109 z = location.z + self.layerDeltas[0]
110 self.oldLocation = location
111 return self.distanceFeedRate.getLineWithZ( line, splitLine, z )
113 def parseInitialization(self):
114 'Parse gcode initialization and store the parameters.'
115 for self.lineIndex in xrange(len(self.lines)):
116 line = self.lines[self.lineIndex].lstrip()
117 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
118 firstWord = gcodec.getFirstWord(splitLine)
119 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
120 if firstWord == '(</extruderInitialization>)':
121 self.distanceFeedRate.addTagBracketedProcedure('whittle')
123 elif firstWord == '(<layerHeight>':
124 self.setLayerThinknessVerticalDeltas(splitLine)
125 self.distanceFeedRate.addTagBracketedLine('layerStep', self.layerStep )
126 self.distanceFeedRate.addLine(line)
128 def parseLine(self, line):
129 "Parse a gcode line and add it to the whittle skein."
130 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
131 if len(splitLine) < 1:
133 firstWord = splitLine[0]
134 if firstWord == 'G1':
135 line = self.getLinearMove( line, splitLine )
136 elif firstWord == 'M103':
138 self.distanceFeedRate.addLine(line)
140 def repeatLines(self):
141 "Repeat the lines at decreasing altitude."
142 for layerDelta in self.layerDeltas[1 :]:
143 for movementLine in self.movementLines:
144 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(movementLine)
145 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
146 z = location.z + layerDelta
147 self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( movementLine, splitLine, z ) )
148 self.movementLines = []
150 def setLayerThinknessVerticalDeltas( self, splitLine ):
151 "Set the layer height and the vertical deltas."
152 self.layerHeight = float(splitLine[1])
153 numberOfSteps = int( math.ceil( self.layerHeight / self.whittleRepository.maximumVerticalStep.value ) )
154 self.layerStep = self.layerHeight / float( numberOfSteps )
155 self.layerDeltas = []
156 halfDeltaMinusHalfTop = 0.5 * self.layerStep * ( 1.0 - numberOfSteps )
157 for layerDeltaIndex in xrange( numberOfSteps - 1, - 1, - 1 ):
158 layerDelta = layerDeltaIndex * self.layerStep + halfDeltaMinusHalfTop
159 self.layerDeltas.append( layerDelta )
163 "Display the whittle dialog."
164 if len(sys.argv) > 1:
165 writeOutput(' '.join(sys.argv[1 :]))
167 settings.startMainLoopFromConstructor(getNewRepository())
169 if __name__ == "__main__":