summaryrefslogtreecommitdiff
path: root/trunk/reprap/miscellaneous/python-beanshell-scripts/skeinforge_tools/craft_plugins/export.py
blob: d02bd6f2d335349f85694816538aaa2c59a12ada (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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
"""
This page is in the table of contents.
Export is a script to pick an export plugin and optionally print the output to a file.

The export manual page is at:
http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Export

==Operation==
The default 'Activate Export' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions will not be called.

==Settings==
===Also Send Output To===
Default is empty.

Defines the output name for sending to a file or pipe.  A common choice is sys.stdout to print the output in the shell screen.  Another common choice is sys.stderr.  With the empty default, nothing will be done.

===Delete Comments===
Default is on.

When selected, export will delete the comments.  The comments are not necessary to run a fabricator.

===Export Operations===
Export presents the user with a choice of the export plugins in the export_plugins folder.  The chosen plugin will then modify the gcode or translate it into another format.  There is also the "Do Not Change Output" choice, which will not change the output.  An export plugin is a script in the export_plugins folder which has the functions getOutput, isReplaceable and if it's output is not replaceable, writeOutput.

===File Extension===
Default is gcode.

Defines the file extension added to the name of the output file.

===Save Penultimate Gcode===
Default is off.

When selected, export will save the gcode with the suffix '_penultimate.gcode' just before it is exported.  This is useful because the code after it is exported could be in a form which the viewers can not display.

==Alterations==
Export looks for alteration files in the alterations folder in the .skeinforge folder in the home directory.  Export does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names.  If it doesn't find the file it then looks in the alterations folder in the skeinforge_tools folder. If it doesn't find anything there it looks in the skeinforge_tools folder.

===replace.csv===
When export is exporting the code, if there is a file replace.csv, it will replace the word in the first column by its replacement in the second column.  There is an example file replace_example.csv to demonstrate the comma separated format, which can be edited in a text editor or a spreadsheet.

==Examples==
The following examples export the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and export.py.


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


> python export.py Screw Holder Bottom.stl
The export tool is parsing the file:
Screw Holder Bottom.stl
..
The export tool has created the file:
.. Screw Holder Bottom_export.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 export
>>> export.main()
This brings up the export dialog.


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

"""

from __future__ import absolute_import
#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 analyze
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 euclidean
from skeinforge_tools.skeinforge_utilities import gcodec
from skeinforge_tools.skeinforge_utilities import intercircle
from skeinforge_tools.skeinforge_utilities import interpret
from skeinforge_tools.skeinforge_utilities import settings
import cStringIO
import os
import sys
import time


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


def getCraftedTextFromText( gcodeText, exportRepository = None ):
	"Export a gcode linear move text."
	if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'export' ):
		return gcodeText
	if exportRepository == None:
		exportRepository = settings.getReadRepository( ExportRepository() )
	if not exportRepository.activateExport.value:
		return gcodeText
	return ExportSkein().getCraftedGcode( exportRepository, gcodeText )

def getDistanceGcode( exportText ):
	"Get gcode lines with distance variable added.G2 X-0.148 Y-0.062 Z0.0 I-0.148 J0.148G2 X-0.148 Y-0.062 Z0.0 R0.21"
	lines = gcodec.getTextLines( exportText )
	oldLocation = None
	for line in lines:
		splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
		firstWord = None
		if len( splitLine ) > 0:
			firstWord = splitLine[ 0 ]
		if firstWord == 'G1':
			location = gcodec.getLocationFromSplitLine( oldLocation, splitLine )
			if oldLocation != None:
				distance = location.distance( oldLocation )
				print( distance )
			oldLocation = location
	return exportText

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

