summaryrefslogtreecommitdiff
path: root/trunk/reprap/miscellaneous/python-beanshell-scripts/skeinforge_tools/craft_plugins/whittle.py
blob: 2ea3259336e9e5bbe38abc0847d0b45acdc87fb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
"""
This page is in the table of contents.
Whittle will convert each polygon of a gcode file into a helix which has a vertical step down on each rotation.

==Operation==
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.

==Settings==
===Maximum Vertical Step'===
Default is 0.1 mm.

Defines the maximum distance that the helix will step down on each rotation.  The number of steps in the helix will be the layer thickness divided by the 'Maximum Vertical Step', rounded up.  The amount the helix will step down is the layer thickness 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.

==Examples==
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.


> python whittle.py
This brings up the whittle dialog.


> python whittle.py Screw Holder Bottom.stl
The whittle tool is parsing the file:
Screw Holder Bottom.stl
..
The whittle tool has created the file:
.. Screw Holder Bottom_whittle.gcode


> python
Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import whittle
>>> whittle.main()
This brings up the whittle dialog.


>>> whittle.writeOutput( 'Screw Holder Bottom.stl' )
The whittle tool is parsing the file:
Screw Holder Bottom.stl
..
The whittle tool has created the file:
.. Screw Holder Bottom_whittle.gcode


"""

from __future__ import absolute_import
try:
	import psyco
	psyco.full()
except:
	pass
#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.
import __init__

from skeinforge_tools import profile
from skeinforge_tools.meta_plugins import polyfile
from skeinforge_tools.skeinforge_utilities import consecution
from skeinforge_tools.skeinforge_utilities import gcodec
from skeinforge_tools.skeinforge_utilities import interpret
from skeinforge_tools.skeinforge_utilities import settings
import math
import sys


__author__ = "Enrique Perez (perez_enrique@yahoo.com)"
__date__ = "$Date: 2008/28/04 $"
__license__ = "GPL 3.0"


def getCraftedText( fileName, text = '', whittleRepository = None ):
	"Whittle the preface file or text."
	return getCraftedTextFromText( gcodec.getTextIfEmpty( fileName, text ), whittleRepository )

def getCraftedTextFromText( gcodeText, whittleRepository = None ):
	"Whittle the preface gcode text."
	if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle' ):
		return gcodeText
	if whittleRepository == None:
		whittleRepository = settings.getReadRepository( WhittleRepository() )
	if not whittleRepository.activateWhittle.value:
		return gcodeText
	return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText )

def getNewRepository():
	"Get the repository constructor."
	return WhittleRepository()

def writeOutput( fileName = '' ):
	"Whittle the carving of a gcode file.  If no fileName is specified, whittle the first unmodified gcode file in this folder."
	fileName = interpret.getFirstTranslatorFileNameUnmodified( fileName )
	if fileName == '':
		return
	consecution.writeChainTextWithNounMessage( fileName, 'whittle' )


class WhittleRepository:
	"A class to handle the whittle settings."
	def __init__( self ):
		"Set the default settings, execute title & settings fileName."
		profile.addListsToCraftTypeRepository( 'skeinforge_tools.craft_plugins.whittle.html', self )
		self.fileNameInput = settings.FileNameInput().getFromFileName( interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Whittled', self, '' )
		self.activateWhittle = settings.BooleanSetting().getFromValue( 'Activate Whittle:', self, False )
		self.maximumVerticalStep = settings.FloatSpin().getFromValue( 0.02, 'Maximum Vertical Step (mm):', self, 0.42, 0.1 )
		self.executeTitle = 'Whittle'

	def execute( self ):
		"Whittle button has been clicked."
		fileNames = polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled )
		for fileName in fileNames:
			writeOutput( fileName )


class WhittleSkein:
	"A class to whittle a skein of extrusions."
	def __init__( self ):
		self.distanceFeedRate = gcodec.DistanceFeedRate()
		self.layerThickness = 0.3333333333
		self.lineIndex = 0
		self.movementLines = []
		self.oldLocation = None

	def getCraftedGcode( self, whittleRepository, gcodeText ):
		"Parse gcode text and store the whittle gcode."
		self.whittleRepository = whittleRepository
		self.lines = gcodec.getTextLines( gcodeText )
		self.parseInitialization()
		for line in self.lines[ self.lineIndex : ]:
			self.parseLine( line )
		return self.distanceFeedRate.output.getvalue()

	def getLinearMove( self, line, splitLine ):
		"Get the linear move."
		location = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
		self.movementLines.append( line )
		z = location.z + self.layerDeltas[ 0 ]
		self.oldLocation = location
		return self.distanceFeedRate.getLineWithZ( line, splitLine, z )

	def parseInitialization( self ):
		"Parse gcode initialization and store the parameters."
		for self.lineIndex in xrange( len( self.lines ) ):
			line = self.lines[ self.lineIndex ].lstrip()
			splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
			firstWord = gcodec.getFirstWord( splitLine )
			self.distanceFeedRate.parseSplitLine( firstWord, splitLine )
			if firstWord == '(</extruderInitialization>)':
				self.distanceFeedRate.addTagBracketedLine( 'procedureDone', 'whittle' )
				return
			elif firstWord == '(<layerThickness>':
				self.setLayerThinknessVerticalDeltas( splitLine )
				self.distanceFeedRate.addTagBracketedLine( 'layerStep', self.layerStep )
			self.distanceFeedRate.addLine( line )

	def parseLine( self, line ):
		"Parse a gcode line and add it to the whittle skein."
		splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
		if len( splitLine ) < 1:
			return
		firstWord = splitLine[ 0 ]
		if firstWord == 'G1':
			line = self.getLinearMove( line, splitLine )
		elif firstWord == 'M103':
			self.repeatLines()
		self.distanceFeedRate.addLine( line )

	def repeatLines( self ):
		"Repeat the lines at decreasing altitude."
		for layerDelta in self.layerDeltas[ 1 : ]:
			for movementLine in self.movementLines:
				splitLine = movementLine.split()
				location = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
				z = location.z + layerDelta
				self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( movementLine, splitLine, z ) )
		self.movementLines = []

	def setLayerThinknessVerticalDeltas( self, splitLine ):
		"Set the layer thickness and the vertical deltas."
		self.layerThickness = float( splitLine[ 1 ] )
		numberOfSteps = int( math.ceil( self.layerThickness / self.whittleRepository.maximumVerticalStep.value ) )
		self.layerStep = self.layerThickness / float( numberOfSteps )
		self.layerDeltas = []
		halfDeltaMinusHalfTop = 0.5 * self.layerStep * ( 1.0 - numberOfSteps )
		for layerDeltaIndex in xrange( numberOfSteps - 1, - 1, - 1 ):
			layerDelta = layerDeltaIndex * self.layerStep + halfDeltaMinusHalfTop
			self.layerDeltas.append( layerDelta )


def main():
	"Display the whittle dialog."
	if len( sys.argv ) > 1:
		writeOutput( ' '.join( sys.argv[ 1 : ] ) )
	else:
		settings.startMainLoopFromConstructor( getNewRepository() )

if __name__ == "__main__":
	main()