summaryrefslogtreecommitdiff
path: root/trunk/reprap/miscellaneous/python-beanshell-scripts/skeinforge_tools/craft_plugins/raft.py
blob: 2277f78bd01b4b153a4d5bf50eff218790f2c26d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
"""
This page is in the table of contents.
Raft is a script to create a raft, elevate the nozzle and set the temperature.

The raft manual page is at:
http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Raft

Allan Ecker aka The Masked Retriever's has written two quicktips for raft which follow below.
"Skeinforge Quicktip: The Raft, Part 1" at:
http://blog.thingiverse.com/2009/07/14/skeinforge-quicktip-the-raft-part-1/
"Skeinforge Quicktip: The Raft, Part II" at:
http://blog.thingiverse.com/2009/08/04/skeinforge-quicktip-the-raft-part-ii/

Pictures of rafting in action are available from the Metalab blog at:
http://reprap.soup.io/?search=rafting

Raft 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.

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.

==Operation==
The default 'Activate Raft' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions will not be called.  The raft script sets the temperature.

==Settings==
===Add Raft, Elevate Nozzle, Orbit and Set Altitude===
When selected, the script will also create a raft, elevate the nozzle, orbit and set the altitude of the bottom of the raft.

===Base===
====Base Feed Rate Multiplier====
Default is one.

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.

====Base Flow Rate Multiplier====
Default is one.

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.

====Base Infill Density====
Default is 0.5.

Defines the infill density ratio of the base of the raft.

====Base Layer Height over Layer Thickness====
Default is two.

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.

====Base Layers====
Default is one.

Defines the number of base layers.

====Base Nozzle Lift over Base Layer Thickness====
Default is 0.4.

Defines the amount the nozzle is above the center of the base extrusion divided by the base layer thickness.

===Bottom Altitude===
Default is zero.

Defines the altitude of the bottom of the raft.

===Infill Overhang over Extrusion Width===
Default is 0.05.

Defines the ratio of the infill overhang over the the extrusion width of the raft.

===Interface===
====Interface Feed Rate Multiplier====
Default is one.

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.

====Interface Flow Rate Multiplier====
Default is one.

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.

====Interface Infill Density====
Default is 0.5.

Defines the infill density ratio of the interface of the raft.

====Interface Layer Thickness over Extrusion Height====
Default is one.

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.

====Interface Layers====
Default is two.

Defines the number of interface layers.

====Interface Nozzle Lift over Interface Layer Thickness====
Default is 0.45.

Defines the amount the nozzle is above the center of the interface extrusion divided by the interface layer thickness.

===Object First Layer Feed Rate Infill Multiplier===
Default is one.

Defines the object first layer infill feed rate multiplier.  The greater the 'Object First Layer Feed Rate Infill Multiplier, the thinner the infill, the lower the 'Object First Layer Feed Rate Infill Multiplier', the thicker the infill.

===Object First Layer Feed Rate Perimeter Multiplier===
Default is one.

Defines the object first layer perimeter feed rate multiplier.  The greater the 'Object First Layer Feed Rate Perimeter Multiplier, the thinner the perimeter, the lower the 'Object First Layer Feed Rate Perimeter Multiplier', the thicker the perimeter.

====Object First Layer Flow Rate Multiplier====
Default is one.

Defines the object first layer flow rate multiplier.  The greater the 'Object First Layer Flow Rate Multiplier', the thicker the first layer, the lower the 'Object First Layer Flow Rate Multiplier, the thinner the first layer.

===Operating Nozzle Lift over Layer Thickness===
Default is 0.5.

Defines the amount the nozzle is above the center of the operating extrusion divided by the layer thickness.

===Raft Size===
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.

====Raft Margin====
Default is three millimeters.

====Raft Additional Margin over Length====
Default is 1 percent.

===Support===
====Support Cross Hatch====
Default is off.

When selected, the support material will cross hatched.

====Support Flow Rate over Operating Flow Rate====
Default is 0.9.

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.

====Support Gap over Perimeter Extrusion Width====
Default is 0.5.

Defines the gap between the support material and the object over the perimeter extrusion width.

====Support Material Choice====
Default is 'None' because the raft takes time to generate.

=====Empty Layers Only=====
When selected, support material will be only on the empty layers.  This is useful when making identical objects in a stack.

=====Everywhere=====
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.

=====Exterior Only=====
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.

=====None=====
When selected, raft will not add support material.

====Support Minimum Angle====
Default is sixty degrees.

Defines the minimum angle that a surface overhangs before support material is added.

==Alterations==
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_tools folder. If it doesn't find anything there it looks in the craft_plugins folder.

===support_start.gcode===
If support material is generated, raft will add support_start.gcode, if it exists, to the start of the support gcode.

===support_end.gcode===
If support material is generated, raft will add support_end.gcode, if it exists, to the end of the support gcode.

==Examples==
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.


> python raft.py
This brings up the raft dialog.


> python raft.py Screw Holder Bottom.stl
The raft tool is parsing the file:
Screw Holder Bottom.stl
..
The raft tool has created the file:
Screw Holder Bottom_raft.gcode


> python
Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import raft
>>> raft.main()
This brings up the raft dialog.


>>> raft.writeOutput( 'Screw Holder Bottom.stl' )
Screw Holder Bottom.stl
The raft tool is parsing the file:
Screw Holder Bottom.stl
..
The raft tool has created the file:
Screw Holder Bottom_raft.gcode


"""

from __future__ import absolute_import
#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.
import __init__

from skeinforge_tools import profile
from skeinforge_tools.meta_plugins import polyfile
from skeinforge_tools.skeinforge_utilities import consecution
from skeinforge_tools.skeinforge_utilities import euclidean
from skeinforge_tools.skeinforge_utilities import gcodec
from skeinforge_tools.skeinforge_utilities import intercircle
from skeinforge_tools.skeinforge_utilities import interpret
from skeinforge_tools.skeinforge_utilities import settings
from skeinforge_tools.skeinforge_utilities.vector3 import Vector3
import math
import os
import sys


__author__ = "Enrique Perez (perez_enrique@yahoo.com)"
__date__ = "$Date: 2008/21/04 $"
__license__ = "GPL 3.0"


#maybe later wide support
#raft outline temperature http://hydraraptor.blogspot.com/2008/09/screw-top-pot.html
def getCraftedText( fileName, text = '', repository = None ):
	"Raft the file or text."
	return getCraftedTextFromText( gcodec.getTextIfEmpty( fileName, text ), repository )

def getCraftedTextFromText( gcodeText, repository = None ):
	"Raft a gcode linear move text."
	if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'raft' ):
		return gcodeText
	if repository == None:
		repository = settings.getReadRepository( RaftRepository() )
	if not repository.activateRaft.value:
		return gcodeText
	return RaftSkein().getCraftedGcode( gcodeText, repository )

