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.
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.
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.
12 This brings up the outset dialog.
14 > python outset.py Screw Holder Bottom.stl
15 The outset tool is parsing the file:
16 Screw Holder Bottom.stl
18 The outset tool has created the file:
19 .. Screw Holder Bottom_outset.gcode
23 from __future__ import absolute_import
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.
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
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'
50 def getCraftedText( fileName, text='', repository=None):
51 'Outset the preface file or text.'
52 return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
54 def getCraftedTextFromText(gcodeText, repository=None):
55 'Outset the preface gcode text.'
56 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'outset'):
58 if repository == None:
59 repository = settings.getReadRepository( OutsetRepository() )
60 if not repository.activateOutset.value:
62 return OutsetSkein().getCraftedGcode(gcodeText, repository)
64 def getNewRepository():
66 return OutsetRepository()
68 def writeOutput(fileName, shouldAnalyze=True):
69 'Outset the carving of a gcode file.'
70 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'outset', shouldAnalyze)
73 class OutsetRepository:
74 'A class to handle the outset settings.'
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'
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:
90 'A class to outset a skein of extrusions.'
93 self.distanceFeedRate = gcodec.DistanceFeedRate()
94 self.layerCount = settings.LayerCount()
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>)')
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)
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()
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')
132 elif firstWord == '(<edgeWidth>':
133 self.absoluteHalfEdgeWidth = 0.5 * abs(float(splitLine[1]))
134 self.distanceFeedRate.addLine(line)
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:
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>)':
155 self.loopLayer.loops.append( self.boundary )
156 if self.loopLayer == None:
157 self.distanceFeedRate.addLine(line)
161 'Display the outset dialog.'
162 if len(sys.argv) > 1:
163 writeOutput(' '.join(sys.argv[1 :]))
165 settings.startMainLoopFromConstructor(getNewRepository())
167 if __name__ == '__main__':