2 This page is in the table of contents.
3 Splodge turns the extruder on just before the start of a thread. This is to give the extrusion a bit anchoring at the beginning.
5 The splodge manual page is at:
6 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge
9 The default 'Activate Splodge' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called.
13 ====Initial Lift over Extra Thickness====
16 Defines the amount the extruder will be lifted over the extra thickness of the initial splodge thread. The higher the ratio, the more the extruder will be lifted over the splodge, if the ratio is too low the extruder might plow through the splodge extrusion.
18 ====Initial Splodge Feed Rate====
19 Default is one millimeter per second.
21 Defines the feed rate at which the initial extra extrusion will be added. With the default feed rate, the splodge will be added slower so it will be thicker than the regular extrusion.
23 ====Initial Splodge Quantity Length====
24 Default is thirty millimeters.
26 Defines the quantity length of extra extrusion at the operating feed rate that will be added to the initial thread. If a splodge quantity length is smaller than 0.1 times the edge width, no splodge of that type will be added.
29 ====Operating Lift over Extra Thickness====
32 Defines the amount the extruder will be lifted over the extra thickness of the operating splodge thread.
34 ====Operating Splodge Feed Rate====
35 Default is one millimeter per second.
37 Defines the feed rate at which the next extra extrusions will be added.
39 ====Operating Splodge Quantity Length====
40 Default is thirty millimeters.
42 Defines the quantity length of extra extrusion at the operating feed rate that will be added for the next threads.
45 The following examples splodge the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and splodge.py.
48 This brings up the splodge dialog.
50 > python splodge.py Screw Holder Bottom.stl
51 The splodge tool is parsing the file:
52 Screw Holder Bottom.stl
54 The splodge tool has created the file:
55 .. Screw Holder Bottom_splodge.gcode
59 from __future__ import absolute_import
60 #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.
63 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
64 from fabmetheus_utilities import archive
65 from fabmetheus_utilities import euclidean
66 from fabmetheus_utilities import gcodec
67 from fabmetheus_utilities import settings
68 from skeinforge_application.skeinforge_utilities import skeinforge_craft
69 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
70 from skeinforge_application.skeinforge_utilities import skeinforge_profile
75 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
76 __date__ = '$Date: 2008/21/04 $'
77 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
80 def getCraftedText( fileName, text, splodgeRepository = None ):
81 "Splodge a gcode linear move file or text."
82 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), splodgeRepository )
84 def getCraftedTextFromText( gcodeText, splodgeRepository = None ):
85 "Splodge a gcode linear move text."
86 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'splodge'):
88 if splodgeRepository == None:
89 splodgeRepository = settings.getReadRepository( SplodgeRepository() )
90 if not splodgeRepository.activateSplodge.value:
92 return SplodgeSkein().getCraftedGcode( gcodeText, splodgeRepository )
94 def getNewRepository():
96 return SplodgeRepository()
98 def writeOutput(fileName, shouldAnalyze=True):
99 "Splodge a gcode linear move file."
100 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'splodge', shouldAnalyze)
103 class SplodgeRepository:
104 "A class to handle the splodge settings."
106 "Set the default settings, execute title & settings fileName."
107 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html', self )
108 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Splodge', self, '')
109 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge')
110 self.activateSplodge = settings.BooleanSetting().getFromValue('Activate Splodge', self, False )
111 settings.LabelSeparator().getFromRepository(self)
112 settings.LabelDisplay().getFromName('- Initial -', self )
113 self.initialLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Initial Lift over Extra Thickness (ratio):', self, 1.5, 1.0 )
114 self.initialSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Initial Splodge Feed Rate (mm/s):', self, 2.4, 1.0 )
115 self.initialSplodgeQuantityLength = settings.FloatSpin().getFromValue( 10.0, 'Initial Splodge Quantity Length (millimeters):', self, 90.0, 30.0 )
116 settings.LabelSeparator().getFromRepository(self)
117 settings.LabelDisplay().getFromName('- Operating -', self )
118 self.operatingLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Operating Lift over Extra Thickness (ratio):', self, 1.5, 1.0 )
119 self.operatingSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Operating Splodge Feed Rate (mm/s):', self, 2.4, 1.0 )
120 self.operatingSplodgeQuantityLength = settings.FloatSpin().getFromValue(0.4, 'Operating Splodge Quantity Length (millimeters):', self, 2.4, 1.0)
121 settings.LabelSeparator().getFromRepository(self)
122 self.executeTitle = 'Splodge'
125 "Splodge button has been clicked."
126 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
127 for fileName in fileNames:
128 writeOutput(fileName)
132 "A class to splodge a skein of extrusions."
134 self.distanceFeedRate = gcodec.DistanceFeedRate()
135 self.feedRateMinute = 961.0
136 self.isExtruderActive = False
137 self.hasInitialSplodgeBeenAdded = False
138 self.isLastExtruderCommandActivate = False
139 self.lastLineOutput = None
142 self.oldLocation = None
143 self.operatingFeedRatePerSecond = 15.0
145 def addLineUnlessIdentical(self, line):
146 "Add a line, unless it is identical to the last line."
147 if line == self.lastLineOutput:
149 self.lastLineOutput = line
150 self.distanceFeedRate.addLine(line)
152 def addLineUnlessIdenticalReactivate(self, line):
153 "Add a line, unless it is identical to the last line or another M101."
154 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
155 if len(splitLine) < 1:
157 firstWord = splitLine[0]
158 if firstWord == 'M101':
159 if not self.isLastExtruderCommandActivate:
160 self.addLineUnlessIdentical(line)
161 self.isLastExtruderCommandActivate = True
163 if firstWord == 'M103':
164 self.isLastExtruderCommandActivate = False
165 self.addLineUnlessIdentical(line)
167 def getCraftedGcode( self, gcodeText, splodgeRepository ):
168 "Parse gcode text and store the splodge gcode."
169 self.lines = archive.getTextLines(gcodeText)
171 self.splodgeRepository = splodgeRepository
172 self.parseInitialization( splodgeRepository )
173 self.boundingRectangle = gcodec.BoundingRectangle().getFromGcodeLines( self.lines[self.lineIndex :], 0.5 * self.edgeWidth )
174 self.initialSplodgeFeedRateMinute = 60.0 * splodgeRepository.initialSplodgeFeedRate.value
175 self.initialStartupDistance = splodgeRepository.initialSplodgeQuantityLength.value * splodgeRepository.initialSplodgeFeedRate.value / self.operatingFeedRatePerSecond
176 self.operatingSplodgeFeedRateMinute = 60.0 * splodgeRepository.operatingSplodgeFeedRate.value
177 self.operatingStartupDistance = splodgeRepository.operatingSplodgeQuantityLength.value * splodgeRepository.operatingSplodgeFeedRate.value / self.operatingFeedRatePerSecond
178 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
179 line = self.lines[self.lineIndex]
181 return self.distanceFeedRate.output.getvalue()
183 def getInitialSplodgeLine( self, line, location ):
184 "Add the initial splodge line."
185 if not self.isJustBeforeExtrusion():
187 self.hasInitialSplodgeBeenAdded = True
188 if self.splodgeRepository.initialSplodgeQuantityLength.value < self.minimumQuantityLength:
190 return self.getSplodgeLineGivenDistance( self.initialSplodgeFeedRateMinute, line, self.splodgeRepository.initialLiftOverExtraThickness.value, location, self.initialStartupDistance )
192 def getNextActiveLocationComplex(self):
193 "Get the next active line."
195 for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ):
196 line = self.lines[lineIndex]
197 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
198 firstWord = gcodec.getFirstWord(splitLine)
199 if firstWord == 'M101':
201 if firstWord == 'G1' and isActive:
202 return gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).dropAxis()
205 def getOperatingSplodgeLine( self, line, location ):
206 "Get the operating splodge line."
207 if not self.isJustBeforeExtrusion():
209 if self.splodgeRepository.operatingSplodgeQuantityLength.value < self.minimumQuantityLength:
211 return self.getSplodgeLineGivenDistance( self.operatingSplodgeFeedRateMinute, line, self.splodgeRepository.operatingLiftOverExtraThickness.value, location, self.operatingStartupDistance )
213 def getSplodgeLine(self, line, location, splitLine):
214 "Get splodged gcode line."
215 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
216 if self.hasInitialSplodgeBeenAdded:
217 return self.getOperatingSplodgeLine(line, location)
218 return self.getInitialSplodgeLine(line, location)
220 def getSplodgeLineGivenDistance( self, feedRateMinute, line, liftOverExtraThickness, location, startupDistance ):
221 "Add the splodge line."
222 locationComplex = location.dropAxis()
223 relativeStartComplex = None
224 nextLocationComplex = self.getNextActiveLocationComplex()
225 if nextLocationComplex != None:
226 if nextLocationComplex != locationComplex:
227 relativeStartComplex = locationComplex - nextLocationComplex
228 if relativeStartComplex == None:
229 relativeStartComplex = complex( 19.9, 9.9 )
230 if self.oldLocation != None:
231 oldLocationComplex = self.oldLocation.dropAxis()
232 if oldLocationComplex != locationComplex:
233 relativeStartComplex = oldLocationComplex - locationComplex
234 relativeStartComplex *= startupDistance / abs( relativeStartComplex )
235 startComplex = self.getStartInsideBoundingRectangle( locationComplex, relativeStartComplex )
236 feedRateMultiplier = feedRateMinute / self.operatingFeedRatePerSecond / 60.0
237 splodgeLayerThickness = self.layerHeight / math.sqrt( feedRateMultiplier )
238 extraLayerThickness = splodgeLayerThickness - self.layerHeight
239 lift = extraLayerThickness * liftOverExtraThickness
240 startLine = self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.feedRateMinute, startComplex, location.z + lift )
241 self.addLineUnlessIdenticalReactivate( startLine )
242 self.addLineUnlessIdenticalReactivate('M101')
243 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
244 lineLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
245 self.distanceFeedRate.addGcodeMovementZWithFeedRate( feedRateMinute, locationComplex, lineLocation.z + lift )
248 def getStartInsideBoundingRectangle( self, locationComplex, relativeStartComplex ):
249 "Get a start inside the bounding rectangle."
250 startComplex = locationComplex + relativeStartComplex
251 if self.boundingRectangle.isPointInside( startComplex ):
253 for rotation in self.rotations:
254 rotatedRelativeStartComplex = relativeStartComplex * rotation
255 startComplex = locationComplex + rotatedRelativeStartComplex
256 if self.boundingRectangle.isPointInside( startComplex ):
260 def isJustBeforeExtrusion(self):
261 "Determine if activate command is before linear move command."
262 for lineIndex in xrange(self.lineIndex + 1, len(self.lines)):
263 line = self.lines[lineIndex]
264 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
265 firstWord = gcodec.getFirstWord(splitLine)
266 if firstWord == 'G1' or firstWord == 'M103':
268 if firstWord == 'M101':
272 def parseInitialization( self, splodgeRepository ):
273 'Parse gcode initialization and store the parameters.'
274 for self.lineIndex in xrange(len(self.lines)):
275 line = self.lines[self.lineIndex]
276 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
277 firstWord = gcodec.getFirstWord(splitLine)
278 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
279 if firstWord == '(</extruderInitialization>)':
280 self.addLineUnlessIdenticalReactivate(gcodec.getTagBracketedProcedure('splodge'))
282 elif firstWord == '(<layerHeight>':
283 self.layerHeight = float(splitLine[1])
284 elif firstWord == '(<operatingFeedRatePerSecond>':
285 self.operatingFeedRatePerSecond = float(splitLine[1])
286 elif firstWord == '(<edgeWidth>':
287 self.edgeWidth = float(splitLine[1])
288 self.minimumQuantityLength = 0.1 * self.edgeWidth
289 self.addLineUnlessIdenticalReactivate(line)
291 def parseLine(self, line):
292 "Parse a gcode line and add it to the bevel gcode."
293 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
294 if len(splitLine) < 1:
296 firstWord = splitLine[0]
297 if firstWord == 'G1':
298 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
299 line = self.getSplodgeLine(line, location, splitLine)
300 self.oldLocation = location
301 elif firstWord == 'M101':
302 self.isExtruderActive = True
303 elif firstWord == 'M103':
304 self.isExtruderActive = False
305 self.addLineUnlessIdenticalReactivate(line)
307 def setRotations(self):
309 self.rootHalf = math.sqrt( 0.5 )
311 self.rotations.append( complex( self.rootHalf, self.rootHalf ) )
312 self.rotations.append( complex( self.rootHalf, - self.rootHalf ) )
313 self.rotations.append( complex( 0.0, 1.0 ) )
314 self.rotations.append( complex(0.0, -1.0) )
315 self.rotations.append( complex( - self.rootHalf, self.rootHalf ) )
316 self.rotations.append( complex( - self.rootHalf, - self.rootHalf ) )
320 "Display the splodge dialog."
321 if len(sys.argv) > 1:
322 writeOutput(' '.join(sys.argv[1 :]))
324 settings.startMainLoopFromConstructor(getNewRepository())
326 if __name__ == "__main__":