chiark / gitweb /
Add back the ultimaker platform, and made the platform mesh simpler.
[cura.git] / Cura / slice / cura_sf / 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
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
38 import math
39 import sys
40
41
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'
45
46
47 def getCraftedText( fileName, text='', whittleRepository = None ):
48         "Whittle the preface file or text."
49         return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), whittleRepository )
50
51 def getCraftedTextFromText( gcodeText, whittleRepository = None ):
52         "Whittle the preface gcode text."
53         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle'):
54                 return gcodeText
55         if whittleRepository == None:
56                 whittleRepository = settings.getReadRepository( WhittleRepository() )
57         if not whittleRepository.activateWhittle.value:
58                 return gcodeText
59         return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText )
60
61 def getNewRepository():
62         'Get new repository.'
63         return WhittleRepository()
64
65 def writeOutput(fileName, shouldAnalyze=True):
66         "Whittle the carving of a gcode file."
67         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'whittle', shouldAnalyze)
68
69
70 class WhittleRepository(object):
71         "A class to handle the whittle settings."
72         def __init__(self):
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'
79
80         def execute(self):
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:
84                         writeOutput(fileName)
85
86
87 class WhittleSkein(object):
88         "A class to whittle a skein of extrusions."
89         def __init__(self):
90                 self.distanceFeedRate = gcodec.DistanceFeedRate()
91                 self.layerHeight = 0.3333333333
92                 self.lineIndex = 0
93                 self.movementLines = []
94                 self.oldLocation = None
95
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 :]:
102                         self.parseLine(line)
103                 return self.distanceFeedRate.output.getvalue()
104
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 )
112
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')
122                                 return
123                         elif firstWord == '(<layerHeight>':
124                                 self.setLayerThinknessVerticalDeltas(splitLine)
125                                 self.distanceFeedRate.addTagBracketedLine('layerStep', self.layerStep )
126                         self.distanceFeedRate.addLine(line)
127
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:
132                         return
133                 firstWord = splitLine[0]
134                 if firstWord == 'G1':
135                         line = self.getLinearMove( line, splitLine )
136                 elif firstWord == 'M103':
137                         self.repeatLines()
138                 self.distanceFeedRate.addLine(line)
139
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 = []
149
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 )
160
161
162 def main():
163         "Display the whittle dialog."
164         if len(sys.argv) > 1:
165                 writeOutput(' '.join(sys.argv[1 :]))
166         else:
167                 settings.startMainLoopFromConstructor(getNewRepository())
168
169 if __name__ == "__main__":
170         main()