def getReplaced( exportText ):
	"Get text with words replaced according to replace.csv file."
	replaceText = settings.getFileInAlterationsOrGivenDirectory( os.path.dirname( __file__ ), 'Replace.csv' )
	if replaceText == '':
		return exportText
	lines = gcodec.getTextLines( replaceText )
	for line in lines:
		replacedLine = line.replace( ',', ' ' )
		replacedLine = replacedLine.replace( '\t', ' ' )
		splitLine = replacedLine.split()
		if len( splitLine ) > 1:
			exportText = exportText.replace( splitLine[ 0 ], splitLine[ 1 ] )
	return exportText

def getSelectedPluginModule( plugins ):
	"Get the selected plugin module."
	for plugin in plugins:
		if plugin.value:
			return gcodec.getModuleWithDirectoryPath( plugin.directoryPath, plugin.name )
	return None

def writeOutput( fileName = '' ):
	"Export a gcode linear move file."
	fileName = interpret.getFirstTranslatorFileNameUnmodified( fileName )
	if fileName == '':
		return
	exportRepository = ExportRepository()
	settings.getReadRepository( exportRepository )
	startTime = time.time()
	print( 'File ' + gcodec.getSummarizedFileName( fileName ) + ' is being chain exported.' )
	suffixFileName = fileName[ : fileName.rfind( '.' ) ] + '_export.' + exportRepository.fileExtension.value
	gcodeText = gcodec.getGcodeFileText( fileName, '' )
	procedures = consecution.getProcedures( 'export', gcodeText )
	gcodeText = consecution.getChainTextFromProcedures( fileName, procedures[ : - 1 ], gcodeText )
	if gcodeText == '':
		return
	analyze.writeOutput( suffixFileName, gcodeText )
	if exportRepository.savePenultimateGcode.value:
		penultimateFileName = fileName[ : fileName.rfind( '.' ) ] + '_penultimate.gcode'
		gcodec.writeFileText( penultimateFileName, gcodeText )
		print( 'The penultimate file is saved as ' + gcodec.getSummarizedFileName( penultimateFileName ) )
	exportChainGcode = getCraftedTextFromText( gcodeText, exportRepository )
	replaceableExportChainGcode = None
	selectedPluginModule = getSelectedPluginModule( exportRepository.exportPlugins )
	if selectedPluginModule == None:
		replaceableExportChainGcode = exportChainGcode
	else:
		if selectedPluginModule.isReplaceable():
			replaceableExportChainGcode = selectedPluginModule.getOutput( exportChainGcode )
		else:
			selectedPluginModule.writeOutput( suffixFileName, exportChainGcode )
	if replaceableExportChainGcode != None:
		replaceableExportChainGcode = getReplaced( replaceableExportChainGcode )
		gcodec.writeFileText( suffixFileName, replaceableExportChainGcode )
		print( 'The exported file is saved as ' + gcodec.getSummarizedFileName( suffixFileName ) )
	if exportRepository.alsoSendOutputTo.value != '':
		if replaceableExportChainGcode == None:
			replaceableExportChainGcode = selectedPluginModule.getOutput( exportChainGcode )
		exec( 'print >> ' + exportRepository.alsoSendOutputTo.value + ', replaceableExportChainGcode' )
	print( 'It took ' + str( int( round( time.time() - startTime ) ) ) + ' seconds to export the file.' )


