chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / skeinforge_application / skeinforge_plugins / craft_plugins / outset.py
1 """
2 This page is in the table of contents.
3 Outset outsets the edges of the slices of a gcode file.  The outside edges will be outset by half the edge width, and the inside edges will be inset by half the edge width.  Outset is needed for subtractive machining, like cutting or milling.
4
5 ==Operation==
6 The default 'Activate Outset' checkbox is on.  When it is on, the gcode will be outset, when it is off, the gcode will not be changed.
7
8 ==Examples==
9 The following examples outset the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and outset.py.
10
11 > python outset.py
12 This brings up the outset dialog.
13
14 > python outset.py Screw Holder Bottom.stl
15 The outset tool is parsing the file:
16 Screw Holder Bottom.stl
17 ..
18 The outset tool has created the file:
19 .. Screw Holder Bottom_outset.gcode
20
21 """
22
23 from __future__ import absolute_import
24 try:
25         import psyco
26         psyco.full()
27 except:
28         pass
29 #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.
30 import __init__
31
32 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
33 from fabmetheus_utilities.geometry.solids import triangle_mesh
34 from fabmetheus_utilities import archive
35 from fabmetheus_utilities import euclidean
36 from fabmetheus_utilities import gcodec
37 from fabmetheus_utilities import intercircle
38 from fabmetheus_utilities import settings
39 from skeinforge_application.skeinforge_utilities import skeinforge_craft
40 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
41 from skeinforge_application.skeinforge_utilities import skeinforge_profile
42 import sys
43
44
45 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
46 __date__ = '$Date: 2008/02/05 $'
47 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
48
49
50 def getCraftedText( fileName, text='', repository=None):
51         'Outset the preface file or text.'
52         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
53
54 def getCraftedTextFromText(gcodeText, repository=None):
55         'Outset the preface gcode text.'
56         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'outset'):
57                 return gcodeText
58         if repository == None:
59                 repository = settings.getReadRepository( OutsetRepository() )
60         if not repository.activateOutset.value:
61                 return gcodeText
62         return OutsetSkein().getCraftedGcode(gcodeText, repository)
63
64 def getNewRepository():
65         'Get new repository.'
66         return OutsetRepository()
67
68 def writeOutput(fileName, shouldAnalyze=True):
69         'Outset the carving of a gcode file.'
70         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'outset', shouldAnalyze)
71
72
73 class OutsetRepository:
74         'A class to handle the outset settings.'
75         def __init__(self):
76                 'Set the default settings, execute title & settings fileName.'
77                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.outset.html', self )
78                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Outset', self, '')
79                 self.activateOutset = settings.BooleanSetting().getFromValue('Activate Outset', self, True )
80                 self.executeTitle = 'Outset'
81
82         def execute(self):
83                 'Outset button has been clicked.'
84                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
85                 for fileName in fileNames:
86                         writeOutput(fileName)
87
88
89 class OutsetSkein:
90         'A class to outset a skein of extrusions.'
91         def __init__(self):
92                 self.boundary = None
93                 self.distanceFeedRate = gcodec.DistanceFeedRate()
94                 self.layerCount = settings.LayerCount()
95                 self.lineIndex = 0
96                 self.loopLayer = None
97
98         def addGcodeFromRemainingLoop( self, loop, radius, z ):
99                 'Add the remainder of the loop.'
100                 boundary = intercircle.getLargestInsetLoopFromLoopRegardless( loop, radius )
101                 euclidean.addNestedRingBeginning( self.distanceFeedRate, boundary, z )
102                 self.distanceFeedRate.addPerimeterBlock(loop, z)
103                 self.distanceFeedRate.addLine('(</boundaryPerimeter>)')
104                 self.distanceFeedRate.addLine('(</nestedRing>)')
105
106         def addOutset(self, loopLayer):
107                 'Add outset to the layer.'
108                 extrudateLoops = intercircle.getInsetLoopsFromLoops(loopLayer.loops, -self.absoluteHalfEdgeWidth)
109                 triangle_mesh.sortLoopsInOrderOfArea(False, extrudateLoops)
110                 for extrudateLoop in extrudateLoops:
111                         self.addGcodeFromRemainingLoop(extrudateLoop, self.absoluteHalfEdgeWidth, loopLayer.z)
112
113         def getCraftedGcode(self, gcodeText, repository):
114                 'Parse gcode text and store the bevel gcode.'
115                 self.repository = repository
116                 self.lines = archive.getTextLines(gcodeText)
117                 self.parseInitialization()
118                 for lineIndex in xrange(self.lineIndex, len(self.lines)):
119                         self.parseLine( lineIndex )
120                 return self.distanceFeedRate.output.getvalue()
121
122         def parseInitialization(self):
123                 'Parse gcode initialization and store the parameters.'
124                 for self.lineIndex in xrange(len(self.lines)):
125                         line = self.lines[self.lineIndex].lstrip()
126                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
127                         firstWord = gcodec.getFirstWord(splitLine)
128                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
129                         if firstWord == '(</extruderInitialization>)':
130                                 self.distanceFeedRate.addTagBracketedProcedure('outset')
131                                 return
132                         elif firstWord == '(<edgeWidth>':
133                                 self.absoluteHalfEdgeWidth = 0.5 * abs(float(splitLine[1]))
134                         self.distanceFeedRate.addLine(line)
135
136         def parseLine( self, lineIndex ):
137                 'Parse a gcode line and add it to the outset skein.'
138                 line = self.lines[lineIndex].lstrip()
139                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
140                 if len(splitLine) < 1:
141                         return
142                 firstWord = splitLine[0]
143                 if firstWord == '(<boundaryPoint>':
144                         location = gcodec.getLocationFromSplitLine(None, splitLine)
145                         self.boundary.append(location.dropAxis())
146                 elif firstWord == '(<layer>':
147                         self.layerCount.printProgressIncrement('outset')
148                         self.loopLayer = euclidean.LoopLayer(float(splitLine[1]))
149                         self.distanceFeedRate.addLine(line)
150                 elif firstWord == '(</layer>)':
151                         self.addOutset( self.loopLayer )
152                         self.loopLayer = None
153                 elif firstWord == '(<nestedRing>)':
154                         self.boundary = []
155                         self.loopLayer.loops.append( self.boundary )
156                 if self.loopLayer == None:
157                         self.distanceFeedRate.addLine(line)
158
159
160 def main():
161         'Display the outset dialog.'
162         if len(sys.argv) > 1:
163                 writeOutput(' '.join(sys.argv[1 :]))
164         else:
165                 settings.startMainLoopFromConstructor(getNewRepository())
166
167 if __name__ == '__main__':
168         main()