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
199 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
200 from fabmetheus_utilities.geometry.solids import triangle_mesh
201 from fabmetheus_utilities.vector3 import Vector3
202 from fabmetheus_utilities import archive
203 from fabmetheus_utilities import euclidean
204 from fabmetheus_utilities import gcodec
205 from fabmetheus_utilities import intercircle
206 from fabmetheus_utilities import settings
207 from skeinforge_application.skeinforge_utilities import skeinforge_craft
208 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
209 from skeinforge_application.skeinforge_utilities import skeinforge_profile
215 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
216 __date__ = '$Date: 2008/21/04 $'
217 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
220 #maybe later wide support
221 #raft outline temperature http://hydraraptor.blogspot.com/2008/09/screw-top-pot.html
222 def getCraftedText( fileName, text='', repository=None):
223 'Raft the file or text.'
224 return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
226 def getCraftedTextFromText(gcodeText, repository=None):
227 'Raft a gcode linear move text.'
228 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'raft'):
230 if repository == None:
231 repository = settings.getReadRepository( RaftRepository() )
232 if not repository.activateRaft.value:
234 return RaftSkein().getCraftedGcode(gcodeText, repository)
236 def getCrossHatchPointLine( crossHatchPointLineTable, y ):
237 'Get the cross hatch point line.'
238 if not crossHatchPointLineTable.has_key(y):
239 crossHatchPointLineTable[ y ] = {}
240 return crossHatchPointLineTable[ y ]
242 def getEndpointsFromYIntersections( x, yIntersections ):
243 'Get endpoints from the y intersections.'
245 for yIntersectionIndex in xrange( 0, len( yIntersections ), 2 ):
246 firstY = yIntersections[ yIntersectionIndex ]
247 secondY = yIntersections[ yIntersectionIndex + 1 ]
248 if firstY != secondY:
249 firstComplex = complex( x, firstY )
250 secondComplex = complex( x, secondY )
251 endpointFirst = euclidean.Endpoint()
252 endpointSecond = euclidean.Endpoint().getFromOtherPoint( endpointFirst, secondComplex )
253 endpointFirst.getFromOtherPoint( endpointSecond, firstComplex )
254 endpoints.append( endpointFirst )
255 endpoints.append( endpointSecond )
258 def getExtendedLineSegment(extensionDistance, lineSegment, loopXIntersections):
259 'Get extended line segment.'
260 pointBegin = lineSegment[0].point
261 pointEnd = lineSegment[1].point
262 segment = pointEnd - pointBegin
263 segmentLength = abs(segment)
264 if segmentLength <= 0.0:
265 print('This should never happen in getExtendedLineSegment in raft, the segment should have a length greater than zero.')
268 segmentExtend = segment * extensionDistance / segmentLength
269 lineSegment[0].point -= segmentExtend
270 lineSegment[1].point += segmentExtend
271 for loopXIntersection in loopXIntersections:
272 setExtendedPoint(lineSegment[0], pointBegin, loopXIntersection)
273 setExtendedPoint(lineSegment[1], pointEnd, loopXIntersection)
276 def getLoopsBySegmentsDictionary(segmentsDictionary, width):
277 'Get loops from a horizontal segments dictionary.'
279 for endpoint in getVerticalEndpoints(segmentsDictionary, width, 0.1 * width, width):
280 points.append(endpoint.point)
281 for endpoint in euclidean.getEndpointsFromSegmentTable(segmentsDictionary):
282 points.append(endpoint.point)
283 return triangle_mesh.getDescendingAreaOrientedLoops(points, points, width + width)
285 def getNewRepository():
286 'Get new repository.'
287 return RaftRepository()
289 def getVerticalEndpoints(horizontalSegmentsTable, horizontalStep, verticalOverhang, verticalStep):
290 'Get vertical endpoints.'
291 interfaceSegmentsTableKeys = horizontalSegmentsTable.keys()
292 interfaceSegmentsTableKeys.sort()
293 verticalTableTable = {}
294 for interfaceSegmentsTableKey in interfaceSegmentsTableKeys:
295 interfaceSegments = horizontalSegmentsTable[interfaceSegmentsTableKey]
296 for interfaceSegment in interfaceSegments:
297 begin = int(round(interfaceSegment[0].point.real / verticalStep))
298 end = int(round(interfaceSegment[1].point.real / verticalStep))
299 for stepIndex in xrange(begin, end + 1):
300 if stepIndex not in verticalTableTable:
301 verticalTableTable[stepIndex] = {}
302 verticalTableTable[stepIndex][interfaceSegmentsTableKey] = None
303 verticalTableTableKeys = verticalTableTable.keys()
304 verticalTableTableKeys.sort()
305 verticalEndpoints = []
306 for verticalTableTableKey in verticalTableTableKeys:
307 verticalTable = verticalTableTable[verticalTableTableKey]
308 verticalTableKeys = verticalTable.keys()
309 verticalTableKeys.sort()
311 for verticalTableKey in verticalTableKeys:
312 y = verticalTableKey * horizontalStep
313 if verticalTableKey - 1 not in verticalTableKeys:
314 xIntersections.append(y - verticalOverhang)
315 if verticalTableKey + 1 not in verticalTableKeys:
316 xIntersections.append(y + verticalOverhang)
317 for segment in euclidean.getSegmentsFromXIntersections(xIntersections, verticalTableTableKey * verticalStep):
318 for endpoint in segment:
319 endpoint.point = complex(endpoint.point.imag, endpoint.point.real)
320 verticalEndpoints.append(endpoint)
321 return verticalEndpoints
323 def setExtendedPoint( lineSegmentEnd, pointOriginal, x ):
324 'Set the point in the extended line segment.'
325 if min( lineSegmentEnd.point.real, pointOriginal.real ) < x < max( lineSegmentEnd.point.real, pointOriginal.real ):
326 lineSegmentEnd.point = complex( x, pointOriginal.imag )
328 def writeOutput(fileName, shouldAnalyze=True):
329 'Raft a gcode linear move file.'
330 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'raft', shouldAnalyze)
333 class RaftRepository(object):
334 'A class to handle the raft settings.'
336 'Set the default settings, execute title & settings fileName.'
337 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.raft.html', self)
338 self.fileNameInput = settings.FileNameInput().getFromFileName(
339 fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Raft', self, '')
340 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute(
341 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft')
342 self.activateRaft = settings.BooleanSetting().getFromValue('Activate Raft', self, True)
343 self.addRaftElevateNozzleOrbitSetAltitude = settings.BooleanSetting().getFromValue(
344 'Add Raft, Elevate Nozzle, Orbit:', self, True)
345 settings.LabelSeparator().getFromRepository(self)
346 settings.LabelDisplay().getFromName('- Base -', self)
347 self.baseFeedRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Feed Rate Multiplier (ratio):', self, 1.1, 1.0)
348 self.baseFlowRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Flow Rate Multiplier (ratio):', self, 1.1, 1.0)
349 self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5)
350 self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue(
351 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0)
352 self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0)
353 self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue(
354 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4)
355 settings.LabelSeparator().getFromRepository(self)
356 self.initialCircling = settings.BooleanSetting().getFromValue('Initial Circling:', self, False)
357 self.infillOverhangOverExtrusionWidth = settings.FloatSpin().getFromValue(
358 0.0, 'Infill Overhang over Extrusion Width (ratio):', self, 0.5, 0.05)
359 settings.LabelSeparator().getFromRepository(self)
360 settings.LabelDisplay().getFromName('- Interface -', self)
361 self.interfaceFeedRateMultiplier = settings.FloatSpin().getFromValue(
362 0.7, 'Interface Feed Rate Multiplier (ratio):', self, 1.1, 1.0)
363 self.interfaceFlowRateMultiplier = settings.FloatSpin().getFromValue(
364 0.7, 'Interface Flow Rate Multiplier (ratio):', self, 1.1, 1.0)
365 self.interfaceInfillDensity = settings.FloatSpin().getFromValue(
366 0.3, 'Interface Infill Density (ratio):', self, 0.9, 0.5)
367 self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue(
368 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0)
369 self.interfaceLayers = settings.IntSpin().getFromValue(
370 0, 'Interface Layers (integer):', self, 3, 0)
371 self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue(
372 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45)
373 settings.LabelSeparator().getFromRepository(self)
374 settings.LabelDisplay().getFromName('- Name of Alteration Files -', self)
375 self.nameOfSupportEndFile = settings.StringSetting().getFromValue('Name of Support End File:', self, 'support_end.gcode')
376 self.nameOfSupportStartFile = settings.StringSetting().getFromValue(
377 'Name of Support Start File:', self, 'support_start.gcode')
378 settings.LabelSeparator().getFromRepository(self)
379 self.operatingNozzleLiftOverLayerThickness = settings.FloatSpin().getFromValue(
380 0.3, 'Operating Nozzle Lift over Layer Thickness (ratio):', self, 0.7, 0.5)
381 settings.LabelSeparator().getFromRepository(self)
382 settings.LabelDisplay().getFromName('- Raft Size -', self)
383 self.raftAdditionalMarginOverLengthPercent = settings.FloatSpin().getFromValue(
384 0.5, 'Raft Additional Margin over Length (%):', self, 1.5, 1.0)
385 self.raftMargin = settings.FloatSpin().getFromValue(
386 1.0, 'Raft Margin (mm):', self, 5.0, 3.0)
387 settings.LabelSeparator().getFromRepository(self)
388 settings.LabelDisplay().getFromName('- Support -', self)
389 self.supportCrossHatch = settings.BooleanSetting().getFromValue('Support Cross Hatch', self, False)
390 self.supportFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue(
391 0.7, 'Support Flow Rate over Operating Flow Rate (ratio):', self, 1.1, 1.0)
392 self.supportGapOverPerimeterExtrusionWidth = settings.FloatSpin().getFromValue(
393 0.5, 'Support Gap over Perimeter Extrusion Width (ratio):', self, 1.5, 1.0)
394 self.supportMaterialChoice = settings.MenuButtonDisplay().getFromName('Support Material Choice: ', self)
395 self.supportChoiceNone = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'None', self, True)
396 self.supportChoiceEmptyLayersOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Empty Layers Only', self, False)
397 self.supportChoiceEverywhere = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Everywhere', self, False)
398 self.supportChoiceExteriorOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Exterior Only', self, False)
399 self.supportMinimumAngle = settings.FloatSpin().getFromValue(40.0, 'Support Minimum Angle (degrees):', self, 80.0, 60.0)
400 self.executeTitle = 'Raft'
401 self.supportMargin = settings.FloatSpin().getFromValue(
402 1.0, 'Support Margin (mm):', self, 5.0, 3.0)
403 self.supportOffsetX = settings.FloatSpin().getFromValue(0.0, 'Support Offset X (mm):', self, 100.0, 0.0)
404 self.supportOffsetY = settings.FloatSpin().getFromValue(0.0, 'Support Offset Y (mm):', self, 100.0, 0.0)
407 'Raft button has been clicked.'
408 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
409 for fileName in fileNames:
410 writeOutput(fileName)
413 class RaftSkein(object):
414 'A class to raft a skein of extrusions.'
416 self.addLineLayerStart = True
417 self.baseTemperature = None
418 self.beginLoop = None
419 self.boundaryLayers = []
420 self.coolingRate = None
421 self.distanceFeedRate = gcodec.DistanceFeedRate()
423 self.extrusionStart = True
424 self.extrusionTop = 0.0
425 self.feedRateMinute = 961.0
426 self.heatingRate = None
428 self.interfaceTemperature = None
429 self.isEdgePath = False
430 self.isNestedRing = True
431 self.isStartupEarly = False
432 self.layerIndex = - 1
433 self.layerStarted = False
434 self.layerHeight = 0.4
437 self.objectFirstLayerInfillTemperature = None
438 self.objectFirstLayerPerimeterTemperature = None
439 self.objectNextLayersTemperature = None
440 self.oldFlowRate = None
441 self.oldLocation = None
442 self.oldTemperatureOutputString = None
443 self.operatingFeedRateMinute = None
444 self.operatingFlowRate = None
445 self.operatingLayerEndLine = '(<operatingLayerEnd> </operatingLayerEnd>)'
446 self.operatingJump = None
447 self.orbitalFeedRatePerSecond = 2.01
448 self.sharpestProduct = 0.94
449 self.supportFlowRate = None
450 self.supportLayers = []
451 self.supportLayersTemperature = None
452 self.supportedLayersTemperature = None
453 self.travelFeedRateMinute = None
455 def addBaseLayer(self):
457 baseLayerThickness = self.layerHeight * self.baseLayerThicknessOverLayerThickness
458 zCenter = self.extrusionTop + 0.5 * baseLayerThickness
459 z = zCenter + baseLayerThickness * self.repository.baseNozzleLiftOverBaseLayerThickness.value
460 if len(self.baseEndpoints) < 1:
461 print('This should never happen, the base layer has a size of zero.')
463 self.addLayerFromEndpoints(
465 self.repository.baseFeedRateMultiplier.value,
466 self.repository.baseFlowRateMultiplier.value,
468 self.baseLayerThicknessOverLayerThickness,
472 def addBaseSegments(self, baseExtrusionWidth):
473 'Add the base segments.'
474 baseOverhang = self.repository.infillOverhangOverExtrusionWidth.value * baseExtrusionWidth
475 self.baseEndpoints = getVerticalEndpoints(self.interfaceSegmentsTable, self.interfaceStep, baseOverhang, self.baseStep)
477 def addEmptyLayerSupport( self, boundaryLayerIndex ):
478 'Add support material to a layer if it is empty.'
479 supportLayer = SupportLayer([])
480 self.supportLayers.append(supportLayer)
481 if len( self.boundaryLayers[ boundaryLayerIndex ].loops ) > 0:
483 aboveXIntersectionsTable = {}
484 euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsAbove(boundaryLayerIndex), aboveXIntersectionsTable, self.interfaceStep )
485 belowXIntersectionsTable = {}
486 euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsBelow(boundaryLayerIndex), belowXIntersectionsTable, self.interfaceStep )
487 supportLayer.xIntersectionsTable = euclidean.getIntersectionOfXIntersectionsTables( [ aboveXIntersectionsTable, belowXIntersectionsTable ] )
489 def addFlowRate(self, flowRate):
490 'Add a flow rate value if different.'
492 self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
494 def addInterfaceLayer(self):
495 'Add an interface layer.'
496 interfaceLayerThickness = self.layerHeight * self.interfaceLayerThicknessOverLayerThickness
497 zCenter = self.extrusionTop + 0.5 * interfaceLayerThickness
498 z = zCenter + interfaceLayerThickness * self.repository.interfaceNozzleLiftOverInterfaceLayerThickness.value
499 if len(self.interfaceEndpoints) < 1:
500 print('This should never happen, the interface layer has a size of zero.')
502 self.addLayerFromEndpoints(
503 self.interfaceEndpoints,
504 self.repository.interfaceFeedRateMultiplier.value,
505 self.repository.interfaceFlowRateMultiplier.value,
506 interfaceLayerThickness,
507 self.interfaceLayerThicknessOverLayerThickness,
511 def addInterfaceTables(self, interfaceExtrusionWidth):
512 'Add interface tables.'
513 overhang = self.repository.infillOverhangOverExtrusionWidth.value * interfaceExtrusionWidth
514 self.interfaceEndpoints = []
515 self.interfaceIntersectionsTableKeys = self.interfaceIntersectionsTable.keys()
516 self.interfaceSegmentsTable = {}
517 for yKey in self.interfaceIntersectionsTableKeys:
518 self.interfaceIntersectionsTable[yKey].sort()
519 y = yKey * self.interfaceStep
520 lineSegments = euclidean.getSegmentsFromXIntersections(self.interfaceIntersectionsTable[yKey], y)
521 xIntersectionIndexList = []
522 for lineSegmentIndex in xrange(len(lineSegments)):
523 lineSegment = lineSegments[lineSegmentIndex]
524 endpointBegin = lineSegment[0]
525 endpointEnd = lineSegment[1]
526 endpointBegin.point = complex(self.baseStep * math.floor(endpointBegin.point.real / self.baseStep) - overhang, y)
527 endpointEnd.point = complex(self.baseStep * math.ceil(endpointEnd.point.real / self.baseStep) + overhang, y)
528 if endpointEnd.point.real > endpointBegin.point.real:
529 euclidean.addXIntersectionIndexesFromSegment(lineSegmentIndex, lineSegment, xIntersectionIndexList)
530 xIntersections = euclidean.getJoinOfXIntersectionIndexes(xIntersectionIndexList)
531 joinedSegments = euclidean.getSegmentsFromXIntersections(xIntersections, y)
532 if len(joinedSegments) > 0:
533 self.interfaceSegmentsTable[yKey] = joinedSegments
534 for joinedSegment in joinedSegments:
535 self.interfaceEndpoints += joinedSegment
537 def addLayerFromEndpoints(
546 'Add a layer from endpoints and raise the extrusion top.'
547 layerThicknessRatioSquared = layerThicknessRatio * layerThicknessRatio
548 feedRateMinute = self.feedRateMinute * feedRateMultiplier / layerThicknessRatioSquared
549 if len(endpoints) < 1:
551 aroundPixelTable = {}
552 aroundWidth = 0.34321 * step
553 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, self.sharpestProduct, aroundWidth)
555 if self.operatingFlowRate != None:
556 self.addFlowRate(flowRateMultiplier * self.operatingFlowRate)
558 simplifiedPath = euclidean.getSimplifiedPath(path, step)
559 self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinute, simplifiedPath, self.travelFeedRateMinute, z)
560 self.extrusionTop += layerLayerThickness
561 self.addFlowRate(self.oldFlowRate)
563 def addLayerLine(self, z):
564 'Add the layer gcode line and close the last layer gcode block.'
565 if self.layerStarted:
566 self.distanceFeedRate.addLine('(</layer>)')
567 self.distanceFeedRate.addLine('(<layer> %s )' % self.distanceFeedRate.getRounded(z)) # Indicate that a new layer is starting.
568 if self.beginLoop != None:
569 zBegin = self.extrusionTop + self.layerHeight
570 intercircle.addOrbitsIfLarge(self.distanceFeedRate, self.beginLoop, self.orbitalFeedRatePerSecond, self.temperatureChangeTimeBeforeRaft, zBegin)
571 self.beginLoop = None
572 self.layerStarted = True
574 def addOperatingOrbits(self, boundaryLoops, pointComplex, temperatureChangeTime, z):
575 'Add the orbits before the operating layers.'
576 if len(boundaryLoops) < 1:
578 insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, self.edgeWidth)
579 if len(insetBoundaryLoops) < 1:
580 insetBoundaryLoops = boundaryLoops
581 largestLoop = euclidean.getLargestLoop(insetBoundaryLoops)
582 if pointComplex != None:
583 largestLoop = euclidean.getLoopStartingClosest(self.edgeWidth, pointComplex, largestLoop)
584 intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureChangeTime, z)
588 self.baseLayerThicknessOverLayerThickness = self.repository.baseLayerThicknessOverLayerThickness.value
589 baseExtrusionWidth = self.edgeWidth * self.baseLayerThicknessOverLayerThickness
590 self.baseStep = baseExtrusionWidth / self.repository.baseInfillDensity.value
591 self.interfaceLayerThicknessOverLayerThickness = self.repository.interfaceLayerThicknessOverLayerThickness.value
592 interfaceExtrusionWidth = self.edgeWidth * self.interfaceLayerThicknessOverLayerThickness
593 self.interfaceStep = interfaceExtrusionWidth / self.repository.interfaceInfillDensity.value
595 self.cornerMinimumComplex = self.cornerMinimum.dropAxis()
596 originalExtent = self.cornerMaximumComplex - self.cornerMinimumComplex
597 self.raftOutsetRadius = self.repository.raftMargin.value + self.repository.raftAdditionalMarginOverLengthPercent.value * 0.01 * max(originalExtent.real, originalExtent.imag)
598 self.supportOutsetRadius = self.repository.supportMargin.value
599 self.setBoundaryLayers()
600 if len(self.boundaryLayers) < 1:
601 print('this should never happen, there are no boundary layers in addRaft')
603 outsetSeparateLoops = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[0].loops, -self.raftOutsetRadius, 0.8)
604 self.interfaceIntersectionsTable = {}
605 euclidean.addXIntersectionsFromLoopsForTable(outsetSeparateLoops, self.interfaceIntersectionsTable, self.interfaceStep)
606 if len(self.supportLayers) > 0:
607 supportIntersectionsTable = self.supportLayers[0].xIntersectionsTable
608 euclidean.joinXIntersectionsTables(supportIntersectionsTable, self.interfaceIntersectionsTable)
609 self.addInterfaceTables(interfaceExtrusionWidth)
610 self.addRaftPerimeters()
611 self.baseIntersectionsTable = {}
612 complexRadius = complex(self.raftOutsetRadius, self.raftOutsetRadius)
613 self.complexHigh = complexRadius + self.cornerMaximumComplex
614 self.complexLow = self.cornerMinimumComplex - complexRadius
615 self.beginLoop = euclidean.getSquareLoopWiddershins(self.cornerMinimumComplex, self.cornerMaximumComplex)
616 if not intercircle.orbitsAreLarge(self.beginLoop, self.temperatureChangeTimeBeforeRaft):
617 self.beginLoop = None
618 if self.repository.baseLayers.value > 0:
619 self.addTemperatureLineIfDifferent(self.baseTemperature)
620 self.addBaseSegments(baseExtrusionWidth)
621 for baseLayerIndex in xrange(self.repository.baseLayers.value):
623 if self.repository.interfaceLayers.value > 0:
624 self.addTemperatureLineIfDifferent(self.interfaceTemperature)
625 self.interfaceIntersectionsTableKeys.sort()
626 for interfaceLayerIndex in xrange(self.repository.interfaceLayers.value):
627 self.addInterfaceLayer()
628 self.operatingJump = self.extrusionTop + self.layerHeight * self.repository.operatingNozzleLiftOverLayerThickness.value
629 for boundaryLayer in self.boundaryLayers:
630 if self.operatingJump != None:
631 boundaryLayer.z += self.operatingJump
632 if self.repository.baseLayers.value > 0 or self.repository.interfaceLayers.value > 0:
633 boundaryZ = self.boundaryLayers[0].z
634 if self.layerStarted:
635 self.distanceFeedRate.addLine('(</layer>)')
636 self.layerStarted = False
637 self.distanceFeedRate.addLine('(<raftLayerEnd> </raftLayerEnd>)')
638 self.addLayerLine(boundaryZ)
639 temperatureChangeTimeBeforeFirstLayer = self.getTemperatureChangeTime(self.objectFirstLayerPerimeterTemperature)
640 self.addTemperatureLineIfDifferent(self.objectFirstLayerPerimeterTemperature)
641 largestOutsetLoop = intercircle.getLargestInsetLoopFromLoop(euclidean.getLargestLoop(outsetSeparateLoops), -self.raftOutsetRadius)
642 intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestOutsetLoop, self.orbitalFeedRatePerSecond, temperatureChangeTimeBeforeFirstLayer, boundaryZ)
643 self.addLineLayerStart = False
645 def addRaftedLine( self, splitLine ):
646 'Add elevated gcode line with operating feed rate.'
647 self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
648 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
649 z = self.oldLocation.z
650 if self.operatingJump != None:
651 z += self.operatingJump
652 temperature = self.objectNextLayersTemperature
653 if self.layerIndex == 0:
655 temperature = self.objectFirstLayerPerimeterTemperature
657 temperature = self.objectFirstLayerInfillTemperature
658 self.addTemperatureLineIfDifferent(temperature)
659 self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, self.oldLocation.dropAxis(), z)
661 def addRaftPerimeters(self):
662 'Add raft edges if there is a raft.'
663 interfaceOutset = self.halfEdgeWidth * self.interfaceLayerThicknessOverLayerThickness
664 for supportLayer in self.supportLayers:
665 supportSegmentTable = supportLayer.supportSegmentTable
666 if len(supportSegmentTable) > 0:
667 outset = interfaceOutset
668 self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(supportSegmentTable, self.interfaceStep), outset)
669 if self.repository.baseLayers.value < 1 and self.repository.interfaceLayers.value < 1:
671 overhangMultiplier = 1.0 + self.repository.infillOverhangOverExtrusionWidth.value + self.repository.infillOverhangOverExtrusionWidth.value
672 outset = self.halfEdgeWidth
673 if self.repository.interfaceLayers.value > 0:
674 outset = max(interfaceOutset * overhangMultiplier, outset)
675 if self.repository.baseLayers.value > 0:
676 outset = max(self.halfEdgeWidth * self.baseLayerThicknessOverLayerThickness * overhangMultiplier, outset)
677 self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(self.interfaceSegmentsTable, self.interfaceStep), outset)
679 def addRaftPerimetersByLoops(self, loops, outset):
680 'Add raft edges to the gcode for loops.'
681 loops = intercircle.getInsetSeparateLoopsFromLoops(loops, -outset)
683 self.distanceFeedRate.addLine('(<raftPerimeter>)')
685 roundedX = self.distanceFeedRate.getRounded(point.real)
686 roundedY = self.distanceFeedRate.getRounded(point.imag)
687 self.distanceFeedRate.addTagBracketedLine('raftPoint', 'X%s Y%s' % (roundedX, roundedY))
688 self.distanceFeedRate.addLine('(</raftPerimeter>)')
690 def addSegmentTablesToSupportLayers(self):
691 'Add segment tables to the support layers.'
692 for supportLayer in self.supportLayers:
693 supportLayer.supportSegmentTable = {}
694 xIntersectionsTable = supportLayer.xIntersectionsTable
695 for xIntersectionsTableKey in xIntersectionsTable:
696 y = xIntersectionsTableKey * self.interfaceStep
697 supportLayer.supportSegmentTable[ xIntersectionsTableKey ] = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], y )
699 def addSupportLayerTemperature(self, endpoints, z):
700 'Add support layer and temperature before the object layer.'
701 self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportStartLines)
702 self.distanceFeedRate.addLine('(<supportLayer>)')
703 self.addTemperatureOrbits(endpoints, self.supportedLayersTemperature, z)
704 aroundPixelTable = {}
705 aroundWidth = 0.34321 * self.interfaceStep
706 boundaryLoops = self.boundaryLayers[self.layerIndex].loops
707 halfSupportOutset = 0.5 * self.supportOutset
708 aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset)
709 for aroundBoundaryLoop in aroundBoundaryLoops:
710 euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth)
711 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, self.sharpestProduct, aroundWidth)
712 feedRateMinuteMultiplied = self.operatingFeedRateMinute
713 supportFlowRateMultiplied = self.supportFlowRate
714 if self.layerIndex == 0:
715 feedRateMinuteMultiplied *= self.objectFirstLayerFeedRateInfillMultiplier
716 if supportFlowRateMultiplied != None:
717 supportFlowRateMultiplied = self.operatingFlowRate * self.objectFirstLayerFlowRateInfillMultiplier
718 self.addFlowRate(supportFlowRateMultiplied)
720 path = map(lambda p: p + complex(self.supportOffsetX, self.supportOffsetY), path)
721 self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinuteMultiplied, path, self.travelFeedRateMinute, z)
722 self.addFlowRate(self.oldFlowRate)
723 self.addTemperatureOrbits(endpoints, self.supportLayersTemperature, z)
724 self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportEndLines)
725 self.distanceFeedRate.addLine('(</supportLayer>)')
727 def addSupportSegmentTable( self, layerIndex ):
728 'Add support segments from the boundary layers.'
729 aboveLayer = self.boundaryLayers[ layerIndex + 1 ]
730 aboveLoops = aboveLayer.loops
731 supportLayer = self.supportLayers[layerIndex]
732 if len( aboveLoops ) < 1:
734 boundaryLayer = self.boundaryLayers[layerIndex]
735 rise = aboveLayer.z - boundaryLayer.z
736 outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.minimumSupportRatio * rise)
738 subStepSize = self.interfaceStep / float( numberOfSubSteps )
739 aboveIntersectionsTable = {}
740 euclidean.addXIntersectionsFromLoopsForTable( aboveLoops, aboveIntersectionsTable, subStepSize )
741 outsetIntersectionsTable = {}
742 euclidean.addXIntersectionsFromLoopsForTable( outsetSupportLoops, outsetIntersectionsTable, subStepSize )
743 euclidean.subtractXIntersectionsTable( aboveIntersectionsTable, outsetIntersectionsTable )
744 for aboveIntersectionsTableKey in aboveIntersectionsTable.keys():
745 supportIntersectionsTableKey = int( round( float( aboveIntersectionsTableKey ) / numberOfSubSteps ) )
746 xIntersectionIndexList = []
747 if supportIntersectionsTableKey in supportLayer.xIntersectionsTable:
748 euclidean.addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] )
749 euclidean.addXIntersectionIndexesFromXIntersections( 1, xIntersectionIndexList, aboveIntersectionsTable[ aboveIntersectionsTableKey ] )
750 supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
752 def addTemperatureLineIfDifferent(self, temperature):
753 'Add a line of temperature if different.'
754 if temperature == None:
756 temperatureOutputString = euclidean.getRoundedToThreePlaces(temperature)
757 if temperatureOutputString == self.oldTemperatureOutputString:
759 if temperatureOutputString != None:
760 self.distanceFeedRate.addLine('M104 S' + temperatureOutputString) # Set temperature.
761 self.oldTemperatureOutputString = temperatureOutputString
763 def addTemperatureOrbits( self, endpoints, temperature, z ):
764 'Add the temperature and orbits around the support layer.'
765 if self.layerIndex < 0:
767 boundaryLoops = self.boundaryLayers[self.layerIndex].loops
768 temperatureTimeChange = self.getTemperatureChangeTime( temperature )
769 self.addTemperatureLineIfDifferent( temperature )
770 if len( boundaryLoops ) < 1:
771 layerCornerHigh = complex(-987654321.0, -987654321.0)
772 layerCornerLow = complex(987654321.0, 987654321.0)
773 for endpoint in endpoints:
774 layerCornerHigh = euclidean.getMaximum( layerCornerHigh, endpoint.point )
775 layerCornerLow = euclidean.getMinimum( layerCornerLow, endpoint.point )
776 squareLoop = euclidean.getSquareLoopWiddershins( layerCornerLow, layerCornerHigh )
777 intercircle.addOrbitsIfLarge( self.distanceFeedRate, squareLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )
779 edgeInset = 0.4 * self.edgeWidth
780 insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, edgeInset)
781 if len( insetBoundaryLoops ) < 1:
782 insetBoundaryLoops = boundaryLoops
783 largestLoop = euclidean.getLargestLoop( insetBoundaryLoops )
784 intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )
786 def addToFillXIntersectionIndexTables( self, supportLayer ):
787 'Add fill segments from the boundary layers.'
788 supportLoops = supportLayer.supportLoops
789 supportLayer.fillXIntersectionsTable = {}
790 if len(supportLoops) < 1:
792 euclidean.addXIntersectionsFromLoopsForTable( supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep )
794 def extendXIntersections( self, loops, radius, xIntersectionsTable ):
795 'Extend the support segments.'
796 xIntersectionsTableKeys = xIntersectionsTable.keys()
797 for xIntersectionsTableKey in xIntersectionsTableKeys:
798 lineSegments = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], xIntersectionsTableKey )
799 xIntersectionIndexList = []
800 loopXIntersections = []
801 euclidean.addXIntersectionsFromLoops( loops, loopXIntersections, xIntersectionsTableKey )
802 for lineSegmentIndex in xrange( len( lineSegments ) ):
803 lineSegment = lineSegments[ lineSegmentIndex ]
804 extendedLineSegment = getExtendedLineSegment( radius, lineSegment, loopXIntersections )
805 if extendedLineSegment != None:
806 euclidean.addXIntersectionIndexesFromSegment( lineSegmentIndex, extendedLineSegment, xIntersectionIndexList )
807 xIntersections = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
808 if len( xIntersections ) > 0:
809 xIntersectionsTable[ xIntersectionsTableKey ] = xIntersections
811 del xIntersectionsTable[ xIntersectionsTableKey ]
813 def getCraftedGcode(self, gcodeText, repository):
814 'Parse gcode text and store the raft gcode.'
815 self.repository = repository
816 self.minimumSupportRatio = math.tan( math.radians( repository.supportMinimumAngle.value ) )
817 self.supportEndLines = settings.getAlterationFileLines(repository.nameOfSupportEndFile.value)
818 self.supportStartLines = settings.getAlterationFileLines(repository.nameOfSupportStartFile.value)
819 self.supportOffsetX = repository.supportOffsetX.value
820 self.supportOffsetY = repository.supportOffsetY.value
821 self.lines = archive.getTextLines(gcodeText)
822 self.parseInitialization()
823 self.temperatureChangeTimeBeforeRaft = 0.0
824 if self.repository.initialCircling.value:
825 maxBaseInterfaceTemperature = max(self.baseTemperature, self.interfaceTemperature)
826 firstMaxTemperature = max(maxBaseInterfaceTemperature, self.objectFirstLayerPerimeterTemperature)
827 self.temperatureChangeTimeBeforeRaft = self.getTemperatureChangeTime(firstMaxTemperature)
828 if repository.addRaftElevateNozzleOrbitSetAltitude.value:
830 self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
831 for line in self.lines[self.lineIndex :]:
833 return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue())
835 def getElevatedBoundaryLine( self, splitLine ):
836 'Get elevated boundary gcode line.'
837 location = gcodec.getLocationFromSplitLine(None, splitLine)
838 if self.operatingJump != None:
839 location.z += self.operatingJump
840 return self.distanceFeedRate.getBoundaryLine( location )
842 def getInsetLoops( self, boundaryLayerIndex ):
843 'Inset the support loops if they are not already inset.'
844 if boundaryLayerIndex not in self.insetTable:
845 self.insetTable[ boundaryLayerIndex ] = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[ boundaryLayerIndex ].loops, self.quarterEdgeWidth)
846 return self.insetTable[ boundaryLayerIndex ]
848 def getInsetLoopsAbove( self, boundaryLayerIndex ):
849 'Get the inset loops above the boundary layer index.'
850 for aboveLayerIndex in xrange( boundaryLayerIndex + 1, len(self.boundaryLayers) ):
851 if len( self.boundaryLayers[ aboveLayerIndex ].loops ) > 0:
852 return self.getInsetLoops( aboveLayerIndex )
855 def getInsetLoopsBelow( self, boundaryLayerIndex ):
856 'Get the inset loops below the boundary layer index.'
857 for belowLayerIndex in xrange( boundaryLayerIndex - 1, - 1, - 1 ):
858 if len( self.boundaryLayers[ belowLayerIndex ].loops ) > 0:
859 return self.getInsetLoops( belowLayerIndex )
862 def getStepsUntilEnd( self, begin, end, stepSize ):
863 'Get steps from the beginning until the end.'
871 def getSupportEndpoints(self):
872 'Get the support layer segments.'
873 if len(self.supportLayers) <= self.layerIndex:
875 supportSegmentTable = self.supportLayers[self.layerIndex].supportSegmentTable
876 if self.layerIndex % 2 == 1 and self.repository.supportCrossHatch.value:
877 return getVerticalEndpoints(supportSegmentTable, self.interfaceStep, 0.1 * self.edgeWidth, self.interfaceStep)
878 return euclidean.getEndpointsFromSegmentTable(supportSegmentTable)
880 def getTemperatureChangeTime( self, temperature ):
881 'Get the temperature change time.'
882 if temperature == None:
884 oldTemperature = 25.0 # typical chamber temperature
885 if self.oldTemperatureOutputString != None:
886 oldTemperature = float( self.oldTemperatureOutputString )
887 if temperature == oldTemperature:
889 if temperature > oldTemperature:
890 return ( temperature - oldTemperature ) / self.heatingRate
891 return ( oldTemperature - temperature ) / abs( self.coolingRate )
893 def parseInitialization(self):
894 'Parse gcode initialization and store the parameters.'
895 for self.lineIndex in xrange(len(self.lines)):
896 line = self.lines[self.lineIndex]
897 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
898 firstWord = gcodec.getFirstWord(splitLine)
899 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
900 if firstWord == '(<baseTemperature>':
901 self.baseTemperature = float(splitLine[1])
902 elif firstWord == '(<coolingRate>':
903 self.coolingRate = float(splitLine[1])
904 elif firstWord == '(<edgeWidth>':
905 self.edgeWidth = float(splitLine[1])
906 self.halfEdgeWidth = 0.5 * self.edgeWidth
907 self.quarterEdgeWidth = 0.25 * self.edgeWidth
908 self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value
909 elif firstWord == '(</extruderInitialization>)':
910 self.distanceFeedRate.addTagBracketedProcedure('raft')
911 elif firstWord == '(<heatingRate>':
912 self.heatingRate = float(splitLine[1])
913 elif firstWord == '(<interfaceTemperature>':
914 self.interfaceTemperature = float(splitLine[1])
915 elif firstWord == '(<layer>':
917 elif firstWord == '(<layerHeight>':
918 self.layerHeight = float(splitLine[1])
919 elif firstWord == 'M108':
920 self.oldFlowRate = float(splitLine[1][1 :])
921 elif firstWord == '(<objectFirstLayerFeedRateInfillMultiplier>':
922 self.objectFirstLayerFeedRateInfillMultiplier = float(splitLine[1])
923 elif firstWord == '(<objectFirstLayerFlowRateInfillMultiplier>':
924 self.objectFirstLayerFlowRateInfillMultiplier = float(splitLine[1])
925 elif firstWord == '(<objectFirstLayerInfillTemperature>':
926 self.objectFirstLayerInfillTemperature = float(splitLine[1])
927 elif firstWord == '(<objectFirstLayerPerimeterTemperature>':
928 self.objectFirstLayerPerimeterTemperature = float(splitLine[1])
929 elif firstWord == '(<objectNextLayersTemperature>':
930 self.objectNextLayersTemperature = float(splitLine[1])
931 elif firstWord == '(<orbitalFeedRatePerSecond>':
932 self.orbitalFeedRatePerSecond = float(splitLine[1])
933 elif firstWord == '(<operatingFeedRatePerSecond>':
934 self.operatingFeedRateMinute = 60.0 * float(splitLine[1])
935 self.feedRateMinute = self.operatingFeedRateMinute
936 elif firstWord == '(<operatingFlowRate>':
937 self.operatingFlowRate = float(splitLine[1])
938 self.oldFlowRate = self.operatingFlowRate
939 self.supportFlowRate = self.operatingFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value
940 elif firstWord == '(<sharpestProduct>':
941 self.sharpestProduct = float(splitLine[1])
942 elif firstWord == '(<supportLayersTemperature>':
943 self.supportLayersTemperature = float(splitLine[1])
944 elif firstWord == '(<supportedLayersTemperature>':
945 self.supportedLayersTemperature = float(splitLine[1])
946 elif firstWord == '(<travelFeedRatePerSecond>':
947 self.travelFeedRateMinute = 60.0 * float(splitLine[1])
948 self.distanceFeedRate.addLine(line)
950 def parseLine(self, line):
951 'Parse a gcode line and add it to the raft skein.'
952 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
953 if len(splitLine) < 1:
955 firstWord = splitLine[0]
956 if firstWord == 'G1':
957 if self.extrusionStart:
958 self.addRaftedLine(splitLine)
960 elif firstWord == 'M101':
961 if self.isStartupEarly:
962 self.isStartupEarly = False
964 elif firstWord == 'M108':
965 self.oldFlowRate = float(splitLine[1][1 :])
966 elif firstWord == '(<boundaryPoint>':
967 line = self.getElevatedBoundaryLine(splitLine)
968 elif firstWord == '(</crafting>)':
969 self.extrusionStart = False
970 self.distanceFeedRate.addLine( self.operatingLayerEndLine )
971 elif firstWord == '(<layer>':
973 settings.printProgress(self.layerIndex, 'raft')
975 layerZ = self.extrusionTop + float(splitLine[1])
976 if len(self.boundaryLayers) > 0:
977 boundaryLayer = self.boundaryLayers[self.layerIndex]
978 layerZ = boundaryLayer.z
979 if self.operatingJump != None:
980 line = '(<layer> %s )' % self.distanceFeedRate.getRounded( layerZ )
981 if self.layerStarted and self.addLineLayerStart:
982 self.distanceFeedRate.addLine('(</layer>)')
983 self.layerStarted = False
984 if self.layerIndex > len(self.supportLayers) + 1:
985 self.distanceFeedRate.addLine( self.operatingLayerEndLine )
986 self.operatingLayerEndLine = ''
987 if self.addLineLayerStart:
988 self.distanceFeedRate.addLine(line)
989 self.addLineLayerStart = True
991 endpoints = self.getSupportEndpoints()
992 if self.layerIndex == 1:
993 if len(endpoints) < 1:
994 temperatureChangeTimeBeforeNextLayers = self.getTemperatureChangeTime( self.objectNextLayersTemperature )
995 self.addTemperatureLineIfDifferent( self.objectNextLayersTemperature )
996 if self.repository.addRaftElevateNozzleOrbitSetAltitude.value and boundaryLayer != None and len( boundaryLayer.loops ) > 0:
997 self.addOperatingOrbits( boundaryLayer.loops, euclidean.getXYComplexFromVector3( self.oldLocation ), temperatureChangeTimeBeforeNextLayers, layerZ )
998 if len(endpoints) > 0:
999 self.addSupportLayerTemperature( endpoints, layerZ )
1000 elif firstWord == '(<edge>' or firstWord == '(<edgePath>)':
1001 self.isEdgePath = True
1002 elif firstWord == '(</edge>)' or firstWord == '(</edgePath>)':
1003 self.isEdgePath = False
1004 self.distanceFeedRate.addLine(line)
1006 def setBoundaryLayers(self):
1007 'Set the boundary layers.'
1008 if self.repository.supportChoiceNone.value:
1010 if len(self.boundaryLayers) < 2:
1012 if self.repository.supportChoiceEmptyLayersOnly.value:
1013 supportLayer = SupportLayer([])
1014 self.supportLayers.append(supportLayer)
1015 for boundaryLayerIndex in xrange(1, len(self.boundaryLayers) -1):
1016 self.addEmptyLayerSupport(boundaryLayerIndex)
1017 self.truncateSupportSegmentTables()
1018 self.addSegmentTablesToSupportLayers()
1020 for boundaryLayer in self.boundaryLayers:
1021 # thresholdRadius of 0.8 is needed to avoid the ripple inset bug http://hydraraptor.blogspot.com/2010/12/crackers.html
1022 supportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.supportOutset, 0.8)
1023 supportLayer = SupportLayer(supportLoops)
1024 self.supportLayers.append(supportLayer)
1025 for supportLayerIndex in xrange(len(self.supportLayers) - 1):
1026 self.addSupportSegmentTable(supportLayerIndex)
1027 self.truncateSupportSegmentTables()
1028 for supportLayerIndex in xrange(len(self.supportLayers) - 1):
1029 boundaryLoops = self.boundaryLayers[supportLayerIndex].loops
1030 self.extendXIntersections( boundaryLoops, self.supportOutset, self.supportLayers[supportLayerIndex].xIntersectionsTable)
1031 for supportLayer in self.supportLayers:
1032 self.addToFillXIntersectionIndexTables(supportLayer)
1033 if self.repository.supportChoiceExteriorOnly.value:
1034 for supportLayerIndex in xrange(1, len(self.supportLayers)):
1035 self.subtractJoinedFill(supportLayerIndex)
1036 for supportLayer in self.supportLayers:
1037 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable)
1038 for supportLayerIndex in xrange(len(self.supportLayers) - 2, -1, -1):
1039 xIntersectionsTable = self.supportLayers[supportLayerIndex].xIntersectionsTable
1040 aboveXIntersectionsTable = self.supportLayers[supportLayerIndex + 1].xIntersectionsTable
1041 euclidean.joinXIntersectionsTables(aboveXIntersectionsTable, xIntersectionsTable)
1042 for supportLayerIndex in xrange(len(self.supportLayers)):
1043 supportLayer = self.supportLayers[supportLayerIndex]
1044 self.extendXIntersections(supportLayer.supportLoops, self.supportOutsetRadius, supportLayer.xIntersectionsTable)
1045 for supportLayer in self.supportLayers:
1046 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable)
1047 self.addSegmentTablesToSupportLayers()
1049 def setCornersZ(self):
1050 'Set maximum and minimum corners and z.'
1052 boundaryLayer = None
1054 self.cornerMaximumComplex = complex(-912345678.0, -912345678.0)
1055 self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0)
1056 self.firstLayerLoops = []
1057 for line in self.lines[self.lineIndex :]:
1058 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
1059 firstWord = gcodec.getFirstWord(splitLine)
1060 if firstWord == '(</boundaryPerimeter>)':
1062 elif firstWord == '(<boundaryPoint>':
1063 location = gcodec.getLocationFromSplitLine(None, splitLine)
1064 if boundaryLoop == None:
1066 boundaryLayer.loops.append(boundaryLoop)
1067 boundaryLoop.append(location.dropAxis())
1068 self.cornerMaximumComplex = euclidean.getMaximum(self.cornerMaximumComplex, location.dropAxis())
1069 self.cornerMinimum.minimize(location)
1070 elif firstWord == '(<layer>':
1071 z = float(splitLine[1])
1072 boundaryLayer = euclidean.LoopLayer(z)
1073 self.boundaryLayers.append(boundaryLayer)
1074 elif firstWord == '(<layer>':
1076 if self.repository.supportChoiceNone.value:
1080 def subtractJoinedFill( self, supportLayerIndex ):
1081 'Join the fill then subtract it from the support layer table.'
1082 supportLayer = self.supportLayers[supportLayerIndex]
1083 fillXIntersectionsTable = supportLayer.fillXIntersectionsTable
1084 belowFillXIntersectionsTable = self.supportLayers[ supportLayerIndex - 1 ].fillXIntersectionsTable
1085 euclidean.joinXIntersectionsTables( belowFillXIntersectionsTable, supportLayer.fillXIntersectionsTable )
1086 euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable )
1088 def truncateSupportSegmentTables(self):
1089 'Truncate the support segments after the last support segment which contains elements.'
1090 for supportLayerIndex in xrange( len(self.supportLayers) - 1, - 1, - 1 ):
1091 if len( self.supportLayers[supportLayerIndex].xIntersectionsTable ) > 0:
1092 self.supportLayers = self.supportLayers[ : supportLayerIndex + 1 ]
1094 self.supportLayers = []
1097 class SupportLayer(object):
1098 'Support loops with segment tables.'
1099 def __init__( self, supportLoops ):
1100 self.supportLoops = supportLoops
1101 self.supportSegmentTable = {}
1102 self.xIntersectionsTable = {}
1105 'Get the string representation of this loop layer.'
1106 return '%s' % ( self.supportLoops )
1110 'Display the raft dialog.'
1111 if len(sys.argv) > 1:
1112 writeOutput(' '.join(sys.argv[1 :]))
1114 settings.startMainLoopFromConstructor(getNewRepository())
1116 if __name__ == '__main__':