1<html>
2<head>
3<div height="0" hidden="true">
4
5Skia UnitTests: --match PathOpsOp$ --resourcePath resources\ SK_DEBUG
6<div id="android1">
7seg=1 {{{-5, 0}, {1075, 0}}}
8seg=2 {{{1075, 0}, {1075, 242}}}
9seg=3 {{{1075, 242}, {-5, 242}}}
10seg=4 {{{-5, 242}, {-5, 0}}}
11op sect
12seg=5 {{{0, 0}, {1080, 0}}}
13seg=6 {{{1080, 0}, {1080, 242}}}
14seg=7 {{{1080, 242}, {0, 242}}}
15seg=8 {{{0, 242}, {0, 0}}}
16debugShowLineIntersection wtTs[0]=0 {{{1075,0}, {1075,242}}} {{1075,0}} wnTs[0]=1 {{{-5,0}, {1075,0}}}
17debugShowLineIntersection wtTs[0]=1 {{{-5,242}, {-5,0}}} {{-5,0}} wnTs[0]=0 {{{-5,0}, {1075,0}}}
18debugShowLineIntersection wtTs[0]=0 {{{1075,242}, {-5,242}}} {{1075,242}} wnTs[0]=1 {{{1075,0}, {1075,242}}}
19debugShowLineIntersection wtTs[0]=0 {{{-5,242}, {-5,0}}} {{-5,242}} wnTs[0]=1 {{{1075,242}, {-5,242}}}
20debugShowLineIntersection wtTs[0]=0 {{{0,0}, {1080,0}}} {{0,0}} wtTs[1]=0.99537037 {{1075,0}} wnTs[0]=0.00462963 {{{-5,0}, {1075,0}}} wnTs[1]=1
21SkOpSegment::addT insert t=0.00462962963 segID=1 spanID=17
22SkOpSegment::addT insert t=0.99537037 segID=5 spanID=18
23debugShowLineIntersection wtTs[0]=1 {{{0,242}, {0,0}}} {{0,0}} wnTs[0]=0.00462963 {{{-5,0}, {1075,0}}}
24debugShowLineIntersection wtTs[0]=0.99537037 {{{0,0}, {1080,0}}} {{1075,0}} wnTs[0]=0 {{{1075,0}, {1075,242}}}
25debugShowLineIntersection wtTs[0]=0.00462962963 {{{1080,242}, {0,242}}} {{1075,242}} wnTs[0]=1 {{{1075,0}, {1075,242}}}
26SkOpSegment::addT insert t=0.00462962963 segID=7 spanID=19
27debugShowLineIntersection wtTs[0]=0.00462962963 {{{1080,242}, {0,242}}} {{1075,242}} wtTs[1]=1 {{0,242}} wnTs[0]=0 {{{1075,242}, {-5,242}}} wnTs[1]=0.99537037
28SkOpSegment::addT insert t=0.99537037 segID=3 spanID=20
29debugShowLineIntersection wtTs[0]=0 {{{0,242}, {0,0}}} {{0,242}} wnTs[0]=0.99537 {{{1075,242}, {-5,242}}}
30debugShowLineIntersection wtTs[0]=0 {{{1080,0}, {1080,242}}} {{1080,0}} wnTs[0]=1 {{{0,0}, {1080,0}}}
31debugShowLineIntersection wtTs[0]=1 {{{0,242}, {0,0}}} {{0,0}} wnTs[0]=0 {{{0,0}, {1080,0}}}
32debugShowLineIntersection wtTs[0]=0 {{{1080,242}, {0,242}}} {{1080,242}} wnTs[0]=1 {{{1080,0}, {1080,242}}}
33debugShowLineIntersection wtTs[0]=0 {{{0,242}, {0,0}}} {{0,242}} wnTs[0]=1 {{{1080,242}, {0,242}}}
34------------------x--x---------------- addExpanded
3500:  seg/base=3/5 seg/base=7/19 MarkCoinStart
3601:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
3702:  seg/base=1/17 seg/base=5/9 MarkCoinStart
3803:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
39SkOpSegment::debugShowActiveSpans id=1 (-5,0 -8.8817842e-16,0) t=0 tEnd=0.00462962963 windSum=? windValue=1
40SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? windValue=1
41SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
42SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=? windValue=1
43SkOpSegment::debugShowActiveSpans id=3 (2.22044605e-14,242 -5,242) t=0.99537037 tEnd=1 windSum=? windValue=1
44SkOpSegment::debugShowActiveSpans id=4 (-5,242 -5,0) t=0 tEnd=1 windSum=? windValue=1
45SkOpSegment::debugShowActiveSpans id=5 (0,0 1075,0) t=0 tEnd=0.99537037 windSum=? windValue=1
46SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
47SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
48SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
49SkOpSegment::debugShowActiveSpans id=7 (1075,242 0,242) t=0.00462962963 tEnd=1 windSum=? windValue=1
50SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=? windValue=1
51------------------x--x---------------- move_multiples
5200:  seg/base=3/5 seg/base=7/19 MarkCoinStart
5301:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
5402:  seg/base=1/17 seg/base=5/9 MarkCoinStart
5503:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
56------------------x--x---------------- move_nearby
5700:  seg/base=3/5 seg/base=7/19 MarkCoinStart
5801:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
5902:  seg/base=1/17 seg/base=5/9 MarkCoinStart
6003:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
61------------------x--x---------------- correctEnds
6200:  seg/base=3/5 seg/base=7/19 MarkCoinStart
6301:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
6402:  seg/base=1/17 seg/base=5/9 MarkCoinStart
6503:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
66------------------x--x---------------- addEndMovedSpans
6700:  seg/base=3/5 seg/base=7/19 MarkCoinStart
6801:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
6902:  seg/base=1/17 seg/base=5/9 MarkCoinStart
7003:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
71------------------x--x---------------- expand
7200:  seg/base=3/5 seg/base=7/19 MarkCoinStart
7301:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
7402:  seg/base=1/17 seg/base=5/9 MarkCoinStart
7503:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
76------------------x--x---------------- addExpanded
7700:  seg/base=3/5 seg/base=7/19 MarkCoinStart
7801:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
7902:  seg/base=1/17 seg/base=5/9 MarkCoinStart
8003:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
81------------------x--x---------------- mark
8200:  seg/base=3/5 seg/base=7/19 MarkCoinStart
8301:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
8402:  seg/base=1/17 seg/base=5/9 MarkCoinStart
8503:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
86-------------------------------------- missing_coincidence
87-------------------------------------- expand
88-------------------------------------- expand
89-------------------------------------- apply
90SkOpSegment::markDone id=7 (1080,242 0,242) t=0.00462962963 [19] (1075,242) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
91SkOpSegment::markDone id=5 (0,0 1080,0) t=0 [9] (0,0) tEnd=0.99537037 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
92-------------------------------------- findOverlaps
93SkOpSegment::debugShowActiveSpans id=1 (-5,0 -8.8817842e-16,0) t=0 tEnd=0.00462962963 windSum=? windValue=1
94SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? oppSum=? windValue=1 oppValue=1
95SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
96SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=? oppSum=? windValue=1 oppValue=1
97SkOpSegment::debugShowActiveSpans id=3 (2.22044605e-14,242 -5,242) t=0.99537037 tEnd=1 windSum=? windValue=1
98SkOpSegment::debugShowActiveSpans id=4 (-5,242 -5,0) t=0 tEnd=1 windSum=? windValue=1
99SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
100SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
101SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
102SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=? windValue=1
103-------------------------------------- calc_angles
104SkOpSegment::sortAngles [1] tStart=0.00462962963 [17]
105SkOpAngle::after [1/1] 15/15 tStart=0.00462962963 tEnd=0 < [8/12] 23/23 tStart=1 tEnd=0 < [1/2] 31/31 tStart=0.00462962963 tEnd=1  T 4
106SkOpAngle::afterPart {{{0,0}, {-5,0}}} id=1
107SkOpAngle::afterPart {{{0,0}, {0,242}}} id=8
108SkOpAngle::afterPart {{{0,0}, {1075,0}}} id=1
109SkOpSegment::sortAngles [1] tStart=1 [2]
110SkOpAngle::after [1/3] 15/15 tStart=1 tEnd=0.00462962963 < [2/4] 23/23 tStart=0 tEnd=1 < [5/9] 31/31 tStart=0.99537037 tEnd=1  T 4
111SkOpAngle::afterPart {{{1075,0}, {-8.8817842e-16,0}}} id=1
112SkOpAngle::afterPart {{{1075,0}, {1075,242}}} id=2
113SkOpAngle::afterPart {{{1075,0}, {1080,0}}} id=5
114SkOpSegment::sortAngles [2] tStart=0 [3]
115SkOpSegment::sortAngles [2] tStart=1 [4]
116SkOpAngle::after [2/5] 7/7 tStart=1 tEnd=0 < [3/6] 15/15 tStart=0 tEnd=0.99537037 < [7/10] 31/31 tStart=0.00462962963 tEnd=0  T 4
117SkOpAngle::afterPart {{{1075,242}, {1075,0}}} id=2
118SkOpAngle::afterPart {{{1075,242}, {2.22044605e-14,242}}} id=3
119SkOpAngle::afterPart {{{1075,242}, {1080,242}}} id=7
120SkOpSegment::sortAngles [3] tStart=0 [5]
121SkOpSegment::sortAngles [3] tStart=0.99537037 [20]
122SkOpAngle::after [3/7] 31/31 tStart=0.99537037 tEnd=0 < [8/11] 7/7 tStart=0 tEnd=1 < [3/8] 15/15 tStart=0.99537037 tEnd=1  T 4
123SkOpAngle::afterPart {{{0,242}, {1075,242}}} id=3
124SkOpAngle::afterPart {{{0,242}, {0,0}}} id=8
125SkOpAngle::afterPart {{{0,242}, {-5,242}}} id=3
126SkOpSegment::sortAngles [5] tStart=0.99537037 [18]
127SkOpSegment::sortAngles [7] tStart=0.00462962963 [19]
128SkOpSegment::sortAngles [8] tStart=0 [15]
129SkOpSegment::sortAngles [8] tStart=1 [16]
130coinSpan - id=3 t=0 tEnd=0.99537037
131coinSpan + id=7 t=0.00462962963 tEnd=1
132coinSpan - id=1 t=0.00462962963 tEnd=1
133coinSpan + id=5 t=0 tEnd=0.99537037
134SkOpSpan::sortableTop dir=kTop seg=1 t=0.00231481481 pt=(-2.5,0)
135SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=1 seg=1 {{{-5, 0}, {1075, 0}}} t=0.00231481481 pt=(-2.5,0) slope=(1080,0)
136SkOpSegment::markWinding id=1 (-5,0 1075,0) t=0 [1] (-5,0) tEnd=0.00462962963 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
137SkOpSegment::markWinding id=1 (-5,0 1075,0) t=0 [1] (-5,0) tEnd=0.00462962963 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
138SkOpSegment::markWinding id=4 (-5,242 -5,0) t=0 [7] (-5,242) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
139SkOpSegment::markWinding id=3 (1075,242 -5,242) t=0.99537037 [20] (2.22044605e-14,242) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
140SkOpSegment::activeOp id=1 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=0 result=0
141SkOpSegment::markDone id=1 (-5,0 1075,0) t=0 [1] (-5,0) tEnd=0.00462962963 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
142SkOpSegment::markDone id=4 (-5,242 -5,0) t=0 [7] (-5,242) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
143SkOpSegment::markDone id=3 (1075,242 -5,242) t=0.99537037 [20] (2.22044605e-14,242) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
144bridgeOp chase.append id=3 windSum=-1
145SkOpSegment::markWinding id=3 (1075,242 -5,242) t=0 [5] (1075,242) tEnd=0.99537037 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
146SkOpSegment::markAngle last segment=3 span=5 windSum=-1
147SkOpSegment::markWinding id=8 (0,242 0,0) t=0 [15] (0,242) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
148SkOpSegment::markAngle last segment=8 span=16
149SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? oppSum=? windValue=1 oppValue=1
150SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
151SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=-1 oppSum=-1 windValue=1 oppValue=1
152SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
153SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
154SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
155SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=-1 oppSum=-1 windValue=1 oppValue=0
156SkOpSegment::activeOp id=3 t=0.99537037 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
157SkOpSegment::markWinding id=7 (1080,242 0,242) t=0 [13] (1080,242) tEnd=0.00462962963 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
158SkOpSegment::markWinding id=6 (1080,0 1080,242) t=0 [11] (1080,0) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
159SkOpSegment::markWinding id=5 (0,0 1080,0) t=0.99537037 [18] (1075,0) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
160SkOpSegment::markAngle last segment=5 span=18 windSum=-1
161SkOpSegment::markWinding id=2 (1075,0 1075,242) t=0 [3] (1075,0) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
162SkOpSegment::markAngle last segment=2 span=3 windSum=-1
163SkOpSegment::findNextOp
164SkOpAngle::dumpOne [3/6] next=7/10 sect=15/15  s=0 [5] e=0.99537037 [20] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1
165SkOpAngle::dumpOne [7/10] next=2/5 sect=31/31  s=0.00462962963 [19] e=0 [13] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
166SkOpAngle::dumpOne [2/5] next=3/6 sect=7/7  s=1 [4] e=0 [3] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
167SkOpSegment::activeOp id=7 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
168SkOpSegment::markDone id=7 (1080,242 0,242) t=0 [13] (1080,242) tEnd=0.00462962963 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
169SkOpSegment::markDone id=6 (1080,0 1080,242) t=0 [11] (1080,0) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
170SkOpSegment::markDone id=5 (0,0 1080,0) t=0.99537037 [18] (1075,0) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
171SkOpSegment::findNextOp chase.append segment=5 span=18 windSum=-1
172SkOpSegment::activeOp id=2 t=1 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=1 suTo=1 result=1
173SkOpSegment::findNextOp chase.append segment=2 span=3 windSum=-1
174SkOpSegment::markDone id=3 (1075,242 -5,242) t=0 [5] (1075,242) tEnd=0.99537037 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
175SkOpSegment::findNextOp from:[3] to:[2] start=90366152 end=90366008
176bridgeOp current id=3 from=(2.22044605e-14,242) to=(1075,242)
177SkOpSegment::markWinding id=1 (-5,0 1075,0) t=0.00462962963 [17] (-8.8817842e-16,0) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
178SkOpSegment::markAngle last segment=1 span=17 windSum=-1
179SkOpSegment::findNextOp
180SkOpAngle::dumpOne [2/4] next=5/9 sect=23/23  s=0 [3] e=1 [4] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
181SkOpAngle::dumpOne [5/9] next=1/3 sect=31/31  s=0.99537037 [18] e=1 [10] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done operand
182SkOpAngle::dumpOne [1/3] next=2/4 sect=15/15  s=1 [2] e=0.00462962963 [17] sgn=1 windVal=1 windSum=-1 oppVal=1 oppSum=-1
183SkOpSegment::activeOp id=5 t=0.99537037 tEnd=1 op=sect miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
184SkOpSegment::activeOp id=1 t=1 tEnd=0.00462962963 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
185SkOpSegment::findNextOp chase.append segment=1 span=17 windSum=-1
186SkOpSegment::markDone id=2 (1075,0 1075,242) t=0 [3] (1075,0) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
187SkOpSegment::findNextOp from:[2] to:[1] start=90365736 end=90368360
188bridgeOp current id=2 from=(1075,242) to=(1075,0)
189path.moveTo(2.22044605e-14,242);
190path.lineTo(1075,242);
191SkOpSegment::findNextOp
192SkOpAngle::dumpOne [1/2] next=1/1 sect=31/31  s=0.00462962963 [17] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1
193SkOpAngle::dumpOne [1/1] next=8/12 sect=15/15  s=0.00462962963 [17] e=0 [1] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done
194SkOpAngle::dumpOne [8/12] next=1/2 sect=23/23  s=1 [16] e=0 [15] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
195SkOpSegment::activeOp id=1 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=0 result=0
196SkOpSegment::activeOp id=8 t=1 tEnd=0 op=sect miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
197SkOpSegment::markDone id=1 (-5,0 1075,0) t=0.00462962963 [17] (-8.8817842e-16,0) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
198SkOpSegment::findNextOp from:[1] to:[8] start=90368192 end=90368048
199bridgeOp current id=1 from=(1075,0) to=(-8.8817842e-16,0)
200path.lineTo(1075,0);
201SkOpSegment::findNextOp
202SkOpAngle::dumpOne [8/11] next=3/8 sect=7/7  s=0 [15] e=1 [16] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
203SkOpAngle::dumpOne [3/8] next=3/7 sect=15/15  s=0.99537037 [20] e=1 [6] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done
204SkOpAngle::dumpOne [3/7] next=8/11 sect=31/31  s=0.99537037 [20] e=0 [5] sgn=1 windVal=1 windSum=-1 oppVal=1 oppSum=-1 done
205SkOpSegment::activeOp id=3 t=0.99537037 tEnd=1 op=sect miFrom=1 miTo=0 suFrom=0 suTo=0 result=0
206SkOpSegment::activeOp id=3 t=0.99537037 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
207SkOpSegment::markDone id=8 (0,242 0,0) t=0 [15] (0,242) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
208SkOpSegment::findNextOp from:[8] to:[3] start=90368840 end=90366336
209bridgeOp current id=8 from=(0,0) to=(0,242)
210path.lineTo(-8.8817842e-16,0);
211path.lineTo(0,242);
212path.close();
213</div>
214
215</div>
216
217<script type="text/javascript">
218
219    var testDivs = [
220        android1,
221    ];
222
223var decimal_places = 3; // make this 3 to show more precision
224
225var tests = [];
226var testLines = [];
227var testTitles = [];
228var testIndex = 0;
229var ctx;
230
231var xmin, xmax, focusXmin, focusXmax;
232var ymin, ymax, focusYmin, focusYmax;
233var scale;
234var mouseX, mouseY;
235var srcLeft, srcTop;
236var screenWidth, screenHeight;
237var drawnPts, drawnLines, drawnQuads, drawnConics, drawnCubics;
238var curveT = 0;
239
240var pt_labels = 2;
241var collect_bounds = false;
242var control_lines = 0;
243var curve_t = false;
244var debug_xy = 1;
245var focus_enabled = false;
246var focus_on_selection = false;
247var step_limit = 0;
248var draw_active = false;
249var draw_add = false;
250var draw_angle = 0;
251var draw_coincidence = false;
252var draw_deriviatives = 0;
253var draw_direction = false;
254var draw_hints = false;
255var draw_id = false;
256var draw_intersection = 0;
257var draw_intersectT = false;
258var draw_legend = true;
259var draw_log = false;
260var draw_mark = false;
261var draw_midpoint = false;
262var draw_op = 0;
263var draw_sequence = false;
264var draw_sort = 0;
265var draw_top = false;
266var draw_path = 3;
267var draw_computed = 0;
268var retina_scale = !!window.devicePixelRatio;
269
270var activeCount = 0;
271var addCount = 0;
272var angleCount = 0;
273var coinCount = 0;
274var opCount = 0;
275var sectCount = 0;
276var sortCount = 0;
277var topCount = 0;
278var markCount = 0;
279var activeMax = 0;
280var addMax = 0;
281var angleMax = 0;
282var coinMax = 0;
283var sectMax = 0;
284var sectMax2 = 0;
285var sortMax = 0;
286var topMax = 0;
287var markMax = 0;
288var opMax = 0;
289var stepMax = 0;
290var lastIndex = 0;
291var hasPath = false;
292var hasAlignedPath = false;
293var hasComputedPath = false;
294var angleBetween = false;
295var afterIndex = 0;
296
297var firstActiveSpan = -1;
298var logStart = -1;
299var logRange = 0;
300
301var SPAN_ID = 0;
302var SPAN_X1 = SPAN_ID + 1;
303var SPAN_Y1 = SPAN_X1 + 1;
304var SPAN_X2 = SPAN_Y1 + 1;
305var SPAN_Y2 = SPAN_X2 + 1;
306
307var SPAN_L_TX = SPAN_Y2 + 1;
308var SPAN_L_TY = SPAN_L_TX + 1;
309var SPAN_L_OTHER = SPAN_L_TY + 1;
310var SPAN_L_OTHERT = SPAN_L_OTHER + 1;
311var SPAN_L_OTHERI = SPAN_L_OTHERT + 1;
312var SPAN_L_SUM = SPAN_L_OTHERI + 1;
313var SPAN_L_VAL = SPAN_L_SUM + 1;
314var SPAN_L_OPP = SPAN_L_VAL + 1;
315
316var SPAN_X3 = SPAN_Y2 + 1;
317var SPAN_Y3 = SPAN_X3 + 1;
318
319var SPAN_Q_TX = SPAN_Y3 + 1;
320var SPAN_Q_TY = SPAN_Q_TX + 1;
321var SPAN_Q_OTHER = SPAN_Q_TY + 1;
322var SPAN_Q_OTHERT = SPAN_Q_OTHER + 1;
323var SPAN_Q_OTHERI = SPAN_Q_OTHERT + 1;
324var SPAN_Q_SUM = SPAN_Q_OTHERI + 1;
325var SPAN_Q_VAL = SPAN_Q_SUM + 1;
326var SPAN_Q_OPP = SPAN_Q_VAL + 1;
327
328var SPAN_K_W = SPAN_Y3 + 1;
329var SPAN_K_TX = SPAN_K_W + 1;
330var SPAN_K_TY = SPAN_K_TX + 1;
331var SPAN_K_OTHER = SPAN_K_TY + 1;
332var SPAN_K_OTHERT = SPAN_K_OTHER + 1;
333var SPAN_K_OTHERI = SPAN_K_OTHERT + 1;
334var SPAN_K_SUM = SPAN_K_OTHERI + 1;
335var SPAN_K_VAL = SPAN_K_SUM + 1;
336var SPAN_K_OPP = SPAN_K_VAL + 1;
337
338var SPAN_X4 = SPAN_Y3 + 1;
339var SPAN_Y4 = SPAN_X4 + 1;
340
341var SPAN_C_TX = SPAN_Y4 + 1;
342var SPAN_C_TY = SPAN_C_TX + 1;
343var SPAN_C_OTHER = SPAN_C_TY + 1;
344var SPAN_C_OTHERT = SPAN_C_OTHER + 1;
345var SPAN_C_OTHERI = SPAN_C_OTHERT + 1;
346var SPAN_C_SUM = SPAN_C_OTHERI + 1;
347var SPAN_C_VAL = SPAN_C_SUM + 1;
348var SPAN_C_OPP = SPAN_C_VAL + 1;
349
350var ACTIVE_LINE_SPAN =        1;
351var ACTIVE_QUAD_SPAN =        ACTIVE_LINE_SPAN + 1;
352var ACTIVE_CONIC_SPAN =       ACTIVE_QUAD_SPAN + 1;
353var ACTIVE_CUBIC_SPAN =       ACTIVE_CONIC_SPAN + 1;
354
355var ADD_MOVETO =              ACTIVE_CUBIC_SPAN + 1;
356var ADD_LINETO =              ADD_MOVETO + 1;
357var ADD_QUADTO =              ADD_LINETO + 1;
358var ADD_CONICTO =             ADD_QUADTO + 1;
359var ADD_CUBICTO =             ADD_CONICTO + 1;
360var ADD_CLOSE =               ADD_CUBICTO + 1;
361var ADD_FILL =                ADD_CLOSE + 1;
362
363var PATH_LINE =               ADD_FILL + 1;
364var PATH_QUAD =               PATH_LINE + 1;
365var PATH_CONIC =              PATH_QUAD + 1;
366var PATH_CUBIC =              PATH_CONIC + 1;
367
368var INTERSECT_LINE =          PATH_CUBIC + 1;
369var INTERSECT_LINE_2 =        INTERSECT_LINE + 1;
370var INTERSECT_LINE_NO =       INTERSECT_LINE_2 + 1;
371var INTERSECT_QUAD_LINE =     INTERSECT_LINE_NO + 1;
372var INTERSECT_QUAD_LINE_2 =   INTERSECT_QUAD_LINE + 1;
373var INTERSECT_QUAD_LINE_NO =  INTERSECT_QUAD_LINE_2 + 1;
374var INTERSECT_QUAD =          INTERSECT_QUAD_LINE_NO + 1;
375var INTERSECT_QUAD_2 =        INTERSECT_QUAD + 1;
376var INTERSECT_QUAD_NO =       INTERSECT_QUAD_2 + 1;
377var INTERSECT_CONIC_LINE =    INTERSECT_QUAD_NO + 1;
378var INTERSECT_CONIC_LINE_2 =  INTERSECT_CONIC_LINE + 1;
379var INTERSECT_CONIC_LINE_NO = INTERSECT_CONIC_LINE_2 + 1;
380var INTERSECT_CONIC_QUAD =    INTERSECT_CONIC_LINE_NO + 1;
381var INTERSECT_CONIC_QUAD_2 =  INTERSECT_CONIC_QUAD + 1;
382var INTERSECT_CONIC_QUAD_3 =  INTERSECT_CONIC_QUAD_2 + 1;
383var INTERSECT_CONIC_QUAD_4 =  INTERSECT_CONIC_QUAD_3 + 1;
384var INTERSECT_CONIC_QUAD_NO = INTERSECT_CONIC_QUAD_4 + 1;
385var INTERSECT_CONIC =         INTERSECT_CONIC_QUAD_NO + 1;
386var INTERSECT_CONIC_2 =       INTERSECT_CONIC + 1;
387var INTERSECT_CONIC_NO =      INTERSECT_CONIC_2 + 1;
388var INTERSECT_SELF_CUBIC =    INTERSECT_CONIC_NO + 1;
389var INTERSECT_SELF_CUBIC_NO = INTERSECT_SELF_CUBIC + 1;
390var INTERSECT_CUBIC_LINE =    INTERSECT_SELF_CUBIC_NO + 1;
391var INTERSECT_CUBIC_LINE_2 =  INTERSECT_CUBIC_LINE + 1;
392var INTERSECT_CUBIC_LINE_3 =  INTERSECT_CUBIC_LINE_2 + 1;
393var INTERSECT_CUBIC_LINE_NO = INTERSECT_CUBIC_LINE_3 + 1;
394var INTERSECT_CUBIC_QUAD =    INTERSECT_CUBIC_LINE_NO + 1;
395var INTERSECT_CUBIC_QUAD_2 =  INTERSECT_CUBIC_QUAD + 1;
396var INTERSECT_CUBIC_QUAD_3 =  INTERSECT_CUBIC_QUAD_2 + 1;
397var INTERSECT_CUBIC_QUAD_4 =  INTERSECT_CUBIC_QUAD_3 + 1;
398var INTERSECT_CUBIC_QUAD_NO = INTERSECT_CUBIC_QUAD_4 + 1;
399var INTERSECT_CUBIC =         INTERSECT_CUBIC_QUAD_NO + 1;
400var INTERSECT_CUBIC_2 =       INTERSECT_CUBIC + 1;
401var INTERSECT_CUBIC_3 =       INTERSECT_CUBIC_2 + 1;
402var INTERSECT_CUBIC_4 =       INTERSECT_CUBIC_3 + 1;
403// FIXME: add cubic 5- 9
404var INTERSECT_CUBIC_NO =      INTERSECT_CUBIC_4 + 1;
405
406var SORT_UNARY =              INTERSECT_CUBIC_NO + 1;
407var SORT_BINARY =             SORT_UNARY + 1;
408
409var OP_DIFFERENCE =           SORT_BINARY + 1;
410var OP_INTERSECT =            OP_DIFFERENCE + 1;
411var OP_UNION =                OP_INTERSECT + 1;
412var OP_XOR =                  OP_UNION + 1;
413
414var MARK_LINE =               OP_XOR + 1;
415var MARK_QUAD =               MARK_LINE + 1;
416var MARK_CONIC =              MARK_QUAD + 1;
417var MARK_CUBIC =              MARK_CONIC + 1;
418var MARK_DONE_LINE =          MARK_CUBIC + 1;
419var MARK_DONE_QUAD =          MARK_DONE_LINE + 1;
420var MARK_DONE_CONIC =         MARK_DONE_QUAD + 1;
421var MARK_DONE_CUBIC =         MARK_DONE_CONIC + 1;
422var MARK_UNSORTABLE_LINE =    MARK_DONE_CUBIC + 1;
423var MARK_UNSORTABLE_QUAD =    MARK_UNSORTABLE_LINE + 1;
424var MARK_UNSORTABLE_CONIC =   MARK_UNSORTABLE_QUAD + 1;
425var MARK_UNSORTABLE_CUBIC =   MARK_UNSORTABLE_CONIC + 1;
426var MARK_SIMPLE_LINE =        MARK_UNSORTABLE_CUBIC + 1;
427var MARK_SIMPLE_QUAD =        MARK_SIMPLE_LINE + 1;
428var MARK_SIMPLE_CONIC =       MARK_SIMPLE_QUAD + 1;
429var MARK_SIMPLE_CUBIC =       MARK_SIMPLE_CONIC + 1;
430var MARK_SIMPLE_DONE_LINE =   MARK_SIMPLE_CUBIC + 1;
431var MARK_SIMPLE_DONE_QUAD =   MARK_SIMPLE_DONE_LINE + 1;
432var MARK_SIMPLE_DONE_CONIC =  MARK_SIMPLE_DONE_QUAD + 1;
433var MARK_SIMPLE_DONE_CUBIC =  MARK_SIMPLE_DONE_CONIC + 1;
434var MARK_DONE_UNARY_LINE =    MARK_SIMPLE_DONE_CUBIC + 1;
435var MARK_DONE_UNARY_QUAD =    MARK_DONE_UNARY_LINE + 1;
436var MARK_DONE_UNARY_CONIC =   MARK_DONE_UNARY_QUAD + 1;
437var MARK_DONE_UNARY_CUBIC =   MARK_DONE_UNARY_CONIC + 1;
438var MARK_ANGLE_LAST =         MARK_DONE_UNARY_CUBIC + 1;
439
440var COMPUTED_SET_1 =          MARK_ANGLE_LAST + 1;
441var COMPUTED_SET_2 =          COMPUTED_SET_1 + 1;
442
443var ANGLE_AFTER =             COMPUTED_SET_2 + 1;
444var ANGLE_AFTERPART =         ANGLE_AFTER + 1;
445
446var ACTIVE_OP =               ANGLE_AFTERPART + 1;
447
448var COIN_MAIN_SPAN =          ACTIVE_OP + 1;
449var COIN_OPP_SPAN =           COIN_MAIN_SPAN + 1;
450
451var FRAG_TYPE_LAST =          COIN_OPP_SPAN;
452
453var REC_TYPE_UNKNOWN = -1;
454var REC_TYPE_PATH = 0;
455var REC_TYPE_PATH2 = 1;
456var REC_TYPE_SECT = 2;
457var REC_TYPE_ACTIVE = 3;
458var REC_TYPE_ADD = 4;
459var REC_TYPE_SORT = 5;
460var REC_TYPE_OP = 6;
461var REC_TYPE_MARK = 7;
462var REC_TYPE_COMPUTED = 8;
463var REC_TYPE_COIN = 9;
464var REC_TYPE_ANGLE = 10;
465var REC_TYPE_ACTIVE_OP = 11;
466var REC_TYPE_AFTERPART = 12;
467var REC_TYPE_TOP = 13;
468var REC_TYPE_COINCIDENCE = 14;
469var REC_TYPE_ALIGNED = 15;
470var REC_TYPE_LAST = REC_TYPE_ALIGNED;
471
472function strs_to_nums(strs) {
473    var result = [];
474    for (var idx = 1; idx < strs.length; ++idx) {
475        var str = strs[idx];
476        var num = parseFloat(str);
477        if (isNaN(num)) {
478            result.push(str);
479        } else {
480            result.push(num);
481        }
482    }
483    return result;
484}
485
486function filter_str_by(id, str, regex, array) {
487    if (regex.test(str)) {
488        var strs = regex.exec(str);
489        var result = strs_to_nums(strs);
490        array.push(id);
491        array.push(result);
492        return true;
493    }
494    return false;
495}
496
497function construct_regexp2(pattern) {
498    var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
499    escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
500    escape = escape.replace(/CUBIC_VAL/g, "\\(P_VAL P_VAL P_VAL P_VAL\\)");
501    escape = escape.replace(/CONIC_VAL/g, "\\(P_VAL P_VAL P_VAL W_VAL\\)");
502    escape = escape.replace(/QUAD_VAL/g, "\\(P_VAL P_VAL P_VAL\\)");
503    escape = escape.replace(/LINE_VAL/g, "\\(P_VAL P_VAL\\)");
504    escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
505    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
506    escape = escape.replace(/PT_VAL/g, "\\(P_VAL\\)");
507    escape = escape.replace(/P_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, ?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
508    escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
509    escape = escape.replace(/W_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
510    escape = escape.replace(/PATH/g, "pathB?");
511    escape = escape.replace(/IDX/g, "(-?\\d+)");
512    escape = escape.replace(/NUM/g, "(-?\\d+)");
513    escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
514    return new RegExp(escape, 'i');
515}
516
517function construct_regexp2c(pattern) {
518    var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
519    escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
520    escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
521    escape = escape.replace(/CONIC_VAL/g, "(?:\\$\\d = )?\\{\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}, W_VAL\\}");
522    escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
523    escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
524    escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
525    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
526    escape = escape.replace(/PT_VAL/g, "\\{\\{P_VAL\\}\\}");
527    escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, *(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
528    escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
529    escape = escape.replace(/W_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
530    escape = escape.replace(/OPER/g, "[a-z]+");
531    escape = escape.replace(/PATH/g, "pathB?");
532    escape = escape.replace(/T_F/g, "([TF])");
533    escape = escape.replace(/IDX/g, "(-?\\d+)");
534    escape = escape.replace(/NUM/g, "(-?\\d+)");
535    escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
536    return new RegExp(escape, 'i');
537}
538
539function match_regexp(str, lineNo, array, id, pattern) {
540    var regex = construct_regexp2(pattern);
541    if (filter_str_by(id, str, regex, array)) {
542        return true;
543    }
544    regex = construct_regexp2c(pattern);
545    return filter_str_by(id, str, regex, array);
546}
547
548function endsWith(str, suffix) {
549    return str.indexOf(suffix, str.length - suffix.length) !== -1;
550}
551
552function parse_all(test) {
553    var lines = test.match(/[^\r\n]+/g);
554    var records = []; // a rec can be the original paths, a set of intersections, a set of active spans, a sort, or a path add
555    var record = [];
556    var recType = REC_TYPE_UNKNOWN;
557    var lastLineNo;
558    var moveX, moveY;
559    for (var lineNo = 0; lineNo < lines.length; ++lineNo) {
560        var line = lines[lineNo];
561        if (line.length == 0) {
562            continue;
563        }
564        var opStart = "SkOpSegment::";
565        if (line.lastIndexOf(opStart, 0) === 0) {
566            line = line.substr(opStart.length);
567        }
568        var angleStart = "SkOpAngle::";
569        if (line.lastIndexOf(angleStart, 0) === 0) {
570            line = line.substr(angleStart.length);
571        }
572        var coinStart = "SkOpCoincidence::";
573        if (line.lastIndexOf(coinStart, 0) === 0) {
574            line = line.substr(coinStart.length);
575        }
576        var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE
577                : line.lastIndexOf("debugShowCoincidence", 0) === 0 ? REC_TYPE_COINCIDENCE
578                : line.lastIndexOf("((SkOpSegment*)", 0) === 0 ? REC_TYPE_PATH2
579                : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN
580                : line.lastIndexOf("afterPart", 0) === 0 ? REC_TYPE_AFTERPART
581                : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
582                : line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
583                : line.lastIndexOf("computed", 0) === 0 ? REC_TYPE_COMPUTED
584                : line.lastIndexOf("debugOne", 0) === 0 ? REC_TYPE_SORT
585                : line.lastIndexOf("aligned=", 0) === 0 ? REC_TYPE_ALIGNED
586                : line.lastIndexOf("dumpOne", 0) === 0 ? REC_TYPE_SORT
587                : line.lastIndexOf("findTop", 0) === 0 ? REC_TYPE_TOP
588                : line.lastIndexOf("pathB.", 0) === 0 ? REC_TYPE_ADD
589                : line.lastIndexOf("path.", 0) === 0 ? REC_TYPE_ADD
590                : line.lastIndexOf("after", 0) === 0 ? REC_TYPE_ANGLE
591                : line.lastIndexOf("mark", 0) === 0 ? REC_TYPE_MARK
592                : line.lastIndexOf("  {{", 0) === 0 ? REC_TYPE_COMPUTED
593                : line.lastIndexOf("seg=", 0) === 0 ? REC_TYPE_PATH
594                : line.lastIndexOf("op", 0) === 0 ? REC_TYPE_OP
595                : line.lastIndexOf("$", 0) === 0 ? REC_TYPE_PATH
596                : REC_TYPE_UNKNOWN;
597        if (recType != type || recType == REC_TYPE_ADD || recType == REC_TYPE_SECT
598                || recType == REC_TYPE_ACTIVE_OP || recType == REC_TYPE_ANGLE) {
599            if (recType != REC_TYPE_UNKNOWN) {
600                records.push(recType);
601                records.push(lastLineNo);
602                records.push(record);
603            }
604            record = [];
605            recType = type;
606            lastLineNo = lineNo;
607        }
608        var found = false;
609        switch (recType) {
610            case REC_TYPE_ACTIVE:
611                found = match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
612" id=IDX LINE_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
613                ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
614" id=IDX QUAD_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
615                ) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
616" id=IDX CONIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
617                ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
618" id=IDX CUBIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
619                ) || match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
620" id=IDX LINE_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
621                ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
622" id=IDX QUAD_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
623                ) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
624" id=IDX CONIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
625                ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
626" id=IDX CUBIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
627                );
628                break;
629            case REC_TYPE_ACTIVE_OP:
630                found = match_regexp(line, lineNo, record, ACTIVE_OP, "activeOp" +
631" id=IDX t=T_VAL tEnd=T_VAL op=OPER miFrom=NUM miTo=NUM suFrom=NUM suTo=NUM result=IDX"
632                );
633                break;
634            case REC_TYPE_ADD:
635                if (match_regexp(line, lineNo, record, ADD_MOVETO, "PATH.moveTo(P_VAL);")) {
636                    moveX = record[1][0];
637                    moveY = record[1][1];
638                    found = true;
639                } else if (match_regexp(line, lineNo, record, ADD_LINETO, "PATH.lineTo(P_VAL);")) {
640                    record[1].unshift(moveY);
641                    record[1].unshift(moveX);
642                    moveX = record[1][2];
643                    moveY = record[1][3];
644                    found = true;
645                } else if (match_regexp(line, lineNo, record, ADD_QUADTO, "PATH.quadTo(P_VAL, P_VAL);")) {
646                    record[1].unshift(moveY);
647                    record[1].unshift(moveX);
648                    moveX = record[1][4];
649                    moveY = record[1][5];
650                    found = true;
651                } else if (match_regexp(line, lineNo, record, ADD_CONICTO, "PATH.conicTo(P_VAL, P_VAL, T_VAL);")) {
652                    record[1].unshift(moveY);
653                    record[1].unshift(moveX);
654                    moveX = record[1][4];
655                    moveY = record[1][5];
656                    found = true;
657                } else if (match_regexp(line, lineNo, record, ADD_CUBICTO, "PATH.cubicTo(P_VAL, P_VAL, P_VAL);")) {
658                    record[1].unshift(moveY);
659                    record[1].unshift(moveX);
660                    moveX = record[1][6];
661                    moveY = record[1][7];
662                    found = true;
663                } else if (match_regexp(line, lineNo, record, ADD_FILL, "PATH.setFillType(FILL_TYPE);")) {
664                    found = true;
665                } else {
666                    found = match_regexp(line, lineNo, record, ADD_CLOSE, "PATH.close();");
667                }
668                break;
669            case REC_TYPE_AFTERPART:
670                found = match_regexp(line, lineNo, record, PATH_LINE, "afterPart LINE_VAL id=IDX")
671                    || match_regexp(line, lineNo, record, PATH_QUAD, "afterPart QUAD_VAL id=IDX")
672                    || match_regexp(line, lineNo, record, PATH_CONIC, "afterPart CONIC_VAL id=IDX")
673                    || match_regexp(line, lineNo, record, PATH_CUBIC, "afterPart CUBIC_VAL id=IDX")
674                break;
675            case REC_TYPE_ALIGNED:
676                found = match_regexp(line, lineNo, record, PATH_LINE, "aligned=IDX LINE_VAL"
677                ) || match_regexp(line, lineNo, record, PATH_QUAD, "aligned=IDX QUAD_VAL"
678                ) || match_regexp(line, lineNo, record, PATH_CONIC, "aligned=IDX CONIC_VAL"
679                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "aligned=IDX CUBIC_VAL"
680                );
681                break;
682            case REC_TYPE_ANGLE:
683                found = match_regexp(line, lineNo, record, ANGLE_AFTER, "after " +
684"[IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL  T_F IDX");
685                break;
686            case REC_TYPE_COIN:
687                found = true;
688                break;
689            case REC_TYPE_COINCIDENCE:
690                found = match_regexp(line, lineNo, record, COIN_MAIN_SPAN, "debugShowCoincidence" +
691" + id=IDX t=T_VAL tEnd=T_VAL"
692                ) || match_regexp(line, lineNo, record, COIN_OPP_SPAN, "debugShowCoincidence" +
693" - id=IDX t=T_VAL tEnd=T_VAL"
694                );
695                break;
696            case REC_TYPE_COMPUTED:
697                found = line ==  "computed quadratics given"
698                  || match_regexp(line, lineNo, record, COMPUTED_SET_1, "computed quadratics set 1"
699                ) || match_regexp(line, lineNo, record, COMPUTED_SET_2, "computed quadratics set 2"
700                ) || match_regexp(line, lineNo, record, PATH_QUAD, "  QUAD_VAL,"
701                ) || match_regexp(line, lineNo, record, PATH_CONIC, "  CONIC_VAL,"
702                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "  CUBIC_VAL,"
703                );
704                break;
705            case REC_TYPE_PATH:
706                found = match_regexp(line, lineNo, record, PATH_LINE, "seg=IDX LINE_VAL"
707                ) || match_regexp(line, lineNo, record, PATH_QUAD, "seg=IDX QUAD_VAL"
708                ) || match_regexp(line, lineNo, record, PATH_CONIC, "seg=IDX CONIC_VAL"
709                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "seg=IDX CUBIC_VAL"
710                );
711                break;
712            case REC_TYPE_PATH2:
713                found = match_regexp(line, lineNo, record, PATH_LINE, "((SkOpSegment*) PTR_VAL) [IDX] {LINE_VAL}"
714                ) || match_regexp(line, lineNo, record, PATH_QUAD, "((SkOpSegment*) PTR_VAL) [IDX] {QUAD_VAL}"
715                ) || match_regexp(line, lineNo, record, PATH_CONIC, "((SkOpSegment*) PTR_VAL) [IDX] {CONIC_VAL}"
716                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "((SkOpSegment*) PTR_VAL) [IDX] {CUBIC_VAL}"
717                );
718                break;
719            case REC_TYPE_SECT:
720                found = match_regexp(line, lineNo, record, INTERSECT_LINE, "debugShowLineIntersection" +
721" wtTs[0]=T_VAL LINE_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
722                ) || match_regexp(line, lineNo, record, INTERSECT_LINE_2, "debugShowLineIntersection" +
723" wtTs[0]=T_VAL LINE_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
724                ) || match_regexp(line, lineNo, record, INTERSECT_LINE_NO, "debugShowLineIntersection" +
725" no intersect LINE_VAL LINE_VAL"
726                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE, "debugShowQuadLineIntersection" +
727" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
728                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_2, "debugShowQuadLineIntersection" +
729" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
730                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_NO, "debugShowQuadLineIntersection" +
731" no intersect QUAD_VAL LINE_VAL"
732                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD, "debugShowQuadIntersection" +
733" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
734                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_2, "debugShowQuadIntersection" +
735" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
736                ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_NO, "debugShowQuadIntersection" +
737" no intersect QUAD_VAL QUAD_VAL"
738
739                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE, "debugShowConicLineIntersection" +
740" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
741                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_2, "debugShowConicLineIntersection" +
742" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
743                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_NO, "debugShowConicLineIntersection" +
744" no intersect CONIC_VAL LINE_VAL"
745
746                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD, "debugShowConicQuadIntersection" +
747" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
748                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_2, "debugShowConicQuadIntersection" +
749" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
750                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_3, "debugShowConicQuadIntersection" +
751" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
752                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_4, "debugShowConicQuadIntersection" +
753" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL"
754                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_NO, "debugShowConicQuadIntersection" +
755" no intersect CONIC_VAL QUAD_VAL"
756
757                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC, "debugShowConicIntersection" +
758" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL CONIC_VAL"
759                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_2, "debugShowConicIntersection" +
760" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL CONIC_VAL wnTs[1]=T_VAL"
761                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_NO, "debugShowConicIntersection" +
762" no intersect CONIC_VAL CONIC_VAL"
763                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE, "debugShowCubicLineIntersection" +
764" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
765                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_2, "debugShowCubicLineIntersection" +
766" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
767                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_3, "debugShowCubicLineIntersection" +
768" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
769                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_NO, "debugShowCubicLineIntersection" +
770" no intersect CUBIC_VAL LINE_VAL"
771                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD, "debugShowCubicQuadIntersection" +
772" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
773                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_2, "debugShowCubicQuadIntersection" +
774" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
775                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_3, "debugShowCubicQuadIntersection" +
776" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
777                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_4, "debugShowCubicQuadIntersection" +
778" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL"
779                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_NO, "debugShowCubicQuadIntersection" +
780" no intersect CUBIC_VAL QUAD_VAL"
781                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC, "debugShowCubicIntersection" +
782" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL"
783                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_2, "debugShowCubicIntersection" +
784" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL"
785                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_3, "debugShowCubicIntersection" +
786" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
787                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_4, "debugShowCubicIntersection" +
788" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL"
789                ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_NO, "debugShowCubicIntersection" +
790" no intersect CUBIC_VAL CUBIC_VAL"
791                ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC, "debugShowCubicIntersection" +
792" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL"
793                ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC_NO, "debugShowCubicIntersection" +
794" no self intersect CUBIC_VAL"
795                );
796                break;
797            case REC_TYPE_SORT:
798                var hasDone = / done/.test(line);
799                var hasUnorderable = / unorderable/.test(line);
800                var hasSmall = / small/.test(line);
801                var hasTiny = / tiny/.test(line);
802                var hasOperand = / operand/.test(line);
803                var hasStop = / stop/.test(line);
804                line.replace(/[ a-z]+$/, "");
805                found = match_regexp(line, lineNo, record, SORT_UNARY, "debugOne" +
806" [IDX/IDX] next=IDX/IDX sect=IDX/IDX  s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT"
807                ) || match_regexp(line, lineNo, record, SORT_BINARY, "debugOne" +
808" [IDX/IDX] next=IDX/IDX sect=IDX/IDX  s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT oppVal=IDX oppSum=OPT"
809                ) || match_regexp(line, lineNo, record, SORT_UNARY, "dumpOne" +
810" [IDX/IDX] next=IDX/IDX sect=NUM/NUM  s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT"
811                ) || match_regexp(line, lineNo, record, SORT_BINARY, "dumpOne" +
812" [IDX/IDX] next=IDX/IDX sect=NUM/NUM  s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT oppVal=IDX oppSum=OPT"
813                );
814                if (found) {
815                    record[1].push(hasDone);
816                    record[1].push(hasUnorderable);
817                    record[1].push(hasSmall);
818                    record[1].push(hasTiny);
819                    record[1].push(hasOperand);
820                    record[1].push(hasStop);
821                }
822                break;
823            case REC_TYPE_TOP:
824                found = match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
825" id=IDX s=T_VAL e=T_VAL cw=NUM swap=NUM inflections=NUM monotonic=NUM"
826                ) || match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
827" id=IDX s=T_VAL e=T_VAL (-) cw=NUM swap=NUM inflections=NUM monotonic=NUM"
828                ) || match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
829" id=IDX s=T_VAL e=T_VAL (+) cw=NUM swap=NUM inflections=NUM monotonic=NUM"
830                );
831                break;
832            case REC_TYPE_MARK:
833                found = match_regexp(line, lineNo, record, MARK_LINE, "markWinding" +
834" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
835                ) || match_regexp(line, lineNo, record, MARK_QUAD, "markWinding" +
836" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
837                ) || match_regexp(line, lineNo, record, MARK_CONIC, "markWinding" +
838" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
839                ) || match_regexp(line, lineNo, record, MARK_CUBIC, "markWinding" +
840" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
841                ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDone" +
842" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
843                ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDone" +
844" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
845                ) || match_regexp(line, lineNo, record, MARK_DONE_CONIC, "markDone" +
846" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
847                ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDone" +
848" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
849                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_LINE, "markWinding" +
850" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
851                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_QUAD, "markWinding" +
852" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
853                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CONIC, "markWinding" +
854" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
855                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CUBIC, "markWinding" +
856" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
857                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
858" last segment=IDX span=IDX"
859                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
860" last seg=IDX span=IDX"
861                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
862" last segment=IDX span=IDX windSum=OPT"
863                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
864" last seg=IDX span=IDX windSum=OPT"
865                );
866                break;
867            case REC_TYPE_OP:
868                if (line.lastIndexOf("oppSign oppSign=", 0) === 0
869                        || line.lastIndexOf("operator<", 0) === 0) {
870                    found = true;
871                    break;
872                }
873                found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op diff"
874                ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op intersect"
875                ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op sect"
876                ) || match_regexp(line, lineNo, record, OP_UNION, "op union"
877                ) || match_regexp(line, lineNo, record, OP_XOR, "op xor"
878                );
879                break;
880            case REC_TYPE_UNKNOWN:
881                found = true;
882                break;
883        }
884        if (!found) {
885            console.log(line + " [" + lineNo + "] of type " + type + " not found");
886        }
887    }
888    if (recType != REC_TYPE_UNKNOWN) {
889        records.push(recType);
890        records.push(lastLineNo);
891        records.push(record);
892    }
893    if (records.length >= 1) {
894        tests[testIndex] = records;
895        testLines[testIndex] = lines;
896    }
897}
898
899function init(test) {
900    var canvas = document.getElementById('canvas');
901    if (!canvas.getContext) return;
902    ctx = canvas.getContext('2d');
903    var resScale = retina_scale && window.devicePixelRatio ? window.devicePixelRatio : 1;
904    var unscaledWidth = window.innerWidth - 20;
905    var unscaledHeight = window.innerHeight - 20;
906    screenWidth = unscaledWidth;
907    screenHeight = unscaledHeight;
908    canvas.width = unscaledWidth * resScale;
909    canvas.height = unscaledHeight * resScale;
910    canvas.style.width = unscaledWidth + 'px';
911    canvas.style.height = unscaledHeight + 'px';
912    if (resScale != 1) {
913        ctx.scale(resScale, resScale);
914    }
915    xmin = Infinity;
916    xmax = -Infinity;
917    ymin = Infinity;
918    ymax = -Infinity;
919    hasPath = hasAlignedPath = hasComputedPath = false;
920    firstActiveSpan = -1;
921    for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
922        var recType = test[tIndex];
923        if (!typeof recType == 'number' || recType < REC_TYPE_UNKNOWN || recType > REC_TYPE_LAST) {
924            console.log("unknown rec type: " + recType);
925            throw "stop execution";
926        }
927        var records = test[tIndex + 2];
928        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
929            var fragType = records[recordIndex];
930            if (!typeof fragType == 'number' || fragType < 1 || fragType > FRAG_TYPE_LAST) {
931                console.log("unknown in range frag type: " + fragType);
932                throw "stop execution";
933            }
934            var frags = records[recordIndex + 1];
935            var first = 0;
936            var last = -1;
937            var first2 = 0;
938            var last2 = 0;
939            switch (recType) {
940                case REC_TYPE_ALIGNED:
941                    hasAlignedPath = true;
942                case REC_TYPE_COMPUTED:
943                    if (fragType == COMPUTED_SET_1 || fragType == COMPUTED_SET_2) {
944                        break;
945                    }
946                    if (REC_TYPE_COMPUTED == recType) {
947                        hasComputedPath = true;
948                    }
949                case REC_TYPE_PATH:
950                    first = 1;
951                    switch (fragType) {
952                        case PATH_LINE:
953                            last = 5;
954                            break;
955                        case PATH_CONIC:
956                        case PATH_QUAD:
957                            last = 7;
958                            break;
959                        case PATH_CUBIC:
960                            last = 9;
961                            break;
962                        default:
963                            console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH"
964                                    : "REC_TYPE_COMPUTED") + " frag type:" + fragType);
965                            throw "stop execution";
966                    }
967                    if (recType == REC_TYPE_PATH) {
968                        hasPath = true;
969                    }
970                    break;
971                case REC_TYPE_PATH2:
972                    first = 1;
973                    switch (fragType) {
974                        case PATH_LINE:
975                            last = 5;
976                            break;
977                        case PATH_CONIC:
978                        case PATH_QUAD:
979                            last = 7;
980                            break;
981                        case PATH_CUBIC:
982                            last = 9;
983                            break;
984                        default:
985                            console.log("unknown " + (recType == REC_TYPE_PATH2 ? "REC_TYPE_PATH2"
986                                    : "REC_TYPE_COMPUTED") + " frag type:" + fragType);
987                            throw "stop execution";
988                    }
989                    if (recType == REC_TYPE_PATH2) {
990                        hasPath = true;
991                    }
992                    break;
993                case REC_TYPE_ACTIVE:
994                    if (firstActiveSpan < 0) {
995                        firstActiveSpan = tIndex;
996                    }
997                    first = 1;
998                    switch (fragType) {
999                        case ACTIVE_LINE_SPAN:
1000                            last = 5;
1001                            break;
1002                        case ACTIVE_CONIC_SPAN:
1003                        case ACTIVE_QUAD_SPAN:
1004                            last = 7;
1005                            break;
1006                        case ACTIVE_CUBIC_SPAN:
1007                            last = 9;
1008                            break;
1009                        default:
1010                            console.log("unknown REC_TYPE_ACTIVE frag type: " + fragType);
1011                            throw "stop execution";
1012                    }
1013                    break;
1014                case REC_TYPE_ADD:
1015                    switch (fragType) {
1016                        case ADD_MOVETO:
1017                            break;
1018                        case ADD_LINETO:
1019                            last = 4;
1020                            break;
1021                        case ADD_CONICTO:
1022                        case ADD_QUADTO:
1023                            last = 6;
1024                            break;
1025                        case ADD_CUBICTO:
1026                            last = 8;
1027                            break;
1028                        case ADD_CLOSE:
1029                        case ADD_FILL:
1030                            break;
1031                        default:
1032                            console.log("unknown REC_TYPE_ADD frag type: " + fragType);
1033                            throw "stop execution";
1034                    }
1035                    break;
1036                case REC_TYPE_AFTERPART:
1037                    switch (fragType) {
1038                        case PATH_LINE:
1039                            last = 4;
1040                            break;
1041                        case PATH_CONIC:
1042                        case PATH_QUAD:
1043                            last = 6;
1044                            break;
1045                        case PATH_CUBIC:
1046                            last = 8;
1047                            break;
1048                        default:
1049                            console.log("unknown REC_TYPE_ACTIVEPART frag type: " + fragType);
1050                            throw "stop execution";
1051                    }
1052                    break;
1053                case REC_TYPE_SECT:
1054                    switch (fragType) {
1055                        case INTERSECT_LINE:
1056                            first = 1; last = 5; first2 = 8; last2 = 12;
1057                            break;
1058                        case INTERSECT_LINE_2:
1059                            first = 1; last = 5; first2 = 11; last2 = 15;
1060                            break;
1061                        case INTERSECT_LINE_NO:
1062                            first = 0; last = 4; first2 = 4; last2 = 8;
1063                            break;
1064                        case INTERSECT_CONIC_LINE:
1065                            first = 1; last = 7; first2 = 11; last2 = 15;
1066                            break;
1067                        case INTERSECT_QUAD_LINE:
1068                            first = 1; last = 7; first2 = 10; last2 = 14;
1069                            break;
1070                        case INTERSECT_CONIC_LINE_2:
1071                            first = 1; last = 7; first2 = 14; last2 = 18;
1072                            break;
1073                        case INTERSECT_QUAD_LINE_2:
1074                            first = 1; last = 7; first2 = 13; last2 = 17;
1075                            break;
1076                        case INTERSECT_CONIC_LINE_NO:
1077                            first = 0; last = 6; first2 = 7; last2 = 11;
1078                            break;
1079                        case INTERSECT_QUAD_LINE_NO:
1080                            first = 0; last = 6; first2 = 6; last2 = 10;
1081                            break;
1082                        case INTERSECT_CONIC:
1083                            first = 1; last = 7; first2 = 11; last2 = 17;
1084                            break;
1085                        case INTERSECT_QUAD:
1086                            first = 1; last = 7; first2 = 10; last2 = 16;
1087                            break;
1088                        case INTERSECT_CONIC_2:
1089                            first = 1; last = 7; first2 = 14; last2 = 20;
1090                            break;
1091                        case INTERSECT_QUAD_2:
1092                            first = 1; last = 7; first2 = 13; last2 = 19;
1093                            break;
1094                        case INTERSECT_CONIC_NO:
1095                            first = 0; last = 6; first2 = 7; last2 = 13;
1096                            break;
1097                        case INTERSECT_QUAD_NO:
1098                            first = 0; last = 6; first2 = 6; last2 = 12;
1099                            break;
1100                        case INTERSECT_SELF_CUBIC:
1101                            first = 1; last = 9;
1102                            break;
1103                        case INTERSECT_SELF_CUBIC_NO:
1104                            first = 0; last = 8;
1105                            break;
1106                        case INTERSECT_CUBIC_LINE:
1107                            first = 1; last = 9; first2 = 12; last2 = 16;
1108                            break;
1109                        case INTERSECT_CUBIC_LINE_2:
1110                            first = 1; last = 9; first2 = 15; last2 = 19;
1111                            break;
1112                        case INTERSECT_CUBIC_LINE_3:
1113                            first = 1; last = 9; first2 = 18; last2 = 22;
1114                            break;
1115                        case INTERSECT_CUBIC_LINE_NO:
1116                            first = 0; last = 8; first2 = 8; last2 = 12;
1117                            break;
1118                        case INTERSECT_CONIC_QUAD:
1119                            first = 1; last = 7; first2 = 11; last2 = 17;
1120                            break;
1121                        case INTERSECT_CONIC_QUAD_2:
1122                            first = 1; last = 7; first2 = 14; last2 = 20;
1123                            break;
1124                        case INTERSECT_CONIC_QUAD_3:
1125                            first = 1; last = 7; first2 = 17; last2 = 23;
1126                            break;
1127                        case INTERSECT_CONIC_QUAD_4:
1128                            first = 1; last = 7; first2 = 20; last2 = 26;
1129                            break;
1130                        case INTERSECT_CONIC_QUAD_NO:
1131                            first = 0; last = 6; first2 = 7; last2 = 13;
1132                            break;
1133                        case INTERSECT_CUBIC_QUAD:
1134                            first = 1; last = 9; first2 = 12; last2 = 18;
1135                            break;
1136                        case INTERSECT_CUBIC_QUAD_2:
1137                            first = 1; last = 9; first2 = 15; last2 = 21;
1138                            break;
1139                        case INTERSECT_CUBIC_QUAD_3:
1140                            first = 1; last = 9; first2 = 18; last2 = 24;
1141                            break;
1142                        case INTERSECT_CUBIC_QUAD_4:
1143                            first = 1; last = 9; first2 = 21; last2 = 27;
1144                            break;
1145                        case INTERSECT_CUBIC_QUAD_NO:
1146                            first = 0; last = 8; first2 = 8; last2 = 14;
1147                            break;
1148                        case INTERSECT_CUBIC:
1149                            first = 1; last = 9; first2 = 12; last2 = 20;
1150                            break;
1151                        case INTERSECT_CUBIC_2:
1152                            first = 1; last = 9; first2 = 15; last2 = 23;
1153                            break;
1154                        case INTERSECT_CUBIC_3:
1155                            first = 1; last = 9; first2 = 18; last2 = 26;
1156                            break;
1157                        case INTERSECT_CUBIC_4:
1158                            first = 1; last = 9; first2 = 21; last2 = 29;
1159                            break;
1160                        case INTERSECT_CUBIC_NO:
1161                            first = 0; last = 8; first2 = 8; last2 = 16;
1162                            break;
1163                        default:
1164                            console.log("unknown REC_TYPE_SECT frag type: " + fragType);
1165                            throw "stop execution";
1166                    }
1167                    break;
1168                default:
1169                    continue;
1170            }
1171            for (var idx = first; idx < last; idx += 2) {
1172                xmin = Math.min(xmin, frags[idx]);
1173                xmax = Math.max(xmax, frags[idx]);
1174                ymin = Math.min(ymin, frags[idx + 1]);
1175                ymax = Math.max(ymax, frags[idx + 1]);
1176            }
1177            for (var idx = first2; idx < last2; idx += 2) {
1178                xmin = Math.min(xmin, frags[idx]);
1179                xmax = Math.max(xmax, frags[idx]);
1180                ymin = Math.min(ymin, frags[idx + 1]);
1181                ymax = Math.max(ymax, frags[idx + 1]);
1182            }
1183        }
1184    }
1185    var angleBounds = [Infinity, Infinity, -Infinity, -Infinity];
1186    for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
1187        var recType = test[tIndex];
1188        var records = test[tIndex + 2];
1189        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
1190            var fragType = records[recordIndex];
1191            var frags = records[recordIndex + 1];
1192            switch (recType) {
1193                case REC_TYPE_ACTIVE_OP:
1194                    if (!draw_op) {
1195                        break;
1196                    }
1197                    {
1198                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
1199                        curve_extremes(curve, angleBounds);
1200                    }
1201                    break;
1202                case REC_TYPE_ANGLE:
1203                    if (!draw_angle) {
1204                        break;
1205                    }
1206                    {
1207                        var curve = curvePartialByID(test, frags[0], frags[4], frags[5]);
1208                        curve_extremes(curve, angleBounds);
1209                        curve = curvePartialByID(test, frags[6], frags[10], frags[11]);
1210                        curve_extremes(curve, angleBounds);
1211                        curve = curvePartialByID(test, frags[12], frags[16], frags[17]);
1212                    }
1213                    break;
1214                case REC_TYPE_COINCIDENCE:
1215                    if (!draw_coincidence) {
1216                        break;
1217                    }
1218                    {
1219                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
1220                        curve_extremes(curve, angleBounds);
1221                    }
1222                    break;
1223                case REC_TYPE_SORT:
1224                    if (!draw_sort) {
1225                        break;
1226                    }
1227                    if (fragType == SORT_UNARY || fragType == SORT_BINARY) {
1228                        var curve = curvePartialByID(test, frags[0], frags[6], frags[8]);
1229                        curve_extremes(curve, angleBounds);
1230                    }
1231                    break;
1232                case REC_TYPE_TOP:
1233                    if (!draw_top) {
1234                        break;
1235                    }
1236                    {
1237                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
1238                        curve_extremes(curve, angleBounds);
1239                    }
1240                    break;
1241            }
1242        }
1243    }
1244    xmin = Math.min(xmin, angleBounds[0]);
1245    ymin = Math.min(ymin, angleBounds[1]);
1246    xmax = Math.max(xmax, angleBounds[2]);
1247    ymax = Math.max(ymax, angleBounds[3]);
1248    setScale(xmin, xmax, ymin, ymax);
1249    if (hasPath == false && hasComputedPath == true && !draw_computed) {
1250        draw_computed = 7; // show quadratics, conics, and cubics
1251    }
1252    if (hasPath == true && hasComputedPath == false && draw_computed) {
1253        draw_computed = 0;
1254    }
1255}
1256
1257function curveByIDMatch(test, id, recMatch) {
1258    var tIndex = -3;
1259    while ((tIndex += 3) < test.length) {
1260        var recType = test[tIndex];
1261        if (recType == REC_TYPE_OP) {
1262            continue;
1263        }
1264        if (recType != recMatch) {
1265            return [];
1266        }
1267        var records = test[tIndex + 2];
1268        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
1269            var fragType = records[recordIndex];
1270            var frags = records[recordIndex + 1];
1271            if (frags[0] == id) {
1272                switch (fragType) {
1273                    case PATH_LINE:
1274                        return [frags[1], frags[2], frags[3], frags[4]];
1275                    case PATH_QUAD:
1276                        return [frags[1], frags[2], frags[3], frags[4],
1277                                frags[5], frags[6]];
1278                    case PATH_CONIC:
1279                        return [frags[1], frags[2], frags[3], frags[4],
1280                                frags[5], frags[6], frags[7]];
1281                    case PATH_CUBIC:
1282                        return [frags[1], frags[2], frags[3], frags[4],
1283                                frags[5], frags[6], frags[7], frags[8]];
1284                }
1285            }
1286        }
1287    }
1288    return [];
1289}
1290
1291function curveByID(test, id) {
1292    var result = draw_path >= 4 ? curveByIDMatch(test, id, REC_TYPE_ALIGNED) : [];
1293    if (!result.length) {
1294        result = curveByIDMatch(test, id, REC_TYPE_PATH);
1295    }
1296    return result;
1297}
1298
1299function curvePartialByIDMatch(test, id, t0, t1, recMatch) {
1300    var tIndex = -3;
1301    while ((tIndex += 3) < test.length) {
1302        var recType = test[tIndex];
1303        if (recType == REC_TYPE_OP) {
1304            continue;
1305        }
1306        if (recType != recMatch) {
1307            return [];
1308        }
1309        var records = test[tIndex + 2];
1310        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
1311            var fragType = records[recordIndex];
1312            var frags = records[recordIndex + 1];
1313            if (frags[0] == id) {
1314                switch (fragType) {
1315                    case PATH_LINE:
1316                        return linePartial(frags[1], frags[2], frags[3], frags[4], t0, t1);
1317                    case PATH_QUAD:
1318                        return quadPartial(frags[1], frags[2], frags[3], frags[4],
1319                                frags[5], frags[6], t0, t1);
1320                    case PATH_CONIC:
1321                        return conicPartial(frags[1], frags[2], frags[3], frags[4],
1322                                frags[5], frags[6], frags[7], t0, t1);
1323                    case PATH_CUBIC:
1324                        return cubicPartial(frags[1], frags[2], frags[3], frags[4],
1325                                frags[5], frags[6], frags[7], frags[8], t0, t1);
1326                }
1327            }
1328        }
1329    }
1330    return [];
1331}
1332
1333function curvePartialByID(test, id, t0, t1) {
1334    var result = draw_path >= 4 ? curvePartialByIDMatch(test, id, t0, t1, REC_TYPE_ALIGNED) : [];
1335    if (!result.length) {
1336        result = curvePartialByIDMatch(test, id, t0, t1, REC_TYPE_PATH);
1337    }
1338    return result;
1339}
1340
1341function idByCurveIDMatch(test, frag, type, recMatch) {
1342    var tIndex = 0;
1343    while (tIndex < test.length) {
1344        var recType = test[tIndex];
1345        if (recType != recMatch) {
1346            ++tIndex;
1347            continue;
1348        }
1349        var records = test[tIndex + 2];
1350        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
1351            var fragType = records[recordIndex];
1352            var frags = records[recordIndex + 1];
1353            if (frag.length != frags.length - 1) {
1354                continue;
1355            }
1356            switch (fragType) {
1357                case PATH_LINE:
1358                    if (frag[0] != frags[1] || frag[1] != frags[2]
1359                            || frag[2] != frags[3] || frag[3] != frags[4]) {
1360                        continue;
1361                    }
1362                    return frags[0];
1363                case PATH_QUAD:
1364                    if (frag[0] != frags[1] || frag[1] != frags[2]
1365                            || frag[2] != frags[3] || frag[3] != frags[4]
1366                            || frag[4] != frags[5] || frag[5] != frags[6]) {
1367                        continue;
1368                    }
1369                    return frags[0];
1370                case PATH_CONIC:
1371                    if (frag[0] != frags[1] || frag[1] != frags[2]
1372                            || frag[2] != frags[3] || frag[3] != frags[4]
1373                            || frag[4] != frags[5] || frag[5] != frags[6]
1374                            || frag[6] != frags[7]) {
1375                        continue;
1376                    }
1377                    return frags[0];
1378                case PATH_CUBIC:
1379                    if (frag[0] != frags[1] || frag[1] != frags[2]
1380                            || frag[2] != frags[3] || frag[3] != frags[4]
1381                            || frag[4] != frags[5] || frag[5] != frags[6]
1382                            || frag[6] != frags[7] || frag[7] != frags[8]) {
1383                        continue;
1384                    }
1385                    return frags[0];
1386            }
1387        }
1388        ++tIndex;
1389    }
1390    return -1;
1391}
1392
1393function idByCurve(test, frag, type) {
1394    var result = draw_path >= 4 ? idByCurveIDMatch(test, frag, type, REC_TYPE_ALIGNED) : [];
1395    if (!result.length) {
1396        result = idByCurveIDMatch(test, frag, type, REC_TYPE_PATH);
1397    }
1398    return result;
1399}
1400
1401function curve_extremes(curve, bounds) {
1402    var length = curve.length == 7 ? 6 : curve.length;
1403    for (var index = 0; index < length; index += 2) {
1404        var x = curve[index];
1405        var y = curve[index + 1];
1406        bounds[0] = Math.min(bounds[0], x);
1407        bounds[1] = Math.min(bounds[1], y);
1408        bounds[2] = Math.max(bounds[2], x);
1409        bounds[3] = Math.max(bounds[3], y);
1410    }
1411}
1412
1413function setScale(x0, x1, y0, y1) {
1414    var srcWidth = x1 - x0;
1415    var srcHeight = y1 - y0;
1416    var usableWidth = screenWidth;
1417    var xDigits = Math.ceil(Math.log(Math.abs(xmax)) / Math.log(10));
1418    var yDigits = Math.ceil(Math.log(Math.abs(ymax)) / Math.log(10));
1419    usableWidth -= (xDigits + yDigits) * 10;
1420    usableWidth -= decimal_places * 10;
1421    if (draw_legend) {
1422        usableWidth -= 40;
1423    }
1424    var hscale = usableWidth / srcWidth;
1425    var vscale = screenHeight / srcHeight;
1426    scale = Math.min(hscale, vscale);
1427    var invScale = 1 / scale;
1428    var sxmin = x0 - invScale * 5;
1429    var symin = y0 - invScale * 10;
1430    var sxmax = x1 + invScale * (6 * decimal_places + 10);
1431    var symax = y1 + invScale * 10;
1432    srcWidth = sxmax - sxmin;
1433    srcHeight = symax - symin;
1434    hscale = usableWidth / srcWidth;
1435    vscale = screenHeight / srcHeight;
1436    scale = Math.min(hscale, vscale);
1437    srcLeft = sxmin;
1438    srcTop = symin;
1439}
1440
1441function drawArc(curve, op, from, to) {
1442    var type = PATH_LINE + (curve.length / 2 - 2);
1443    var pt = pointAtT(curve, type, op ? 0.4 : 0.6);
1444    var dy = pt.y - curve[1];
1445    var dx = pt.x - curve[0];
1446    var dist = Math.sqrt(dy * dy + dx * dx);
1447    var _dist = dist * scale;
1448    var angle = Math.atan2(dy, dx);
1449    var _px = (curve[0] - srcLeft) * scale;
1450    var _py = (curve[1] - srcTop) * scale;
1451    var divisor = 4;
1452    var endDist;
1453    do {
1454        var ends = [];
1455        for (var index = -1; index <= 1; index += 2) {
1456            var px = Math.cos(index * Math.PI / divisor);
1457            var py = Math.sin(index * Math.PI / divisor);
1458            ends.push(px);
1459            ends.push(py);
1460        }
1461        var endDx = (ends[2] - ends[0]) * scale * dist;
1462        var endDy = (ends[3] - ends[1]) * scale * dist;
1463        endDist = Math.sqrt(endDx * endDx + endDy * endDy);
1464        if (endDist < 100) {
1465            break;
1466        }
1467        divisor *= 2;
1468    } while (true);
1469    if (endDist < 30) {
1470        return;
1471    }
1472    if (op) {
1473        divisor *= 2;
1474    }
1475    ctx.strokeStyle = op ? "rgba(210,0,45, 0.4)" : "rgba(90,90,90, 0.5)";
1476    ctx.beginPath();
1477    ctx.arc(_px, _py, _dist, angle - Math.PI / divisor, angle + Math.PI / divisor, false);
1478    ctx.stroke();
1479    var saveAlign = ctx.textAlign;
1480    var saveStyle = ctx.fillStyle;
1481    var saveFont = ctx.font;
1482    ctx.textAlign = "center";
1483    ctx.fillStyle = "black";
1484    ctx.font = "normal 24px Arial";
1485    divisor *= 0.8;
1486    for (var index = -1; index <= 1; index += 2) {
1487        var px = curve[0] + Math.cos(angle + index * Math.PI / divisor) * dist;
1488        var py = curve[1] + Math.sin(angle + index * Math.PI / divisor) * dist;
1489        var _px = (px - srcLeft) * scale;
1490        var _py = (py - srcTop) * scale;
1491        ctx.fillText(index < 0 ? to.toString() : from.toString(), _px, _py + 8);
1492    }
1493    ctx.textAlign = saveAlign;
1494    ctx.fillStyle = saveStyle;
1495    ctx.font = saveFont;
1496}
1497
1498function drawPoint(px, py, end) {
1499    var length = drawnPts.length == 7 ? 6 : drawnPts.length;
1500    for (var pts = 0; pts < length; pts += 2) {
1501        var x = drawnPts[pts];
1502        var y = drawnPts[pts + 1];
1503        if (px == x && py == y) {
1504            return;
1505        }
1506    }
1507    drawnPts.push(px);
1508    drawnPts.push(py);
1509    var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
1510    var _px = (px - srcLeft) * scale;
1511    var _py = (py - srcTop) * scale;
1512    ctx.beginPath();
1513    ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
1514    ctx.closePath();
1515    if (end) {
1516        ctx.fill();
1517    } else {
1518        ctx.stroke();
1519    }
1520    if (debug_xy) {
1521        ctx.textAlign = "left";
1522        ctx.fillText(label, _px + 5, _py);
1523    }
1524}
1525
1526function coordCount(curveType) {
1527    switch (curveType) {
1528        case PATH_LINE:
1529            return 4;
1530        case PATH_QUAD:
1531            return 6;
1532        case PATH_CONIC:
1533            return 6;
1534        case PATH_CUBIC:
1535            return 8;
1536    }
1537    return -1;
1538}
1539
1540function drawPoints(ptArray, curveType, drawControls) {
1541    var count = coordCount(curveType);
1542    for (var idx = 0; idx < count; idx += 2) {
1543        if (!drawControls && idx != 0 && idx != count - 2) {
1544            continue;
1545        }
1546        drawPoint(ptArray[idx], ptArray[idx + 1], idx == 0 || idx == count - 2);
1547    }
1548}
1549
1550function drawControlLines(curve, curveType, drawEnd) {
1551    if (curveType == PATH_LINE) {
1552        return;
1553    }
1554    ctx.strokeStyle = "rgba(0,0,0, 0.3)";
1555    drawLine(curve[0], curve[1], curve[2], curve[3]);
1556    drawLine(curve[2], curve[3], curve[4], curve[5]);
1557    if (curveType == PATH_CUBIC) {
1558        drawLine(curve[4], curve[5], curve[6], curve[7]);
1559        if (drawEnd > 1) {
1560            drawLine(curve[6], curve[7], curve[0], curve[1]);
1561            if (drawEnd > 2) {
1562                drawLine(curve[0], curve[1], curve[4], curve[5]);
1563                drawLine(curve[6], curve[7], curve[2], curve[3]);
1564            }
1565        }
1566    } else if (drawEnd > 1) {
1567        drawLine(curve[4], curve[5], curve[0], curve[1]);
1568    }
1569}
1570
1571function pointAtT(curve, curveType, t) {
1572    var xy = {};
1573    switch (curveType) {
1574        case PATH_LINE:
1575            var a = 1 - t;
1576            var b = t;
1577            xy.x = a * curve[0] + b * curve[2];
1578            xy.y = a * curve[1] + b * curve[3];
1579            break;
1580        case PATH_QUAD:
1581            var one_t = 1 - t;
1582            var a = one_t * one_t;
1583            var b = 2 * one_t * t;
1584            var c = t * t;
1585            xy.x = a * curve[0] + b * curve[2] + c * curve[4];
1586            xy.y = a * curve[1] + b * curve[3] + c * curve[5];
1587            break;
1588        case PATH_CONIC:
1589            var one_t = 1 - t;
1590            var a = one_t * one_t;
1591            var b = 2 * one_t * t;
1592            var c = t * t;
1593            xy.x = a * curve[0] + b * curve[2] * curve[6] + c * curve[4];
1594            xy.y = a * curve[1] + b * curve[3] * curve[6] + c * curve[5];
1595            var d = a + b * curve[6] + c;
1596            xy.x /= d;
1597            xy.y /= d;
1598            break;
1599        case PATH_CUBIC:
1600            var one_t = 1 - t;
1601            var one_t2 = one_t * one_t;
1602            var a = one_t2 * one_t;
1603            var b = 3 * one_t2 * t;
1604            var t2 = t * t;
1605            var c = 3 * one_t * t2;
1606            var d = t2 * t;
1607            xy.x = a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
1608            xy.y = a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
1609            break;
1610    }
1611    return xy;
1612}
1613
1614function drawPointAtT(curve, curveType) {
1615    var x, y;
1616    var xy = pointAtT(curve, curveType, curveT);
1617    drawPoint(xy.x, xy.y, true);
1618    if (!draw_intersectT) {
1619        return;
1620    }
1621    ctx.fillStyle = "red";
1622    drawTAtPointUp(xy.x, xy.y, curveT);
1623}
1624
1625function drawTAtPointUp(px, py, t) {
1626    var label = t.toFixed(decimal_places);
1627    var _px = (px - srcLeft)* scale;
1628    var _py = (py - srcTop) * scale;
1629    ctx.fillText(label, _px + 5, _py - 10);
1630}
1631
1632function drawTAtPointDown(px, py, t) {
1633    var label = t.toFixed(decimal_places);
1634    var _px = (px - srcLeft)* scale;
1635    var _py = (py - srcTop) * scale;
1636    ctx.fillText(label, _px + 5, _py + 10);
1637}
1638
1639function alreadyDrawnLine(x1, y1, x2, y2) {
1640    if (collect_bounds) {
1641        if (focus_enabled) {
1642            focusXmin = Math.min(focusXmin, x1, x2);
1643            focusYmin = Math.min(focusYmin, y1, y2);
1644            focusXmax = Math.max(focusXmax, x1, x2);
1645            focusYmax = Math.max(focusYmax, y1, y2);
1646        }
1647        return true;
1648    }
1649    for (var pts = 0; pts < drawnLines.length; pts += 4) {
1650        if (x1 == drawnLines[pts] && y1 == drawnLines[pts + 1]
1651                && x2 == drawnLines[pts + 2] && y2 == drawnLines[pts + 3]) {
1652            return true;
1653        }
1654    }
1655    drawnLines.push(x1);
1656    drawnLines.push(y1);
1657    drawnLines.push(x2);
1658    drawnLines.push(y2);
1659    return false;
1660}
1661
1662function drawLine(x1, y1, x2, y2) {
1663    if (alreadyDrawnLine(x1, y1, x2, y2)) {
1664        return;
1665    }
1666    ctx.beginPath();
1667    ctx.moveTo((x1 - srcLeft) * scale,
1668            (y1 - srcTop) * scale);
1669    ctx.lineTo((x2 - srcLeft) * scale,
1670            (y2 - srcTop) * scale);
1671    ctx.stroke();
1672}
1673
1674function linePartial(x1, y1, x2, y2, t1, t2) {
1675    var dx = x1 - x2;
1676    var dy = y1 - y2;
1677    var array = [
1678        x1 - t1 * dx,
1679        y1 - t1 * dy,
1680        x1 - t2 * dx,
1681        y1 - t2 * dy
1682    ];
1683    return array;
1684}
1685
1686function drawLinePartial(x1, y1, x2, y2, t1, t2) {
1687    var a = linePartial(x1, y1, x2, y2, t1, t2);
1688    var ax = a[0];
1689    var ay = a[1];
1690    var bx = a[2];
1691    var by = a[3];
1692    if (alreadyDrawnLine(ax, ay, bx, by)) {
1693        return;
1694    }
1695    ctx.beginPath();
1696    ctx.moveTo((ax - srcLeft) * scale,
1697            (ay - srcTop) * scale);
1698    ctx.lineTo((bx - srcLeft) * scale,
1699            (by - srcTop) * scale);
1700    ctx.stroke();
1701}
1702
1703function alreadyDrawnQuad(x1, y1, x2, y2, x3, y3) {
1704    if (collect_bounds) {
1705        if (focus_enabled) {
1706            focusXmin = Math.min(focusXmin, x1, x2, x3);
1707            focusYmin = Math.min(focusYmin, y1, y2, y3);
1708            focusXmax = Math.max(focusXmax, x1, x2, x3);
1709            focusYmax = Math.max(focusYmax, y1, y2, y3);
1710        }
1711        return true;
1712    }
1713    for (var pts = 0; pts < drawnQuads.length; pts += 6) {
1714        if (x1 == drawnQuads[pts] && y1 == drawnQuads[pts + 1]
1715                && x2 == drawnQuads[pts + 2] && y2 == drawnQuads[pts + 3]
1716                && x3 == drawnQuads[pts + 4] && y3 == drawnQuads[pts + 5]) {
1717            return true;
1718        }
1719    }
1720    drawnQuads.push(x1);
1721    drawnQuads.push(y1);
1722    drawnQuads.push(x2);
1723    drawnQuads.push(y2);
1724    drawnQuads.push(x3);
1725    drawnQuads.push(y3);
1726    return false;
1727}
1728
1729function drawQuad(x1, y1, x2, y2, x3, y3) {
1730    if (alreadyDrawnQuad(x1, y1, x2, y2, x3, y3)) {
1731        return;
1732    }
1733    ctx.beginPath();
1734    ctx.moveTo((x1 - srcLeft) * scale,
1735            (y1 - srcTop) * scale);
1736    ctx.quadraticCurveTo((x2 - srcLeft) * scale,
1737            (y2 - srcTop) * scale,
1738            (x3 - srcLeft) * scale,
1739            (y3 - srcTop) * scale);
1740    ctx.stroke();
1741}
1742
1743function interp(A, B, t) {
1744    return A + (B - A) * t;
1745}
1746
1747function interp_quad_coords(x1, x2, x3, t)
1748{
1749    var ab = interp(x1, x2, t);
1750    var bc = interp(x2, x3, t);
1751    var abc = interp(ab, bc, t);
1752    return abc;
1753}
1754
1755function quadPartial(x1, y1, x2, y2, x3, y3, t1, t2) {
1756    var ax = interp_quad_coords(x1, x2, x3, t1);
1757    var ay = interp_quad_coords(y1, y2, y3, t1);
1758    var dx = interp_quad_coords(x1, x2, x3, (t1 + t2) / 2);
1759    var dy = interp_quad_coords(y1, y2, y3, (t1 + t2) / 2);
1760    var cx = interp_quad_coords(x1, x2, x3, t2);
1761    var cy = interp_quad_coords(y1, y2, y3, t2);
1762    var bx = 2*dx - (ax + cx)/2;
1763    var by = 2*dy - (ay + cy)/2;
1764    var array = [
1765        ax, ay, bx, by, cx, cy
1766    ];
1767    return array;
1768}
1769
1770function drawQuadPartial(x1, y1, x2, y2, x3, y3, t1, t2) {
1771    var a = quadPartial(x1, y1, x2, y2, x3, y3, t1, t2);
1772    var ax = a[0];
1773    var ay = a[1];
1774    var bx = a[2];
1775    var by = a[3];
1776    var cx = a[4];
1777    var cy = a[5];
1778    if (alreadyDrawnQuad(ax, ay, bx, by, cx, cy)) {
1779        return;
1780    }
1781    ctx.beginPath();
1782    ctx.moveTo((ax - srcLeft) * scale,
1783            (ay - srcTop) * scale);
1784    ctx.quadraticCurveTo((bx - srcLeft) * scale,
1785            (by - srcTop) * scale,
1786            (cx - srcLeft) * scale,
1787            (cy - srcTop) * scale);
1788    ctx.stroke();
1789}
1790
1791function alreadyDrawnConic(x1, y1, x2, y2, x3, y3, w) {
1792    if (collect_bounds) {
1793        if (focus_enabled) {
1794            focusXmin = Math.min(focusXmin, x1, x2, x3);
1795            focusYmin = Math.min(focusYmin, y1, y2, y3);
1796            focusXmax = Math.max(focusXmax, x1, x2, x3);
1797            focusYmax = Math.max(focusYmax, y1, y2, y3);
1798        }
1799        return true;
1800    }
1801    for (var pts = 0; pts < drawnConics.length; pts += 8) {
1802        if (x1 == drawnConics[pts] && y1 == drawnCubics[pts + 1]
1803                && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
1804                && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
1805                && w == drawnCubics[pts + 6]) {
1806            return true;
1807        }
1808    }
1809    drawnConics.push(x1);
1810    drawnConics.push(y1);
1811    drawnConics.push(x2);
1812    drawnConics.push(y2);
1813    drawnConics.push(x3);
1814    drawnConics.push(y3);
1815    drawnCubics.push(w);
1816    return false;
1817}
1818
1819var kMaxConicToQuadPOW2 = 5;
1820
1821function computeQuadPOW2(curve, tol) {
1822    var a = curve[6] - 1;
1823    var k = a / (4 * (2 + a));
1824    var x = k * (curve[0] - 2 * curve[2] + curve[4]);
1825    var y = k * (curve[1] - 2 * curve[3] + curve[5]);
1826
1827    var error = Math.sqrt(x * x + y * y);
1828    var pow2;
1829    for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
1830        if (error <= tol) {
1831            break;
1832        }
1833        error *= 0.25;
1834    }
1835    return pow2;
1836}
1837
1838function subdivide_w_value(w) {
1839    return Math.sqrt(0.5 + w * 0.5);
1840}
1841
1842function chop(curve, part1, part2) {
1843    var w = curve[6];
1844    var scale = 1 / (1 + w);
1845    part1[0] = curve[0];
1846    part1[1] = curve[1];
1847    part1[2] = (curve[0] + curve[2] * w) * scale;
1848    part1[3] = (curve[1] + curve[3] * w) * scale;
1849    part1[4] = part2[0] = (curve[0] + (curve[2] * w) * 2 + curve[4]) * scale * 0.5;
1850    part1[5] = part2[1] = (curve[1] + (curve[3] * w) * 2 + curve[5]) * scale * 0.5;
1851    part2[2] = (curve[2] * w + curve[4]) * scale;
1852    part2[3] = (curve[3] * w + curve[5]) * scale;
1853    part2[4] = curve[4];
1854    part2[5] = curve[5];
1855    part1[6] = part2[6] = subdivide_w_value(w);
1856}
1857
1858function subdivide(curve, level, pts) {
1859    if (0 == level) {
1860        pts.push(curve[2]);
1861        pts.push(curve[3]);
1862        pts.push(curve[4]);
1863        pts.push(curve[5]);
1864    } else {
1865        var part1 = [], part2 = [];
1866        chop(curve, part1, part2);
1867        --level;
1868        subdivide(part1, level, pts);
1869        subdivide(part2, level, pts);
1870    }
1871}
1872
1873function chopIntoQuadsPOW2(curve, pow2, pts) {
1874    subdivide(curve, pow2, pts);
1875    return 1 << pow2;
1876}
1877
1878function drawConicWithQuads(x1, y1, x2, y2, x3, y3, w) {
1879    if (alreadyDrawnConic(x1, y1, x2, y2, x3, y3, w)) {
1880        return;
1881    }
1882    ctx.beginPath();
1883    ctx.moveTo((x1 - srcLeft) * scale,
1884            (y1 - srcTop) * scale);
1885    var tol = 1 / scale;
1886    var curve = [x1, y1, x2, y2, x3, y3, w];
1887    var pow2 = computeQuadPOW2(curve, tol);
1888    var pts = [];
1889    chopIntoQuadsPOW2(curve, pow2, pts);
1890    for (var i = 0; i < pts.length; i += 4) {
1891        ctx.quadraticCurveTo(
1892            (pts[i + 0] - srcLeft) * scale, (pts[i + 1] - srcTop) * scale,
1893            (pts[i + 2] - srcLeft) * scale, (pts[i + 3] - srcTop) * scale);
1894    }
1895    ctx.stroke();
1896}
1897
1898function conic_eval_numerator(x1, x2, x3, w, t) {
1899    var src2w = x2 * w;
1900    var C = x1;
1901    var A = x3 - 2 * src2w + C;
1902    var B = 2 * (src2w - C);
1903    return (A * t + B) * t + C;
1904}
1905
1906
1907function conic_eval_denominator(w, t) {
1908    var B = 2 * (w - 1);
1909    var C = 1;
1910    var A = -B;
1911    return (A * t + B) * t + C;
1912}
1913
1914function conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
1915    var ax = conic_eval_numerator(x1, x2, x3, w, t1);
1916    var ay = conic_eval_numerator(y1, y2, y3, w, t1);
1917    var az = conic_eval_denominator(w, t1);
1918    var midT = (t1 + t2) / 2;
1919    var dx = conic_eval_numerator(x1, x2, x3, w, midT);
1920    var dy = conic_eval_numerator(y1, y2, y3, w, midT);
1921    var dz = conic_eval_denominator(w, midT);
1922    var cx = conic_eval_numerator(x1, x2, x3, w, t2);
1923    var cy = conic_eval_numerator(y1, y2, y3, w, t2);
1924    var cz = conic_eval_denominator(w, t2);
1925    var bx = 2 * dx - (ax + cx) / 2;
1926    var by = 2 * dy - (ay + cy) / 2;
1927    var bz = 2 * dz - (az + cz) / 2;
1928    var dt = t2 - t1;
1929    var dt_1 = 1 - dt;
1930    var array = [
1931        ax / az, ay / az, bx / bz, by / bz, cx / cz, cy / cz, 0
1932    ];
1933    var dMidAC = { x:(array[0] + array[4]) / 2, y:(array[1] + array[5]) / 2 };
1934    var dMid = { x:dx / dz, y:dy / dz };
1935    var dWNumer = { x:dMidAC.x - dMid.x, y:dMidAC.y - dMid.y };
1936    var dWDenom = { x:dMid.x - array[2], y:dMid.y - array[3] };
1937    var partW = Math.sqrt(dWNumer.x * dWNumer.x + dWNumer.y * dWNumer.y)
1938                / Math.sqrt(dWDenom.x * dWDenom.x + dWDenom.y * dWDenom.y);
1939    array[6] = partW;
1940    return array;
1941}
1942
1943function drawConicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
1944    var a = conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2);
1945    var ax = a[0];
1946    var ay = a[1];
1947    var bx = a[2];
1948    var by = a[3];
1949    var cx = a[4];
1950    var cy = a[5];
1951    var w_ = a[6];
1952    drawConicWithQuads(ax, ay, bx, by, cx, cy, w_);
1953}
1954
1955function alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
1956    if (collect_bounds) {
1957        if (focus_enabled) {
1958            focusXmin = Math.min(focusXmin, x1, x2, x3, x4);
1959            focusYmin = Math.min(focusYmin, y1, y2, y3, y4);
1960            focusXmax = Math.max(focusXmax, x1, x2, x3, x4);
1961            focusYmax = Math.max(focusYmax, y1, y2, y3, y4);
1962        }
1963        return true;
1964    }
1965    for (var pts = 0; pts < drawnCubics.length; pts += 8) {
1966        if (x1 == drawnCubics[pts] && y1 == drawnCubics[pts + 1]
1967                && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
1968                && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
1969                && x4 == drawnCubics[pts + 6] && y4 == drawnCubics[pts + 7]) {
1970            return true;
1971        }
1972    }
1973    drawnCubics.push(x1);
1974    drawnCubics.push(y1);
1975    drawnCubics.push(x2);
1976    drawnCubics.push(y2);
1977    drawnCubics.push(x3);
1978    drawnCubics.push(y3);
1979    drawnCubics.push(x4);
1980    drawnCubics.push(y4);
1981    return false;
1982}
1983
1984function drawCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
1985    if (alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4)) {
1986        return;
1987    }
1988    ctx.beginPath();
1989    ctx.moveTo((x1 - srcLeft) * scale,
1990            (y1 - srcTop) * scale);
1991    ctx.bezierCurveTo((x2 - srcLeft) * scale,
1992            (y2 - srcTop) * scale,
1993            (x3 - srcLeft) * scale,
1994            (y3 - srcTop) * scale,
1995            (x4 - srcLeft) * scale,
1996            (y4 - srcTop) * scale);
1997    ctx.stroke();
1998}
1999
2000function interp_cubic_coords(x1, x2, x3, x4, t)
2001{
2002    var ab = interp(x1, x2, t);
2003    var bc = interp(x2, x3, t);
2004    var cd = interp(x3, x4, t);
2005    var abc = interp(ab, bc, t);
2006    var bcd = interp(bc, cd, t);
2007    var abcd = interp(abc, bcd, t);
2008    return abcd;
2009}
2010
2011function cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
2012    var ax = interp_cubic_coords(x1, x2, x3, x4, t1);
2013    var ay = interp_cubic_coords(y1, y2, y3, y4, t1);
2014    var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3);
2015    var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3);
2016    var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3);
2017    var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3);
2018    var dx = interp_cubic_coords(x1, x2, x3, x4, t2);
2019    var dy = interp_cubic_coords(y1, y2, y3, y4, t2);
2020    var mx = ex * 27 - ax * 8 - dx;
2021    var my = ey * 27 - ay * 8 - dy;
2022    var nx = fx * 27 - ax - dx * 8;
2023    var ny = fy * 27 - ay - dy * 8;
2024    var bx = (mx * 2 - nx) / 18;
2025    var by = (my * 2 - ny) / 18;
2026    var cx = (nx * 2 - mx) / 18;
2027    var cy = (ny * 2 - my) / 18;
2028    var array = [
2029        ax, ay, bx, by, cx, cy, dx, dy
2030    ];
2031    return array;
2032}
2033
2034function drawCubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
2035    var a = cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
2036    var ax = a[0];
2037    var ay = a[1];
2038    var bx = a[2];
2039    var by = a[3];
2040    var cx = a[4];
2041    var cy = a[5];
2042    var dx = a[6];
2043    var dy = a[7];
2044    if (alreadyDrawnCubic(ax, ay, bx, by, cx, cy, dx, dy)) {
2045        return;
2046    }
2047    ctx.beginPath();
2048    ctx.moveTo((ax - srcLeft) * scale,
2049            (ay - srcTop) * scale);
2050    ctx.bezierCurveTo((bx - srcLeft) * scale,
2051            (by - srcTop) * scale,
2052            (cx - srcLeft) * scale,
2053            (cy - srcTop) * scale,
2054            (dx - srcLeft) * scale,
2055            (dy - srcTop) * scale);
2056    ctx.stroke();
2057}
2058
2059function drawCurve(c) {
2060    switch (c.length) {
2061        case 4:
2062            drawLine(c[0], c[1], c[2], c[3]);
2063            break;
2064        case 6:
2065            drawQuad(c[0], c[1], c[2], c[3], c[4], c[5]);
2066            break;
2067        case 7:
2068            drawConicWithQuads(c[0], c[1], c[2], c[3], c[4], c[5], c[6]);
2069            break;
2070        case 8:
2071            drawCubic(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
2072            break;
2073    }
2074}
2075
2076function boundsWidth(pts) {
2077    var min = pts[0];
2078    var max = pts[0];
2079    var length = pts.length == 7 ? 6 : pts.length;
2080    for (var idx = 2; idx < length; idx += 2) {
2081        min = Math.min(min, pts[idx]);
2082        max = Math.max(max, pts[idx]);
2083    }
2084    return max - min;
2085}
2086
2087function boundsHeight(pts) {
2088    var min = pts[1];
2089    var max = pts[1];
2090    var length = pts.length == 7 ? 6 : pts.length;
2091    for (var idx = 3; idx < length; idx += 2) {
2092        min = Math.min(min, pts[idx]);
2093        max = Math.max(max, pts[idx]);
2094    }
2095    return max - min;
2096}
2097
2098function tangent(pts) {
2099    var dx = pts[2] - pts[0];
2100    var dy = pts[3] - pts[1];
2101    if (dx == 0 && dy == 0 && pts.length > 4) {
2102        dx = pts[4] - pts[0];
2103        dy = pts[5] - pts[1];
2104        if (dx == 0 && dy == 0 && pts.length == 8) {
2105            dx = pts[6] - pts[0];
2106            dy = pts[7] - pts[1];
2107        }
2108    }
2109    return Math.atan2(-dy, dx);
2110}
2111
2112function hodograph(cubic) {
2113    var hodo = [];
2114    hodo[0] = 3 * (cubic[2] - cubic[0]);
2115    hodo[1] = 3 * (cubic[3] - cubic[1]);
2116    hodo[2] = 3 * (cubic[4] - cubic[2]);
2117    hodo[3] = 3 * (cubic[5] - cubic[3]);
2118    hodo[4] = 3 * (cubic[6] - cubic[4]);
2119    hodo[5] = 3 * (cubic[7] - cubic[5]);
2120    return hodo;
2121}
2122
2123function hodograph2(cubic) {
2124    var quad = hodograph(cubic);
2125    var hodo = [];
2126    hodo[0] = 2 * (quad[2] - quad[0]);
2127    hodo[1] = 2 * (quad[3] - quad[1]);
2128    hodo[2] = 2 * (quad[4] - quad[2]);
2129    hodo[3] = 2 * (quad[5] - quad[3]);
2130    return hodo;
2131}
2132
2133function quadraticRootsReal(A, B, C, s) {
2134    if (A == 0) {
2135        if (B == 0) {
2136            s[0] = 0;
2137            return C == 0;
2138        }
2139        s[0] = -C / B;
2140        return 1;
2141    }
2142    /* normal form: x^2 + px + q = 0 */
2143    var p = B / (2 * A);
2144    var q = C / A;
2145    var p2 = p * p;
2146    if (p2 < q) {
2147        return 0;
2148    }
2149    var sqrt_D = 0;
2150    if (p2 > q) {
2151        sqrt_D = sqrt(p2 - q);
2152    }
2153    s[0] = sqrt_D - p;
2154    s[1] = -sqrt_D - p;
2155    return 1 + s[0] != s[1];
2156}
2157
2158function add_valid_ts(s, realRoots, t) {
2159    var foundRoots = 0;
2160    for (var index = 0; index < realRoots; ++index) {
2161        var tValue = s[index];
2162        if (tValue >= 0 && tValue <= 1) {
2163            for (var idx2 = 0; idx2 < foundRoots; ++idx2) {
2164                if (t[idx2] != tValue) {
2165                    t[foundRoots++] = tValue;
2166                }
2167            }
2168        }
2169    }
2170    return foundRoots;
2171}
2172
2173function quadraticRootsValidT(a, b, c, t) {
2174    var s = [];
2175    var realRoots = quadraticRootsReal(A, B, C, s);
2176    var foundRoots = add_valid_ts(s, realRoots, t);
2177    return foundRoots != 0;
2178}
2179
2180function find_cubic_inflections(cubic, tValues) {
2181    var Ax = src[2] - src[0];
2182    var Ay = src[3] - src[1];
2183    var Bx = src[4] - 2 * src[2] + src[0];
2184    var By = src[5] - 2 * src[3] + src[1];
2185    var Cx = src[6] + 3 * (src[2] - src[4]) - src[0];
2186    var Cy = src[7] + 3 * (src[3] - src[5]) - src[1];
2187    return quadraticRootsValidT(Bx * Cy - By * Cx, (Ax * Cy - Ay * Cx),
2188            Ax * By - Ay * Bx, tValues);
2189}
2190
2191function dxy_at_t(curve, type, t) {
2192    var dxy = {};
2193    if (type == PATH_LINE) {
2194        dxy.x = curve[2] - curve[0];
2195        dxy.y = curve[3] - curve[1];
2196    } else if (type == PATH_QUAD) {
2197        var a = t - 1;
2198        var b = 1 - 2 * t;
2199        var c = t;
2200        dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
2201        dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
2202    } else if (type == PATH_CONIC) {
2203        var p20x = curve[4] - curve[0];
2204        var p20y = curve[5] - curve[1];
2205        var p10xw = (curve[2] - curve[0]) * curve[6];
2206        var p10yw = (curve[3] - curve[1]) * curve[6];
2207        var coeff0x = curve[6] * p20x - p20x;
2208        var coeff0y = curve[6] * p20y - p20y;
2209        var coeff1x = p20x - 2 * p10xw;
2210        var coeff1y = p20y - 2 * p10yw;
2211        dxy.x = t * (t * coeff0x + coeff1x) + p10xw;
2212        dxy.y = t * (t * coeff0y + coeff1y) + p10yw;
2213    } else if (type == PATH_CUBIC) {
2214        var one_t = 1 - t;
2215        var a = curve[0];
2216        var b = curve[2];
2217        var c = curve[4];
2218        var d = curve[6];
2219        dxy.x = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
2220        a = curve[1];
2221        b = curve[3];
2222        c = curve[5];
2223        d = curve[7];
2224        dxy.y = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
2225    }
2226    return dxy;
2227}
2228
2229function dpt_at_t(curve, t) {
2230    var type = PATH_LINE + (curve.length / 2 - 2);
2231    return dxy_at_t(curve, type, t);
2232}
2233
2234function drawLabel(num, px, py) {
2235    ctx.beginPath();
2236    ctx.arc(px, py, 8, 0, Math.PI*2, true);
2237    ctx.closePath();
2238    ctx.strokeStyle = "rgba(0,0,0, 0.4)";
2239    ctx.lineWidth = num == 0 || num == 3 ? 2 : 1;
2240    ctx.stroke();
2241    ctx.fillStyle = "black";
2242    ctx.font = "normal 10px Arial";
2243  //  ctx.rotate(0.001);
2244    ctx.fillText(num, px - 2, py + 3);
2245  //  ctx.rotate(-0.001);
2246}
2247
2248function drawLabelX(ymin, num, loc) {
2249    var px = (loc - srcLeft) * scale;
2250    var py = (ymin - srcTop) * scale - 20;
2251    drawLabel(num, px, py);
2252}
2253
2254function drawLabelY(xmin, num, loc) {
2255    var px = (xmin - srcLeft) * scale - 20;
2256    var py = (loc - srcTop) * scale;
2257    drawLabel(num, px, py);
2258}
2259
2260function drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY) {
2261    ctx.beginPath();
2262    ctx.moveTo(hx, hy - 100);
2263    ctx.lineTo(hx, hy);
2264    ctx.strokeStyle = hMinY < 0 ? "green" : "blue";
2265    ctx.stroke();
2266    ctx.beginPath();
2267    ctx.moveTo(hx, hy);
2268    ctx.lineTo(hx, hy + 100);
2269    ctx.strokeStyle = hMaxY > 0 ? "green" : "blue";
2270    ctx.stroke();
2271    ctx.beginPath();
2272    ctx.moveTo(hx - 100, hy);
2273    ctx.lineTo(hx, hy);
2274    ctx.strokeStyle = hMinX < 0 ? "green" : "blue";
2275    ctx.stroke();
2276    ctx.beginPath();
2277    ctx.moveTo(hx, hy);
2278    ctx.lineTo(hx + 100, hy);
2279    ctx.strokeStyle = hMaxX > 0 ? "green" : "blue";
2280    ctx.stroke();
2281}
2282
2283function scalexy(x, y, mag) {
2284    var length = Math.sqrt(x * x + y * y);
2285    return mag / length;
2286}
2287
2288function drawArrow(x, y, dx, dy, s) {
2289    var dscale = scalexy(dx, dy, 1 / scale * 100 * s);
2290    dx *= dscale;
2291    dy *= dscale;
2292    ctx.beginPath();
2293    ctx.moveTo((x - srcLeft) * scale, (y - srcTop) * scale);
2294    x += dx;
2295    y += dy;
2296    ctx.lineTo((x - srcLeft) * scale, (y - srcTop) * scale);
2297    dx /= 10;
2298    dy /= 10;
2299    ctx.lineTo((x - dy - srcLeft) * scale, (y + dx - srcTop) * scale);
2300    ctx.lineTo((x + dx * 2 - srcLeft) * scale, (y + dy * 2 - srcTop) * scale);
2301    ctx.lineTo((x + dy - srcLeft) * scale, (y - dx - srcTop) * scale);
2302    ctx.lineTo((x - srcLeft) * scale, (y - srcTop) * scale);
2303    ctx.strokeStyle = "rgba(0,75,0, 0.4)";
2304    ctx.stroke();
2305}
2306
2307function x_at_t(curve, t) {
2308    var one_t = 1 - t;
2309    if (curve.length == 4) {
2310        return one_t * curve[0] + t * curve[2];
2311    }
2312    var one_t2 = one_t * one_t;
2313    var t2 = t * t;
2314    if (curve.length == 6) {
2315        return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
2316    }
2317    if (curve.length == 7) {
2318        return (one_t2 * curve[0] + 2 * one_t * t * curve[2] * curve[6] + t2 * curve[4])
2319                / (one_t2 +2 * one_t * t * curve[6] + t2);
2320    }
2321    var a = one_t2 * one_t;
2322    var b = 3 * one_t2 * t;
2323    var c = 3 * one_t * t2;
2324    var d = t2 * t;
2325    return a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
2326}
2327
2328function y_at_t(curve, t) {
2329    var one_t = 1 - t;
2330    if (curve.length == 4) {
2331        return one_t * curve[1] + t * curve[3];
2332    }
2333    var one_t2 = one_t * one_t;
2334    var t2 = t * t;
2335    if (curve.length == 6) {
2336        return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
2337    }
2338    if (curve.length == 7) {
2339        return (one_t2 * curve[1] + 2 * one_t * t * curve[3] * curve[6] + t2 * curve[5])
2340                / (one_t2 +2 * one_t * t * curve[6] + t2);
2341    }
2342    var a = one_t2 * one_t;
2343    var b = 3 * one_t2 * t;
2344    var c = 3 * one_t * t2;
2345    var d = t2 * t;
2346    return a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
2347}
2348
2349function pt_at_t(curve, t) {
2350    var pt = {};
2351    pt.x = x_at_t(curve, t);
2352    pt.y = y_at_t(curve, t);
2353    return pt;
2354}
2355
2356function drawOrder(curve, t, label) {
2357    var px = x_at_t(curve, t);
2358    var py = y_at_t(curve, t);
2359    var _px = (px - srcLeft) * scale;
2360    var _py = (py - srcTop) * scale;
2361    ctx.beginPath();
2362    ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
2363    ctx.closePath();
2364    ctx.fillStyle = "white";
2365    ctx.fill();
2366    if (label == 'L') {
2367        ctx.strokeStyle = "rgba(255,0,0, 1)";
2368        ctx.fillStyle = "rgba(255,0,0, 1)";
2369    } else {
2370        ctx.strokeStyle = "rgba(0,0,255, 1)";
2371        ctx.fillStyle = "rgba(0,0,255, 1)";
2372    }
2373    ctx.stroke();
2374    ctx.font = "normal 16px Arial";
2375    ctx.textAlign = "center";
2376    ctx.fillText(label, _px, _py + 5);
2377    ctx.font = "normal 10px Arial";
2378}
2379
2380function drawVisibleOrder(curve, label) {
2381    var s = pt_at_t(curve, 0);
2382    var e = pt_at_t(curve, 1);
2383    var sOn = ptOnScreen(s);
2384    var eOn = ptOnScreen(e);
2385    var defaultT = 0.85;
2386    if (sOn && eOn)
2387        return drawOrder(curve, defaultT, label);
2388    if (sOn || eOn) {
2389        if (eOn) {
2390            defaultT = 1 - defaultT;
2391        }
2392        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
2393        var t = defaultT;
2394        var tries = 16;
2395        do {
2396            var mid = pt_at_t(curve, t);
2397            if (ptOnScreen(mid))
2398                return drawOrder(curve, t, label);
2399            t += step;
2400            step /= 2;
2401        } while (--tries > 0);
2402        drawOrder(curve, defaultT, label);
2403    }
2404    // scattershot until we find a visible point
2405    var denom = 2;  // visit odd number num / denom to hit unique pts
2406    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
2407    do {
2408        for (var numer = 1; numer < denom; numer += 2) {
2409            var t = numer / denom + 0.1;
2410            if (t >= 1) {
2411                break;
2412            }
2413            var mid = pt_at_t(curve, t);
2414            if (ptOnScreen(mid))
2415                return drawOrder(curve, t, label);
2416        }
2417        denom *= 2;
2418    } while (--tries > 0);
2419    drawOrder(curve, defaultT, label);
2420}
2421
2422function set_length(pt, newLen) {
2423    var len = Math.sqrt(pt.x * pt.x + pt.y * pt.y);
2424    var scale = newLen / len;
2425    var newPt = { x: pt.x * scale, y: pt.y * scale };
2426    return newPt;
2427}
2428
2429function drawDirection(curve, t) {
2430    var d = dpt_at_t(curve, t);
2431    d = set_length(d, 16);
2432    var pt = localToGlobal(pt_at_t(curve, t));
2433    ctx.beginPath();
2434    ctx.moveTo(pt.x - d.y, pt.y + d.x);
2435    ctx.lineTo(pt.x + d.x, pt.y + d.y);
2436    ctx.lineTo(pt.x + d.y, pt.y - d.x);
2437    ctx.strokeStyle = "rgba(0,75,0, 0.4)";
2438    ctx.stroke();
2439}
2440
2441function drawVisibleDirection(curve) {
2442    var s = pt_at_t(curve, 0);
2443    var e = pt_at_t(curve, 1);
2444    var sOn = ptOnScreen(s);
2445    var eOn = ptOnScreen(e);
2446    var defaultT = 0.65;
2447    if (sOn && eOn) {
2448        return drawDirection(curve, defaultT);
2449    }
2450    if (sOn || eOn) {
2451        if (eOn) {
2452            defaultT = 1 - defaultT;
2453        }
2454        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
2455        var t = defaultT;
2456        var tries = 16;
2457        do {
2458            var mid = pt_at_t(curve, t);
2459            if (ptOnScreen(mid))
2460                return drawDirection(curve, t);
2461            t += step;
2462            step /= 2;
2463        } while (--tries > 0);
2464        drawDirection(curve, defaultT);
2465    }
2466    // scattershot until we find a visible point
2467    var denom = 2;  // visit odd number num / denom to hit unique pts
2468    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
2469    do {
2470        for (var numer = 1; numer < denom; numer += 2) {
2471            var t = numer / denom + 0.1;
2472            if (t >= 1) {
2473                break;
2474            }
2475            var mid = pt_at_t(curve, t);
2476            if (ptOnScreen(mid))
2477                return drawDirection(curve, t);
2478        }
2479        denom *= 2;
2480    } while (--tries > 0);
2481    drawDirection(curve, defaultT);
2482}
2483
2484function drawID(curve, t, id) {
2485    var px = x_at_t(curve, t);
2486    var py = y_at_t(curve, t);
2487    var _px = (px - srcLeft) * scale;
2488    var _py = (py - srcTop) * scale;
2489    draw_id_at(id, _px, _py);
2490}
2491
2492function localToGlobal(local) {
2493    var global = {};
2494    global.x = (local.x - srcLeft) * scale;
2495    global.y = (local.y - srcTop) * scale;
2496    return global;
2497}
2498
2499function ptOnScreen(local) {
2500    var pt = localToGlobal(local);
2501    return 10 <= pt.x && pt.x <= screenWidth - 10
2502        && 10 <= pt.y && pt.y <= screenHeight - 10;
2503}
2504
2505function drawVisibleID(curve, defaultT, id) {
2506    // determine if either or both ends are visible
2507    var s = pt_at_t(curve, 0);
2508    var e = pt_at_t(curve, 1);
2509    var sOn = ptOnScreen(s);
2510    var eOn = ptOnScreen(e);
2511    if (sOn && eOn)
2512        return drawID(curve, defaultT, id);
2513    if (sOn || eOn) {
2514        var step = sOn ? -defaultT / 2 : (1 - defaultT) / 2;
2515        var t = defaultT;
2516        var tries = 16;
2517        do {
2518            var mid = pt_at_t(curve, t);
2519            if (ptOnScreen(mid))
2520                return drawID(curve, t, id);
2521            t += step;
2522            step /= 2;
2523        } while (--tries > 0);
2524        drawID(curve, defaultT, id);
2525    }
2526    // scattershot until we find a visible point
2527    var denom = 2;  // visit odd number num / denom to hit unique pts
2528    var tries = 6; // tries 1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16 ...
2529    do {
2530        for (var numer = 1; numer < denom; numer += 2) {
2531            var t = numer / denom;
2532            var mid = pt_at_t(curve, t);
2533            if (ptOnScreen(mid))
2534                return drawID(curve, t, id);
2535        }
2536        denom *= 2;
2537    } while (--tries > 0);
2538    drawID(curve, defaultT, id);
2539}
2540
2541function draw_id_at(id, _px, _py) {
2542    ctx.beginPath();
2543    ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
2544    ctx.closePath();
2545    ctx.fillStyle = "white";
2546    ctx.fill();
2547    ctx.strokeStyle = "rgba(127,127,0, 1)";
2548    ctx.fillStyle = "rgba(127,127,0, 1)";
2549    ctx.stroke();
2550    ctx.font = "normal 16px Arial";
2551    ctx.textAlign = "center";
2552    ctx.fillText(id, _px, _py + 5);
2553    ctx.font = "normal 10px Arial";
2554}
2555
2556function drawLinePartialID(id, x1, y1, x2, y2, t1, t2) {
2557    var curve = [x1, y1, x2, y2];
2558    drawCurvePartialID(id, curve, t1, t2);
2559}
2560
2561function drawLineID(id, x1, y1, x2, y2) {
2562    drawLinePartialID(id, x1, y1, x2, y2, 0, 1);
2563}
2564
2565function drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, t1, t2) {
2566    var curve = [x1, y1, x2, y2, x3, y3];
2567    drawCurvePartialID(id, curve, t1, t2);
2568}
2569
2570function drawQuadID(id, x1, y1, x2, y2, x3, y3) {
2571    drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, 0, 1);
2572}
2573
2574function drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, t1, t2) {
2575    var curve = [x1, y1, x2, y2, x3, y3, w];
2576    drawCurvePartialID(id, curve, t1, t2);
2577}
2578
2579function drawConicID(id, x1, y1, x2, y2, x3, y3, w) {
2580    drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, 0, 1);
2581}
2582
2583function drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
2584    var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
2585    drawCurvePartialID(id, curve, t1, t2);
2586}
2587
2588function drawCubicID(id, x1, y1, x2, y2, x3, y3, x4, y4) {
2589    drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, 0, 1);
2590}
2591
2592function  drawCurvePartialID(id, curve, t1, t2) {
2593    drawVisibleID(curve, (t1 + t2) / 2, id);
2594}
2595
2596function drawCurveSpecials(test, curve, type) {
2597    if (pt_labels) {
2598        drawPoints(curve, type, pt_labels == 2);
2599    }
2600    if (control_lines != 0) {
2601        drawControlLines(curve, type, control_lines);
2602    }
2603    if (curve_t) {
2604        drawPointAtT(curve, type);
2605    }
2606    if (draw_midpoint) {
2607        var mid = pointAtT(curve, type, 0.5);
2608        drawPoint(mid.x, mid.y, true);
2609    }
2610    if (draw_id) {
2611        var id = idByCurve(test, curve, type);
2612        if (id >= 0) {
2613            drawVisibleID(curve, 0.5, id);
2614        }
2615    }
2616    if (draw_direction) {
2617        drawVisibleDirection(curve);
2618    }
2619    if (type == PATH_LINE) {
2620        return;
2621    }
2622    if (draw_deriviatives > 0) {
2623        var d = dxy_at_t(curve, type, 0);
2624        drawArrow(curve[0], curve[1], d.x, d.y, 1);
2625        if (draw_deriviatives == 2) {
2626            d = dxy_at_t(curve, type, 1);
2627            if (type == PATH_CUBIC) {
2628                drawArrow(curve[6], curve[7], d.x, d.y, 1);
2629            } else {
2630                drawArrow(curve[4], curve[5], d.x, d.y, 1);
2631            }
2632        }
2633        if (draw_midpoint) {
2634            var mid = pointAtT(curve, type, 0.5);
2635            d = dxy_at_t(curve, type, 0.5);
2636            drawArrow(mid.x, mid.y, d.x, d.y, 1);
2637        }
2638    }
2639    if (type != PATH_CUBIC) {
2640        return;
2641    }
2642    if (draw_sequence) {
2643        var ymin = Math.min(curve[1], curve[3], curve[5], curve[7]);
2644        for (var i = 0; i < 8; i+= 2) {
2645            drawLabelX(ymin, i >> 1, curve[i]);
2646        }
2647        var xmin = Math.min(curve[0], curve[2], curve[4], curve[6]);
2648        for (var i = 1; i < 8; i+= 2) {
2649            drawLabelY(xmin, i >> 1, curve[i]);
2650        }
2651    }
2652}
2653
2654function logCurves(test) {
2655    for (curves in test) {
2656        var curve = test[curves];
2657        dumpCurve(curve);
2658    }
2659}
2660
2661function curveToString(curve) {
2662    var str = "{{";
2663    var length = curve.length == 7 ? 6 : curve.length;
2664    if (curve.length == 7) {
2665        str += "{";
2666    }
2667    for (i = 0; i < length; i += 2) {
2668        str += curve[i].toFixed(decimal_places) + "," + curve[i + 1].toFixed(decimal_places);
2669        if (i < curve.length - 2) {
2670            str += "}, {";
2671        }
2672    }
2673    str += "}";
2674    if (curve.length == 7) {
2675        str += "}, " + curve[6].toFixed(decimal_places);
2676    }
2677    str += "}";
2678    return str;
2679}
2680
2681function dumpCurve(curve) {
2682    console.log(curveToString(curve));
2683}
2684
2685function draw(test, lines, title) {
2686    ctx.fillStyle = "rgba(0,0,0, 0.1)";
2687    ctx.font = "normal 50px Arial";
2688    ctx.textAlign = "left";
2689    ctx.fillText(title, 50, 50);
2690    ctx.font = "normal 10px Arial";
2691    ctx.lineWidth = "1.001"; "0.999";
2692    var secondPath = test.length;
2693    var closeCount = 0;
2694    logStart = -1;
2695    logRange = 0;
2696    // find last active rec type at this step
2697    var curType = test[0];
2698    var curStep = 0;
2699    var hasOp = false;
2700    var lastActive = 0;
2701    var lastAdd = 0;
2702    var lastCoin = 0;
2703    var lastSect = 0;
2704    var lastSort = 0;
2705    var lastMark = 0;
2706    var lastTop = 0;
2707    activeCount = 0;
2708    addCount = 0;
2709    angleCount = 0;
2710    opCount = 0;
2711    sectCount = 0;
2712    sortCount = 0;
2713    topCount = 0;
2714    markCount = 0;
2715    activeMax = 0;
2716    addMax = 0;
2717    angleMax = 0;
2718    coinMax = 0;
2719    opMax = 0;
2720    sectMax = 0;
2721    sectMax2 = 0;
2722    sortMax = 0;
2723    topMax = 0;
2724    markMax = 0;
2725    lastIndex = test.length - 3;
2726    for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
2727        var recType = test[tIndex];
2728        if (!typeof recType == 'number' || recType < REC_TYPE_UNKNOWN || recType > REC_TYPE_LAST) {
2729            console.log("unknown rec type: " + recType);
2730            throw "stop execution";
2731        }
2732   //     if (curType == recType && curType != REC_TYPE_ADD) {
2733   //         continue;
2734   //     }
2735        var inStepRange = step_limit == 0 || curStep < step_limit;
2736        curType = recType;
2737        if (recType == REC_TYPE_OP) {
2738            hasOp = true;
2739            continue;
2740        }
2741        if (recType == REC_TYPE_UNKNOWN) {
2742            // these types do not advance step
2743            continue;
2744        }
2745        var bumpStep = false;
2746        var records = test[tIndex + 2];
2747        var fragType = records[0];
2748        if (recType == REC_TYPE_ADD) {
2749            if (records.length != 2) {
2750                console.log("expect only two elements: " + records.length);
2751                throw "stop execution";
2752            }
2753            if (fragType == ADD_MOVETO || fragType == ADD_CLOSE) {
2754                continue;
2755            }
2756            ++addMax;
2757            if (!draw_add || !inStepRange) {
2758                continue;
2759            }
2760            lastAdd = tIndex;
2761            ++addCount;
2762            bumpStep = true;
2763        }
2764        if (recType == REC_TYPE_PATH && hasOp) {
2765            secondPath = tIndex;
2766        }
2767        if (recType == REC_TYPE_PATH2 && hasOp) {
2768            secondPath = tIndex;
2769        }
2770        if (recType == REC_TYPE_ACTIVE) {
2771            ++activeMax;
2772            if (!draw_active || !inStepRange) {
2773                continue;
2774            }
2775            lastActive = tIndex;
2776            ++activeCount;
2777            bumpStep = true;
2778        }
2779        if (recType == REC_TYPE_ACTIVE_OP) {
2780            ++opMax;
2781            if (!draw_op || !inStepRange) {
2782                continue;
2783            }
2784            lastOp = tIndex;
2785            ++opCount;
2786            bumpStep = true;
2787        }
2788        if (recType == REC_TYPE_AFTERPART) {
2789            if (draw_angle != 3 || !inStepRange) {
2790                continue;
2791            }
2792            lastAngle = tIndex;
2793            ++angleCount;
2794            bumpStep = true;
2795        }
2796        if (recType == REC_TYPE_ANGLE) {
2797            ++angleMax;
2798            if (draw_angle == 0 || draw_angle == 3 || !inStepRange) {
2799                continue;
2800            }
2801            lastAngle = tIndex;
2802            ++angleCount;
2803            bumpStep = true;
2804        }
2805        if (recType == REC_TYPE_COINCIDENCE) {
2806            ++coinMax;
2807            if (!draw_coincidence || !inStepRange) {
2808                continue;
2809            }
2810            lastCoin = tIndex;
2811            ++coinCount;
2812            bumpStep = true;
2813        }
2814        if (recType == REC_TYPE_SECT) {
2815            if (records.length != 2) {
2816                console.log("expect only two elements: " + records.length);
2817                throw "stop execution";
2818            }
2819            ++sectMax;
2820            var sectBump = 1;
2821            switch (fragType) {
2822                case INTERSECT_LINE:
2823                case INTERSECT_QUAD_LINE:
2824                case INTERSECT_QUAD:
2825                case INTERSECT_CONIC_LINE:
2826                case INTERSECT_CONIC_QUAD:
2827                case INTERSECT_CONIC:
2828                case INTERSECT_SELF_CUBIC:
2829                case INTERSECT_CUBIC_LINE:
2830                case INTERSECT_CUBIC_QUAD:
2831                case INTERSECT_CUBIC:
2832                    sectBump = 1;
2833                    break;
2834                case INTERSECT_LINE_2:
2835                case INTERSECT_QUAD_LINE_2:
2836                case INTERSECT_QUAD_2:
2837                case INTERSECT_CONIC_LINE_2:
2838                case INTERSECT_CONIC_QUAD_2:
2839                case INTERSECT_CONIC_2:
2840                case INTERSECT_CUBIC_LINE_2:
2841                case INTERSECT_CUBIC_QUAD_2:
2842                case INTERSECT_CUBIC_2:
2843                    sectBump = 2;
2844                    break;
2845                case INTERSECT_LINE_NO:
2846                case INTERSECT_QUAD_LINE_NO:
2847                case INTERSECT_QUAD_NO:
2848                case INTERSECT_CONIC_LINE_NO:
2849                case INTERSECT_CONIC_QUAD_NO:
2850                case INTERSECT_CONIC_NO:
2851                case INTERSECT_SELF_CUBIC_NO:
2852                case INTERSECT_CUBIC_LINE_NO:
2853                case INTERSECT_CUBIC_QUAD_NO:
2854                case INTERSECT_CUBIC_NO:
2855                    sectBump = 0;
2856                    break;
2857                case INTERSECT_CONIC_QUAD_3:
2858                case INTERSECT_CUBIC_LINE_3:
2859                case INTERSECT_CUBIC_QUAD_3:
2860                case INTERSECT_CUBIC_3:
2861                    sectBump = 3;
2862                    break;
2863                case INTERSECT_CONIC_QUAD_4:
2864                case INTERSECT_CUBIC_QUAD_4:
2865                case INTERSECT_CUBIC_4:
2866                    sectBump = 4;
2867                    break;
2868                default:
2869                    console.log("missing case " + records.length);
2870                    throw "stop execution";
2871            }
2872            sectMax2 += sectBump;
2873            if (draw_intersection <= 1 || !inStepRange) {
2874                continue;
2875            }
2876            lastSect = tIndex;
2877            sectCount += sectBump;
2878            bumpStep = true;
2879        }
2880        if (recType == REC_TYPE_SORT) {
2881            ++sortMax;
2882            if (!draw_sort || !inStepRange) {
2883                continue;
2884            }
2885            lastSort = tIndex;
2886            ++sortCount;
2887            bumpStep = true;
2888        }
2889        if (recType == REC_TYPE_TOP) {
2890            ++topMax;
2891            if (!draw_top || !inStepRange) {
2892                continue;
2893            }
2894            lastTop = tIndex;
2895            ++topCount;
2896            bumpStep = true;
2897        }
2898        if (recType == REC_TYPE_MARK) {
2899            ++markMax;
2900            if (!draw_mark || !inStepRange) {
2901                continue;
2902            }
2903            lastMark = tIndex;
2904            ++markCount;
2905            bumpStep = true;
2906        }
2907        if (bumpStep) {
2908            lastIndex = tIndex;
2909            logStart = test[tIndex + 1];
2910            logRange = records.length / 2;
2911            ++curStep;
2912        }
2913    }
2914    stepMax = (draw_add ? addMax : 0)
2915            + (draw_active ? activeMax : 0)
2916            + (draw_angle ? angleMax : 0)
2917            + (draw_coincidence ? coinMax : 0)
2918            + (draw_op ? opMax : 0)
2919            + (draw_sort ? sortMax : 0)
2920            + (draw_top ? topMax : 0)
2921            + (draw_mark ? markMax : 0)
2922            + (draw_intersection == 2 ? sectMax : draw_intersection == 3 ? sectMax2 : 0);
2923    if (stepMax == 0) {
2924        stepMax = addMax + activeMax + angleMax + coinMax + opMax + sortMax + topMax + markMax;
2925    }
2926    drawnPts = [];
2927    drawnLines = [];
2928    drawnQuads = [];
2929    drawnConics = [];
2930    drawnCubics = [];
2931    focusXmin = focusYmin = Infinity;
2932    focusXmax = focusYmax = -Infinity;
2933    var pathIndex = 0;
2934    var opLetter = 'S';
2935    for (var tIndex = lastIndex; tIndex >= 0; tIndex -= 3) {
2936        var recType = test[tIndex];
2937        var records = test[tIndex + 2];
2938        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
2939            var fragType = records[recordIndex];
2940            if (!typeof fragType == 'number' || fragType < 1 || fragType > FRAG_TYPE_LAST) {
2941                console.log("unknown in range frag type: " + fragType);
2942                throw "stop execution";
2943            }
2944            var frags = records[recordIndex + 1];
2945            focus_enabled = false;
2946            switch (recType) {
2947                case REC_TYPE_COMPUTED:
2948                    if (draw_computed == 0) {
2949                        continue;
2950                    }
2951                    ctx.lineWidth = 1;
2952                    ctx.strokeStyle = pathIndex == 0 ? "black" : "red";
2953                    ctx.fillStyle = "blue";
2954                    var drawThis = false;
2955                    switch (fragType) {
2956                        case PATH_QUAD:
2957                            if ((draw_computed & 0x9) == 1 || ((draw_computed & 8) != 0
2958                                    && (draw_computed & 7) == pathIndex)) {
2959                                drawQuad(frags[0], frags[1], frags[2], frags[3],
2960                                        frags[4], frags[5]);
2961                                drawThis = true;
2962                            }
2963                            break;
2964                        case PATH_CONIC:
2965                            if ((draw_computed & 0xA) == 2 || ((draw_computed & 8) != 0
2966                                    && (draw_computed & 7) == pathIndex)) {
2967                                drawConicWithQuads(frags[0], frags[1], frags[2], frags[3],
2968                                        frags[4], frags[5], frags[6]);
2969                                drawThis = true;
2970                            }
2971                            break;
2972                        case PATH_CUBIC:
2973                            if ((draw_computed & 0xC) == 4 || ((draw_computed & 8) != 0
2974                                     && (draw_computed & 7) == pathIndex)) {
2975                                drawCubic(frags[0], frags[1], frags[2], frags[3],
2976                                        frags[4], frags[5], frags[6], frags[7]);
2977                                drawThis = true;
2978                            }
2979                            ++pathIndex;
2980                            break;
2981                        case COMPUTED_SET_1:
2982                            pathIndex = 0;
2983                            break;
2984                        case COMPUTED_SET_2:
2985                            pathIndex = 1;
2986                            break;
2987                        default:
2988                            console.log("unknown REC_TYPE_COMPUTED frag type: " + fragType);
2989                            throw "stop execution";
2990                    }
2991                    if (!drawThis || collect_bounds) {
2992                        break;
2993                    }
2994                    drawCurveSpecials(test, frags, fragType);
2995                    break;
2996                case REC_TYPE_ALIGNED:
2997                    if (draw_path < 4) {
2998                        continue;
2999                    }
3000                case REC_TYPE_PATH:
3001                case REC_TYPE_PATH2:
3002                    if (REC_TYPE_ALIGNED != recType && draw_path >= 4) {
3003                        continue;
3004                    }
3005                    if (!draw_path) {
3006                        continue;
3007                    }
3008                    var firstPath = tIndex < secondPath;
3009                    if ((draw_path & (firstPath ? 1 : 2)) == 0) {
3010                        continue;
3011                    }
3012                    ctx.lineWidth = 1;
3013                    ctx.strokeStyle = firstPath ? "black" : "red";
3014                    ctx.fillStyle = "blue";
3015                    var frags2 = [];
3016                    switch (fragType) {
3017                        case PATH_LINE:
3018                            for (var i = 0; i < 4; ++ i) { frags2[i] = frags[i + 1]; }
3019                            drawLine(frags2[0], frags2[1], frags2[2], frags2[3]);
3020                            break;
3021                        case PATH_QUAD:
3022                            for (var i = 0; i < 6; ++ i) { frags2[i] = frags[i + 1]; }
3023                            drawQuad(frags2[0], frags2[1], frags2[2], frags2[3],
3024                                    frags2[4], frags2[5]);
3025                            break;
3026                        case PATH_CONIC:
3027                            for (var i = 0; i < 7; ++ i) { frags2[i] = frags[i + 1]; }
3028                            drawConicWithQuads(frags2[0], frags2[1], frags2[2], frags2[3],
3029                                    frags2[4], frags2[5], frags2[6]);
3030                            break;
3031                        case PATH_CUBIC:
3032                            for (var i = 0; i < 8; ++ i) { frags2[i] = frags[i + 1]; }
3033                            drawCubic(frags2[0], frags2[1], frags2[2], frags2[3],
3034                                    frags2[4], frags2[5], frags2[6], frags2[7]);
3035                            break;
3036                        default:
3037                            console.log("unknown " + recType + " frag type: " + fragType);
3038                            throw "stop execution";
3039                    }
3040                    if (collect_bounds) {
3041                        break;
3042                    }
3043                    drawCurveSpecials(test, frags2, fragType);
3044                    break;
3045                case REC_TYPE_OP:
3046                    switch (fragType) {
3047                        case OP_INTERSECT: opLetter = 'I'; break;
3048                        case OP_DIFFERENCE: opLetter = 'D'; break;
3049                        case OP_UNION: opLetter = 'U'; break;
3050                        case OP_XOR: opLetter = 'X'; break;
3051                        default:
3052                            console.log("unknown REC_TYPE_OP frag type: " + fragType);
3053                            throw "stop execution";
3054                    }
3055                    break;
3056                case REC_TYPE_ACTIVE:
3057                    if (!draw_active || (step_limit > 0 && tIndex < lastActive)) {
3058                        continue;
3059                    }
3060                    var x1 = frags[SPAN_X1];
3061                    var y1 = frags[SPAN_Y1];
3062                    var x2 = frags[SPAN_X2];
3063                    var y2 = frags[SPAN_Y2];
3064                    var x3, y3, x3, y4, w;
3065                    ctx.lineWidth = 3;
3066                    ctx.strokeStyle = "rgba(0,0,255, 0.3)";
3067                    focus_enabled = true;
3068                    switch (fragType) {
3069                        case ACTIVE_LINE_SPAN:
3070                            drawLine(x1, y1, x2, y2);
3071                            if (draw_id) {
3072                                drawLineID(frags[0], x1, y1, x2, y2);
3073                            }
3074                            if (pt_labels) {
3075                                var curve = [x1, y1, x2, y2];
3076                                ctx.fillStyle = "blue";
3077                                drawPoints(curve, PATH_LINE, pt_labels == 2);
3078                            }
3079                             break;
3080                        case ACTIVE_QUAD_SPAN:
3081                            x3 = frags[SPAN_X3];
3082                            y3 = frags[SPAN_Y3];
3083                            drawQuad(x1, y1, x2, y2, x3, y3);
3084                            if (draw_id) {
3085                                drawQuadID(frags[0], x1, y1, x2, y2, x3, y3);
3086                            }
3087                            if (pt_labels) {
3088                                var curve = [x1, y1, x2, y2, x3, y3];
3089                                ctx.fillStyle = "blue";
3090                                drawPoints(curve, PATH_QUAD, pt_labels == 2);
3091                            }
3092                            break;
3093                        case ACTIVE_CONIC_SPAN:
3094                            x3 = frags[SPAN_X3];
3095                            y3 = frags[SPAN_Y3];
3096                            w = frags[SPAN_K_W];
3097                            drawConicWithQuads(x1, y1, x2, y2, x3, y3, w);
3098                            if (draw_id) {
3099                                drawConicID(frags[0], x1, y1, x2, y2, x3, y3, w);
3100                            }
3101                            if (pt_labels) {
3102                                var curve = [x1, y1, x2, y2, x3, y3, w];
3103                                ctx.fillStyle = "blue";
3104                                drawPoints(curve, PATH_CONIC, pt_labels == 2);
3105                            }
3106                            break;
3107                        case ACTIVE_CUBIC_SPAN:
3108                            x3 = frags[SPAN_X3];
3109                            y3 = frags[SPAN_Y3];
3110                            x4 = frags[SPAN_X4];
3111                            y4 = frags[SPAN_Y4];
3112                            drawCubic(x1, y1, x2, y2, x3, y3, x4, y4);
3113                            if (draw_id) {
3114                                drawCubicID(frags[0], x1, y1, x2, y2, x3, y3, x4, y4);
3115                            }
3116                            if (pt_labels) {
3117                                var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
3118                                ctx.fillStyle = "blue";
3119                                drawPoints(curve, PATH_CUBIC, pt_labels == 2);
3120                            }
3121                            break;
3122                        default:
3123                            console.log("unknown REC_TYPE_ACTIVE frag type: " + fragType);
3124                            throw "stop execution";
3125                    }
3126                    break;
3127                case REC_TYPE_ACTIVE_OP:
3128                    if (!draw_op || (step_limit > 0 && tIndex < lastOp)) {
3129                        continue;
3130                    }
3131                    focus_enabled = true;
3132                    ctx.lineWidth = 3;
3133                    var activeSpan = frags[7] == "1";
3134                    ctx.strokeStyle = activeSpan ? "rgba(45,160,0, 0.3)" : "rgba(255,45,0, 0.5)";
3135                    var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
3136                    drawCurve(curve);
3137                    if (draw_op > 1) {
3138                        drawArc(curve, false, frags[3], frags[4]);
3139                        drawArc(curve, true, frags[5], frags[6]);
3140                    }
3141                    break;
3142                case REC_TYPE_ADD:
3143                    if (!draw_add) {
3144                        continue;
3145                    }
3146                    ctx.lineWidth = 3;
3147                    ctx.strokeStyle = closeCount == 0 ? "rgba(0,0,255, 0.3)"
3148                            : closeCount == 1 ? "rgba(0,127,0, 0.3)"
3149                            : closeCount == 2 ? "rgba(0,127,127, 0.3)"
3150                            : closeCount == 3 ? "rgba(127,127,0, 0.3)"
3151                            : "rgba(127,0,127, 0.3)";
3152                    focus_enabled = true;
3153                    switch (fragType) {
3154                        case ADD_MOVETO:
3155                            break;
3156                        case ADD_LINETO:
3157                            if (step_limit == 0 || tIndex >= lastAdd) {
3158                                drawLine(frags[0], frags[1], frags[2], frags[3]);
3159                            }
3160                            break;
3161                        case ADD_QUADTO:
3162                            if (step_limit == 0 || tIndex >= lastAdd) {
3163                                drawQuad(frags[0], frags[1], frags[2], frags[3], frags[4], frags[5]);
3164                            }
3165                            break;
3166                        case ADD_CONICTO:
3167                            if (step_limit == 0 || tIndex >= lastAdd) {
3168                                drawConicWithQuads(frags[0], frags[1], frags[2], frags[3],
3169                                        frags[4], frags[5], frags[6]);
3170                            }
3171                            break;
3172                        case ADD_CUBICTO:
3173                            if (step_limit == 0 || tIndex >= lastAdd) {
3174                                drawCubic(frags[0], frags[1], frags[2], frags[3],
3175                                        frags[4], frags[5], frags[6], frags[7]);
3176                            }
3177                            break;
3178                        case ADD_CLOSE:
3179                            ++closeCount;
3180                            break;
3181                        case ADD_FILL:
3182                            break;
3183                        default:
3184                            console.log("unknown REC_TYPE_ADD frag type: " + fragType);
3185                            throw "stop execution";
3186                    }
3187                    break;
3188                case REC_TYPE_ANGLE:
3189                    angleBetween = frags[18] == "T";
3190                    afterIndex = 0;
3191                    if (draw_angle == 0 || draw_angle == 3 || (step_limit > 0 && tIndex < lastAngle)) {
3192                        continue;
3193                    }
3194                    focus_enabled = true;
3195                    ctx.lineWidth = 3;
3196                    ctx.strokeStyle = "rgba(127,45,127, 0.3)";
3197                    var leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
3198                    var midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
3199                    var rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
3200                    drawCurve(leftCurve);
3201                    drawCurve(rightCurve);
3202                    ctx.strokeStyle = angleBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
3203                    drawCurve(midCurve);
3204                    if (draw_angle > 1) {
3205                        drawVisibleOrder(leftCurve, 'L');
3206                        drawVisibleOrder(rightCurve, 'R');
3207                    }
3208                    if (draw_id) {
3209                        drawVisibleID(leftCurve, 0.5, frags[0]);
3210                        drawVisibleID(midCurve, 0.5, frags[6]);
3211                        drawVisibleID(rightCurve, 0.5, frags[12]);
3212                    }
3213                    break;
3214                case REC_TYPE_AFTERPART:
3215                    if (draw_angle != 3 || (step_limit > 0 && tIndex < lastAngle)) {
3216                        continue;
3217                    }
3218                    ctx.strokeStyle = afterIndex == 0 ? "rgba(255,0,0, 1.0)"
3219                            : (afterIndex == 1) == angleBetween ? "rgba(0,128,0, 1.0)"
3220                            : "rgba(0,0,255, 1.0)";
3221                    var curve;
3222                    var id;
3223                    switch (fragType) {
3224                        case PATH_LINE:
3225                            curve = [ frags[0], frags[1], frags[2], frags[3] ];
3226                            id = frags[4];
3227                            break;
3228                        case PATH_QUAD:
3229                            curve = [ frags[0], frags[1], frags[2], frags[3],
3230                                     frags[4], frags[5] ];
3231                            id = frags[6];
3232                            break;
3233                        case PATH_CONIC:
3234                            curve = [ frags[0], frags[1], frags[2], frags[3],
3235                                     frags[4], frags[5], frags[6] ];
3236                            id = frags[7];
3237                            break;
3238                        case PATH_CUBIC:
3239                            curve = [ frags[0], frags[1], frags[2], frags[3],
3240                                     frags[4], frags[5], frags[6], frags[7] ];
3241                            id = frags[8];
3242                            break;
3243                        default:
3244                            console.log("unknown REC_TYPE_AFTERPART frag type: " + fragType);
3245                            throw "stop execution";
3246                    }
3247                    drawCurve(curve);
3248                    if (draw_id) {
3249                        drawVisibleID(curve, 0.5, id);
3250                    }
3251                    ++afterIndex;
3252                    break;
3253                case REC_TYPE_COINCIDENCE:
3254                    if (!draw_coincidence || (step_limit > 0 && tIndex < lastCoin)) {
3255                        continue;
3256                    }
3257                    focus_enabled = true;
3258                    ctx.lineWidth = 3;
3259                    ctx.strokeStyle = "rgba(127,45,63, 0.3)";
3260                    var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
3261                    drawCurve(curve);
3262                    break;
3263                case REC_TYPE_SECT:
3264                    if (!draw_intersection) {
3265                        continue;
3266                    }
3267                    if (draw_intersection != 1 && (step_limit > 0 && tIndex < lastSect)) {
3268                        continue;
3269                    }
3270                    // draw_intersection == 1 : show all
3271                    // draw_intersection == 2 : step == 0 ? show all : show intersection line #step
3272                    // draw_intersection == 3 : step == 0 ? show all : show intersection #step
3273                    ctx.lineWidth = 1;
3274                    ctx.strokeStyle = "rgba(0,0,255, 0.3)";
3275                    ctx.fillStyle = "blue";
3276                    focus_enabled = true;
3277                    var f = [];
3278                    var c1s;
3279                    var c1l;
3280                    var c2s;
3281                    var c2l;
3282                    switch (fragType) {
3283                        case INTERSECT_LINE:
3284                            f.push(5, 6, 0, 7);
3285                            c1s = 1; c1l = 4; c2s = 8; c2l = 4;
3286                            break;
3287                        case INTERSECT_LINE_2:
3288                            f.push(5, 6, 0, 10);
3289                            f.push(8, 9, 7, 15);
3290                            c1s = 1; c1l = 4; c2s = 11; c2l = 4;
3291                            break;
3292                        case INTERSECT_LINE_NO:
3293                            c1s = 0; c1l = 4; c2s = 4; c2l = 4;
3294                            break;
3295                        case INTERSECT_QUAD_LINE:
3296                            f.push(7, 8, 0, 9);
3297                            c1s = 1; c1l = 6; c2s = 10; c2l = 4;
3298                            break;
3299                        case INTERSECT_QUAD_LINE_2:
3300                            f.push(7, 8, 0, 12);
3301                            f.push(10, 11, 9, 17);
3302                            c1s = 1; c1l = 6; c2s = 13; c2l = 4;
3303                            break;
3304                        case INTERSECT_QUAD_LINE_NO:
3305                            c1s = 0; c1l = 6; c2s = 6; c2l = 4;
3306                            break;
3307                        case INTERSECT_QUAD:
3308                            f.push(7, 8, 0, 9);
3309                            c1s = 1; c1l = 6; c2s = 10; c2l = 6;
3310                            break;
3311                        case INTERSECT_QUAD_2:
3312                            f.push(7, 8, 0, 12);
3313                            f.push(10, 11, 9, 19);
3314                            c1s = 1; c1l = 6; c2s = 13; c2l = 6;
3315                            break;
3316                        case INTERSECT_QUAD_NO:
3317                            c1s = 0; c1l = 6; c2s = 6; c2l = 6;
3318                            break;
3319                        case INTERSECT_CONIC_LINE:
3320                            f.push(8, 9, 0, 10);
3321                            c1s = 1; c1l = 7; c2s = 11; c2l = 4;
3322                            break;
3323                        case INTERSECT_CONIC_LINE_2:
3324                            f.push(8, 9, 0, 12);
3325                            f.push(11, 12, 10, 18);
3326                            c1s = 1; c1l = 7; c2s = 14; c2l = 4;
3327                            break;
3328                        case INTERSECT_CONIC_LINE_NO:
3329                            c1s = 0; c1l = 7; c2s = 7; c2l = 4;
3330                            break;
3331                        case INTERSECT_CONIC_QUAD:
3332                            f.push(8, 9, 0, 10);
3333                            c1s = 1; c1l = 7; c2s = 11; c2l = 6;
3334                            break;
3335                        case INTERSECT_CONIC_QUAD_2:
3336                            f.push(8, 9, 0, 12);
3337                            f.push(11, 12, 10, 18);
3338                            c1s = 1; c1l = 7; c2s = 14; c2l = 6;
3339                            break;
3340                        case INTERSECT_CONIC_QUAD_3:
3341                            f.push(8, 9, 0, 15);
3342                            f.push(11, 12, 10, 21);
3343                            f.push(14, 15, 13, 22);
3344                            c1s = 1; c1l = 7; c2s = 17; c2l = 6;
3345                            break;
3346                        case INTERSECT_CONIC_QUAD_4:
3347                            f.push(8, 9, 0, 18);
3348                            f.push(11, 12, 10, 24);
3349                            f.push(14, 15, 13, 25);
3350                            f.push(17, 18, 16, 26);
3351                            c1s = 1; c1l = 7; c2s = 20; c2l = 6;
3352                            break;
3353                        case INTERSECT_CONIC_QUAD_NO:
3354                            c1s = 0; c1l = 7; c2s = 7; c2l = 6;
3355                            break;
3356                        case INTERSECT_CONIC:
3357                            f.push(8, 9, 0, 10);
3358                            c1s = 1; c1l = 7; c2s = 11; c2l = 7;
3359                            break;
3360                        case INTERSECT_CONIC_2:
3361                            f.push(8, 9, 0, 13);
3362                            f.push(11, 12, 10, 21);
3363                            c1s = 1; c1l = 7; c2s = 14; c2l = 7;
3364                            break;
3365                        case INTERSECT_CONIC_NO:
3366                            c1s = 0; c1l = 7; c2s = 7; c2l = 7;
3367                            break;
3368                        case INTERSECT_SELF_CUBIC:
3369                            f.push(9, 10, 0, 11);
3370                            c1s = 1; c1l = 8; c2s = 0; c2l = 0;
3371                            break;
3372                        case INTERSECT_SELF_CUBIC_NO:
3373                            c1s = 0; c1l = 8; c2s = 0; c2l = 0;
3374                            break;
3375                        case INTERSECT_CUBIC_LINE:
3376                            f.push(9, 10, 0, 11);
3377                            c1s = 1; c1l = 8; c2s = 12; c2l = 4;
3378                            break;
3379                        case INTERSECT_CUBIC_LINE_2:
3380                            f.push(9, 10, 0, 14);
3381                            f.push(12, 13, 11, 19);
3382                            c1s = 1; c1l = 8; c2s = 15; c2l = 4;
3383                            break;
3384                        case INTERSECT_CUBIC_LINE_3:
3385                            f.push(9, 10, 0, 17);
3386                            f.push(12, 13, 11, 22);
3387                            f.push(15, 16, 14, 23);
3388                            c1s = 1; c1l = 8; c2s = 18; c2l = 4;
3389                            break;
3390                        case INTERSECT_CUBIC_QUAD_NO:
3391                            c1s = 0; c1l = 8; c2s = 8; c2l = 6;
3392                            break;
3393                        case INTERSECT_CUBIC_QUAD:
3394                            f.push(9, 10, 0, 11);
3395                            c1s = 1; c1l = 8; c2s = 12; c2l = 6;
3396                            break;
3397                        case INTERSECT_CUBIC_QUAD_2:
3398                            f.push(9, 10, 0, 14);
3399                            f.push(12, 13, 11, 21);
3400                            c1s = 1; c1l = 8; c2s = 15; c2l = 6;
3401                            break;
3402                        case INTERSECT_CUBIC_QUAD_3:
3403                            f.push(9, 10, 0, 17);
3404                            f.push(12, 13, 11, 24);
3405                            f.push(15, 16, 14, 25);
3406                            c1s = 1; c1l = 8; c2s = 18; c2l = 6;
3407                            break;
3408                        case INTERSECT_CUBIC_QUAD_4:
3409                            f.push(9, 10, 0, 20);
3410                            f.push(12, 13, 11, 27);
3411                            f.push(15, 16, 14, 28);
3412                            f.push(18, 19, 17, 29);
3413                            c1s = 1; c1l = 8; c2s = 21; c2l = 6;
3414                            break;
3415                        case INTERSECT_CUBIC_LINE_NO:
3416                            c1s = 0; c1l = 8; c2s = 8; c2l = 4;
3417                            break;
3418                        case INTERSECT_CUBIC:
3419                            f.push(9, 10, 0, 11);
3420                            c1s = 1; c1l = 8; c2s = 12; c2l = 8;
3421                            break;
3422                        case INTERSECT_CUBIC_2:
3423                            f.push(9, 10, 0, 14);
3424                            f.push(12, 13, 11, 23);
3425                            c1s = 1; c1l = 8; c2s = 15; c2l = 8;
3426                            break;
3427                        case INTERSECT_CUBIC_3:
3428                            f.push(9, 10, 0, 17);
3429                            f.push(12, 13, 11, 26);
3430                            f.push(15, 16, 14, 27);
3431                            c1s = 1; c1l = 8; c2s = 18; c2l = 8;
3432                            break;
3433                        case INTERSECT_CUBIC_4:
3434                            f.push(9, 10, 0, 20);
3435                            f.push(12, 13, 11, 29);
3436                            f.push(15, 16, 14, 30);
3437                            f.push(18, 19, 17, 31);
3438                            c1s = 1; c1l = 8; c2s = 21; c2l = 8;
3439                            break;
3440                        case INTERSECT_CUBIC_NO:
3441                            c1s = 0; c1l = 8; c2s = 8; c2l = 8;
3442                            break;
3443                        default:
3444                            console.log("unknown REC_TYPE_SECT frag type: " + fragType);
3445                            throw "stop execution";
3446                    }
3447                    if (draw_intersection != 1) {
3448                        var id = -1;
3449                        var curve;
3450                        switch (c1l) {
3451                            case 4:
3452                                drawLine(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]);
3453                                if (draw_id) {
3454                                    curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]];
3455                                    id = idByCurve(test, curve, PATH_LINE);
3456                                }
3457                                break;
3458                            case 6:
3459                                drawQuad(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3460                                        frags[c1s + 4], frags[c1s + 5]);
3461                                if (draw_id) {
3462                                    curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3463                                            frags[c1s + 4], frags[c1s + 5]];
3464                                    id = idByCurve(test, curve, PATH_QUAD);
3465                                }
3466                                break;
3467                            case 7:
3468                                drawConicWithQuads(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3469                                        frags[c1s + 4], frags[c1s + 5], frags[c1s + 6]);
3470                                if (draw_id) {
3471                                    curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3472                                            frags[c1s + 4], frags[c1s + 5], frags[c1s + 6]];
3473                                    id = idByCurve(test, curve, PATH_CONIC);
3474                                }
3475                                break;
3476                            case 8:
3477                                drawCubic(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3478                                        frags[c1s + 4], frags[c1s + 5], frags[c1s + 6], frags[c1s + 7]);
3479                                if (draw_id) {
3480                                    curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
3481                                            frags[c1s + 4], frags[c1s + 5], frags[c1s + 6], frags[c1s + 7]];
3482                                    id = idByCurve(test, curve, PATH_CUBIC);
3483                                }
3484                                break;
3485                        }
3486                        if (id >= 0) {
3487                            drawVisibleID(curve, 0.5, id);
3488                        }
3489                        id = -1;
3490                        switch (c2l) {
3491                            case 0:
3492                                break;
3493                            case 4:
3494                                drawLine(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]);
3495                                if (draw_id) {
3496                                    curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]];
3497                                    id = idByCurve(test, curve, PATH_LINE);
3498                                }
3499                                break;
3500                            case 6:
3501                                drawQuad(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3502                                        frags[c2s + 4], frags[c2s + 5]);
3503                                if (draw_id) {
3504                                    curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3505                                            frags[c2s + 4], frags[c2s + 5]];
3506                                    id = idByCurve(test, curve, PATH_QUAD);
3507                                }
3508                                break;
3509                            case 7:
3510                                drawConicWithQuads(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3511                                        frags[c2s + 4], frags[c2s + 5], frags[c2s + 6]);
3512                                if (draw_id) {
3513                                    curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3514                                            frags[c2s + 4], frags[c2s + 5], frags[c2s + 6]];
3515                                    id = idByCurve(test, curve, PATH_CONIC);
3516                                }
3517                                break;
3518                            case 8:
3519                                drawCubic(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3520                                        frags[c2s + 4], frags[c2s + 5], frags[c2s + 6], frags[c2s + 7]);
3521                                if (draw_id) {
3522                                    curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
3523                                            frags[c2s + 4], frags[c2s + 5], frags[c2s + 6], frags[c2s + 7]];
3524                                    id = idByCurve(test, curve, PATH_CUBIC);
3525                                }
3526                                break;
3527                        }
3528                        if (id >= 0) {
3529                            drawVisibleID(curve, 0.5, id);
3530                        }
3531                    }
3532                    if (collect_bounds) {
3533                        break;
3534                    }
3535                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
3536                        for (var idx = 0; idx < f.length; idx += 4) {
3537                            drawPoint(frags[f[idx]], frags[f[idx + 1]], true);
3538                        }
3539                    }
3540                    if (!draw_intersectT) {
3541                        break;
3542                    }
3543                    ctx.fillStyle = "red";
3544                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
3545                        for (var idx = 0; idx < f.length; idx += 4) {
3546                            drawTAtPointUp(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 2]]);
3547                            drawTAtPointDown(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 3]]);
3548                        }
3549                    }
3550                    break;
3551                case REC_TYPE_SORT:
3552                    if (!draw_sort || (step_limit > 0 && tIndex < lastSort)) {
3553                        continue;
3554                    }
3555                    ctx.lineWidth = 3;
3556                    ctx.strokeStyle = "rgba(127,127,0, 0.5)";
3557                    focus_enabled = true;
3558                    switch (fragType) {
3559                        case SORT_UNARY:
3560                        case SORT_BINARY:
3561                            var curve = curvePartialByID(test, frags[0], frags[6], frags[8]);
3562                            drawCurve(curve);
3563                            break;
3564                        default:
3565                            console.log("unknown REC_TYPE_SORT frag type: " + fragType);
3566                            throw "stop execution";
3567                    }
3568                    break;
3569                case REC_TYPE_TOP:
3570                    if (!draw_top || (step_limit > 0 && tIndex < lastTop)) {
3571                        continue;
3572                    }
3573                    ctx.lineWidth = 3;
3574                    ctx.strokeStyle = "rgba(127,127,0, 0.5)";
3575                    focus_enabled = true;
3576                    {
3577                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
3578                        drawCurve(curve);
3579                        var type = PATH_LINE + (curve.length / 2 - 2);
3580                        var mid = pointAtT(curve, type, 0.5);
3581                        var d = dxy_at_t(curve, type, 0.5);
3582                        drawArrow(mid.x, mid.y, d.x, d.y, 0.3);
3583                    }
3584                    break;
3585                case REC_TYPE_MARK:
3586                    if (!draw_mark || (step_limit > 0 && tIndex < lastMark)) {
3587                        continue;
3588                    }
3589                    ctx.lineWidth = 3;
3590                    ctx.strokeStyle = fragType >= MARK_DONE_LINE ?
3591                            "rgba(127,0,127, 0.5)" : "rgba(127,127,0, 0.5)";
3592                    focus_enabled = true;
3593                    switch (fragType) {
3594                        case MARK_LINE:
3595                        case MARK_DONE_LINE:
3596                        case MARK_UNSORTABLE_LINE:
3597                        case MARK_SIMPLE_LINE:
3598                        case MARK_SIMPLE_DONE_LINE:
3599                        case MARK_DONE_UNARY_LINE:
3600                            drawLinePartial(frags[1], frags[2], frags[3], frags[4],
3601                                frags[5], frags[9]);
3602                            if (draw_id) {
3603                                drawLinePartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
3604                                frags[5], frags[9]);
3605                            }
3606                            break;
3607                        case MARK_QUAD:
3608                        case MARK_DONE_QUAD:
3609                        case MARK_UNSORTABLE_QUAD:
3610                        case MARK_SIMPLE_QUAD:
3611                        case MARK_SIMPLE_DONE_QUAD:
3612                        case MARK_DONE_UNARY_QUAD:
3613                            drawQuadPartial(frags[1], frags[2], frags[3], frags[4],
3614                                frags[5], frags[6], frags[7], frags[11]);
3615                            if (draw_id) {
3616                                drawQuadPartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
3617                                frags[5], frags[6], frags[7], frags[11]);
3618                            }
3619                            break;
3620                        case MARK_CUBIC:
3621                        case MARK_DONE_CUBIC:
3622                        case MARK_UNSORTABLE_CUBIC:
3623                        case MARK_SIMPLE_CUBIC:
3624                        case MARK_SIMPLE_DONE_CUBIC:
3625                        case MARK_DONE_UNARY_CUBIC:
3626                            drawCubicPartial(frags[1], frags[2], frags[3], frags[4],
3627                                frags[5], frags[6], frags[7], frags[8], frags[9], frags[13]);
3628                            if (draw_id) {
3629                                drawCubicPartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
3630                                frags[5], frags[6], frags[7], frags[8], frags[9], frags[13]);
3631                            }
3632                            break;
3633                        case MARK_ANGLE_LAST:
3634                            // FIXME: ignored for now
3635                            break;
3636                        default:
3637                            console.log("unknown REC_TYPE_MARK frag type: " + fragType);
3638                            throw "stop execution";
3639                    }
3640                    break;
3641                default:
3642                    continue;
3643            }
3644        }
3645        switch (recType) {
3646            case REC_TYPE_SORT:
3647                if (!draw_sort || (step_limit > 0 && tIndex < lastSort)) {
3648                    break;
3649                }
3650                var angles = []; // use tangent lines to describe arcs
3651                var windFrom = [];
3652                var windTo = [];
3653                var opp = [];
3654                var minXY = Number.MAX_VALUE;
3655                var partial;
3656                focus_enabled = true;
3657                var someUnsortable = false;
3658                for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
3659                    var fragType = records[recordIndex];
3660                    var frags = records[recordIndex + 1];
3661                    var unsortable = (fragType == SORT_UNARY && frags[14]) ||
3662                            (fragType == SORT_BINARY && frags[16]);
3663                    someUnsortable |= unsortable;
3664                    switch (fragType) {
3665                        case SORT_UNARY:
3666                        case SORT_BINARY:
3667                            partial = curvePartialByID(test, frags[0], frags[6], frags[8]);
3668                            break;
3669                        default:
3670                            console.log("unknown REC_TYPE_SORT frag type: " + fragType);
3671                            throw "stop execution";
3672                    }
3673                    var dx = boundsWidth(partial);
3674                    var dy = boundsHeight(partial);
3675                    minXY = Math.min(minXY, dx * dx + dy * dy);
3676                    if (collect_bounds) {
3677                        continue;
3678                    }
3679                    angles.push(tangent(partial));
3680                    var from = frags[12];
3681                    var to = frags[12];
3682                    var sgn = frags[10];
3683                    if (sgn < 0) {
3684                        from -= frags[11];
3685                    } else if (sgn > 0) {
3686                        to -= frags[11];
3687                    }
3688                    windFrom.push(from + (unsortable ? "!" : ""));
3689                    windTo.push(to + (unsortable ? "!" : ""));
3690                    opp.push(fragType == SORT_BINARY);
3691                    if (draw_sort == 1) {
3692                        drawVisibleOrder(partial, frags[12]);
3693                    } else {
3694                        drawVisibleOrder(partial, (recordIndex / 2) + 1);
3695                    }
3696                }
3697                var radius = Math.sqrt(minXY) / 2 * scale;
3698                radius = Math.min(50, radius);
3699                var scaledRadius = radius / scale;
3700                var centerX = partial[0];
3701                var centerY = partial[1];
3702                if (collect_bounds) {
3703                    if (focus_enabled) {
3704                        focusXmin = Math.min(focusXmin, centerX - scaledRadius);
3705                        focusYmin = Math.min(focusYmin, centerY - scaledRadius);
3706                        focusXmax = Math.max(focusXmax, centerX + scaledRadius);
3707                        focusYmax = Math.max(focusYmax, centerY + scaledRadius);
3708                    }
3709                    break;
3710                }
3711                break;
3712            default:
3713                break;
3714        }
3715    }
3716    if (collect_bounds) {
3717        return;
3718    }
3719    if (draw_log && logStart >= 0) {
3720        ctx.font = "normal 10px Arial";
3721        ctx.textAlign = "left";
3722        ctx.beginPath();
3723        var top = screenHeight - 20 - (logRange + 2) * 10;
3724        ctx.rect(50, top, screenWidth - 100, (logRange + 2) * 10);
3725        ctx.fillStyle = "white";
3726        ctx.fill();
3727        ctx.fillStyle = "rgba(0,0,0, 0.5)";
3728        if (logStart > 0) {
3729            ctx.fillText(lines[logStart - 1], 50, top + 8);
3730        }
3731        ctx.fillStyle = "black";
3732        for (var idx = 0; idx < logRange; ++idx) {
3733            ctx.fillText(lines[logStart + idx], 50, top + 18 + 10 * idx);
3734        }
3735        ctx.fillStyle = "rgba(0,0,0, 0.5)";
3736        if (logStart + logRange < lines.length) {
3737            ctx.fillText(lines[logStart + logRange], 50, top + 18 + 10 * logRange);
3738        }
3739    }
3740    if (draw_legend) {
3741        var pos = 0;
3742        var drawSomething = draw_add | draw_active | draw_angle | draw_coincidence | draw_sort | draw_mark;
3743   //     drawBox(pos++, "yellow", "black", opLetter, true, '');
3744        drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_intersection > 1 ? sectCount : sectMax2, draw_intersection, intersectionKey);
3745        drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_add ? addCount : addMax, draw_add, addKey);
3746        drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_active ? activeCount : activeMax, draw_active, activeKey);
3747        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_angle ? angleCount : angleMax, draw_angle, angleKey);
3748        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_coincidence ? coinCount : coinMax, draw_coincidence, coincidenceKey);
3749        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_op ? opCount : opMax, draw_op, opKey);
3750        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_sort ? sortCount : sortMax, draw_sort, sortKey);
3751        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_top ? topCount : topMax, draw_top, topKey);
3752        drawBox(pos++, "rgba(127,0,127, 0.3)", "black", draw_mark ? markCount : markMax, draw_mark, markKey);
3753        drawBox(pos++, "black", "white",
3754                (new Array('P', 'P1', 'P2', 'P', 'p', 'p1', 'p2'))[draw_path], draw_path != 0, pathKey);
3755        drawBox(pos++, "rgba(0,63,0, 0.7)", "white",
3756                (new Array('Q', 'Q', 'C', 'QC', 'Qc', 'Cq'))[draw_computed],
3757                draw_computed != 0, computedKey);
3758        drawBox(pos++, "green", "black", step_limit, drawSomething, '');
3759        drawBox(pos++, "green", "black", stepMax, drawSomething, '');
3760        drawBox(pos++, "rgba(255,0,0, 0.6)", "black", lastIndex, drawSomething & draw_log, '');
3761        drawBox(pos++, "rgba(255,0,0, 0.6)", "black", test.length - 1, drawSomething & draw_log, '');
3762        if (curve_t) {
3763            drawCurveTControl();
3764        }
3765        ctx.font = "normal 20px Arial";
3766        ctx.fillStyle = "rgba(0,0,0, 0.3)";
3767        ctx.textAlign = "right";
3768        ctx.fillText(scale.toFixed(decimal_places) + 'x' , screenWidth - 10, screenHeight - 5);
3769    }
3770    if (draw_hints) {
3771        ctx.font = "normal 10px Arial";
3772        ctx.fillStyle = "rgba(0,0,0, 0.5)";
3773        ctx.textAlign = "right";
3774        var y = 4;
3775        ctx.fillText("control lines : " +  controlLinesKey, ctx.screenWidthwidth - 10, pos * 50 + y++ * 10);
3776        ctx.fillText("curve t : " +  curveTKey, screenWidth - 10, pos * 50 + y++ * 10);
3777        ctx.fillText("deriviatives : " +  deriviativesKey, screenWidth - 10, pos * 50 + y++ * 10);
3778        ctx.fillText("intersect t : " +  intersectTKey, screenWidth - 10, pos * 50 + y++ * 10);
3779        ctx.fillText("log : " +  logKey, screenWidth - 10, pos * 50 + y++ * 10);
3780        ctx.fillText("log curve : " +  logCurvesKey, screenWidth - 10, pos * 50 + y++ * 10);
3781        ctx.fillText("mid point : " +  midpointKey, screenWidth - 10, pos * 50 + y++ * 10);
3782        ctx.fillText("points : " +  ptsKey, screenWidth - 10, pos * 50 + y++ * 10);
3783        ctx.fillText("sequence : " +  sequenceKey, screenWidth - 10, pos * 50 + y++ * 10);
3784        ctx.fillText("xy : " +  xyKey, screenWidth - 10, pos * 50 + y++ * 10);
3785    }
3786}
3787
3788function drawBox(y, backC, foreC, str, enable, label) {
3789    ctx.beginPath();
3790    ctx.fillStyle = backC;
3791    ctx.rect(screenWidth - 40, y * 50 + 10, 40, 30);
3792    ctx.fill();
3793    ctx.font = "normal 16px Arial";
3794    ctx.fillStyle = foreC;
3795    ctx.textAlign = "center";
3796    ctx.fillText(str, screenWidth - 20, y * 50 + 32);
3797    if (!enable) {
3798        ctx.fillStyle = "rgba(255,255,255, 0.5)";
3799        ctx.fill();
3800    }
3801    if (label != '') {
3802        ctx.font = "normal 9px Arial";
3803        ctx.fillStyle = "black";
3804        ctx.fillText(label, screenWidth - 47, y * 50 + 40);
3805    }
3806}
3807
3808function drawCurveTControl() {
3809    ctx.lineWidth = 2;
3810    ctx.strokeStyle = "rgba(0,0,0, 0.3)";
3811    ctx.beginPath();
3812    ctx.rect(screenWidth - 80, 40, 28, screenHeight - 80);
3813    ctx.stroke();
3814    var ty = 40 + curveT * (screenHeight - 80);
3815    ctx.beginPath();
3816    ctx.moveTo(screenWidth - 80, ty);
3817    ctx.lineTo(screenWidth - 85, ty - 5);
3818    ctx.lineTo(screenWidth - 85, ty + 5);
3819    ctx.lineTo(screenWidth - 80, ty);
3820    ctx.fillStyle = "rgba(0,0,0, 0.6)";
3821    ctx.fill();
3822    var num = curveT.toFixed(decimal_places);
3823    ctx.font = "normal 10px Arial";
3824    ctx.textAlign = "left";
3825    ctx.fillText(num, screenWidth - 78, ty);
3826}
3827
3828function ptInTControl() {
3829    var e = window.event;
3830    var tgt = e.target || e.srcElement;
3831    var left = tgt.offsetLeft;
3832    var top = tgt.offsetTop;
3833    var x = (e.clientX - left);
3834    var y = (e.clientY - top);
3835    if (x < screenWidth - 80 || x > screenWidth - 50) {
3836        return false;
3837    }
3838    if (y < 40 || y > screenHeight - 80) {
3839        return false;
3840    }
3841    curveT = (y - 40) / (screenHeight - 120);
3842    if (curveT < 0 || curveT > 1) {
3843        throw "stop execution";
3844    }
3845    return true;
3846}
3847
3848function drawTop() {
3849    if (tests[testIndex] == null) {
3850        var str = testDivs[testIndex].textContent;
3851        parse_all(str);
3852        var title = testDivs[testIndex].id.toString();
3853        testTitles[testIndex] = title;
3854    }
3855    init(tests[testIndex]);
3856    redraw();
3857}
3858
3859function redraw() {
3860    if (focus_on_selection) {
3861        collect_bounds = true;
3862        draw(tests[testIndex], testLines[testIndex], testTitles[testIndex]);
3863        collect_bounds = false;
3864        if (focusXmin < focusXmax && focusYmin < focusYmax) {
3865            setScale(focusXmin, focusXmax, focusYmin, focusYmax);
3866        }
3867    }
3868    ctx.beginPath();
3869    ctx.fillStyle = "white";
3870    ctx.rect(0, 0, screenWidth, screenHeight);
3871    ctx.fill();
3872    draw(tests[testIndex], testLines[testIndex], testTitles[testIndex]);
3873}
3874
3875function dumpCurvePartial(test, id, t0, t1) {
3876    var curve = curveByID(test, id);
3877    var name = ["line", "quad", "cubic"][curve.length / 2 - 2];
3878    console.log("id=" + id + " " + name + "=" +  curveToString(curve)
3879        + " t0=" + t0 + " t1=" + t1
3880        + " partial=" + curveToString(curvePartialByID(test, id, t0, t1)));
3881}
3882
3883function dumpAngleTest(test, id, t0, t1) {
3884    var curve = curveByID(test, id);
3885    console.log("    { {" + curveToString(curve) + "}, "
3886            + curve.length / 2 + ", " + t0 + ", " + t1 + ", {} }, //");
3887}
3888
3889function dumpLogToConsole() {
3890    if (logStart < 0) {
3891        return;
3892    }
3893    var test = tests[testIndex];
3894    var recType = REC_TYPE_UNKNOWN;
3895    var records;
3896    for (var index = 0; index < test.length; index += 3) {
3897        var lastLineNo = test[index + 1];
3898        if (lastLineNo >= logStart && lastLineNo < logStart + logRange) {
3899            recType = test[index];
3900            records = test[index + 2];
3901            break;
3902        }
3903    }
3904    if (recType == REC_TYPE_UNKNOWN) {
3905        return;
3906    }
3907    var lines = testLines[testIndex];
3908    for (var idx = 0; idx < logRange; ++idx) {
3909        var line = lines[logStart + idx];
3910        console.log(line);
3911        for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
3912            var fragType = records[recordIndex];
3913            var frags = records[recordIndex + 1];
3914            if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER) {
3915                dumpCurvePartial(test, frags[0], frags[4], frags[5]);
3916                dumpCurvePartial(test, frags[6], frags[10], frags[11]);
3917                dumpCurvePartial(test, frags[12], frags[16], frags[17]);
3918                console.log("\nstatic IntersectData intersectDataSet[] = { //");
3919                dumpAngleTest(test, frags[0], frags[4], frags[5]);
3920                dumpAngleTest(test, frags[6], frags[10], frags[11]);
3921                dumpAngleTest(test, frags[12], frags[16], frags[17]);
3922                console.log("}; //");
3923            }
3924        }
3925    }
3926}
3927
3928var activeKey = 'a';
3929var pathKey = 'b';
3930var pathBackKey = 'B';
3931var centerKey = 'c';
3932var coincidenceKey = 'C';
3933var addKey = 'd';
3934var deriviativesKey = 'f';
3935var angleKey = 'g';
3936var angleBackKey = 'G';
3937var intersectionKey = 'i';
3938var intersectionBackKey = 'I';
3939var sequenceKey = 'j';
3940var midpointKey = 'k';
3941var logKey = 'l';
3942var logToConsoleKey = 'L';
3943var markKey = 'm';
3944var sortKey = 'o';
3945var opKey = 'p';
3946var opBackKey = 'P';
3947var computedKey = 'q';
3948var computedBackKey = 'Q';
3949var directionKey = 'r';
3950var stepKey = 's';
3951var stepBackKey = 'S';
3952var intersectTKey = 't';
3953var topKey = 'T';
3954var curveTKey = 'u';
3955var controlLinesBackKey = 'V';
3956var controlLinesKey = 'v';
3957var ptsKey = 'x';
3958var xyKey = 'y';
3959var logCurvesKey = 'z';
3960var focusKey = '`';
3961var idKey = '.';
3962var retinaKey = '\\';
3963
3964function doKeyPress(evt) {
3965    var char = String.fromCharCode(evt.charCode);
3966    var focusWasOn = false;
3967    switch (char) {
3968    case '0':
3969    case '1':
3970    case '2':
3971    case '3':
3972    case '4':
3973    case '5':
3974    case '6':
3975    case '7':
3976    case '8':
3977    case '9':
3978        decimal_places = char - '0';
3979        redraw();
3980        break;
3981    case activeKey:
3982        draw_active ^= true;
3983        redraw();
3984        break;
3985    case addKey:
3986        draw_add ^= true;
3987        redraw();
3988        break;
3989    case angleKey:
3990        draw_angle = (draw_angle + 1) % 4;
3991        redraw();
3992        break;
3993    case angleBackKey:
3994        draw_angle = (draw_angle + 2) % 3;
3995        redraw();
3996        break;
3997    case centerKey:
3998        setScale(xmin, xmax, ymin, ymax);
3999        redraw();
4000        break;
4001    case coincidenceKey:
4002        draw_coincidence ^= true;
4003        redraw();
4004        break;
4005    case controlLinesBackKey:
4006        control_lines = (control_lines + 3) % 4;
4007        redraw();
4008        break;
4009    case controlLinesKey:
4010        control_lines = (control_lines + 1) % 4;
4011        redraw();
4012        break;
4013    case computedBackKey:
4014        draw_computed = (draw_computed + 5) % 6;
4015        redraw();
4016        break;
4017    case computedKey:
4018        draw_computed = (draw_computed + 1) % 6;
4019        redraw();
4020        break;
4021    case curveTKey:
4022        curve_t ^= true;
4023        if (curve_t) {
4024            draw_legend = true;
4025        }
4026        redraw();
4027        break;
4028    case deriviativesKey:
4029        draw_deriviatives = (draw_deriviatives + 1) % 3;
4030        redraw();
4031        break;
4032    case directionKey:
4033        draw_direction ^= true;
4034        redraw();
4035        break;
4036    case focusKey:
4037        focus_on_selection ^= true;
4038        setScale(xmin, xmax, ymin, ymax);
4039        redraw();
4040        break;
4041    case idKey:
4042        draw_id ^= true;
4043        redraw();
4044        break;
4045    case intersectionBackKey:
4046        draw_intersection = (draw_intersection + 3) % 4;
4047        redraw();
4048        break;
4049    case intersectionKey:
4050        draw_intersection = (draw_intersection + 1) % 4;
4051        redraw();
4052        break;
4053    case intersectTKey:
4054        draw_intersectT ^= true;
4055        redraw();
4056        break;
4057    case logCurvesKey:
4058        logCurves(tests[testIndex]);
4059        break;
4060    case logKey:
4061        draw_log ^= true;
4062        redraw();
4063        break;
4064    case logToConsoleKey:
4065        if (draw_log) {
4066            dumpLogToConsole();
4067        }
4068        break;
4069    case markKey:
4070        draw_mark ^= true;
4071        redraw();
4072        break;
4073    case midpointKey:
4074        draw_midpoint ^= true;
4075        redraw();
4076        break;
4077    case opKey:
4078        draw_op = (draw_op + 1) % 3;
4079        redraw();
4080        break;
4081    case opBackKey:
4082        draw_op = (draw_op + 2) % 3;
4083        redraw();
4084        break;
4085    case pathKey:
4086        draw_path = (draw_path + 1) % (4 + (hasAlignedPath ? 3 : 0));
4087        redraw();
4088        break;
4089    case pathBackKey:
4090        draw_path = (draw_path + 3 + (hasAlignedPath ? 3 : 0)) % (4 + (hasAlignedPath ? 3 : 0));
4091        redraw();
4092        break;
4093    case ptsKey:
4094        pt_labels = (pt_labels + 1) % 3;
4095        redraw();
4096        break;
4097    case retinaKey:
4098        retina_scale ^= true;
4099        drawTop();
4100        break;
4101    case sequenceKey:
4102        draw_sequence ^= true;
4103        redraw();
4104        break;
4105    case sortKey:
4106        draw_sort = (draw_sort + 1) % 3;
4107        drawTop();
4108        break;
4109    case stepKey:
4110        step_limit++;
4111        if (step_limit > stepMax) {
4112            step_limit = stepMax;
4113        }
4114        redraw();
4115        break;
4116    case stepBackKey:
4117        step_limit--;
4118        if (step_limit < 0) {
4119            step_limit = 0;
4120        }
4121        redraw();
4122        break;
4123    case topKey:
4124        draw_top ^= true;
4125        redraw();
4126        break;
4127    case xyKey:
4128        debug_xy = (debug_xy + 1) % 3;
4129        redraw();
4130        break;
4131    case '-':
4132        focusWasOn = focus_on_selection;
4133        if (focusWasOn) {
4134            focus_on_selection = false;
4135            scale /= 1.2;
4136        } else {
4137            scale /= 2;
4138            calcLeftTop();
4139        }
4140        redraw();
4141        focus_on_selection = focusWasOn;
4142        break;
4143    case '=':
4144    case '+':
4145        focusWasOn = focus_on_selection;
4146        if (focusWasOn) {
4147            focus_on_selection = false;
4148            scale *= 1.2;
4149        } else {
4150            scale *= 2;
4151            calcLeftTop();
4152        }
4153        redraw();
4154        focus_on_selection = focusWasOn;
4155        break;
4156    case '?':
4157        draw_hints ^= true;
4158        if (draw_hints && !draw_legend) {
4159            draw_legend = true;
4160        }
4161        redraw();
4162        break;
4163    case '/':
4164        draw_legend ^= true;
4165        redraw();
4166        break;
4167    }
4168}
4169
4170function doKeyDown(evt) {
4171    var char = evt.keyCode;
4172    var preventDefault = false;
4173    switch (char) {
4174    case 37: // left arrow
4175        if (evt.shiftKey) {
4176            testIndex -= 9;
4177        }
4178        if (--testIndex < 0)
4179            testIndex = tests.length - 1;
4180        drawTop();
4181        preventDefault = true;
4182        break;
4183    case 39: // right arrow
4184        if (evt.shiftKey) {
4185            testIndex += 9;
4186        }
4187        if (++testIndex >= tests.length)
4188            testIndex = 0;
4189        drawTop();
4190        preventDefault = true;
4191        break;
4192    }
4193    if (preventDefault) {
4194          evt.preventDefault();
4195          return false;
4196    }
4197    return true;
4198}
4199
4200(function() {
4201    var hidden = "hidden";
4202
4203    // Standards:
4204    if (hidden in document)
4205        document.addEventListener("visibilitychange", onchange);
4206    else if ((hidden = "mozHidden") in document)
4207        document.addEventListener("mozvisibilitychange", onchange);
4208    else if ((hidden = "webkitHidden") in document)
4209        document.addEventListener("webkitvisibilitychange", onchange);
4210    else if ((hidden = "msHidden") in document)
4211        document.addEventListener("msvisibilitychange", onchange);
4212    // IE 9 and lower:
4213    else if ('onfocusin' in document)
4214        document.onfocusin = document.onfocusout = onchange;
4215    // All others:
4216    else
4217        window.onpageshow = window.onpagehide
4218            = window.onfocus = window.onblur = onchange;
4219
4220    function onchange (evt) {
4221        var v = 'visible', h = 'hidden',
4222            evtMap = {
4223                focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
4224            };
4225
4226        evt = evt || window.event;
4227        if (evt.type in evtMap)
4228            document.body.className = evtMap[evt.type];
4229        else
4230            document.body.className = this[hidden] ? "hidden" : "visible";
4231    }
4232})();
4233
4234function calcXY() {
4235    var e = window.event;
4236    var tgt = e.target || e.srcElement;
4237    var left = tgt.offsetLeft;
4238    var top = tgt.offsetTop;
4239    mouseX = (e.clientX - left) / scale + srcLeft;
4240    mouseY = (e.clientY - top) / scale + srcTop;
4241}
4242
4243function calcLeftTop() {
4244    srcLeft = mouseX - screenWidth / 2 / scale;
4245    srcTop = mouseY - screenHeight / 2 / scale;
4246}
4247
4248var disableClick = false;
4249
4250function handleMouseClick() {
4251    if (disableClick) {
4252        return;
4253    }
4254    if (!curve_t || !ptInTControl()) {
4255        calcXY();
4256        calcLeftTop();
4257    }
4258    redraw();
4259//    if (!curve_t || !ptInTControl()) {
4260//        mouseX = screenWidth / 2 / scale + srcLeft;
4261//        mouseY = screenHeight / 2 / scale + srcTop;
4262//    }
4263}
4264
4265function handleMouseOver() {
4266    calcXY();
4267    if (debug_xy != 2) {
4268        return;
4269    }
4270    var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
4271    ctx.beginPath();
4272    ctx.rect(300,100,num.length * 6,10);
4273    ctx.fillStyle="white";
4274    ctx.fill();
4275    ctx.font = "normal 10px Arial";
4276    ctx.fillStyle="black";
4277    ctx.textAlign = "left";
4278    ctx.fillText(num, 300, 108);
4279}
4280
4281function start() {
4282    for (var i = 0; i < testDivs.length; ++i) {
4283        tests[i] = null;
4284    }
4285    testIndex = 0;
4286    drawTop();
4287    window.addEventListener('keypress', doKeyPress, true);
4288    window.addEventListener('keydown', doKeyDown, true);
4289    window.onresize = function() {
4290        drawTop();
4291    }
4292    /*
4293    window.onpagehide = function() {
4294        disableClick = true;
4295    }
4296    */
4297    window.onpageshow = function () {
4298        disableClick = false;
4299    }
4300}
4301
4302</script>
4303</head>
4304
4305<body onLoad="start();">
4306<canvas id="canvas" width="750" height="500"
4307    onmousemove="handleMouseOver()"
4308    onclick="handleMouseClick()"
4309    ></canvas >
4310</body>
4311</html>
4312