def getCrossHatchPointLine( crossHatchPointLineTable, y ):
	"Get the cross hatch point line."
	if not crossHatchPointLineTable.has_key( y ):
		crossHatchPointLineTable[ y ] = {}
	return crossHatchPointLineTable[ y ]

def getEndpointsFromYIntersections( x, yIntersections ):
	"Get endpoints from the y intersections."
	endpoints = []
	for yIntersectionIndex in xrange( 0, len( yIntersections ), 2 ):
		firstY = yIntersections[ yIntersectionIndex ]
		secondY = yIntersections[ yIntersectionIndex + 1 ]
		if firstY != secondY:
			firstComplex = complex( x, firstY )
			secondComplex = complex( x, secondY )
			endpointFirst = euclidean.Endpoint()
			endpointSecond = euclidean.Endpoint().getFromOtherPoint( endpointFirst, secondComplex )
			endpointFirst.getFromOtherPoint( endpointSecond, firstComplex )
			endpoints.append( endpointFirst )
			endpoints.append( endpointSecond )
	return endpoints

def getExtendedLineSegment( extensionDistance, lineSegment, loopXIntersections ):
	"Get extended line segment."
	pointBegin = lineSegment[ 0 ].point
	pointEnd = lineSegment[ 1 ].point
	segment = pointEnd - pointBegin
	segmentLength = abs( segment )
	if segmentLength <= 0.0:
		print( "This should never happen in getExtendedLineSegment in raft, the segment should have a length greater than zero." )
		print( lineSegment )
		return None
	segmentExtend = segment * extensionDistance / segmentLength
	lineSegment[ 0 ].point -= segmentExtend
	lineSegment[ 1 ].point += segmentExtend
	for loopXIntersection in loopXIntersections:
		setExtendedPoint( lineSegment[ 0 ], pointBegin, loopXIntersection )
		setExtendedPoint( lineSegment[ 1 ], pointEnd, loopXIntersection )
	return lineSegment

def getNewRepository():
	"Get the repository constructor."
	return RaftRepository()

def setExtendedPoint( lineSegmentEnd, pointOriginal, x ):
	"Set the point in the extended line segment."
	if x > min( lineSegmentEnd.point.real, pointOriginal.real ) and x < max( lineSegmentEnd.point.real, pointOriginal.real ):
		lineSegmentEnd.point = complex( x, pointOriginal.imag )

def writeOutput( fileName = '' ):
	"Raft a gcode linear move file."
	fileName = interpret.getFirstTranslatorFileNameUnmodified( fileName )
	if fileName == '':
		return
	consecution.writeChainTextWithNounMessage( fileName, 'raft' )


class RaftRepository:
	"A class to handle the raft settings."
	def __init__( self ):
		"Set the default settings, execute title & settings fileName."
		profile.addListsToCraftTypeRepository( 'skeinforge_tools.craft_plugins.raft.html', self )
		self.fileNameInput = settings.FileNameInput().getFromFileName( interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Raft', self, '' )
		self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( 'http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Raft' )
		self.activateRaft = settings.BooleanSetting().getFromValue( 'Activate Raft', self, True )
		self.addRaftElevateNozzleOrbitSetAltitude = settings.BooleanSetting().getFromValue( 'Add Raft, Elevate Nozzle, Orbit and Set Altitude:', self, True )
		settings.LabelSeparator().getFromRepository( self )
		settings.LabelDisplay().getFromName( '- Base -', self )
		self.baseFeedRateMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Base Feed Rate Multiplier (ratio):', self, 1.1, 1.0 )
		self.baseFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Base Flow Rate Multiplier (ratio):', self, 1.1, 1.0 )
		self.baseInfillDensity = settings.FloatSpin().getFromValue( 0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5 )
		self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0 )
		self.baseLayers = settings.IntSpin().getFromValue( 0, 'Base Layers (integer):', self, 3, 1 )
		self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4 )
		settings.LabelSeparator().getFromRepository( self )
		self.bottomAltitude = settings.FloatSpin().getFromValue( 0.0, 'Bottom Altitude:', self, 10.0, 0.0 )
		self.infillOverhangOverExtrusionWidth = settings.FloatSpin().getFromValue( 0.0, 'Infill Overhang over Extrusion Width (ratio):', self, 0.5, 0.05 )
		settings.LabelSeparator().getFromRepository( self )
		settings.LabelDisplay().getFromName( '- Interface -', self )
		self.interfaceFeedRateMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Interface Feed Rate Multiplier (ratio):', self, 1.1, 1.0 )
		self.interfaceFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Interface Flow Rate Multiplier (ratio):', self, 1.1, 1.0 )
		self.interfaceInfillDensity = settings.FloatSpin().getFromValue( 0.3, 'Interface Infill Density (ratio):', self, 0.9, 0.5 )
		self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0 )
		self.interfaceLayers = settings.IntSpin().getFromValue( 0, 'Interface Layers (integer):', self, 3, 2 )
		self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45 )
		settings.LabelSeparator().getFromRepository( self )
		settings.LabelDisplay().getFromName( '- Object First Layer -', self )
		self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.1, 1.0 )
		self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.1, 1.0 )
		self.objectFirstLayerFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.7, 'Object First Layer Flow Rate Multiplier (ratio):', self, 1.1, 1.0 )
		settings.LabelSeparator().getFromRepository( self )
		self.operatingNozzleLiftOverLayerThickness = settings.FloatSpin().getFromValue( 0.3, 'Operating Nozzle Lift over Layer Thickness (ratio):', self, 0.7, 0.5 )
		settings.LabelSeparator().getFromRepository( self )
		settings.LabelDisplay().getFromName( '- Raft Size -', self )
		self.raftAdditionalMarginOverLengthPercent = settings.FloatSpin().getFromValue( 0.5, 'Raft Additional Margin over Length (%):', self, 1.5, 1.0 )
		self.raftMargin = settings.FloatSpin().getFromValue( 1.0, 'Raft Margin (mm):', self, 5.0, 3.0 )
		settings.LabelSeparator().getFromRepository( self )
		settings.LabelDisplay().getFromName( '- Support -', self )
		self.supportCrossHatch = settings.BooleanSetting().getFromValue( 'Support Cross Hatch', self, False )
		self.supportFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue( 0.7, 'Support Flow Rate over Operating Flow Rate (ratio):', self, 1.1, 1.0 )
		self.supportGapOverPerimeterExtrusionWidth = settings.FloatSpin().getFromValue( 0.5, 'Support Gap over Perimeter Extrusion Width (ratio):', self, 1.5, 1.0 )
		self.supportMaterialChoice = settings.MenuButtonDisplay().getFromName( 'Support Material Choice: ', self )
		self.supportChoiceNone = settings.MenuRadio().getFromMenuButtonDisplay( self.supportMaterialChoice, 'None', self, True )
		self.supportChoiceEmptyLayersOnly = settings.MenuRadio().getFromMenuButtonDisplay( self.supportMaterialChoice, 'Empty Layers Only', self, False )
		self.supportChoiceEverywhere = settings.MenuRadio().getFromMenuButtonDisplay( self.supportMaterialChoice, 'Everywhere', self, False )
		self.supportChoiceExteriorOnly = settings.MenuRadio().getFromMenuButtonDisplay( self.supportMaterialChoice, 'Exterior Only', self, False )
		self.supportMinimumAngle = settings.FloatSpin().getFromValue( 40.0, 'Support Minimum Angle (degrees):', self, 80.0, 60.0 )
		self.executeTitle = 'Raft'

	def execute( self ):
		"Raft button has been clicked."
		fileNames = polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled )
		for fileName in fileNames:
			writeOutput( fileName )


