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
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
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'
43 def getCraftedText( fileName, text='', repository=None):
44 'Outset the preface file or text.'
45 return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
47 def getCraftedTextFromText(gcodeText, repository=None):
48 'Outset the preface gcode text.'
49 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'outset'):
51 if repository == None:
52 repository = settings.getReadRepository( OutsetRepository() )
53 if not repository.activateOutset.value:
55 return OutsetSkein().getCraftedGcode(gcodeText, repository)
57 def getNewRepository():
59 return OutsetRepository()
61 def writeOutput(fileName, shouldAnalyze=True):
62 'Outset the carving of a gcode file.'
63 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'outset', shouldAnalyze)
66 class OutsetRepository(object):
67 'A class to handle the outset settings.'
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'
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:
82 class OutsetSkein(object):
83 'A class to outset a skein of extrusions.'
86 self.distanceFeedRate = gcodec.DistanceFeedRate()
87 self.layerCount = settings.LayerCount()
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>)')
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)
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()
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')
125 elif firstWord == '(<edgeWidth>':
126 self.absoluteHalfEdgeWidth = 0.5 * abs(float(splitLine[1]))
127 self.distanceFeedRate.addLine(line)
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:
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>)':
148 self.loopLayer.loops.append( self.boundary )
149 if self.loopLayer == None:
150 self.distanceFeedRate.addLine(line)
154 'Display the outset dialog.'
155 if len(sys.argv) > 1:
156 writeOutput(' '.join(sys.argv[1 :]))
158 settings.startMainLoopFromConstructor(getNewRepository())
160 if __name__ == '__main__':