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
61 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
62 from fabmetheus_utilities import archive
63 from fabmetheus_utilities import gcodec
64 from fabmetheus_utilities import settings
65 from skeinforge_application.skeinforge_utilities import skeinforge_craft
66 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
67 from skeinforge_application.skeinforge_utilities import skeinforge_profile
72 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
73 __date__ = '$Date: 2008/21/04 $'
74 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
77 def getCraftedText( fileName, text, splodgeRepository = None ):
78 "Splodge a gcode linear move file or text."
79 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), splodgeRepository )
81 def getCraftedTextFromText( gcodeText, splodgeRepository = None ):
82 "Splodge a gcode linear move text."
83 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'splodge'):
85 if splodgeRepository == None:
86 splodgeRepository = settings.getReadRepository( SplodgeRepository() )
87 if not splodgeRepository.activateSplodge.value:
89 return SplodgeSkein().getCraftedGcode( gcodeText, splodgeRepository )
91 def getNewRepository():
93 return SplodgeRepository()
95 def writeOutput(fileName, shouldAnalyze=True):
96 "Splodge a gcode linear move file."
97 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'splodge', shouldAnalyze)
100 class SplodgeRepository(object):
101 "A class to handle the splodge settings."
103 "Set the default settings, execute title & settings fileName."
104 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html', self )
105 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Splodge', self, '')
106 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge')
107 self.activateSplodge = settings.BooleanSetting().getFromValue('Activate Splodge', self, False )
108 settings.LabelSeparator().getFromRepository(self)
109 settings.LabelDisplay().getFromName('- Initial -', self )
110 self.initialLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Initial Lift over Extra Thickness (ratio):', self, 1.5, 1.0 )
111 self.initialSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Initial Splodge Feed Rate (mm/s):', self, 2.4, 1.0 )
112 self.initialSplodgeQuantityLength = settings.FloatSpin().getFromValue( 10.0, 'Initial Splodge Quantity Length (millimeters):', self, 90.0, 30.0 )
113 settings.LabelSeparator().getFromRepository(self)
114 settings.LabelDisplay().getFromName('- Operating -', self )
115 self.operatingLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Operating Lift over Extra Thickness (ratio):', self, 1.5, 1.0 )
116 self.operatingSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Operating Splodge Feed Rate (mm/s):', self, 2.4, 1.0 )
117 self.operatingSplodgeQuantityLength = settings.FloatSpin().getFromValue(0.4, 'Operating Splodge Quantity Length (millimeters):', self, 2.4, 1.0)
118 settings.LabelSeparator().getFromRepository(self)
119 self.executeTitle = 'Splodge'
122 "Splodge button has been clicked."
123 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
124 for fileName in fileNames:
125 writeOutput(fileName)
128 class SplodgeSkein(object):
129 "A class to splodge a skein of extrusions."
131 self.distanceFeedRate = gcodec.DistanceFeedRate()
132 self.feedRateMinute = 961.0
133 self.isExtruderActive = False
134 self.hasInitialSplodgeBeenAdded = False
135 self.isLastExtruderCommandActivate = False
136 self.lastLineOutput = None
139 self.oldLocation = None
140 self.operatingFeedRatePerSecond = 15.0
142 def addLineUnlessIdentical(self, line):
143 "Add a line, unless it is identical to the last line."
144 if line == self.lastLineOutput:
146 self.lastLineOutput = line
147 self.distanceFeedRate.addLine(line)
149 def addLineUnlessIdenticalReactivate(self, line):
150 "Add a line, unless it is identical to the last line or another M101."
151 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
152 if len(splitLine) < 1:
154 firstWord = splitLine[0]
155 if firstWord == 'M101':
156 if not self.isLastExtruderCommandActivate:
157 self.addLineUnlessIdentical(line)
158 self.isLastExtruderCommandActivate = True
160 if firstWord == 'M103':
161 self.isLastExtruderCommandActivate = False
162 self.addLineUnlessIdentical(line)
164 def getCraftedGcode( self, gcodeText, splodgeRepository ):
165 "Parse gcode text and store the splodge gcode."
166 self.lines = archive.getTextLines(gcodeText)
168 self.splodgeRepository = splodgeRepository
169 self.parseInitialization( splodgeRepository )
170 self.boundingRectangle = gcodec.BoundingRectangle().getFromGcodeLines( self.lines[self.lineIndex :], 0.5 * self.edgeWidth )
171 self.initialSplodgeFeedRateMinute = 60.0 * splodgeRepository.initialSplodgeFeedRate.value
172 self.initialStartupDistance = splodgeRepository.initialSplodgeQuantityLength.value * splodgeRepository.initialSplodgeFeedRate.value / self.operatingFeedRatePerSecond
173 self.operatingSplodgeFeedRateMinute = 60.0 * splodgeRepository.operatingSplodgeFeedRate.value
174 self.operatingStartupDistance = splodgeRepository.operatingSplodgeQuantityLength.value * splodgeRepository.operatingSplodgeFeedRate.value / self.operatingFeedRatePerSecond
175 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
176 line = self.lines[self.lineIndex]
178 return self.distanceFeedRate.output.getvalue()
180 def getInitialSplodgeLine( self, line, location ):
181 "Add the initial splodge line."
182 if not self.isJustBeforeExtrusion():
184 self.hasInitialSplodgeBeenAdded = True
185 if self.splodgeRepository.initialSplodgeQuantityLength.value < self.minimumQuantityLength:
187 return self.getSplodgeLineGivenDistance( self.initialSplodgeFeedRateMinute, line, self.splodgeRepository.initialLiftOverExtraThickness.value, location, self.initialStartupDistance )
189 def getNextActiveLocationComplex(self):
190 "Get the next active line."
192 for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ):
193 line = self.lines[lineIndex]
194 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
195 firstWord = gcodec.getFirstWord(splitLine)
196 if firstWord == 'M101':
198 if firstWord == 'G1' and isActive:
199 return gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).dropAxis()
202 def getOperatingSplodgeLine( self, line, location ):
203 "Get the operating splodge line."
204 if not self.isJustBeforeExtrusion():
206 if self.splodgeRepository.operatingSplodgeQuantityLength.value < self.minimumQuantityLength:
208 return self.getSplodgeLineGivenDistance( self.operatingSplodgeFeedRateMinute, line, self.splodgeRepository.operatingLiftOverExtraThickness.value, location, self.operatingStartupDistance )
210 def getSplodgeLine(self, line, location, splitLine):
211 "Get splodged gcode line."
212 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
213 if self.hasInitialSplodgeBeenAdded:
214 return self.getOperatingSplodgeLine(line, location)
215 return self.getInitialSplodgeLine(line, location)
217 def getSplodgeLineGivenDistance( self, feedRateMinute, line, liftOverExtraThickness, location, startupDistance ):
218 "Add the splodge line."
219 locationComplex = location.dropAxis()
220 relativeStartComplex = None
221 nextLocationComplex = self.getNextActiveLocationComplex()
222 if nextLocationComplex != None:
223 if nextLocationComplex != locationComplex:
224 relativeStartComplex = locationComplex - nextLocationComplex
225 if relativeStartComplex == None:
226 relativeStartComplex = complex( 19.9, 9.9 )
227 if self.oldLocation != None:
228 oldLocationComplex = self.oldLocation.dropAxis()
229 if oldLocationComplex != locationComplex:
230 relativeStartComplex = oldLocationComplex - locationComplex
231 relativeStartComplex *= startupDistance / abs( relativeStartComplex )
232 startComplex = self.getStartInsideBoundingRectangle( locationComplex, relativeStartComplex )
233 feedRateMultiplier = feedRateMinute / self.operatingFeedRatePerSecond / 60.0
234 splodgeLayerThickness = self.layerHeight / math.sqrt( feedRateMultiplier )
235 extraLayerThickness = splodgeLayerThickness - self.layerHeight
236 lift = extraLayerThickness * liftOverExtraThickness
237 startLine = self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.feedRateMinute, startComplex, location.z + lift )
238 self.addLineUnlessIdenticalReactivate( startLine )
239 self.addLineUnlessIdenticalReactivate('M101')
240 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
241 lineLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
242 self.distanceFeedRate.addGcodeMovementZWithFeedRate( feedRateMinute, locationComplex, lineLocation.z + lift )
245 def getStartInsideBoundingRectangle( self, locationComplex, relativeStartComplex ):
246 "Get a start inside the bounding rectangle."
247 startComplex = locationComplex + relativeStartComplex
248 if self.boundingRectangle.isPointInside( startComplex ):
250 for rotation in self.rotations:
251 rotatedRelativeStartComplex = relativeStartComplex * rotation
252 startComplex = locationComplex + rotatedRelativeStartComplex
253 if self.boundingRectangle.isPointInside( startComplex ):
257 def isJustBeforeExtrusion(self):
258 "Determine if activate command is before linear move command."
259 for lineIndex in xrange(self.lineIndex + 1, len(self.lines)):
260 line = self.lines[lineIndex]
261 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
262 firstWord = gcodec.getFirstWord(splitLine)
263 if firstWord == 'G1' or firstWord == 'M103':
265 if firstWord == 'M101':
269 def parseInitialization( self, splodgeRepository ):
270 'Parse gcode initialization and store the parameters.'
271 for self.lineIndex in xrange(len(self.lines)):
272 line = self.lines[self.lineIndex]
273 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
274 firstWord = gcodec.getFirstWord(splitLine)
275 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
276 if firstWord == '(</extruderInitialization>)':
277 self.addLineUnlessIdenticalReactivate(gcodec.getTagBracketedProcedure('splodge'))
279 elif firstWord == '(<layerHeight>':
280 self.layerHeight = float(splitLine[1])
281 elif firstWord == '(<operatingFeedRatePerSecond>':
282 self.operatingFeedRatePerSecond = float(splitLine[1])
283 elif firstWord == '(<edgeWidth>':
284 self.edgeWidth = float(splitLine[1])
285 self.minimumQuantityLength = 0.1 * self.edgeWidth
286 self.addLineUnlessIdenticalReactivate(line)
288 def parseLine(self, line):
289 "Parse a gcode line and add it to the bevel gcode."
290 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
291 if len(splitLine) < 1:
293 firstWord = splitLine[0]
294 if firstWord == 'G1':
295 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
296 line = self.getSplodgeLine(line, location, splitLine)
297 self.oldLocation = location
298 elif firstWord == 'M101':
299 self.isExtruderActive = True
300 elif firstWord == 'M103':
301 self.isExtruderActive = False
302 self.addLineUnlessIdenticalReactivate(line)
304 def setRotations(self):
306 self.rootHalf = math.sqrt( 0.5 )
308 self.rotations.append( complex( self.rootHalf, self.rootHalf ) )
309 self.rotations.append( complex( self.rootHalf, - self.rootHalf ) )
310 self.rotations.append( complex( 0.0, 1.0 ) )
311 self.rotations.append( complex(0.0, -1.0) )
312 self.rotations.append( complex( - self.rootHalf, self.rootHalf ) )
313 self.rotations.append( complex( - self.rootHalf, - self.rootHalf ) )
317 "Display the splodge dialog."
318 if len(sys.argv) > 1:
319 writeOutput(' '.join(sys.argv[1 :]))
321 settings.startMainLoopFromConstructor(getNewRepository())
323 if __name__ == "__main__":