class ExportRepository:
	"A class to handle the export settings."
	def __init__( self ):
		"Set the default settings, execute title & settings fileName."
		settings.addListsToRepository( 'skeinforge_tools.craft_plugins.export.html', '', self )
		self.fileNameInput = settings.FileNameInput().getFromFileName( interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Export', self, '' )
		self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( 'http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Export' )
		self.activateExport = settings.BooleanSetting().getFromValue( 'Activate Export', self, True )
		self.alsoSendOutputTo = settings.StringSetting().getFromValue( 'Also Send Output To:', self, '' )
		self.deleteComments = settings.BooleanSetting().getFromValue( 'Delete Comments', self, True )
		exportPluginsFolderPath = gcodec.getAbsoluteFolderPath( __file__, 'export_plugins' )
		exportStaticDirectoryPath = os.path.join( exportPluginsFolderPath, 'static_plugins' )
		exportPluginFileNames = gcodec.getPluginFileNamesFromDirectoryPath( exportPluginsFolderPath )
		exportStaticPluginFileNames = gcodec.getPluginFileNamesFromDirectoryPath( exportStaticDirectoryPath )
		self.exportLabel = settings.LabelDisplay().getFromName( 'Export Operations: ', self )
		self.exportPlugins = []
		exportLatentStringVar = settings.LatentStringVar()
		self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio( exportLatentStringVar, 'Do Not Change Output', self, True )
		self.doNotChangeOutput.directoryPath = None
		allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames
		for exportPluginFileName in allExportPluginFileNames:
			exportPlugin = None
			if exportPluginFileName in exportPluginFileNames:
				path = os.path.join( exportPluginsFolderPath, exportPluginFileName )
				exportPlugin = settings.RadioCapitalizedButton().getFromPath( exportLatentStringVar, exportPluginFileName, path, self, False )
				exportPlugin.directoryPath = exportPluginsFolderPath
			else:
				exportPlugin = settings.RadioCapitalized().getFromRadio( exportLatentStringVar, exportPluginFileName, self, False )
				exportPlugin.directoryPath = exportStaticDirectoryPath
			self.exportPlugins.append( exportPlugin )
		self.fileExtension = settings.StringSetting().getFromValue( 'File Extension:', self, 'gcode' )
		self.savePenultimateGcode = settings.BooleanSetting().getFromValue( 'Save Penultimate Gcode', self, False )
		self.executeTitle = 'Export'

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


class ExportSkein:
	"A class to export a skein of extrusions."
	def __init__( self ):
		self.decimalPlacesExported = 2
		self.output = cStringIO.StringIO()

	def addLine( self, line ):
		"Add a line of text and a newline to the output."
		if line != '':
			self.output.write( line + '\n' )

	def getCraftedGcode( self, exportRepository, gcodeText ):
		"Parse gcode text and store the export gcode."
		lines = gcodec.getTextLines( gcodeText )
		for line in lines:
			self.parseLine( exportRepository, line )
		return self.output.getvalue()

	def getLineWithTruncatedNumber( self, character, line ):
		'Get a line with the number after the character truncated.'
		indexOfCharacter = line.find( character )
		if indexOfCharacter < 0:
			return line
		indexOfNumberEnd = line.find( ' ', indexOfCharacter )
		if indexOfNumberEnd < 0:
			indexOfNumberEnd = len( line )
		indexOfNumberStart = indexOfCharacter + 1
		numberString = line[ indexOfNumberStart : indexOfNumberEnd ]
		if numberString == '':
			return line
		roundedNumberString = euclidean.getRoundedToDecimalPlacesString( self.decimalPlacesExported, float( numberString ) )
		return line[ : indexOfNumberStart ] + roundedNumberString + line[ indexOfNumberEnd : ]

	def parseLine( self, exportRepository, line ):
		"Parse a gcode line."
		splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
		if len( splitLine ) < 1:
			return
		firstWord = splitLine[ 0 ]
		if firstWord == '(<decimalPlacesCarried>':
			self.decimalPlacesExported = max( 1, int( splitLine[ 1 ] ) - 1 )
		if firstWord[ 0 ] == '(' and exportRepository.deleteComments.value:
			return
		if firstWord == '(</extruderInitialization>)':
			self.addLine( '(<procedureDone> export </procedureDone>)' )
		if firstWord != 'G1' and firstWord != 'G2' and firstWord != 'G3' :
			self.addLine( line )
			return
		line = self.getLineWithTruncatedNumber( 'X', line )
		line = self.getLineWithTruncatedNumber( 'Y', line )
		line = self.getLineWithTruncatedNumber( 'Z', line )
		line = self.getLineWithTruncatedNumber( 'I', line )
		line = self.getLineWithTruncatedNumber( 'J', line )
		line = self.getLineWithTruncatedNumber( 'R', line )
		self.addLine( line )


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

if __name__ == "__main__":
	main()