2 This page is in the table of contents.
3 Raft is a plugin to create a raft, elevate the nozzle and set the temperature. A raft is a flat base structure on top of which your object is being build and has a few different purposes. It fills irregularities like scratches and pits in your printbed and gives you a nice base parallel to the printheads movement. It also glues your object to the bed so to prevent warping in bigger object. The rafts base layer performs these tricks while the sparser interface layer(s) help you removing the object from the raft after printing. It is based on the Nophead's reusable raft, which has a base layer running one way, and a couple of perpendicular layers above. Each set of layers can be set to a different temperature. There is the option of having the extruder orbit the raft for a while, so the heater barrel has time to reach a different temperature, without ooze accumulating around the nozzle.
5 The raft manual page is at:
6 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft
8 The important values for the raft settings are the temperatures of the raft, the first layer and the next layers. These will be different for each material. The default settings for ABS, HDPE, PCL & PLA are extrapolated from Nophead's experiments.
10 You don't necessarily need a raft and especially small object will print fine on a flat bed without one, sometimes its even better when you need a water tight base to print directly on the bed. If you want to only set the temperature or only create support material or only elevate the nozzle without creating a raft, set the Base Layers and Interface Layers to zero.
16 Example of a raft on the left with the interface layers partially removed exposing the base layer. Notice that the first line of the base is rarely printed well because of the startup time of the extruder. On the right you see an object with its raft still attached.
18 The Raft panel has some extra settings, it probably made sense to have them there but they have not that much to do with the actual Raft. First are the Support material settings. Since close to all RepRap style printers have no second extruder for support material Skeinforge offers the option to print support structures with the same material set at a different speed and temperature. The idea is that the support sticks less to the actual object when it is extruded around the minimum possible working temperature. This results in a temperature change EVERY layer so build time will increase seriously.
20 Allan Ecker aka The Masked Retriever's has written two quicktips for raft which follow below.
21 "Skeinforge Quicktip: The Raft, Part 1" at:
22 http://blog.thingiverse.com/2009/07/14/skeinforge-quicktip-the-raft-part-1/
23 "Skeinforge Quicktip: The Raft, Part II" at:
24 http://blog.thingiverse.com/2009/08/04/skeinforge-quicktip-the-raft-part-ii/
26 Nophead has written about rafts on his blog:
27 http://hydraraptor.blogspot.com/2009/07/thoughts-on-rafts.html
29 More pictures of rafting in action are available from the Metalab blog at:
30 http://reprap.soup.io/?search=rafting
35 When it is on, the functions described below will work, when it is off, nothing will be done, so no temperatures will be set, nozzle will not be lifted..
38 ===Add Raft, Elevate Nozzle, Orbit===
41 When selected, the script will also create a raft, elevate the nozzle, orbit and set the altitude of the bottom of the raft. It also turns on support generation.
44 Base layer is the part of the raft that touches the bed.
46 ====Base Feed Rate Multiplier====
49 Defines the base feed rate multiplier. The greater the 'Base Feed Rate Multiplier', the thinner the base, the lower the 'Base Feed Rate Multiplier', the thicker the base.
51 ====Base Flow Rate Multiplier====
54 Defines the base flow rate multiplier. The greater the 'Base Flow Rate Multiplier', the thicker the base, the lower the 'Base Flow Rate Multiplier', the thinner the base.
56 ====Base Infill Density====
59 Defines the infill density ratio of the base of the raft.
61 ====Base Layer Height over Layer Thickness====
64 Defines the ratio of the height & width of the base layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill.
69 Defines the number of base layers.
71 ====Base Nozzle Lift over Base Layer Thickness====
74 Defines the amount the nozzle is above the center of the base extrusion divided by the base layer thickness.
76 ===Initial Circling===
79 When selected, the extruder will initially circle around until it reaches operating temperature.
81 ===Infill Overhang over Extrusion Width===
84 Defines the ratio of the infill overhang over the the extrusion width of the raft.
87 ====Interface Feed Rate Multiplier====
90 Defines the interface feed rate multiplier. The greater the 'Interface Feed Rate Multiplier', the thinner the interface, the lower the 'Interface Feed Rate Multiplier', the thicker the interface.
92 ====Interface Flow Rate Multiplier====
95 Defines the interface flow rate multiplier. The greater the 'Interface Flow Rate Multiplier', the thicker the interface, the lower the 'Interface Flow Rate Multiplier', the thinner the interface.
97 ====Interface Infill Density====
100 Defines the infill density ratio of the interface of the raft.
102 ====Interface Layer Thickness over Extrusion Height====
105 Defines the ratio of the height & width of the interface layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill.
107 ====Interface Layers====
110 Defines the number of interface layers to print.
112 ====Interface Nozzle Lift over Interface Layer Thickness====
115 Defines the amount the nozzle is above the center of the interface extrusion divided by the interface layer thickness.
117 ===Name of Alteration Files===
118 If support material is generated, raft looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Raft does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder.
120 ====Name of Support End File====
121 Default is support_end.gcode.
123 If support material is generated and if there is a file with the name of the "Name of Support End File" setting, it will be added to the end of the support gcode.
125 ====Name of Support Start File====
126 If support material is generated and if there is a file with the name of the "Name of Support Start File" setting, it will be added to the start of the support gcode.
128 ===Operating Nozzle Lift over Layer Thickness===
131 Defines the amount the nozzle is above the center of the operating extrusion divided by the layer height.
134 The raft fills a rectangle whose base size is the rectangle around the bottom layer of the object expanded on each side by the 'Raft Margin' plus the 'Raft Additional Margin over Length (%)' percentage times the length of the side.
136 ====Raft Additional Margin over Length====
137 Default is 1 percent.
140 Default is three millimeters.
143 Good articles on support material are at:
144 http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-1/
145 http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-2/
147 ====Support Cross Hatch====
150 When selected, the support material will cross hatched. Cross hatching the support makes it stronger and harder to remove, which is why the default is off.
152 ====Support Flow Rate over Operating Flow Rate====
155 Defines the ratio of the flow rate when the support is extruded over the operating flow rate. With a number less than one, the support flow rate will be smaller so the support will be thinner and easier to remove.
157 ====Support Gap over Perimeter Extrusion Width====
160 Defines the gap between the support material and the object over the edge extrusion width.
162 ====Support Material Choice====
163 Default is 'None' because the raft takes time to generate.
165 =====Empty Layers Only=====
166 When selected, support material will be only on the empty layers. This is useful when making identical objects in a stack.
169 When selected, support material will be added wherever there are overhangs, even inside the object. Because support material inside objects is hard or impossible to remove, this option should only be chosen if the object has a cavity that needs support and there is some way to extract the support material.
171 =====Exterior Only=====
172 When selected, support material will be added only the exterior of the object. This is the best option for most objects which require support material.
175 When selected, raft will not add support material.
177 ====Support Minimum Angle====
178 Default is sixty degrees.
180 Defines the minimum angle that a surface overhangs before support material is added. If angle is lower then this value the support will be generated. This angle is defined from the vertical, so zero is a vertical wall, ten is a wall with a bit of overhang, thirty is the typical safe angle for filament extrusion, sixty is a really high angle for extrusion and ninety is an unsupported horizontal ceiling.
183 The following examples raft the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and raft.py.
186 This brings up the raft dialog.
188 > python raft.py Screw Holder Bottom.stl
189 The raft tool is parsing the file:
190 Screw Holder Bottom.stl
192 The raft tool has created the file:
193 Screw Holder Bottom_raft.gcode
197 from __future__ import absolute_import
198 #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.
201 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
202 from fabmetheus_utilities.geometry.solids import triangle_mesh
203 from fabmetheus_utilities.vector3 import Vector3
204 from fabmetheus_utilities import archive
205 from fabmetheus_utilities import euclidean
206 from fabmetheus_utilities import gcodec
207 from fabmetheus_utilities import intercircle
208 from fabmetheus_utilities import settings
209 from skeinforge_application.skeinforge_utilities import skeinforge_craft
210 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
211 from skeinforge_application.skeinforge_utilities import skeinforge_profile
217 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
218 __date__ = '$Date: 2008/21/04 $'
219 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
222 #maybe later wide support
223 #raft outline temperature http://hydraraptor.blogspot.com/2008/09/screw-top-pot.html
224 def getCraftedText( fileName, text='', repository=None):
225 'Raft the file or text.'
226 return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
228 def getCraftedTextFromText(gcodeText, repository=None):
229 'Raft a gcode linear move text.'
230 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'raft'):
232 if repository == None:
233 repository = settings.getReadRepository( RaftRepository() )
234 if not repository.activateRaft.value:
236 return RaftSkein().getCraftedGcode(gcodeText, repository)
238 def getCrossHatchPointLine( crossHatchPointLineTable, y ):
239 'Get the cross hatch point line.'
240 if not crossHatchPointLineTable.has_key(y):
241 crossHatchPointLineTable[ y ] = {}
242 return crossHatchPointLineTable[ y ]
244 def getEndpointsFromYIntersections( x, yIntersections ):
245 'Get endpoints from the y intersections.'
247 for yIntersectionIndex in xrange( 0, len( yIntersections ), 2 ):
248 firstY = yIntersections[ yIntersectionIndex ]
249 secondY = yIntersections[ yIntersectionIndex + 1 ]
250 if firstY != secondY:
251 firstComplex = complex( x, firstY )
252 secondComplex = complex( x, secondY )
253 endpointFirst = euclidean.Endpoint()
254 endpointSecond = euclidean.Endpoint().getFromOtherPoint( endpointFirst, secondComplex )
255 endpointFirst.getFromOtherPoint( endpointSecond, firstComplex )
256 endpoints.append( endpointFirst )
257 endpoints.append( endpointSecond )
260 def getExtendedLineSegment(extensionDistance, lineSegment, loopXIntersections):
261 'Get extended line segment.'
262 pointBegin = lineSegment[0].point
263 pointEnd = lineSegment[1].point
264 segment = pointEnd - pointBegin
265 segmentLength = abs(segment)
266 if segmentLength <= 0.0:
267 print('This should never happen in getExtendedLineSegment in raft, the segment should have a length greater than zero.')
270 segmentExtend = segment * extensionDistance / segmentLength
271 lineSegment[0].point -= segmentExtend
272 lineSegment[1].point += segmentExtend
273 for loopXIntersection in loopXIntersections:
274 setExtendedPoint(lineSegment[0], pointBegin, loopXIntersection)
275 setExtendedPoint(lineSegment[1], pointEnd, loopXIntersection)
278 def getLoopsBySegmentsDictionary(segmentsDictionary, width):
279 'Get loops from a horizontal segments dictionary.'
281 for endpoint in getVerticalEndpoints(segmentsDictionary, width, 0.1 * width, width):
282 points.append(endpoint.point)
283 for endpoint in euclidean.getEndpointsFromSegmentTable(segmentsDictionary):
284 points.append(endpoint.point)
285 return triangle_mesh.getDescendingAreaOrientedLoops(points, points, width + width)
287 def getNewRepository():
288 'Get new repository.'
289 return RaftRepository()
291 def getVerticalEndpoints(horizontalSegmentsTable, horizontalStep, verticalOverhang, verticalStep):
292 'Get vertical endpoints.'
293 interfaceSegmentsTableKeys = horizontalSegmentsTable.keys()
294 interfaceSegmentsTableKeys.sort()
295 verticalTableTable = {}
296 for interfaceSegmentsTableKey in interfaceSegmentsTableKeys:
297 interfaceSegments = horizontalSegmentsTable[interfaceSegmentsTableKey]
298 for interfaceSegment in interfaceSegments:
299 begin = int(round(interfaceSegment[0].point.real / verticalStep))
300 end = int(round(interfaceSegment[1].point.real / verticalStep))
301 for stepIndex in xrange(begin, end + 1):
302 if stepIndex not in verticalTableTable:
303 verticalTableTable[stepIndex] = {}
304 verticalTableTable[stepIndex][interfaceSegmentsTableKey] = None
305 verticalTableTableKeys = verticalTableTable.keys()
306 verticalTableTableKeys.sort()
307 verticalEndpoints = []
308 for verticalTableTableKey in verticalTableTableKeys:
309 verticalTable = verticalTableTable[verticalTableTableKey]
310 verticalTableKeys = verticalTable.keys()
311 verticalTableKeys.sort()
313 for verticalTableKey in verticalTableKeys:
314 y = verticalTableKey * horizontalStep
315 if verticalTableKey - 1 not in verticalTableKeys:
316 xIntersections.append(y - verticalOverhang)
317 if verticalTableKey + 1 not in verticalTableKeys:
318 xIntersections.append(y + verticalOverhang)
319 for segment in euclidean.getSegmentsFromXIntersections(xIntersections, verticalTableTableKey * verticalStep):
320 for endpoint in segment:
321 endpoint.point = complex(endpoint.point.imag, endpoint.point.real)
322 verticalEndpoints.append(endpoint)
323 return verticalEndpoints
325 def setExtendedPoint( lineSegmentEnd, pointOriginal, x ):
326 'Set the point in the extended line segment.'
327 if x > min( lineSegmentEnd.point.real, pointOriginal.real ) and x < max( lineSegmentEnd.point.real, pointOriginal.real ):
328 lineSegmentEnd.point = complex( x, pointOriginal.imag )
330 def writeOutput(fileName, shouldAnalyze=True):
331 'Raft a gcode linear move file.'
332 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'raft', shouldAnalyze)
335 class RaftRepository:
336 'A class to handle the raft settings.'
338 'Set the default settings, execute title & settings fileName.'
339 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.raft.html', self)
340 self.fileNameInput = settings.FileNameInput().getFromFileName(
341 fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Raft', self, '')
342 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute(
343 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft')
344 self.activateRaft = settings.BooleanSetting().getFromValue('Activate Raft', self, True)
345 self.addRaftElevateNozzleOrbitSetAltitude = settings.BooleanSetting().getFromValue(
346 'Add Raft, Elevate Nozzle, Orbit:', self, True)
347 settings.LabelSeparator().getFromRepository(self)
348 settings.LabelDisplay().getFromName('- Base -', self)
349 self.baseFeedRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Feed Rate Multiplier (ratio):', self, 1.1, 1.0)
350 self.baseFlowRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Flow Rate Multiplier (ratio):', self, 1.1, 1.0)
351 self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5)
352 self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue(
353 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0)
354 self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0)
355 self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue(
356 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4)
357 settings.LabelSeparator().getFromRepository(self)
358 self.initialCircling = settings.BooleanSetting().getFromValue('Initial Circling:', self, False)
359 self.infillOverhangOverExtrusionWidth = settings.FloatSpin().getFromValue(
360 0.0, 'Infill Overhang over Extrusion Width (ratio):', self, 0.5, 0.05)
361 settings.LabelSeparator().getFromRepository(self)
362 settings.LabelDisplay().getFromName('- Interface -', self)
363 self.interfaceFeedRateMultiplier = settings.FloatSpin().getFromValue(
364 0.7, 'Interface Feed Rate Multiplier (ratio):', self, 1.1, 1.0)
365 self.interfaceFlowRateMultiplier = settings.FloatSpin().getFromValue(
366 0.7, 'Interface Flow Rate Multiplier (ratio):', self, 1.1, 1.0)
367 self.interfaceInfillDensity = settings.FloatSpin().getFromValue(
368 0.3, 'Interface Infill Density (ratio):', self, 0.9, 0.5)
369 self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue(
370 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0)
371 self.interfaceLayers = settings.IntSpin().getFromValue(
372 0, 'Interface Layers (integer):', self, 3, 0)
373 self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue(
374 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45)
375 settings.LabelSeparator().getFromRepository(self)
376 settings.LabelDisplay().getFromName('- Name of Alteration Files -', self)
377 self.nameOfSupportEndFile = settings.StringSetting().getFromValue('Name of Support End File:', self, 'support_end.gcode')
378 self.nameOfSupportStartFile = settings.StringSetting().getFromValue(
379 'Name of Support Start File:', self, 'support_start.gcode')
380 settings.LabelSeparator().getFromRepository(self)
381 self.operatingNozzleLiftOverLayerThickness = settings.FloatSpin().getFromValue(
382 0.3, 'Operating Nozzle Lift over Layer Thickness (ratio):', self, 0.7, 0.5)
383 settings.LabelSeparator().getFromRepository(self)
384 settings.LabelDisplay().getFromName('- Raft Size -', self)
385 self.raftAdditionalMarginOverLengthPercent = settings.FloatSpin().getFromValue(
386 0.5, 'Raft Additional Margin over Length (%):', self, 1.5, 1.0)
387 self.raftMargin = settings.FloatSpin().getFromValue(
388 1.0, 'Raft Margin (mm):', self, 5.0, 3.0)
389 settings.LabelSeparator().getFromRepository(self)
390 settings.LabelDisplay().getFromName('- Support -', self)
391 self.supportCrossHatch = settings.BooleanSetting().getFromValue('Support Cross Hatch', self, False)
392 self.supportFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue(
393 0.7, 'Support Flow Rate over Operating Flow Rate (ratio):', self, 1.1, 1.0)
394 self.supportGapOverPerimeterExtrusionWidth = settings.FloatSpin().getFromValue(
395 0.5, 'Support Gap over Perimeter Extrusion Width (ratio):', self, 1.5, 1.0)
396 self.supportMaterialChoice = settings.MenuButtonDisplay().getFromName('Support Material Choice: ', self)
397 self.supportChoiceNone = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'None', self, True)
398 self.supportChoiceEmptyLayersOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Empty Layers Only', self, False)
399 self.supportChoiceEverywhere = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Everywhere', self, False)
400 self.supportChoiceExteriorOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Exterior Only', self, False)
401 self.supportMinimumAngle = settings.FloatSpin().getFromValue(40.0, 'Support Minimum Angle (degrees):', self, 80.0, 60.0)
402 self.executeTitle = 'Raft'
405 'Raft button has been clicked.'
406 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
407 for fileName in fileNames:
408 writeOutput(fileName)
412 'A class to raft a skein of extrusions.'
414 self.addLineLayerStart = True
415 self.baseTemperature = None
416 self.beginLoop = None
417 self.boundaryLayers = []
418 self.coolingRate = None
419 self.distanceFeedRate = gcodec.DistanceFeedRate()
421 self.extrusionStart = True
422 self.extrusionTop = 0.0
423 self.feedRateMinute = 961.0
424 self.heatingRate = None
426 self.interfaceTemperature = None
427 self.isEdgePath = False
428 self.isNestedRing = True
429 self.isStartupEarly = False
430 self.layerIndex = - 1
431 self.layerStarted = False
432 self.layerHeight = 0.4
435 self.objectFirstLayerInfillTemperature = None
436 self.objectFirstLayerPerimeterTemperature = None
437 self.objectNextLayersTemperature = None
438 self.oldFlowRate = None
439 self.oldLocation = None
440 self.oldTemperatureOutputString = None
441 self.operatingFeedRateMinute = None
442 self.operatingFlowRate = None
443 self.operatingLayerEndLine = '(<operatingLayerEnd> </operatingLayerEnd>)'
444 self.operatingJump = None
445 self.orbitalFeedRatePerSecond = 2.01
446 self.sharpestProduct = 0.94
447 self.supportFlowRate = None
448 self.supportLayers = []
449 self.supportLayersTemperature = None
450 self.supportedLayersTemperature = None
451 self.travelFeedRateMinute = None
453 def addBaseLayer(self):
455 baseLayerThickness = self.layerHeight * self.baseLayerThicknessOverLayerThickness
456 zCenter = self.extrusionTop + 0.5 * baseLayerThickness
457 z = zCenter + baseLayerThickness * self.repository.baseNozzleLiftOverBaseLayerThickness.value
458 if len(self.baseEndpoints) < 1:
459 print('This should never happen, the base layer has a size of zero.')
461 self.addLayerFromEndpoints(
463 self.repository.baseFeedRateMultiplier.value,
464 self.repository.baseFlowRateMultiplier.value,
466 self.baseLayerThicknessOverLayerThickness,
470 def addBaseSegments(self, baseExtrusionWidth):
471 'Add the base segments.'
472 baseOverhang = self.repository.infillOverhangOverExtrusionWidth.value * baseExtrusionWidth
473 self.baseEndpoints = getVerticalEndpoints(self.interfaceSegmentsTable, self.interfaceStep, baseOverhang, self.baseStep)
475 def addEmptyLayerSupport( self, boundaryLayerIndex ):
476 'Add support material to a layer if it is empty.'
477 supportLayer = SupportLayer([])
478 self.supportLayers.append(supportLayer)
479 if len( self.boundaryLayers[ boundaryLayerIndex ].loops ) > 0:
481 aboveXIntersectionsTable = {}
482 euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsAbove(boundaryLayerIndex), aboveXIntersectionsTable, self.interfaceStep )
483 belowXIntersectionsTable = {}
484 euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsBelow(boundaryLayerIndex), belowXIntersectionsTable, self.interfaceStep )
485 supportLayer.xIntersectionsTable = euclidean.getIntersectionOfXIntersectionsTables( [ aboveXIntersectionsTable, belowXIntersectionsTable ] )
487 def addFlowRate(self, flowRate):
488 'Add a flow rate value if different.'
490 self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
492 def addInterfaceLayer(self):
493 'Add an interface layer.'
494 interfaceLayerThickness = self.layerHeight * self.interfaceLayerThicknessOverLayerThickness
495 zCenter = self.extrusionTop + 0.5 * interfaceLayerThickness
496 z = zCenter + interfaceLayerThickness * self.repository.interfaceNozzleLiftOverInterfaceLayerThickness.value
497 if len(self.interfaceEndpoints) < 1:
498 print('This should never happen, the interface layer has a size of zero.')
500 self.addLayerFromEndpoints(
501 self.interfaceEndpoints,
502 self.repository.interfaceFeedRateMultiplier.value,
503 self.repository.interfaceFlowRateMultiplier.value,
504 interfaceLayerThickness,
505 self.interfaceLayerThicknessOverLayerThickness,
509 def addInterfaceTables(self, interfaceExtrusionWidth):
510 'Add interface tables.'
511 overhang = self.repository.infillOverhangOverExtrusionWidth.value * interfaceExtrusionWidth
512 self.interfaceEndpoints = []
513 self.interfaceIntersectionsTableKeys = self.interfaceIntersectionsTable.keys()
514 self.interfaceSegmentsTable = {}
515 for yKey in self.interfaceIntersectionsTableKeys:
516 self.interfaceIntersectionsTable[yKey].sort()
517 y = yKey * self.interfaceStep
518 lineSegments = euclidean.getSegmentsFromXIntersections(self.interfaceIntersectionsTable[yKey], y)
519 xIntersectionIndexList = []
520 for lineSegmentIndex in xrange(len(lineSegments)):
521 lineSegment = lineSegments[lineSegmentIndex]
522 endpointBegin = lineSegment[0]
523 endpointEnd = lineSegment[1]
524 endpointBegin.point = complex(self.baseStep * math.floor(endpointBegin.point.real / self.baseStep) - overhang, y)
525 endpointEnd.point = complex(self.baseStep * math.ceil(endpointEnd.point.real / self.baseStep) + overhang, y)
526 if endpointEnd.point.real > endpointBegin.point.real:
527 euclidean.addXIntersectionIndexesFromSegment(lineSegmentIndex, lineSegment, xIntersectionIndexList)
528 xIntersections = euclidean.getJoinOfXIntersectionIndexes(xIntersectionIndexList)
529 joinedSegments = euclidean.getSegmentsFromXIntersections(xIntersections, y)
530 if len(joinedSegments) > 0:
531 self.interfaceSegmentsTable[yKey] = joinedSegments
532 for joinedSegment in joinedSegments:
533 self.interfaceEndpoints += joinedSegment
535 def addLayerFromEndpoints(
544 'Add a layer from endpoints and raise the extrusion top.'
545 layerThicknessRatioSquared = layerThicknessRatio * layerThicknessRatio
546 feedRateMinute = self.feedRateMinute * feedRateMultiplier / layerThicknessRatioSquared
547 if len(endpoints) < 1:
549 aroundPixelTable = {}
550 aroundWidth = 0.34321 * step
551 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, self.sharpestProduct, aroundWidth)
553 if self.operatingFlowRate != None:
554 self.addFlowRate(flowRateMultiplier * self.operatingFlowRate)
556 simplifiedPath = euclidean.getSimplifiedPath(path, step)
557 self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinute, simplifiedPath, self.travelFeedRateMinute, z)
558 self.extrusionTop += layerLayerThickness
559 self.addFlowRate(self.oldFlowRate)
561 def addLayerLine(self, z):
562 'Add the layer gcode line and close the last layer gcode block.'
563 if self.layerStarted:
564 self.distanceFeedRate.addLine('(</layer>)')
565 self.distanceFeedRate.addLine('(<layer> %s )' % self.distanceFeedRate.getRounded(z)) # Indicate that a new layer is starting.
566 if self.beginLoop != None:
567 zBegin = self.extrusionTop + self.layerHeight
568 intercircle.addOrbitsIfLarge(self.distanceFeedRate, self.beginLoop, self.orbitalFeedRatePerSecond, self.temperatureChangeTimeBeforeRaft, zBegin)
569 self.beginLoop = None
570 self.layerStarted = True
572 def addOperatingOrbits(self, boundaryLoops, pointComplex, temperatureChangeTime, z):
573 'Add the orbits before the operating layers.'
574 if len(boundaryLoops) < 1:
576 insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, self.edgeWidth)
577 if len(insetBoundaryLoops) < 1:
578 insetBoundaryLoops = boundaryLoops
579 largestLoop = euclidean.getLargestLoop(insetBoundaryLoops)
580 if pointComplex != None:
581 largestLoop = euclidean.getLoopStartingClosest(self.edgeWidth, pointComplex, largestLoop)
582 intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureChangeTime, z)
586 if len(self.boundaryLayers) < 0:
587 print('this should never happen, there are no boundary layers in addRaft')
589 self.baseLayerThicknessOverLayerThickness = self.repository.baseLayerThicknessOverLayerThickness.value
590 baseExtrusionWidth = self.edgeWidth * self.baseLayerThicknessOverLayerThickness
591 self.baseStep = baseExtrusionWidth / self.repository.baseInfillDensity.value
592 self.interfaceLayerThicknessOverLayerThickness = self.repository.interfaceLayerThicknessOverLayerThickness.value
593 interfaceExtrusionWidth = self.edgeWidth * self.interfaceLayerThicknessOverLayerThickness
594 self.interfaceStep = interfaceExtrusionWidth / self.repository.interfaceInfillDensity.value
596 self.cornerMinimumComplex = self.cornerMinimum.dropAxis()
597 originalExtent = self.cornerMaximumComplex - self.cornerMinimumComplex
598 self.raftOutsetRadius = self.repository.raftMargin.value + self.repository.raftAdditionalMarginOverLengthPercent.value * 0.01 * max(originalExtent.real, originalExtent.imag)
599 self.setBoundaryLayers()
600 outsetSeparateLoops = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[0].loops, -self.raftOutsetRadius, 0.8)
601 self.interfaceIntersectionsTable = {}
602 euclidean.addXIntersectionsFromLoopsForTable(outsetSeparateLoops, self.interfaceIntersectionsTable, self.interfaceStep)
603 if len(self.supportLayers) > 0:
604 supportIntersectionsTable = self.supportLayers[0].xIntersectionsTable
605 euclidean.joinXIntersectionsTables(supportIntersectionsTable, self.interfaceIntersectionsTable)
606 self.addInterfaceTables(interfaceExtrusionWidth)
607 self.addRaftPerimeters()
608 self.baseIntersectionsTable = {}
609 complexRadius = complex(self.raftOutsetRadius, self.raftOutsetRadius)
610 self.complexHigh = complexRadius + self.cornerMaximumComplex
611 self.complexLow = self.cornerMinimumComplex - complexRadius
612 self.beginLoop = euclidean.getSquareLoopWiddershins(self.cornerMinimumComplex, self.cornerMaximumComplex)
613 if not intercircle.orbitsAreLarge(self.beginLoop, self.temperatureChangeTimeBeforeRaft):
614 self.beginLoop = None
615 if self.repository.baseLayers.value > 0:
616 self.addTemperatureLineIfDifferent(self.baseTemperature)
617 self.addBaseSegments(baseExtrusionWidth)
618 for baseLayerIndex in xrange(self.repository.baseLayers.value):
620 if self.repository.interfaceLayers.value > 0:
621 self.addTemperatureLineIfDifferent(self.interfaceTemperature)
622 self.interfaceIntersectionsTableKeys.sort()
623 for interfaceLayerIndex in xrange(self.repository.interfaceLayers.value):
624 self.addInterfaceLayer()
625 self.operatingJump = self.extrusionTop + self.layerHeight * self.repository.operatingNozzleLiftOverLayerThickness.value
626 for boundaryLayer in self.boundaryLayers:
627 if self.operatingJump != None:
628 boundaryLayer.z += self.operatingJump
629 if self.repository.baseLayers.value > 0 or self.repository.interfaceLayers.value > 0:
630 boundaryZ = self.boundaryLayers[0].z
631 if self.layerStarted:
632 self.distanceFeedRate.addLine('(</layer>)')
633 self.layerStarted = False
634 self.distanceFeedRate.addLine('(<raftLayerEnd> </raftLayerEnd>)')
635 self.addLayerLine(boundaryZ)
636 temperatureChangeTimeBeforeFirstLayer = self.getTemperatureChangeTime(self.objectFirstLayerPerimeterTemperature)
637 self.addTemperatureLineIfDifferent(self.objectFirstLayerPerimeterTemperature)
638 largestOutsetLoop = intercircle.getLargestInsetLoopFromLoop(euclidean.getLargestLoop(outsetSeparateLoops), -self.raftOutsetRadius)
639 intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestOutsetLoop, self.orbitalFeedRatePerSecond, temperatureChangeTimeBeforeFirstLayer, boundaryZ)
640 self.addLineLayerStart = False
642 def addRaftedLine( self, splitLine ):
643 'Add elevated gcode line with operating feed rate.'
644 self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
645 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
646 z = self.oldLocation.z
647 if self.operatingJump != None:
648 z += self.operatingJump
649 temperature = self.objectNextLayersTemperature
650 if self.layerIndex == 0:
652 temperature = self.objectFirstLayerPerimeterTemperature
654 temperature = self.objectFirstLayerInfillTemperature
655 self.addTemperatureLineIfDifferent(temperature)
656 self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, self.oldLocation.dropAxis(), z)
658 def addRaftPerimeters(self):
659 'Add raft edges if there is a raft.'
660 interfaceOutset = self.halfEdgeWidth * self.interfaceLayerThicknessOverLayerThickness
661 for supportLayer in self.supportLayers:
662 supportSegmentTable = supportLayer.supportSegmentTable
663 if len(supportSegmentTable) > 0:
664 outset = interfaceOutset
665 self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(supportSegmentTable, self.interfaceStep), outset)
666 if self.repository.baseLayers.value < 1 and self.repository.interfaceLayers.value < 1:
668 overhangMultiplier = 1.0 + self.repository.infillOverhangOverExtrusionWidth.value + self.repository.infillOverhangOverExtrusionWidth.value
669 outset = self.halfEdgeWidth
670 if self.repository.interfaceLayers.value > 0:
671 outset = max(interfaceOutset * overhangMultiplier, outset)
672 if self.repository.baseLayers.value > 0:
673 outset = max(self.halfEdgeWidth * self.baseLayerThicknessOverLayerThickness * overhangMultiplier, outset)
674 self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(self.interfaceSegmentsTable, self.interfaceStep), outset)
676 def addRaftPerimetersByLoops(self, loops, outset):
677 'Add raft edges to the gcode for loops.'
678 loops = intercircle.getInsetSeparateLoopsFromLoops(loops, -outset)
680 self.distanceFeedRate.addLine('(<raftPerimeter>)')
682 roundedX = self.distanceFeedRate.getRounded(point.real)
683 roundedY = self.distanceFeedRate.getRounded(point.imag)
684 self.distanceFeedRate.addTagBracketedLine('raftPoint', 'X%s Y%s' % (roundedX, roundedY))
685 self.distanceFeedRate.addLine('(</raftPerimeter>)')
687 def addSegmentTablesToSupportLayers(self):
688 'Add segment tables to the support layers.'
689 for supportLayer in self.supportLayers:
690 supportLayer.supportSegmentTable = {}
691 xIntersectionsTable = supportLayer.xIntersectionsTable
692 for xIntersectionsTableKey in xIntersectionsTable:
693 y = xIntersectionsTableKey * self.interfaceStep
694 supportLayer.supportSegmentTable[ xIntersectionsTableKey ] = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], y )
696 def addSupportLayerTemperature(self, endpoints, z):
697 'Add support layer and temperature before the object layer.'
698 self.distanceFeedRate.addLine('(<supportLayer>)')
699 self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportStartLines)
700 self.addTemperatureOrbits(endpoints, self.supportedLayersTemperature, z)
701 aroundPixelTable = {}
702 aroundWidth = 0.34321 * self.interfaceStep
703 boundaryLoops = self.boundaryLayers[self.layerIndex].loops
704 halfSupportOutset = 0.5 * self.supportOutset
705 aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset)
706 for aroundBoundaryLoop in aroundBoundaryLoops:
707 euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth)
708 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, self.sharpestProduct, aroundWidth)
709 feedRateMinuteMultiplied = self.operatingFeedRateMinute
710 supportFlowRateMultiplied = self.supportFlowRate
711 if self.layerIndex == 0:
712 feedRateMinuteMultiplied *= self.objectFirstLayerFeedRateInfillMultiplier
713 if supportFlowRateMultiplied != None:
714 supportFlowRateMultiplied *= self.objectFirstLayerFlowRateInfillMultiplier
715 self.addFlowRate(supportFlowRateMultiplied)
717 self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinuteMultiplied, path, self.travelFeedRateMinute, z)
718 self.addFlowRate(self.oldFlowRate)
719 self.addTemperatureOrbits(endpoints, self.supportLayersTemperature, z)
720 self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportEndLines)
721 self.distanceFeedRate.addLine('(</supportLayer>)')
723 def addSupportSegmentTable( self, layerIndex ):
724 'Add support segments from the boundary layers.'
725 aboveLayer = self.boundaryLayers[ layerIndex + 1 ]
726 aboveLoops = aboveLayer.loops
727 supportLayer = self.supportLayers[layerIndex]
728 if len( aboveLoops ) < 1:
730 boundaryLayer = self.boundaryLayers[layerIndex]
731 rise = aboveLayer.z - boundaryLayer.z
732 outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.minimumSupportRatio * rise)
734 subStepSize = self.interfaceStep / float( numberOfSubSteps )
735 aboveIntersectionsTable = {}
736 euclidean.addXIntersectionsFromLoopsForTable( aboveLoops, aboveIntersectionsTable, subStepSize )
737 outsetIntersectionsTable = {}
738 euclidean.addXIntersectionsFromLoopsForTable( outsetSupportLoops, outsetIntersectionsTable, subStepSize )
739 euclidean.subtractXIntersectionsTable( aboveIntersectionsTable, outsetIntersectionsTable )
740 for aboveIntersectionsTableKey in aboveIntersectionsTable.keys():
741 supportIntersectionsTableKey = int( round( float( aboveIntersectionsTableKey ) / numberOfSubSteps ) )
742 xIntersectionIndexList = []
743 if supportIntersectionsTableKey in supportLayer.xIntersectionsTable:
744 euclidean.addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] )
745 euclidean.addXIntersectionIndexesFromXIntersections( 1, xIntersectionIndexList, aboveIntersectionsTable[ aboveIntersectionsTableKey ] )
746 supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
748 def addTemperatureLineIfDifferent(self, temperature):
749 'Add a line of temperature if different.'
750 if temperature == None:
752 temperatureOutputString = euclidean.getRoundedToThreePlaces(temperature)
753 if temperatureOutputString == self.oldTemperatureOutputString:
755 if temperatureOutputString != None:
756 self.distanceFeedRate.addLine('M104 S' + temperatureOutputString) # Set temperature.
757 self.oldTemperatureOutputString = temperatureOutputString
759 def addTemperatureOrbits( self, endpoints, temperature, z ):
760 'Add the temperature and orbits around the support layer.'
761 if self.layerIndex < 0:
763 boundaryLoops = self.boundaryLayers[self.layerIndex].loops
764 temperatureTimeChange = self.getTemperatureChangeTime( temperature )
765 self.addTemperatureLineIfDifferent( temperature )
766 if len( boundaryLoops ) < 1:
767 layerCornerHigh = complex(-987654321.0, -987654321.0)
768 layerCornerLow = complex(987654321.0, 987654321.0)
769 for endpoint in endpoints:
770 layerCornerHigh = euclidean.getMaximum( layerCornerHigh, endpoint.point )
771 layerCornerLow = euclidean.getMinimum( layerCornerLow, endpoint.point )
772 squareLoop = euclidean.getSquareLoopWiddershins( layerCornerLow, layerCornerHigh )
773 intercircle.addOrbitsIfLarge( self.distanceFeedRate, squareLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )
775 edgeInset = 0.4 * self.edgeWidth
776 insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, edgeInset)
777 if len( insetBoundaryLoops ) < 1:
778 insetBoundaryLoops = boundaryLoops
779 largestLoop = euclidean.getLargestLoop( insetBoundaryLoops )
780 intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )
782 def addToFillXIntersectionIndexTables( self, supportLayer ):
783 'Add fill segments from the boundary layers.'
784 supportLoops = supportLayer.supportLoops
785 supportLayer.fillXIntersectionsTable = {}
786 if len(supportLoops) < 1:
788 euclidean.addXIntersectionsFromLoopsForTable( supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep )
790 def extendXIntersections( self, loops, radius, xIntersectionsTable ):
791 'Extend the support segments.'
792 xIntersectionsTableKeys = xIntersectionsTable.keys()
793 for xIntersectionsTableKey in xIntersectionsTableKeys:
794 lineSegments = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], xIntersectionsTableKey )
795 xIntersectionIndexList = []
796 loopXIntersections = []
797 euclidean.addXIntersectionsFromLoops( loops, loopXIntersections, xIntersectionsTableKey )
798 for lineSegmentIndex in xrange( len( lineSegments ) ):
799 lineSegment = lineSegments[ lineSegmentIndex ]
800 extendedLineSegment = getExtendedLineSegment( radius, lineSegment, loopXIntersections )
801 if extendedLineSegment != None:
802 euclidean.addXIntersectionIndexesFromSegment( lineSegmentIndex, extendedLineSegment, xIntersectionIndexList )
803 xIntersections = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
804 if len( xIntersections ) > 0:
805 xIntersectionsTable[ xIntersectionsTableKey ] = xIntersections
807 del xIntersectionsTable[ xIntersectionsTableKey ]
809 def getCraftedGcode(self, gcodeText, repository):
810 'Parse gcode text and store the raft gcode.'
811 self.repository = repository
812 self.minimumSupportRatio = math.tan( math.radians( repository.supportMinimumAngle.value ) )
813 self.supportEndLines = settings.getAlterationFileLines(repository.nameOfSupportEndFile.value)
814 self.supportStartLines = settings.getAlterationFileLines(repository.nameOfSupportStartFile.value)
815 self.lines = archive.getTextLines(gcodeText)
816 self.parseInitialization()
817 self.temperatureChangeTimeBeforeRaft = 0.0
818 if self.repository.initialCircling.value:
819 maxBaseInterfaceTemperature = max(self.baseTemperature, self.interfaceTemperature)
820 firstMaxTemperature = max(maxBaseInterfaceTemperature, self.objectFirstLayerPerimeterTemperature)
821 self.temperatureChangeTimeBeforeRaft = self.getTemperatureChangeTime(firstMaxTemperature)
822 if repository.addRaftElevateNozzleOrbitSetAltitude.value:
824 self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
825 for line in self.lines[self.lineIndex :]:
827 return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue())
829 def getElevatedBoundaryLine( self, splitLine ):
830 'Get elevated boundary gcode line.'
831 location = gcodec.getLocationFromSplitLine(None, splitLine)
832 if self.operatingJump != None:
833 location.z += self.operatingJump
834 return self.distanceFeedRate.getBoundaryLine( location )
836 def getInsetLoops( self, boundaryLayerIndex ):
837 'Inset the support loops if they are not already inset.'
838 if boundaryLayerIndex not in self.insetTable:
839 self.insetTable[ boundaryLayerIndex ] = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[ boundaryLayerIndex ].loops, self.quarterEdgeWidth)
840 return self.insetTable[ boundaryLayerIndex ]
842 def getInsetLoopsAbove( self, boundaryLayerIndex ):
843 'Get the inset loops above the boundary layer index.'
844 for aboveLayerIndex in xrange( boundaryLayerIndex + 1, len(self.boundaryLayers) ):
845 if len( self.boundaryLayers[ aboveLayerIndex ].loops ) > 0:
846 return self.getInsetLoops( aboveLayerIndex )
849 def getInsetLoopsBelow( self, boundaryLayerIndex ):
850 'Get the inset loops below the boundary layer index.'
851 for belowLayerIndex in xrange( boundaryLayerIndex - 1, - 1, - 1 ):
852 if len( self.boundaryLayers[ belowLayerIndex ].loops ) > 0:
853 return self.getInsetLoops( belowLayerIndex )
856 def getStepsUntilEnd( self, begin, end, stepSize ):
857 'Get steps from the beginning until the end.'
865 def getSupportEndpoints(self):
866 'Get the support layer segments.'
867 if len(self.supportLayers) <= self.layerIndex:
869 supportSegmentTable = self.supportLayers[self.layerIndex].supportSegmentTable
870 if self.layerIndex % 2 == 1 and self.repository.supportCrossHatch.value:
871 return getVerticalEndpoints(supportSegmentTable, self.interfaceStep, 0.1 * self.edgeWidth, self.interfaceStep)
872 return euclidean.getEndpointsFromSegmentTable(supportSegmentTable)
874 def getTemperatureChangeTime( self, temperature ):
875 'Get the temperature change time.'
876 if temperature == None:
878 oldTemperature = 25.0 # typical chamber temperature
879 if self.oldTemperatureOutputString != None:
880 oldTemperature = float( self.oldTemperatureOutputString )
881 if temperature == oldTemperature:
883 if temperature > oldTemperature:
884 return ( temperature - oldTemperature ) / self.heatingRate
885 return ( oldTemperature - temperature ) / abs( self.coolingRate )
887 def parseInitialization(self):
888 'Parse gcode initialization and store the parameters.'
889 for self.lineIndex in xrange(len(self.lines)):
890 line = self.lines[self.lineIndex]
891 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
892 firstWord = gcodec.getFirstWord(splitLine)
893 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
894 if firstWord == '(<baseTemperature>':
895 self.baseTemperature = float(splitLine[1])
896 elif firstWord == '(<coolingRate>':
897 self.coolingRate = float(splitLine[1])
898 elif firstWord == '(<edgeWidth>':
899 self.edgeWidth = float(splitLine[1])
900 self.halfEdgeWidth = 0.5 * self.edgeWidth
901 self.quarterEdgeWidth = 0.25 * self.edgeWidth
902 self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value
903 elif firstWord == '(</extruderInitialization>)':
904 self.distanceFeedRate.addTagBracketedProcedure('raft')
905 elif firstWord == '(<heatingRate>':
906 self.heatingRate = float(splitLine[1])
907 elif firstWord == '(<interfaceTemperature>':
908 self.interfaceTemperature = float(splitLine[1])
909 elif firstWord == '(<layer>':
911 elif firstWord == '(<layerHeight>':
912 self.layerHeight = float(splitLine[1])
913 elif firstWord == 'M108':
914 self.oldFlowRate = float(splitLine[1][1 :])
915 elif firstWord == '(<objectFirstLayerFeedRateInfillMultiplier>':
916 self.objectFirstLayerFeedRateInfillMultiplier = float(splitLine[1])
917 elif firstWord == '(<objectFirstLayerFlowRateInfillMultiplier>':
918 self.objectFirstLayerFlowRateInfillMultiplier = float(splitLine[1])
919 elif firstWord == '(<objectFirstLayerInfillTemperature>':
920 self.objectFirstLayerInfillTemperature = float(splitLine[1])
921 elif firstWord == '(<objectFirstLayerPerimeterTemperature>':
922 self.objectFirstLayerPerimeterTemperature = float(splitLine[1])
923 elif firstWord == '(<objectNextLayersTemperature>':
924 self.objectNextLayersTemperature = float(splitLine[1])
925 elif firstWord == '(<orbitalFeedRatePerSecond>':
926 self.orbitalFeedRatePerSecond = float(splitLine[1])
927 elif firstWord == '(<operatingFeedRatePerSecond>':
928 self.operatingFeedRateMinute = 60.0 * float(splitLine[1])
929 self.feedRateMinute = self.operatingFeedRateMinute
930 elif firstWord == '(<operatingFlowRate>':
931 self.operatingFlowRate = float(splitLine[1])
932 self.oldFlowRate = self.operatingFlowRate
933 self.supportFlowRate = self.operatingFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value
934 elif firstWord == '(<sharpestProduct>':
935 self.sharpestProduct = float(splitLine[1])
936 elif firstWord == '(<supportLayersTemperature>':
937 self.supportLayersTemperature = float(splitLine[1])
938 elif firstWord == '(<supportedLayersTemperature>':
939 self.supportedLayersTemperature = float(splitLine[1])
940 elif firstWord == '(<travelFeedRatePerSecond>':
941 self.travelFeedRateMinute = 60.0 * float(splitLine[1])
942 self.distanceFeedRate.addLine(line)
944 def parseLine(self, line):
945 'Parse a gcode line and add it to the raft skein.'
946 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
947 if len(splitLine) < 1:
949 firstWord = splitLine[0]
950 if firstWord == 'G1':
951 if self.extrusionStart:
952 self.addRaftedLine(splitLine)
954 elif firstWord == 'M101':
955 if self.isStartupEarly:
956 self.isStartupEarly = False
958 elif firstWord == 'M108':
959 self.oldFlowRate = float(splitLine[1][1 :])
960 elif firstWord == '(<boundaryPoint>':
961 line = self.getElevatedBoundaryLine(splitLine)
962 elif firstWord == '(</crafting>)':
963 self.extrusionStart = False
964 self.distanceFeedRate.addLine( self.operatingLayerEndLine )
965 elif firstWord == '(<layer>':
967 settings.printProgress(self.layerIndex, 'raft')
969 layerZ = self.extrusionTop + float(splitLine[1])
970 if len(self.boundaryLayers) > 0:
971 boundaryLayer = self.boundaryLayers[self.layerIndex]
972 layerZ = boundaryLayer.z
973 if self.operatingJump != None:
974 line = '(<layer> %s )' % self.distanceFeedRate.getRounded( layerZ )
975 if self.layerStarted and self.addLineLayerStart:
976 self.distanceFeedRate.addLine('(</layer>)')
977 self.layerStarted = False
978 if self.layerIndex > len(self.supportLayers) + 1:
979 self.distanceFeedRate.addLine( self.operatingLayerEndLine )
980 self.operatingLayerEndLine = ''
981 if self.addLineLayerStart:
982 self.distanceFeedRate.addLine(line)
983 self.addLineLayerStart = True
985 endpoints = self.getSupportEndpoints()
986 if self.layerIndex == 1:
987 if len(endpoints) < 1:
988 temperatureChangeTimeBeforeNextLayers = self.getTemperatureChangeTime( self.objectNextLayersTemperature )
989 self.addTemperatureLineIfDifferent( self.objectNextLayersTemperature )
990 if self.repository.addRaftElevateNozzleOrbitSetAltitude.value and len( boundaryLayer.loops ) > 0:
991 self.addOperatingOrbits( boundaryLayer.loops, euclidean.getXYComplexFromVector3( self.oldLocation ), temperatureChangeTimeBeforeNextLayers, layerZ )
992 if len(endpoints) > 0:
993 self.addSupportLayerTemperature( endpoints, layerZ )
994 elif firstWord == '(<edge>' or firstWord == '(<edgePath>)':
995 self.isEdgePath = True
996 elif firstWord == '(</edge>)' or firstWord == '(</edgePath>)':
997 self.isEdgePath = False
998 self.distanceFeedRate.addLine(line)
1000 def setBoundaryLayers(self):
1001 'Set the boundary layers.'
1002 if self.repository.supportChoiceNone.value:
1004 if len(self.boundaryLayers) < 2:
1006 if self.repository.supportChoiceEmptyLayersOnly.value:
1007 supportLayer = SupportLayer([])
1008 self.supportLayers.append(supportLayer)
1009 for boundaryLayerIndex in xrange(1, len(self.boundaryLayers) -1):
1010 self.addEmptyLayerSupport(boundaryLayerIndex)
1011 self.truncateSupportSegmentTables()
1012 self.addSegmentTablesToSupportLayers()
1014 for boundaryLayer in self.boundaryLayers:
1015 # thresholdRadius of 0.8 is needed to avoid the ripple inset bug http://hydraraptor.blogspot.com/2010/12/crackers.html
1016 supportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.supportOutset, 0.8)
1017 supportLayer = SupportLayer(supportLoops)
1018 self.supportLayers.append(supportLayer)
1019 for supportLayerIndex in xrange(len(self.supportLayers) - 1):
1020 self.addSupportSegmentTable(supportLayerIndex)
1021 self.truncateSupportSegmentTables()
1022 for supportLayerIndex in xrange(len(self.supportLayers) - 1):
1023 boundaryLoops = self.boundaryLayers[supportLayerIndex].loops
1024 self.extendXIntersections( boundaryLoops, self.supportOutset, self.supportLayers[supportLayerIndex].xIntersectionsTable)
1025 for supportLayer in self.supportLayers:
1026 self.addToFillXIntersectionIndexTables(supportLayer)
1027 if self.repository.supportChoiceExteriorOnly.value:
1028 for supportLayerIndex in xrange(1, len(self.supportLayers)):
1029 self.subtractJoinedFill(supportLayerIndex)
1030 for supportLayer in self.supportLayers:
1031 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable)
1032 for supportLayerIndex in xrange(len(self.supportLayers) - 2, -1, -1):
1033 xIntersectionsTable = self.supportLayers[supportLayerIndex].xIntersectionsTable
1034 aboveXIntersectionsTable = self.supportLayers[supportLayerIndex + 1].xIntersectionsTable
1035 euclidean.joinXIntersectionsTables(aboveXIntersectionsTable, xIntersectionsTable)
1036 for supportLayerIndex in xrange(len(self.supportLayers)):
1037 supportLayer = self.supportLayers[supportLayerIndex]
1038 self.extendXIntersections(supportLayer.supportLoops, self.raftOutsetRadius, supportLayer.xIntersectionsTable)
1039 for supportLayer in self.supportLayers:
1040 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable)
1041 self.addSegmentTablesToSupportLayers()
1043 def setCornersZ(self):
1044 'Set maximum and minimum corners and z.'
1046 boundaryLayer = None
1048 self.cornerMaximumComplex = complex(-912345678.0, -912345678.0)
1049 self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0)
1050 self.firstLayerLoops = []
1051 for line in self.lines[self.lineIndex :]:
1052 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
1053 firstWord = gcodec.getFirstWord(splitLine)
1054 if firstWord == '(</boundaryPerimeter>)':
1056 elif firstWord == '(<boundaryPoint>':
1057 location = gcodec.getLocationFromSplitLine(None, splitLine)
1058 if boundaryLoop == None:
1060 boundaryLayer.loops.append(boundaryLoop)
1061 boundaryLoop.append(location.dropAxis())
1062 self.cornerMaximumComplex = euclidean.getMaximum(self.cornerMaximumComplex, location.dropAxis())
1063 self.cornerMinimum.minimize(location)
1064 elif firstWord == '(<layer>':
1065 z = float(splitLine[1])
1066 boundaryLayer = euclidean.LoopLayer(z)
1067 self.boundaryLayers.append(boundaryLayer)
1068 elif firstWord == '(<layer>':
1070 if self.repository.supportChoiceNone.value:
1074 def subtractJoinedFill( self, supportLayerIndex ):
1075 'Join the fill then subtract it from the support layer table.'
1076 supportLayer = self.supportLayers[supportLayerIndex]
1077 fillXIntersectionsTable = supportLayer.fillXIntersectionsTable
1078 belowFillXIntersectionsTable = self.supportLayers[ supportLayerIndex - 1 ].fillXIntersectionsTable
1079 euclidean.joinXIntersectionsTables( belowFillXIntersectionsTable, supportLayer.fillXIntersectionsTable )
1080 euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable )
1082 def truncateSupportSegmentTables(self):
1083 'Truncate the support segments after the last support segment which contains elements.'
1084 for supportLayerIndex in xrange( len(self.supportLayers) - 1, - 1, - 1 ):
1085 if len( self.supportLayers[supportLayerIndex].xIntersectionsTable ) > 0:
1086 self.supportLayers = self.supportLayers[ : supportLayerIndex + 1 ]
1088 self.supportLayers = []
1092 'Support loops with segment tables.'
1093 def __init__( self, supportLoops ):
1094 self.supportLoops = supportLoops
1095 self.supportSegmentTable = {}
1096 self.xIntersectionsTable = {}
1099 'Get the string representation of this loop layer.'
1100 return '%s' % ( self.supportLoops )
1104 'Display the raft dialog.'
1105 if len(sys.argv) > 1:
1106 writeOutput(' '.join(sys.argv[1 :]))
1108 settings.startMainLoopFromConstructor(getNewRepository())
1110 if __name__ == '__main__':