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 / alteration.py
1 #! /usr/bin/env python
2 """
3 This page is in the table of contents.
4 The alteration plugin adds the start and end files to the gcode.
5
6 This plugin also removes the alteration prefix tokens from the alteration lines.  Alteration lines have a prefix token so they can go through the craft plugins without being modified.  However, the tokens are not recognized by the firmware so they have to be removed before export. The alteration token is:
7 (<alterationDeleteThisPrefix/>)
8
9 The alteration manual page is at:
10 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Alteration
11
12 ==Operation==
13 The default 'Activate Alteration' checkbox is on.  When it is on, the functions described below will work, when it is off, nothing will be done.
14
15 ==Settings==
16 Alteration looks for alteration files in the alterations folder in the .skeinforge folder in the home directory.  Alteration 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_plugins folder.
17
18 ===Name of End File===
19 Default is 'end.gcode'.
20
21 If there is a file with the name of the "Name of End File" setting, it will be added to the very end of the gcode.
22
23 ===Name of Start File===
24 Default is 'start.gcode'.
25
26 If there is a file with the name of the "Name of Start File" setting, it will be added to the very beginning of the gcode.
27
28 ===Remove Redundant Mcode===
29 Default: True
30
31 If 'Remove Redundant Mcode' is selected then M104 and M108 lines which are followed by a different value before there is a movement will be removed.  For example, if there is something like:
32 M113 S1.0
33 M104 S60.0
34 (<layer> 0.72 )
35 M104 S200.0
36 (<skirt>)
37
38 with Remove Redundant Mcode selected, that snippet would become:
39 M113 S1.0
40 M104 S200.0
41 (<layer> 0.72 )
42 (<skirt>)
43
44 This is a relatively safe procedure, the only reason it is optional is because someone might make an alteration file which, for some unknown reason, requires the redundant mcode.
45
46 ===Replace Variable with Setting===
47 Default: True
48
49 If 'Replace Variable with Setting' is selected and there is an alteration line with a setting token, the token will be replaced by the value.
50
51 For example, if there is an alteration line like:
52
53 M140 S<setting.chamber.BedTemperature>
54
55 the token would be replaced with the value and assuming the bed chamber was 60.0, the output would be:
56
57 M140 S60.0
58
59 ==Examples==
60 The following examples add the alteration information to the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and alteration.py.
61
62 > python alteration.py
63 This brings up the alteration dialog.
64
65 > python alteration.py Screw Holder Bottom.stl
66 The alteration tool is parsing the file:
67 Screw Holder Bottom.stl
68 ..
69 The alteration tool has created the file:
70 .. Screw Holder Bottom_alteration.gcode
71
72 """
73
74 from __future__ import absolute_import
75
76 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
77 from fabmetheus_utilities import archive
78 from fabmetheus_utilities import gcodec
79 from fabmetheus_utilities import settings
80 from skeinforge_application.skeinforge_utilities import skeinforge_craft
81 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
82 from skeinforge_application.skeinforge_utilities import skeinforge_profile
83 import cStringIO
84 import sys
85
86
87 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
88 __date__ = '$Date: 2008/02/05 $'
89 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
90
91
92 def getCraftedText(fileName, text='', repository=None):
93         'Alteration a gcode linear move text.'
94         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository, fileName)
95
96 def getCraftedTextFromText(gcodeText, repository=None, fileName=''):
97         'Alteration a gcode linear move text.'
98         if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'alteration'):
99                 return gcodeText
100         if repository == None:
101                 repository = settings.getReadRepository(AlterationRepository())
102         if not repository.activateAlteration.value:
103                 return gcodeText
104         return AlterationSkein().getCraftedGcode(gcodeText, repository, fileName)
105
106 def getGcodeTextWithoutRedundantMcode(gcodeText):
107         'Get gcode text without redundant M104 and M108.'
108         lines = archive.getTextLines(gcodeText)
109         lines = getLinesWithoutRedundancy('M104', lines)
110         lines = getLinesWithoutRedundancy('M108', lines)
111         output = cStringIO.StringIO()
112         gcodec.addLinesToCString(output, lines)
113         return output.getvalue()
114
115 def getLinesWithoutRedundancy(duplicateWord, lines):
116         'Get gcode lines without redundant first words.'
117         oldDuplicationIndex = None
118         for lineIndex, line in enumerate(lines):
119                 firstWord = gcodec.getFirstWordFromLine(line)
120                 if firstWord == duplicateWord:
121                         if oldDuplicationIndex == None:
122                                 oldDuplicationIndex = lineIndex
123                         else:
124                                 lines[oldDuplicationIndex] = line
125                                 lines[lineIndex] = ''
126                 elif firstWord.startswith('G') or firstWord == 'M101' or firstWord == 'M103':
127                         oldDuplicationIndex = None
128         return lines
129
130 def getNewRepository():
131         'Get new repository.'
132         return AlterationRepository()
133
134 def writeOutput(fileName, shouldAnalyze=True):
135         'Alteration a gcode linear move file.  Chain alteration the gcode if the alteration procedure has not been done.'
136         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'alteration', shouldAnalyze)
137
138
139 class AlterationRepository(object):
140         "A class to handle the alteration settings."
141         def __init__(self):
142                 "Set the default settings, execute title & settings fileName."
143                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.alteration.html', self )
144                 self.baseNameSynonym = 'bookend.csv'
145                 self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Alteration', self, '')
146                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Alteration')
147                 self.activateAlteration = settings.BooleanSetting().getFromValue('Activate Alteration', self, True)
148                 self.nameOfEndFile = settings.StringSetting().getFromValue('Name of End File:', self, 'end.gcode')
149                 self.nameOfStartFile = settings.StringSetting().getFromValue('Name of Start File:', self, 'start.gcode')
150                 self.removeRedundantMcode = settings.BooleanSetting().getFromValue('Remove Redundant Mcode', self, True)
151                 self.replaceVariableWithSetting = settings.BooleanSetting().getFromValue('Replace Variable with Setting', self, True)
152                 self.executeTitle = 'Alteration'
153
154         def execute(self):
155                 'Alteration button has been clicked.'
156                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
157                 for fileName in fileNames:
158                         writeOutput(fileName)
159
160
161 class AlterationSkein(object):
162         "A class to alteration a skein of extrusions."
163         def __init__(self):
164                 'Initialize.'
165                 self.distanceFeedRate = gcodec.DistanceFeedRate()
166                 self.lineIndex = 0
167                 self.settingDictionary = None
168
169         def addFromUpperLowerFile(self, fileName):
170                 "Add lines of text from the fileName or the lowercase fileName, if there is no file by the original fileName in the directory."
171                 alterationFileLines = map(lambda l: l.replace('?filename?', self.fileName.encode('ascii', 'replace')), settings.getAlterationFileLines(fileName))
172                 self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(alterationFileLines)
173
174         def getCraftedGcode(self, gcodeText, repository, fileName):
175                 "Parse gcode text and store the bevel gcode."
176                 self.fileName = fileName
177                 self.lines = archive.getTextLines(gcodeText)
178                 if repository.replaceVariableWithSetting.value:
179                         self.setSettingDictionary()
180                 self.addFromUpperLowerFile(repository.nameOfStartFile.value) # Add a start file if it exists.
181                 self.parseInitialization()
182                 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
183                         line = self.lines[self.lineIndex]
184                         self.distanceFeedRate.addLine(line)
185                 self.addFromUpperLowerFile(repository.nameOfEndFile.value) # Add an end file if it exists.
186                 gcodeText = self.getReplacedAlterationText()
187                 if repository.removeRedundantMcode.value:
188                         gcodeText = getGcodeTextWithoutRedundantMcode(gcodeText)
189                 return gcodeText
190
191         def getReplacedAlterationLine(self, alterationFileLine, searchIndex=0):
192                 'Get the alteration file line with variables replaced with the settings.'
193                 settingIndex = alterationFileLine.find('setting.', searchIndex)
194                 beginIndex = settingIndex - 1
195                 if beginIndex < 0:
196                         return alterationFileLine
197                 endBracketIndex = alterationFileLine.find('>', settingIndex)
198                 if alterationFileLine[beginIndex] != '<' or endBracketIndex == -1:
199                         return alterationFileLine
200                 endIndex = endBracketIndex + 1
201                 innerToken = alterationFileLine[settingIndex + len('setting.'): endIndex].replace('>', '').replace(' ', '').replace('_', '').lower()
202                 if innerToken in self.settingDictionary:
203                         replacedSetting = self.settingDictionary[innerToken]
204                         replacedAlterationLine = alterationFileLine[: beginIndex] + replacedSetting + alterationFileLine[endIndex :]
205                         return self.getReplacedAlterationLine(replacedAlterationLine, beginIndex + len(replacedSetting))
206                 return alterationFileLine
207
208         def getReplacedAlterationText(self):
209                 'Replace the alteration lines if there are settings.'
210                 if self.settingDictionary == None:
211                         return self.distanceFeedRate.output.getvalue().replace('(<alterationDeleteThisPrefix/>)', '')
212                 lines = archive.getTextLines(self.distanceFeedRate.output.getvalue())
213                 distanceFeedRate = gcodec.DistanceFeedRate()
214                 for line in lines:
215                         if line.startswith('(<alterationDeleteThisPrefix/>)'):
216                                 line = self.getReplacedAlterationLine(line[len('(<alterationDeleteThisPrefix/>)') :])
217                         distanceFeedRate.addLine(line)
218                 return distanceFeedRate.output.getvalue()
219
220         def parseInitialization(self):
221                 'Parse gcode initialization and store the parameters.'
222                 for self.lineIndex in xrange(len(self.lines)):
223                         line = self.lines[self.lineIndex]
224                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
225                         firstWord = gcodec.getFirstWord(splitLine)
226                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
227                         if firstWord == '(</extruderInitialization>)':
228                                 self.distanceFeedRate.addTagBracketedProcedure('alteration')
229                                 return
230                         self.distanceFeedRate.addLine(line)
231
232         def setSettingDictionary(self):
233                 'Set the setting dictionary from the gcode text.'
234                 for line in self.lines:
235                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
236                         firstWord = gcodec.getFirstWord(splitLine)
237                         if firstWord == '(<setting>' and self.settingDictionary != None:
238                                 if len(splitLine) > 4:
239                                         procedure = splitLine[1]
240                                         name = splitLine[2].replace('_', ' ').replace(' ', '')
241                                         if '(' in name:
242                                                 name = name[: name.find('(')]
243                                         value = ' '.join(splitLine[3 : -1])
244                                         self.settingDictionary[(procedure + '.' + name).lower()] = value
245                         elif firstWord == '(<settings>)':
246                                 self.settingDictionary = {}
247                         elif firstWord == '(</settings>)':
248                                 return
249
250
251 def main():
252         "Display the alteration dialog."
253         if len(sys.argv) > 1:
254                 writeOutput(' '.join(sys.argv[1 :]))
255         else:
256                 settings.startMainLoopFromConstructor(getNewRepository())
257
258 if __name__ == "__main__":
259         main()