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 / 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
25 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
26 from fabmetheus_utilities.geometry.solids import triangle_mesh
27 from fabmetheus_utilities import archive
28 from fabmetheus_utilities import euclidean
29 from fabmetheus_utilities import gcodec
30 from fabmetheus_utilities import intercircle
31 from fabmetheus_utilities import settings
32 from skeinforge_application.skeinforge_utilities import skeinforge_craft
33 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
34 from skeinforge_application.skeinforge_utilities import skeinforge_profile
35 import sys
36
37
38 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
39 __date__ = '$Date: 2008/02/05 $'
40 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
41
42
43 def getCraftedText( fileName, text='', repository=None):
44         'Outset the preface file or text.'
45         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
46
47 def getCraftedTextFromText(gcodeText, repository=None):
48         'Outset the preface gcode text.'
49         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'outset'):
50                 return gcodeText
51         if repository == None:
52                 repository = settings.getReadRepository( OutsetRepository() )
53         if not repository.activateOutset.value:
54                 return gcodeText
55         return OutsetSkein().getCraftedGcode(gcodeText, repository)
56
57 def getNewRepository():
58         'Get new repository.'
59         return OutsetRepository()
60
61 def writeOutput(fileName, shouldAnalyze=True):
62         'Outset the carving of a gcode file.'
63         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'outset', shouldAnalyze)
64
65
66 class OutsetRepository(object):
67         'A class to handle the outset settings.'
68         def __init__(self):
69                 'Set the default settings, execute title & settings fileName.'
70                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.outset.html', self )
71                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Outset', self, '')
72                 self.activateOutset = settings.BooleanSetting().getFromValue('Activate Outset', self, True )
73                 self.executeTitle = 'Outset'
74
75         def execute(self):
76                 'Outset button has been clicked.'
77                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
78                 for fileName in fileNames:
79                         writeOutput(fileName)
80
81
82 class OutsetSkein(object):
83         'A class to outset a skein of extrusions.'
84         def __init__(self):
85                 self.boundary = None
86                 self.distanceFeedRate = gcodec.DistanceFeedRate()
87                 self.layerCount = settings.LayerCount()
88                 self.lineIndex = 0
89                 self.loopLayer = None
90
91         def addGcodeFromRemainingLoop( self, loop, radius, z ):
92                 'Add the remainder of the loop.'
93                 boundary = intercircle.getLargestInsetLoopFromLoopRegardless( loop, radius )
94                 euclidean.addNestedRingBeginning( self.distanceFeedRate, boundary, z )
95                 self.distanceFeedRate.addPerimeterBlock(loop, z)
96                 self.distanceFeedRate.addLine('(</boundaryPerimeter>)')
97                 self.distanceFeedRate.addLine('(</nestedRing>)')
98
99         def addOutset(self, loopLayer):
100                 'Add outset to the layer.'
101                 extrudateLoops = intercircle.getInsetLoopsFromLoops(loopLayer.loops, -self.absoluteHalfEdgeWidth)
102                 triangle_mesh.sortLoopsInOrderOfArea(False, extrudateLoops)
103                 for extrudateLoop in extrudateLoops:
104                         self.addGcodeFromRemainingLoop(extrudateLoop, self.absoluteHalfEdgeWidth, loopLayer.z)
105
106         def getCraftedGcode(self, gcodeText, repository):
107                 'Parse gcode text and store the bevel gcode.'
108                 self.repository = repository
109                 self.lines = archive.getTextLines(gcodeText)
110                 self.parseInitialization()
111                 for lineIndex in xrange(self.lineIndex, len(self.lines)):
112                         self.parseLine( lineIndex )
113                 return self.distanceFeedRate.output.getvalue()
114
115         def parseInitialization(self):
116                 'Parse gcode initialization and store the parameters.'
117                 for self.lineIndex in xrange(len(self.lines)):
118                         line = self.lines[self.lineIndex].lstrip()
119                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
120                         firstWord = gcodec.getFirstWord(splitLine)
121                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
122                         if firstWord == '(</extruderInitialization>)':
123                                 self.distanceFeedRate.addTagBracketedProcedure('outset')
124                                 return
125                         elif firstWord == '(<edgeWidth>':
126                                 self.absoluteHalfEdgeWidth = 0.5 * abs(float(splitLine[1]))
127                         self.distanceFeedRate.addLine(line)
128
129         def parseLine( self, lineIndex ):
130                 'Parse a gcode line and add it to the outset skein.'
131                 line = self.lines[lineIndex].lstrip()
132                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
133                 if len(splitLine) < 1:
134                         return
135                 firstWord = splitLine[0]
136                 if firstWord == '(<boundaryPoint>':
137                         location = gcodec.getLocationFromSplitLine(None, splitLine)
138                         self.boundary.append(location.dropAxis())
139                 elif firstWord == '(<layer>':
140                         self.layerCount.printProgressIncrement('outset')
141                         self.loopLayer = euclidean.LoopLayer(float(splitLine[1]))
142                         self.distanceFeedRate.addLine(line)
143                 elif firstWord == '(</layer>)':
144                         self.addOutset( self.loopLayer )
145                         self.loopLayer = None
146                 elif firstWord == '(<nestedRing>)':
147                         self.boundary = []
148                         self.loopLayer.loops.append( self.boundary )
149                 if self.loopLayer == None:
150                         self.distanceFeedRate.addLine(line)
151
152
153 def main():
154         'Display the outset dialog.'
155         if len(sys.argv) > 1:
156                 writeOutput(' '.join(sys.argv[1 :]))
157         else:
158                 settings.startMainLoopFromConstructor(getNewRepository())
159
160 if __name__ == '__main__':
161         main()