class RaftSkein:
	"A class to raft a skein of extrusions."
	def __init__( self ):
		self.addLineLayerStart = True
		self.baseTemperature = None
		self.beginLoop = None
		self.boundaryLayers = []
		self.chamberTemperature = None
		self.coolingRate = None
		self.distanceFeedRate = gcodec.DistanceFeedRate()
		self.extrusionStart = True
		self.extrusionTop = 0.0
		self.feedRateMinute = 961.0
		self.heatingRate = None
		self.insetTable = {}
		self.interfaceTemperature = None
		self.isPerimeter = False
		self.isStartupEarly = False
		self.isSurroundingLoop = True
		self.layerIndex = - 1
		self.layerStarted = False
		self.layerThickness = 0.4
		self.lineIndex = 0
		self.lines = None
		self.objectFirstLayerInfillTemperature = None
		self.objectFirstLayerPerimeterTemperature = None
		self.objectNextLayersTemperature = None
		self.oldFlowRateInput = 1.0
		self.oldFlowRateOutputString = None
		self.oldLocation = None
		self.oldTemperatureOutputString = None
		self.operatingFlowRate = None
		self.operatingLayerEndLine = '(<operatingLayerEnd> </operatingLayerEnd>)'
		self.operatingJump = None
		self.orbitalFeedRatePerSecond = 2.01
		self.perimeterWidth = 0.6
		self.supportFlowRate = None
		self.supportLayers = []
		self.supportLayersTemperature = None
		self.supportedLayersTemperature = None
		self.travelFeedRatePerMinute = None

	def addBaseLayer( self ):
		"Add a base layer."
		baseLayerThickness = self.layerThickness * self.baseLayerThicknessOverLayerThickness
		zCenter = self.extrusionTop + 0.5 * baseLayerThickness
		z = zCenter + baseLayerThickness * self.repository.baseNozzleLiftOverBaseLayerThickness.value
		if len( self.baseSegments ) < 1:
			print( 'This should never happen, the base layer has a size of zero.' )
			return
		feedRateMultiplier = self.repository.baseFeedRateMultiplier.value
		self.addLayerFromSegments( feedRateMultiplier, self.repository.baseFlowRateMultiplier.value, baseLayerThickness, self.baseLayerThicknessOverLayerThickness, self.baseSegments, z )

	def addBaseSegments( self, baseExtrusionWidth, baseStep ):
		"Add the base segments."
		baseOverhang = self.repository.infillOverhangOverExtrusionWidth.value * baseExtrusionWidth
		interfaceSegmentsTableKeys = self.interfaceSegmentsTable.keys()
		interfaceSegmentsTableKeys.sort()
		baseYTableTable = {}
		for interfaceSegmentsTableKey in interfaceSegmentsTableKeys:
			interfaceSegments = self.interfaceSegmentsTable[ interfaceSegmentsTableKey ]
			for interfaceSegment in interfaceSegments:
				begin = int( round( interfaceSegment[ 0 ].point.real / baseStep ) )
				end = int( round( interfaceSegment[ 1 ].point.real / baseStep ) )
				for stepIndex in xrange( begin, end + 1 ):
					if stepIndex not in baseYTableTable:
						baseYTableTable[ stepIndex ] = {}
					baseYTableTable[ stepIndex ][ interfaceSegmentsTableKey ] = None
		baseYTableTableKeys = baseYTableTable.keys()
		baseYTableTableKeys.sort()
		self.baseSegments = []
		for baseYTableTableKey in baseYTableTableKeys:
			baseYTable = baseYTableTable[ baseYTableTableKey ]
			baseYTableKeys = baseYTable.keys()
			baseYTableKeys.sort()
			xIntersections = []
			for baseYTableKey in baseYTableKeys:
				y = baseYTableKey * self.interfaceStep
				if baseYTableKey - 1 not in baseYTableKeys:
					xIntersections.append( y - baseOverhang )
				if baseYTableKey + 1 not in baseYTableKeys:
					xIntersections.append( y + baseOverhang )
			self.baseSegments += euclidean.getSegmentsFromXIntersections( xIntersections, baseYTableTableKey * baseStep )
		for segment in self.baseSegments:
			for endpoint in segment:
				endpoint.point = complex( endpoint.point.imag, endpoint.point.real )

	def addEmptyLayerSupport( self, boundaryLayerIndex ):
		"Add support material to a layer if it is empty."
		supportLayer = SupportLayer( [] )
		self.supportLayers.append( supportLayer )
		if len( self.boundaryLayers[ boundaryLayerIndex ].loops ) > 0:
			return
		aboveXIntersectionsTable = {}
		euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsAbove( boundaryLayerIndex ), aboveXIntersectionsTable, self.interfaceStep )
		belowXIntersectionsTable = {}
		euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsBelow( boundaryLayerIndex ), belowXIntersectionsTable, self.interfaceStep )
		supportLayer.xIntersectionsTable = euclidean.getIntersectionOfXIntersectionsTables( [ aboveXIntersectionsTable, belowXIntersectionsTable ] )

	def addFlowRateLineIfDifferent( self, flowRateOutputString ):
		"Add a line of flow rate if different."
		if self.operatingFlowRate == None:
			return
		if flowRateOutputString == self.oldFlowRateOutputString:
			return
		if flowRateOutputString != None:
			self.distanceFeedRate.addLine( 'M108 S' + flowRateOutputString )
		self.oldFlowRateOutputString = flowRateOutputString

	def addFlowRateValueIfDifferent( self, flowRate ):
		"Add a flow rate value if different."
		if flowRate != None:
			self.addFlowRateLineIfDifferent( euclidean.getFourSignificantFigures( flowRate ) )

	def addInterfaceLayer( self ):
		"Add an interface layer."
		interfaceLayerThickness = self.layerThickness * self.interfaceLayerThicknessOverLayerThickness
		zCenter = self.extrusionTop + 0.5 * interfaceLayerThickness
		z = zCenter + interfaceLayerThickness * self.repository.interfaceNozzleLiftOverInterfaceLayerThickness.value
		self.interfaceIntersectionsTableKeys.sort()
		if len( self.interfaceSegments ) < 1:
			print( 'This should never happen, the interface layer has a size of zero.' )
			return
		feedRateMultiplier = self.repository.interfaceFeedRateMultiplier.value
		flowRateMultiplier = self.repository.interfaceFlowRateMultiplier.value
		self.addLayerFromSegments( feedRateMultiplier, flowRateMultiplier, interfaceLayerThickness, self.interfaceLayerThicknessOverLayerThickness, self.interfaceSegments, z )

	def addInterfaceTables( self, baseStep, interfaceExtrusionWidth ):
		"Add a interface tables."
		interfaceOverhang = self.repository.infillOverhangOverExtrusionWidth.value * interfaceExtrusionWidth
		self.interfaceSegments = []
		self.interfaceIntersectionsTableKeys = self.interfaceIntersectionsTable.keys()
		self.interfaceSegmentsTable = {}
		for yKey in self.interfaceIntersectionsTableKeys:
			self.interfaceIntersectionsTable[ yKey ].sort()
			y = yKey * self.interfaceStep
			lineSegments = euclidean.getSegmentsFromXIntersections( self.interfaceIntersectionsTable[ yKey ], y )
			xIntersectionIndexList = []
			for lineSegmentIndex in xrange( len( lineSegments ) ):
				lineSegment = lineSegments[ lineSegmentIndex ]
				endpointBegin = lineSegment[ 0 ]
				endpointEnd = lineSegment[ 1 ]
				endpointBegin.point = complex( baseStep * math.floor( endpointBegin.point.real / baseStep ) - interfaceOverhang, y )
				endpointEnd.point = complex( baseStep * math.ceil( endpointEnd.point.real / baseStep ) + interfaceOverhang, y )
				if endpointEnd.point.real > endpointBegin.point.real:
					euclidean.addXIntersectionIndexesFromSegment( lineSegmentIndex, lineSegment, xIntersectionIndexList )
			xIntersections = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
			joinedSegments = euclidean.getSegmentsFromXIntersections( xIntersections, y )
			if len( joinedSegments ) > 0:
				self.interfaceSegmentsTable[ yKey ] = joinedSegments
			self.interfaceSegments += joinedSegments

	def addLayerFromSegments( self, feedRateMultiplier, flowRateMultiplier, layerLayerThickness, layerThicknessRatio, segments, z ):
		"Add a layer from segments and raise the extrusion top."
		layerThicknessRatioSquared = layerThicknessRatio * layerThicknessRatio
		feedRateMinute = self.feedRateMinute * feedRateMultiplier / layerThicknessRatioSquared
		endpoints = euclidean.getEndpointsFromSegments( segments )
		if len( endpoints ) < 1:
			return
		aroundPixelTable = {}
		aroundWidth = 0.25 * layerLayerThickness
		paths = euclidean.getPathsFromEndpoints( endpoints, layerLayerThickness, aroundPixelTable, aroundWidth )
		paths = euclidean.getConnectedPaths( paths, aroundPixelTable, aroundWidth ) # this is probably unnecesary
		self.addFlowRateValueIfDifferent( flowRateMultiplier * self.oldFlowRateInput )
		self.addLayerLine( z )
		self.addFlowRateValueIfDifferent( self.oldFlowRateInput )
		for path in paths:
			simplifiedPath = euclidean.getSimplifiedPath( path, layerLayerThickness )
			self.distanceFeedRate.addGcodeFromFeedRateThreadZ( feedRateMinute, simplifiedPath, z )
		self.extrusionTop += layerLayerThickness

	def addLayerLine( self, z ):
		"Add the layer gcode line and close the last layer gcode block."
		if self.layerStarted:
			self.distanceFeedRate.addLine( '(</layer>)' )
		self.distanceFeedRate.addLine( '(<layer> ' + self.distanceFeedRate.getRounded( z ) + ' )' ) # Indicate that a new layer is starting.
		if self.beginLoop != None:
			zBegin = self.extrusionTop + self.layerThickness
			intercircle.addOrbitsIfLarge( self.distanceFeedRate, self.beginLoop, self.orbitalFeedRatePerSecond, self.temperatureChangeTimeBeforeRaft, zBegin )
			self.beginLoop = None
		self.layerStarted = True

	def addOperatingOrbits( self, boundaryLoops, pointComplex, temperatureChangeTime, z ):
		"Add the orbits before the operating layers."
		if len( boundaryLoops ) < 1:
			return
		insetBoundaryLoops = intercircle.getInsetLoopsFromLoops( self.perimeterWidth, boundaryLoops )
		if len( insetBoundaryLoops ) < 1:
			insetBoundaryLoops = boundaryLoops
		largestLoop = euclidean.getLargestLoop( insetBoundaryLoops )
		if pointComplex != None:
			largestLoop = euclidean.getLoopStartingNearest( self.perimeterWidth, pointComplex, largestLoop )
		intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureChangeTime, z )

	def addRaft( self ):
		"Add the raft."
		self.extrusionTop = self.repository.bottomAltitude.value
		if len( self.boundaryLayers ) < 0:
			print( 'this should never happen, there are no boundary layers in addRaft' )
			return
		self.baseLayerThicknessOverLayerThickness = self.repository.baseLayerThicknessOverLayerThickness.value
		baseExtrusionWidth = self.perimeterWidth * self.baseLayerThicknessOverLayerThickness
		baseStep = baseExtrusionWidth / self.repository.baseInfillDensity.value
		self.interfaceLayerThicknessOverLayerThickness = self.repository.interfaceLayerThicknessOverLayerThickness.value
		interfaceExtrusionWidth = self.perimeterWidth * self.interfaceLayerThicknessOverLayerThickness
		self.interfaceStep = interfaceExtrusionWidth / self.repository.interfaceInfillDensity.value
		self.setCornersZ()
		self.cornerLowComplex = self.cornerLow.dropAxis( 2 )
		originalExtent = self.cornerHighComplex - self.cornerLowComplex
		self.raftOutsetRadius = self.repository.raftMargin.value + self.repository.raftAdditionalMarginOverLengthPercent.value * 0.01 * max( originalExtent.real, originalExtent.imag )
		self.setBoundaryLayers()
		outsetSeparateLoops = intercircle.getInsetSeparateLoopsFromLoops( - self.raftOutsetRadius, self.boundaryLayers[ 0 ].loops, 0.8 )
		self.interfaceIntersectionsTable = {}
		euclidean.addXIntersectionsFromLoopsForTable( outsetSeparateLoops, self.interfaceIntersectionsTable, self.interfaceStep )
		if len( self.supportLayers ) > 0:
			supportIntersectionsTable = self.supportLayers[ 0 ].xIntersectionsTable
			euclidean.joinXIntersectionsTables( supportIntersectionsTable, self.interfaceIntersectionsTable )
		self.addInterfaceTables( baseStep, interfaceExtrusionWidth )
		self.baseIntersectionsTable = {}
		complexRadius = complex( self.raftOutsetRadius, self.raftOutsetRadius )
		self.complexHigh = complexRadius + self.cornerHighComplex
		self.complexLow = self.cornerLowComplex - complexRadius
		self.beginLoop = euclidean.getSquareLoop( self.cornerLowComplex, self.cornerHighComplex )
		if not intercircle.orbitsAreLarge( self.beginLoop, self.temperatureChangeTimeBeforeRaft ):
			self.beginLoop = None
		if self.repository.baseLayers.value > 0:
			self.addTemperatureLineIfDifferent( self.baseTemperature )
			self.addBaseSegments( baseExtrusionWidth, baseStep )
		for baseLayerIndex in xrange( self.repository.baseLayers.value ):
			self.addBaseLayer()
		if self.repository.interfaceLayers.value > 0:
			self.addTemperatureLineIfDifferent( self.interfaceTemperature )
		for interfaceLayerIndex in xrange( self.repository.interfaceLayers.value ):
			self.addInterfaceLayer()
		self.operatingJump = self.extrusionTop - self.cornerLow.z + 0.5 * self.layerThickness + self.layerThickness * self.repository.operatingNozzleLiftOverLayerThickness.value
		for boundaryLayer in self.boundaryLayers:
			if self.operatingJump != None:
				boundaryLayer.z += self.operatingJump
		if self.repository.baseLayers.value > 0 or self.repository.interfaceLayers.value > 0:
			boundaryZ = self.boundaryLayers[ 0 ].z
			if self.layerStarted:
				self.distanceFeedRate.addLine( '(</layer>)' )
				self.layerStarted = False
			self.distanceFeedRate.addLine( '(<raftLayerEnd> </raftLayerEnd>)' )
			self.addLayerLine( boundaryZ )
			temperatureChangeTimeBeforeFirstLayer = self.getTemperatureChangeTime( self.objectFirstLayerPerimeterTemperature )
			self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
			largestOutsetLoop = intercircle.getLargestInsetLoopFromLoop( euclidean.getLargestLoop( outsetSeparateLoops ), - self.raftOutsetRadius )
			intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestOutsetLoop, self.orbitalFeedRatePerSecond, temperatureChangeTimeBeforeFirstLayer, boundaryZ )
			self.addLineLayerStart = False

	def addSegmentTablesToSupportLayers( self ):
		"Add segment tables to the support layers."
		for supportLayer in self.supportLayers:
			supportLayer.supportSegmentTable = {}
			xIntersectionsTable = supportLayer.xIntersectionsTable
			for xIntersectionsTableKey in xIntersectionsTable:
				y = xIntersectionsTableKey * self.interfaceStep
				supportLayer.supportSegmentTable[ xIntersectionsTableKey ] = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], y )

	def addSupportSegmentTable( self, layerIndex ):
		"Add support segments from the boundary layers."
		aboveLayer = self.boundaryLayers[ layerIndex + 1 ]
		aboveLoops = aboveLayer.loops
		supportLayer = self.supportLayers[ layerIndex ]
		if len( aboveLoops ) < 1:
			return
		boundaryLayer = self.boundaryLayers[ layerIndex ]
		rise = aboveLayer.z - boundaryLayer.z
		outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops( - self.minimumSupportRatio * rise, boundaryLayer.loops )
		numberOfSubSteps = 4
		subStepSize = self.interfaceStep / float( numberOfSubSteps )
		aboveIntersectionsTable = {}
		euclidean.addXIntersectionsFromLoopsForTable( aboveLoops, aboveIntersectionsTable, subStepSize )
		outsetIntersectionsTable = {}
		euclidean.addXIntersectionsFromLoopsForTable( outsetSupportLoops, outsetIntersectionsTable, subStepSize )
		euclidean.subtractXIntersectionsTable( aboveIntersectionsTable, outsetIntersectionsTable )
		for aboveIntersectionsTableKey in aboveIntersectionsTable.keys():
			supportIntersectionsTableKey = int( round( float( aboveIntersectionsTableKey ) / numberOfSubSteps ) )
			xIntersectionIndexList = []
			if supportIntersectionsTableKey in supportLayer.xIntersectionsTable:
				euclidean.addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] )
			euclidean.addXIntersectionIndexesFromXIntersections( 1, xIntersectionIndexList, aboveIntersectionsTable[ aboveIntersectionsTableKey ] )
			supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )

	def addSupportLayerTemperature( self, endpoints, z ):
		"Add support layer and temperature before the object layer."
		self.distanceFeedRate.addLinesSetAbsoluteDistanceMode( self.supportStartLines )
		self.addTemperatureOrbits( endpoints, self.supportedLayersTemperature, z )
		aroundPixelTable = {}
		layerFillInset = 0.9 * self.perimeterWidth
		aroundWidth = 0.12 * layerFillInset
		boundaryLoops = self.boundaryLayers[ self.layerIndex ].loops
		halfSupportOutset = 0.5 * self.supportOutset
		aroundBoundaryLoops = intercircle.getAroundsFromLoops( boundaryLoops, halfSupportOutset )
		for aroundBoundaryLoop in aroundBoundaryLoops:
			euclidean.addLoopToPixelTable( aroundBoundaryLoop, aroundPixelTable, aroundWidth )
		paths = euclidean.getPathsFromEndpoints( endpoints, layerFillInset, aroundPixelTable, aroundWidth )
		self.addFlowRateValueIfDifferent( self.supportFlowRate )
		for path in paths:
			self.distanceFeedRate.addGcodeFromFeedRateThreadZ( self.feedRateMinute, path, z )
		self.addFlowRateLineIfDifferent( str( self.oldFlowRateInput ) )
		self.addTemperatureOrbits( endpoints, self.supportLayersTemperature, z )
		self.distanceFeedRate.addLinesSetAbsoluteDistanceMode( self.supportEndLines )

	def addTemperatureLineIfDifferent( self, temperature ):
		"Add a line of temperature if different."
		if temperature == None:
			return
		temperatureOutputString = euclidean.getRoundedToThreePlaces( temperature )
		if temperatureOutputString == self.oldTemperatureOutputString:
			return
		if temperatureOutputString != None:
			self.distanceFeedRate.addLine( 'M104 S' + temperatureOutputString ) # Set temperature.
		self.oldTemperatureOutputString = temperatureOutputString

	def addTemperatureOrbits( self, endpoints, temperature, z ):
		"Add the temperature and orbits around the support layer."
		if self.layerIndex < 0:
			return
		boundaryLoops = self.boundaryLayers[ self.layerIndex ].loops
		temperatureTimeChange = self.getTemperatureChangeTime( temperature )
		self.addTemperatureLineIfDifferent( temperature )
		if len( boundaryLoops ) < 1:
			layerCornerHigh = complex( - 999999999.0, - 999999999.0 )
			layerCornerLow = complex( 999999999.0, 999999999.0 )
			for endpoint in endpoints:
				layerCornerHigh = euclidean.getMaximum( layerCornerHigh, endpoint.point )
				layerCornerLow = euclidean.getMinimum( layerCornerLow, endpoint.point )
			squareLoop = euclidean.getSquareLoop( layerCornerLow, layerCornerHigh )
			intercircle.addOrbitsIfLarge( self.distanceFeedRate, squareLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )
			return
		perimeterInset = 0.4 * self.perimeterWidth
		insetBoundaryLoops = intercircle.getInsetLoopsFromLoops( perimeterInset, boundaryLoops )
		if len( insetBoundaryLoops ) < 1:
			insetBoundaryLoops = boundaryLoops
		largestLoop = euclidean.getLargestLoop( insetBoundaryLoops )
		intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z )

	def addToFillXIntersectionIndexTables( self, supportLayer ):
		"Add fill segments from the boundary layers."
		supportLoops = supportLayer.supportLoops
		supportLayer.fillXIntersectionsTable = {}
		if len( supportLoops ) < 1:
			return
		euclidean.addXIntersectionsFromLoopsForTable( supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep )

	def extendXIntersections( self, loops, radius, xIntersectionsTable ):
		"Extend the support segments."
		xIntersectionsTableKeys = xIntersectionsTable.keys()
		for xIntersectionsTableKey in xIntersectionsTableKeys:
			lineSegments = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], xIntersectionsTableKey )
			xIntersectionIndexList = []
			loopXIntersections = []
			euclidean.addXIntersectionsFromLoops( loops, loopXIntersections, xIntersectionsTableKey )
			for lineSegmentIndex in xrange( len( lineSegments ) ):
				lineSegment = lineSegments[ lineSegmentIndex ]
				extendedLineSegment = getExtendedLineSegment( radius, lineSegment, loopXIntersections )
				if extendedLineSegment != None:
					euclidean.addXIntersectionIndexesFromSegment( lineSegmentIndex, extendedLineSegment, xIntersectionIndexList )
			xIntersections = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList )
			if len( xIntersections ) > 0:
				xIntersectionsTable[ xIntersectionsTableKey ] = xIntersections
			else:
				del xIntersectionsTable[ xIntersectionsTableKey ]

	def getCraftedGcode( self, gcodeText, repository ):
		"Parse gcode text and store the raft gcode."
		self.repository = repository
		self.minimumSupportRatio = math.tan( math.radians( repository.supportMinimumAngle.value ) )
		self.supportEndText = settings.getFileInAlterationsOrGivenDirectory( os.path.dirname( __file__ ), 'Support_End.gcode' )
		self.supportEndLines = gcodec.getTextLines( self.supportEndText )
		self.supportStartText = settings.getFileInAlterationsOrGivenDirectory( os.path.dirname( __file__ ), 'Support_Start.gcode' )
		self.supportStartLines = gcodec.getTextLines( self.supportStartText )
		self.lines = gcodec.getTextLines( gcodeText )
		self.parseInitialization()
		self.temperatureChangeTimeBeforeRaft = self.getTemperatureChangeTime( max( max( self.baseTemperature, self.interfaceTemperature ), self.objectFirstLayerPerimeterTemperature ) )
		if repository.addRaftElevateNozzleOrbitSetAltitude.value:
			self.addRaft()
		self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
		for line in self.lines[ self.lineIndex : ]:
			self.parseLine( line )
		return self.distanceFeedRate.output.getvalue()

	def getElevatedBoundaryLine( self, splitLine ):
		"Get elevated boundary gcode line."
		location = gcodec.getLocationFromSplitLine( None, splitLine )
		if self.operatingJump != None:
			location.z += self.operatingJump
		return self.distanceFeedRate.getBoundaryLine( location )

	def getInsetLoops( self, boundaryLayerIndex ):
		"Inset the support loops if they are not already inset."
		if boundaryLayerIndex not in self.insetTable:
			self.insetTable[ boundaryLayerIndex ] = intercircle.getInsetSeparateLoopsFromLoops( self.quarterPerimeterWidth, self.boundaryLayers[ boundaryLayerIndex ].loops )
		return self.insetTable[ boundaryLayerIndex ]

	def getInsetLoopsAbove( self, boundaryLayerIndex ):
		"Get the inset loops above the boundary layer index."
		for aboveLayerIndex in xrange( boundaryLayerIndex + 1, len( self.boundaryLayers ) ):
			if len( self.boundaryLayers[ aboveLayerIndex ].loops ) > 0:
				return self.getInsetLoops( aboveLayerIndex )
		return []

	def getInsetLoopsBelow( self, boundaryLayerIndex ):
		"Get the inset loops below the boundary layer index."
		for belowLayerIndex in xrange( boundaryLayerIndex - 1, - 1, - 1 ):
			if len( self.boundaryLayers[ belowLayerIndex ].loops ) > 0:
				return self.getInsetLoops( belowLayerIndex )
		return []

	def getRaftedLine( self, splitLine ):
		"Get elevated gcode line with operating feed rate."
		location = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
		self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine )
		feedRateMinuteMultiplied = self.feedRateMinute
		self.oldLocation = location
		z = location.z
		if self.operatingJump != None:
			z += self.operatingJump
		if self.layerIndex == 0:
			self.addFlowRateValueIfDifferent( self.repository.objectFirstLayerFlowRateMultiplier.value * self.oldFlowRateInput )
			if self.isPerimeter:
				feedRateMinuteMultiplied *= self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value
				self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature )
			else:
				feedRateMinuteMultiplied *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value
				self.addTemperatureLineIfDifferent( self.objectFirstLayerInfillTemperature )
		else:
			self.addFlowRateValueIfDifferent( self.oldFlowRateInput )
			self.addTemperatureLineIfDifferent( self.objectNextLayersTemperature )
		return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRateMinuteMultiplied, location.dropAxis( 2 ), z )

	def getStepsUntilEnd( self, begin, end, stepSize ):
		"Get steps from the beginning until the end."
		step = begin
		steps = []
		while step < end:
			steps.append( step )
			step += stepSize
		return steps

	def getSupportEndpoints( self ):
		"Get the support layer segments."
		if len( self.supportLayers ) <= self.layerIndex:
			return []
		supportSegmentTable = self.supportLayers[ self.layerIndex ].supportSegmentTable
		endpoints = euclidean.getEndpointsFromSegmentTable( supportSegmentTable )
		if self.layerIndex % 2 == 0 or not self.repository.supportCrossHatch.value:
			return endpoints
		crossEndpoints = []
		crossHatchPointLineTable = {}
		for endpoint in endpoints:
			segmentBeginXStep = int( math.ceil( min( endpoint.point.real, endpoint.otherEndpoint.point.real ) / self.interfaceStep ) )
			segmentEndXStep = int( math.ceil( max( endpoint.point.real, endpoint.otherEndpoint.point.real ) / self.interfaceStep ) )
			for step in xrange( segmentBeginXStep, segmentEndXStep ):
				x = self.interfaceStep * step
				crossHatchPointLine = getCrossHatchPointLine( crossHatchPointLineTable, x )
				crossHatchPointLine[ int( round( endpoint.point.imag / self.interfaceStep ) ) ] = True
		crossHatchPointLineTableKeys = crossHatchPointLineTable.keys()
		crossHatchPointLineTableKeys.sort()
		for crossHatchPointLineTableKey in crossHatchPointLineTableKeys:
			crossHatchPointLine = crossHatchPointLineTable[ crossHatchPointLineTableKey ]
			crossHatchPointLineKeys = crossHatchPointLine.keys()
			for crossHatchPointLineKey in crossHatchPointLineKeys:
				if not crossHatchPointLine.has_key( crossHatchPointLineKey - 1 ) and not crossHatchPointLine.has_key( crossHatchPointLineKey + 1 ):
					del crossHatchPointLine[ crossHatchPointLineKey ]
			crossHatchPointLineKeys = crossHatchPointLine.keys()
			crossHatchPointLineKeys.sort()
			yIntersections = []
			for crossHatchPointLineKey in crossHatchPointLineKeys:
				if crossHatchPointLine.has_key( crossHatchPointLineKey - 1 ) != crossHatchPointLine.has_key( crossHatchPointLineKey + 1 ):
					yIntersection = self.interfaceStep * crossHatchPointLineKey
					yIntersections.append( yIntersection )
			crossEndpoints += getEndpointsFromYIntersections( crossHatchPointLineTableKey, yIntersections )
		return crossEndpoints

	def getTemperatureChangeTime( self, temperature ):
		"Get the temperature change time."
		if temperature == None:
			return 0.0
		oldTemperature = self.chamberTemperature
		if self.oldTemperatureOutputString != None:
			oldTemperature = float( self.oldTemperatureOutputString )
		if temperature == oldTemperature:
			return 0.0
		if temperature > oldTemperature:
			return ( temperature - oldTemperature ) / self.heatingRate
		return ( oldTemperature - temperature ) / abs( self.coolingRate )

	def parseInitialization( self ):
		"Parse gcode initialization and store the parameters."
		for self.lineIndex in xrange( len( self.lines ) ):
			line = self.lines[ self.lineIndex ]
			splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
			firstWord = gcodec.getFirstWord( splitLine )
			self.distanceFeedRate.parseSplitLine( firstWord, splitLine )
			if firstWord == '(<coolingRate>':
				self.coolingRate = float( splitLine[ 1 ] )
			elif firstWord == '(</extruderInitialization>)':
				self.distanceFeedRate.addLine( '(<procedureDone> raft </procedureDone>)' )
			elif firstWord == '(<heatingRate>':
				self.heatingRate = float( splitLine[ 1 ] )
			elif firstWord == '(<layer>':
				return
			elif firstWord == '(<layerThickness>':
				self.layerThickness = float( splitLine[ 1 ] )
			elif firstWord == '(<orbitalFeedRatePerSecond>':
				self.orbitalFeedRatePerSecond = float( splitLine[ 1 ] )
			elif firstWord == '(<operatingFeedRatePerSecond>':
				self.feedRateMinute = 60.0 * float( splitLine[ 1 ] )
			elif firstWord == '(<operatingFlowRate>':
				self.oldFlowRateInput = float( splitLine[ 1 ] )
				self.operatingFlowRate = self.oldFlowRateInput
				self.supportFlowRate = self.operatingFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value
			elif firstWord == '(<perimeterWidth>':
				self.perimeterWidth = float( splitLine[ 1 ] )
				self.quarterPerimeterWidth = 0.25 * self.perimeterWidth
				self.supportOutset = self.perimeterWidth + self.perimeterWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value
			elif firstWord == '(<chamberTemperature>':
				self.chamberTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<baseTemperature>':
				self.baseTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<interfaceTemperature>':
				self.interfaceTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<objectFirstLayerInfillTemperature>':
				self.objectFirstLayerInfillTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<objectFirstLayerPerimeterTemperature>':
				self.objectFirstLayerPerimeterTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<objectNextLayersTemperature>':
				self.objectNextLayersTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<supportLayersTemperature>':
				self.supportLayersTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<supportedLayersTemperature>':
				self.supportedLayersTemperature = float( splitLine[ 1 ] )
			elif firstWord == '(<travelFeedRatePerSecond>':
				self.travelFeedRatePerMinute = 60.0 * float( splitLine[ 1 ] )
			self.distanceFeedRate.addLine( line )

	def parseLine( self, line ):
		"Parse a gcode line and add it to the raft skein."
		splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
		if len( splitLine ) < 1:
			return
		firstWord = splitLine[ 0 ]
		if firstWord == 'G1':
			if self.extrusionStart:
				line = self.getRaftedLine( splitLine )
		elif firstWord == 'M101':
			if self.isStartupEarly:
				self.isStartupEarly = False
				return
		elif firstWord == 'M103':
			self.isPerimeter = False
		elif firstWord == 'M108':
			flowRateOutputString = splitLine[ 1 ][ 1 : ]
			self.addFlowRateLineIfDifferent( flowRateOutputString )
			self.oldFlowRateInput = float( flowRateOutputString )
		elif firstWord == '(<boundaryPoint>':
			line = self.getElevatedBoundaryLine( splitLine )
		elif firstWord == '(</extrusion>)':
			self.extrusionStart = False
			self.distanceFeedRate.addLine( self.operatingLayerEndLine )
		elif firstWord == '(<layer>':
			self.layerIndex += 1
			boundaryLayer = None
			layerZ = self.extrusionTop + float( splitLine[ 1 ] )
			if len( self.boundaryLayers ) > 0:
				boundaryLayer = self.boundaryLayers[ self.layerIndex ]
				layerZ = boundaryLayer.z
			if self.operatingJump != None:
				line = '(<layer> ' + self.distanceFeedRate.getRounded( layerZ ) + ' )'
			if self.layerStarted and self.addLineLayerStart:
				self.distanceFeedRate.addLine( '(</layer>)' )
			self.layerStarted = False
			if self.layerIndex > len( self.supportLayers ) + 1:
				self.distanceFeedRate.addLine( self.operatingLayerEndLine )
				self.operatingLayerEndLine = ''
			if self.addLineLayerStart:
				self.distanceFeedRate.addLine( line )
			self.addLineLayerStart = True
			line = ''
			endpoints = self.getSupportEndpoints()
			if self.layerIndex == 1:
				if len( endpoints ) < 1:
					temperatureChangeTimeBeforeNextLayers = self.getTemperatureChangeTime( self.objectNextLayersTemperature )
					self.addTemperatureLineIfDifferent( self.objectNextLayersTemperature )
					if self.repository.addRaftElevateNozzleOrbitSetAltitude.value and len( boundaryLayer.loops ) > 0:
						self.addOperatingOrbits( boundaryLayer.loops, euclidean.getXYComplexFromVector3( self.oldLocation ), temperatureChangeTimeBeforeNextLayers, layerZ )
			if len( endpoints ) > 0:
				self.addSupportLayerTemperature( endpoints, layerZ )
		elif firstWord == '(<perimeter>':
			self.isPerimeter = True
		self.distanceFeedRate.addLine( line )

	def setBoundaryLayers( self ):
		"Set the boundary layers."
		if self.repository.supportChoiceNone.value:
			return
		if len( self.boundaryLayers ) < 2:
			return
		if self.repository.supportChoiceEmptyLayersOnly.value:
			supportLayer = SupportLayer( [] )
			self.supportLayers.append( supportLayer )
			for boundaryLayerIndex in xrange( 1, len( self.boundaryLayers ) - 1 ):
				self.addEmptyLayerSupport( boundaryLayerIndex )
			self.truncateSupportSegmentTables()
			self.addSegmentTablesToSupportLayers()
			return
		for boundaryLayer in self.boundaryLayers:
			supportLoops = intercircle.getInsetSeparateLoopsFromLoops( - self.supportOutset, boundaryLayer.loops )
			supportLayer = SupportLayer( supportLoops )
			self.supportLayers.append( supportLayer )
		for supportLayerIndex in xrange( len( self.supportLayers ) - 1 ):
			self.addSupportSegmentTable( supportLayerIndex )
		self.truncateSupportSegmentTables()
		for supportLayerIndex in xrange( len( self.supportLayers ) - 1 ):
			self.extendXIntersections( self.boundaryLayers[ supportLayerIndex ].loops, self.supportOutset, self.supportLayers[ supportLayerIndex ].xIntersectionsTable )
		for supportLayer in self.supportLayers:
			self.addToFillXIntersectionIndexTables( supportLayer )
		if self.repository.supportChoiceExteriorOnly.value:
			for supportLayerIndex in xrange( 1, len( self.supportLayers ) ):
				self.subtractJoinedFill( supportLayerIndex )
		for supportLayer in self.supportLayers:
			euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable )
		for supportLayerIndex in xrange( len( self.supportLayers ) - 2, - 1, - 1 ):
			xIntersectionsTable = self.supportLayers[ supportLayerIndex ].xIntersectionsTable
			aboveXIntersectionsTable = self.supportLayers[ supportLayerIndex + 1 ].xIntersectionsTable
			euclidean.joinXIntersectionsTables( aboveXIntersectionsTable, xIntersectionsTable )
		for supportLayerIndex in xrange( len( self.supportLayers ) ):
			supportLayer = self.supportLayers[ supportLayerIndex ]
			self.extendXIntersections( supportLayer.supportLoops, self.raftOutsetRadius, supportLayer.xIntersectionsTable )
		for supportLayer in self.supportLayers:
			euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable )
		self.addSegmentTablesToSupportLayers()

	def setCornersZ( self ):
		"Set maximum and minimum corners and z."
		boundaryLoop = None
		boundaryLayer = None
		layerIndex = - 1
		self.cornerHighComplex = complex( - 999999999.0, - 999999999.0 )
		self.cornerLow = Vector3( 999999999.0, 999999999.0, 999999999.0 )
		self.firstLayerLoops = []
		for line in self.lines[ self.lineIndex : ]:
			splitLine = gcodec.getSplitLineBeforeBracketSemicolon( line )
			firstWord = gcodec.getFirstWord( splitLine )
			if firstWord == '(</boundaryPerimeter>)':
				boundaryLoop = None
			elif firstWord == '(<boundaryPoint>':
				location = gcodec.getLocationFromSplitLine( None, splitLine )
				if boundaryLoop == None:
					boundaryLoop = []
					boundaryLayer.loops.append( boundaryLoop )
				boundaryLoop.append( location.dropAxis( 2 ) )
				self.cornerHighComplex = euclidean.getMaximum( self.cornerHighComplex, location.dropAxis( 2 ) )
				self.cornerLow = euclidean.getPointMinimum( self.cornerLow, location )
			elif firstWord == '(<layer>':
				z = float( splitLine[ 1 ] )
				boundaryLayer = euclidean.LoopLayer( z )
				self.boundaryLayers.append( boundaryLayer )
			elif firstWord == '(<layer>':
				layerIndex += 1
				if self.repository.supportChoiceNone.value:
					if layerIndex > 1:
						return

	def subtractJoinedFill( self, supportLayerIndex ):
		"Join the fill then subtract it from the support layer table."
		supportLayer = self.supportLayers[ supportLayerIndex ]
		fillXIntersectionsTable = supportLayer.fillXIntersectionsTable
		belowFillXIntersectionsTable = self.supportLayers[ supportLayerIndex - 1 ].fillXIntersectionsTable
		euclidean.joinXIntersectionsTables( belowFillXIntersectionsTable, supportLayer.fillXIntersectionsTable )
		euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable )

	def truncateSupportSegmentTables( self ):
		"Truncate the support segments after the last support segment which contains elements."
		for supportLayerIndex in xrange( len( self.supportLayers ) - 1, - 1, - 1 ):
			if len( self.supportLayers[ supportLayerIndex ].xIntersectionsTable ) > 0:
				self.supportLayers = self.supportLayers[ : supportLayerIndex + 1 ]
				return
		self.supportLayers = []


class SupportLayer:
	"Support loops with segment tables."
	def __init__( self, supportLoops ):
		self.supportLoops = supportLoops
		self.supportSegmentTable = {}
		self.xIntersectionsTable = {}

	def __repr__( self ):
		"Get the string representation of this loop layer."
		return '%s' % ( self.supportLoops )


def main():
	"Display the raft dialog."
	if len( sys.argv ) > 1:
		writeOutput( ' '.join( sys.argv[ 1 : ] ) )
	else:
		settings.startMainLoopFromConstructor( getNewRepository() )

if __name__ == "__main__":
	main()