chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / cura_sf / skeinforge_application / skeinforge_plugins / craft_plugins / raft.py
1 """
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.
4
5 The raft manual page is at:
6 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft
7
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.
9
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.
11
12 <gallery perRow="1">
13 Image:Raft.jpg|Raft
14 </gallery>
15
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.
17
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.
19
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/
25
26 Nophead has written about rafts on his blog:
27 http://hydraraptor.blogspot.com/2009/07/thoughts-on-rafts.html
28
29 More pictures of rafting in action are available from the Metalab blog at:
30 http://reprap.soup.io/?search=rafting
31
32 ==Operation==
33 Default: On
34
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..
36
37 ==Settings==
38 ===Add Raft, Elevate Nozzle, Orbit===
39 Default: On
40
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.
42
43 ===Base===
44 Base layer is the part of the raft that touches the bed.
45
46 ====Base Feed Rate Multiplier====
47 Default is one.
48
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.
50
51 ====Base Flow Rate Multiplier====
52 Default is one.
53
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.
55
56 ====Base Infill Density====
57 Default is 0.5.
58
59 Defines the infill density ratio of the base of the raft.
60
61 ====Base Layer Height over Layer Thickness====
62 Default is two.
63
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.
65
66 ====Base Layers====
67 Default is one.
68
69 Defines the number of base layers.
70
71 ====Base Nozzle Lift over Base Layer Thickness====
72 Default is 0.4.
73
74 Defines the amount the nozzle is above the center of the base extrusion divided by the base layer thickness.
75
76 ===Initial Circling===
77 Default is off.
78
79 When selected, the extruder will initially circle around until it reaches operating temperature.
80
81 ===Infill Overhang over Extrusion Width===
82 Default is 0.05.
83
84 Defines the ratio of the infill overhang over the the extrusion width of the raft.
85
86 ===Interface===
87 ====Interface Feed Rate Multiplier====
88 Default is one.
89
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.
91
92 ====Interface Flow Rate Multiplier====
93 Default is one.
94
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.
96
97 ====Interface Infill Density====
98 Default is 0.5.
99
100 Defines the infill density ratio of the interface of the raft.
101
102 ====Interface Layer Thickness over Extrusion Height====
103 Default is one.
104
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.
106
107 ====Interface Layers====
108 Default is two.
109
110 Defines the number of interface layers to print.
111
112 ====Interface Nozzle Lift over Interface Layer Thickness====
113 Default is 0.45.
114
115 Defines the amount the nozzle is above the center of the interface extrusion divided by the interface layer thickness.
116
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.
119
120 ====Name of Support End File====
121 Default is support_end.gcode.
122
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.
124
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.
127
128 ===Operating Nozzle Lift over Layer Thickness===
129 Default is 0.5.
130
131 Defines the amount the nozzle is above the center of the operating extrusion divided by the layer height.
132
133 ===Raft Size===
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.
135
136 ====Raft Additional Margin over Length====
137 Default is 1 percent.
138
139 ====Raft Margin====
140 Default is three millimeters.
141
142 ===Support===
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/
146
147 ====Support Cross Hatch====
148 Default is off.
149
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.
151
152 ====Support Flow Rate over Operating Flow Rate====
153 Default: 0.9.
154
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.
156
157 ====Support Gap over Perimeter Extrusion Width====
158 Default: 0.5.
159
160 Defines the gap between the support material and the object over the edge extrusion width.
161
162 ====Support Material Choice====
163 Default is 'None' because the raft takes time to generate.
164
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.
167
168 =====Everywhere=====
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.
170
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.
173
174 =====None=====
175 When selected, raft will not add support material.
176
177 ====Support Minimum Angle====
178 Default is sixty degrees.
179
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.
181
182 ==Examples==
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.
184
185 > python raft.py
186 This brings up the raft dialog.
187
188 > python raft.py Screw Holder Bottom.stl
189 The raft tool is parsing the file:
190 Screw Holder Bottom.stl
191 ..
192 The raft tool has created the file:
193 Screw Holder Bottom_raft.gcode
194
195 """
196
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.
199 import __init__
200
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
212 import math
213 import os
214 import sys
215
216
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'
220
221
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)
227
228 def getCraftedTextFromText(gcodeText, repository=None):
229         'Raft a gcode linear move text.'
230         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'raft'):
231                 return gcodeText
232         if repository == None:
233                 repository = settings.getReadRepository( RaftRepository() )
234         if not repository.activateRaft.value:
235                 return gcodeText
236         return RaftSkein().getCraftedGcode(gcodeText, repository)
237
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 ]
243
244 def getEndpointsFromYIntersections( x, yIntersections ):
245         'Get endpoints from the y intersections.'
246         endpoints = []
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 )
258         return endpoints
259
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.')
268                 print(lineSegment)
269                 return None
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)
276         return lineSegment
277
278 def getLoopsBySegmentsDictionary(segmentsDictionary, width):
279         'Get loops from a horizontal segments dictionary.'
280         points = []
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)
286
287 def getNewRepository():
288         'Get new repository.'
289         return RaftRepository()
290
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()
312                 xIntersections = []
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
324
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 )
329
330 def writeOutput(fileName, shouldAnalyze=True):
331         'Raft a gcode linear move file.'
332         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'raft', shouldAnalyze)
333
334
335 class RaftRepository:
336         'A class to handle the raft settings.'
337         def __init__(self):
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'
403
404         def execute(self):
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)
409
410
411 class RaftSkein:
412         'A class to raft a skein of extrusions.'
413         def __init__(self):
414                 self.addLineLayerStart = True
415                 self.baseTemperature = None
416                 self.beginLoop = None
417                 self.boundaryLayers = []
418                 self.coolingRate = None
419                 self.distanceFeedRate = gcodec.DistanceFeedRate()
420                 self.edgeWidth = 0.6
421                 self.extrusionStart = True
422                 self.extrusionTop = 0.0
423                 self.feedRateMinute = 961.0
424                 self.heatingRate = None
425                 self.insetTable = {}
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
433                 self.lineIndex = 0
434                 self.lines = None
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
452
453         def addBaseLayer(self):
454                 'Add a base layer.'
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.')
460                         return
461                 self.addLayerFromEndpoints(
462                         self.baseEndpoints,
463                         self.repository.baseFeedRateMultiplier.value,
464                         self.repository.baseFlowRateMultiplier.value,
465                         baseLayerThickness,
466                         self.baseLayerThicknessOverLayerThickness,
467                         self.baseStep,
468                         z)
469
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)
474
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:
480                         return
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 ] )
486
487         def addFlowRate(self, flowRate):
488                 'Add a flow rate value if different.'
489                 if flowRate != None:
490                         self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
491
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.')
499                         return
500                 self.addLayerFromEndpoints(
501                         self.interfaceEndpoints,
502                         self.repository.interfaceFeedRateMultiplier.value,
503                         self.repository.interfaceFlowRateMultiplier.value,
504                         interfaceLayerThickness,
505                         self.interfaceLayerThicknessOverLayerThickness,
506                         self.interfaceStep,
507                         z)
508
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
534
535         def addLayerFromEndpoints(
536                 self,
537                 endpoints,
538                 feedRateMultiplier,
539                 flowRateMultiplier,
540                 layerLayerThickness,
541                 layerThicknessRatio,
542                 step,
543                 z):
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:
548                         return
549                 aroundPixelTable = {}
550                 aroundWidth = 0.34321 * step
551                 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, self.sharpestProduct, aroundWidth)
552                 self.addLayerLine(z)
553                 if self.operatingFlowRate != None:
554                         self.addFlowRate(flowRateMultiplier * self.operatingFlowRate)
555                 for path in paths:
556                         simplifiedPath = euclidean.getSimplifiedPath(path, step)
557                         self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinute, simplifiedPath, self.travelFeedRateMinute, z)
558                 self.extrusionTop += layerLayerThickness
559                 self.addFlowRate(self.oldFlowRate)
560
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
571
572         def addOperatingOrbits(self, boundaryLoops, pointComplex, temperatureChangeTime, z):
573                 'Add the orbits before the operating layers.'
574                 if len(boundaryLoops) < 1:
575                         return
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)
583
584         def addRaft(self):
585                 'Add the raft.'
586                 if len(self.boundaryLayers) < 0:
587                         print('this should never happen, there are no boundary layers in addRaft')
588                         return
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
595                 self.setCornersZ()
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):
619                         self.addBaseLayer()
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
641
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:
651                         if self.isEdgePath:
652                                 temperature = self.objectFirstLayerPerimeterTemperature
653                         else:
654                                 temperature = self.objectFirstLayerInfillTemperature
655                 self.addTemperatureLineIfDifferent(temperature)
656                 self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, self.oldLocation.dropAxis(), z)
657
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:
667                         return
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)
675
676         def addRaftPerimetersByLoops(self, loops, outset):
677                 'Add raft edges to the gcode for loops.'
678                 loops = intercircle.getInsetSeparateLoopsFromLoops(loops, -outset)
679                 for loop in loops:
680                         self.distanceFeedRate.addLine('(<raftPerimeter>)')
681                         for point in loop:
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>)')
686
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 )
695
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)
716                 for path in paths:
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>)')
722
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:
729                         return
730                 boundaryLayer = self.boundaryLayers[layerIndex]
731                 rise = aboveLayer.z - boundaryLayer.z
732                 outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.minimumSupportRatio * rise)
733                 numberOfSubSteps = 4
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 )
747
748         def addTemperatureLineIfDifferent(self, temperature):
749                 'Add a line of temperature if different.'
750                 if temperature == None:
751                         return
752                 temperatureOutputString = euclidean.getRoundedToThreePlaces(temperature)
753                 if temperatureOutputString == self.oldTemperatureOutputString:
754                         return
755                 if temperatureOutputString != None:
756                         self.distanceFeedRate.addLine('M104 S' + temperatureOutputString) # Set temperature.
757                 self.oldTemperatureOutputString = temperatureOutputString
758
759         def addTemperatureOrbits( self, endpoints, temperature, z ):
760                 'Add the temperature and orbits around the support layer.'
761                 if self.layerIndex < 0:
762                         return
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 )
774                         return
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 )
781
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:
787                         return
788                 euclidean.addXIntersectionsFromLoopsForTable( supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep )
789
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
806                         else:
807                                 del xIntersectionsTable[ xIntersectionsTableKey ]
808
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:
823                         self.addRaft()
824                 self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
825                 for line in self.lines[self.lineIndex :]:
826                         self.parseLine(line)
827                 return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue())
828
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 )
835
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 ]
841
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 )
847                 return []
848
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 )
854                 return []
855
856         def getStepsUntilEnd( self, begin, end, stepSize ):
857                 'Get steps from the beginning until the end.'
858                 step = begin
859                 steps = []
860                 while step < end:
861                         steps.append( step )
862                         step += stepSize
863                 return steps
864
865         def getSupportEndpoints(self):
866                 'Get the support layer segments.'
867                 if len(self.supportLayers) <= self.layerIndex:
868                         return []
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)
873
874         def getTemperatureChangeTime( self, temperature ):
875                 'Get the temperature change time.'
876                 if temperature == None:
877                         return 0.0
878                 oldTemperature = 25.0 # typical chamber temperature
879                 if self.oldTemperatureOutputString != None:
880                         oldTemperature = float( self.oldTemperatureOutputString )
881                 if temperature == oldTemperature:
882                         return 0.0
883                 if temperature > oldTemperature:
884                         return ( temperature - oldTemperature ) / self.heatingRate
885                 return ( oldTemperature - temperature ) / abs( self.coolingRate )
886
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>':
910                                 return
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)
943
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:
948                         return
949                 firstWord = splitLine[0]
950                 if firstWord == 'G1':
951                         if self.extrusionStart:
952                                 self.addRaftedLine(splitLine)
953                                 return
954                 elif firstWord == 'M101':
955                         if self.isStartupEarly:
956                                 self.isStartupEarly = False
957                                 return
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>':
966                         self.layerIndex += 1
967                         settings.printProgress(self.layerIndex, 'raft')
968                         boundaryLayer = None
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
984                         line = ''
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)
999
1000         def setBoundaryLayers(self):
1001                 'Set the boundary layers.'
1002                 if self.repository.supportChoiceNone.value:
1003                         return
1004                 if len(self.boundaryLayers) < 2:
1005                         return
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()
1013                         return
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()
1042
1043         def setCornersZ(self):
1044                 'Set maximum and minimum corners and z.'
1045                 boundaryLoop = None
1046                 boundaryLayer = None
1047                 layerIndex = - 1
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>)':
1055                                 boundaryLoop = None
1056                         elif firstWord == '(<boundaryPoint>':
1057                                 location = gcodec.getLocationFromSplitLine(None, splitLine)
1058                                 if boundaryLoop == None:
1059                                         boundaryLoop = []
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>':
1069                                 layerIndex += 1
1070                                 if self.repository.supportChoiceNone.value:
1071                                         if layerIndex > 1:
1072                                                 return
1073
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 )
1081
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 ]
1087                                 return
1088                 self.supportLayers = []
1089
1090
1091 class SupportLayer:
1092         'Support loops with segment tables.'
1093         def __init__( self, supportLoops ):
1094                 self.supportLoops = supportLoops
1095                 self.supportSegmentTable = {}
1096                 self.xIntersectionsTable = {}
1097
1098         def __repr__(self):
1099                 'Get the string representation of this loop layer.'
1100                 return '%s' % ( self.supportLoops )
1101
1102
1103 def main():
1104         'Display the raft dialog.'
1105         if len(sys.argv) > 1:
1106                 writeOutput(' '.join(sys.argv[1 :]))
1107         else:
1108                 settings.startMainLoopFromConstructor(getNewRepository())
1109
1110 if __name__ == '__main__':
1111         main()