Home | History | Annotate | Download | only in tools
      1 <html>
      2 <head>
      3 <div height="0" hidden="true">
      4 
      5 Skia UnitTests: --match PathOpsOp$ --resourcePath resources\ SK_DEBUG
      6 <div id="android1">
      7 seg=1 {{{-5, 0}, {1075, 0}}}
      8 seg=2 {{{1075, 0}, {1075, 242}}}
      9 seg=3 {{{1075, 242}, {-5, 242}}}
     10 seg=4 {{{-5, 242}, {-5, 0}}}
     11 op sect
     12 seg=5 {{{0, 0}, {1080, 0}}}
     13 seg=6 {{{1080, 0}, {1080, 242}}}
     14 seg=7 {{{1080, 242}, {0, 242}}}
     15 seg=8 {{{0, 242}, {0, 0}}}
     16 debugShowLineIntersection wtTs[0]=0 {{{1075,0}, {1075,242}}} {{1075,0}} wnTs[0]=1 {{{-5,0}, {1075,0}}}
     17 debugShowLineIntersection wtTs[0]=1 {{{-5,242}, {-5,0}}} {{-5,0}} wnTs[0]=0 {{{-5,0}, {1075,0}}}
     18 debugShowLineIntersection wtTs[0]=0 {{{1075,242}, {-5,242}}} {{1075,242}} wnTs[0]=1 {{{1075,0}, {1075,242}}}
     19 debugShowLineIntersection wtTs[0]=0 {{{-5,242}, {-5,0}}} {{-5,242}} wnTs[0]=1 {{{1075,242}, {-5,242}}}
     20 debugShowLineIntersection 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
     21 SkOpSegment::addT insert t=0.00462962963 segID=1 spanID=17
     22 SkOpSegment::addT insert t=0.99537037 segID=5 spanID=18
     23 debugShowLineIntersection wtTs[0]=1 {{{0,242}, {0,0}}} {{0,0}} wnTs[0]=0.00462963 {{{-5,0}, {1075,0}}}
     24 debugShowLineIntersection wtTs[0]=0.99537037 {{{0,0}, {1080,0}}} {{1075,0}} wnTs[0]=0 {{{1075,0}, {1075,242}}}
     25 debugShowLineIntersection wtTs[0]=0.00462962963 {{{1080,242}, {0,242}}} {{1075,242}} wnTs[0]=1 {{{1075,0}, {1075,242}}}
     26 SkOpSegment::addT insert t=0.00462962963 segID=7 spanID=19
     27 debugShowLineIntersection 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
     28 SkOpSegment::addT insert t=0.99537037 segID=3 spanID=20
     29 debugShowLineIntersection wtTs[0]=0 {{{0,242}, {0,0}}} {{0,242}} wnTs[0]=0.99537 {{{1075,242}, {-5,242}}}
     30 debugShowLineIntersection wtTs[0]=0 {{{1080,0}, {1080,242}}} {{1080,0}} wnTs[0]=1 {{{0,0}, {1080,0}}}
     31 debugShowLineIntersection wtTs[0]=1 {{{0,242}, {0,0}}} {{0,0}} wnTs[0]=0 {{{0,0}, {1080,0}}}
     32 debugShowLineIntersection wtTs[0]=0 {{{1080,242}, {0,242}}} {{1080,242}} wnTs[0]=1 {{{1080,0}, {1080,242}}}
     33 debugShowLineIntersection wtTs[0]=0 {{{0,242}, {0,0}}} {{0,242}} wnTs[0]=1 {{{1080,242}, {0,242}}}
     34 ------------------x--x---------------- addExpanded
     35 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     36 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     37 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     38 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     39 SkOpSegment::debugShowActiveSpans id=1 (-5,0 -8.8817842e-16,0) t=0 tEnd=0.00462962963 windSum=? windValue=1
     40 SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? windValue=1
     41 SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
     42 SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=? windValue=1
     43 SkOpSegment::debugShowActiveSpans id=3 (2.22044605e-14,242 -5,242) t=0.99537037 tEnd=1 windSum=? windValue=1
     44 SkOpSegment::debugShowActiveSpans id=4 (-5,242 -5,0) t=0 tEnd=1 windSum=? windValue=1
     45 SkOpSegment::debugShowActiveSpans id=5 (0,0 1075,0) t=0 tEnd=0.99537037 windSum=? windValue=1
     46 SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
     47 SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
     48 SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
     49 SkOpSegment::debugShowActiveSpans id=7 (1075,242 0,242) t=0.00462962963 tEnd=1 windSum=? windValue=1
     50 SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=? windValue=1
     51 ------------------x--x---------------- move_multiples
     52 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     53 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     54 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     55 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     56 ------------------x--x---------------- move_nearby
     57 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     58 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     59 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     60 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     61 ------------------x--x---------------- correctEnds
     62 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     63 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     64 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     65 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     66 ------------------x--x---------------- addEndMovedSpans
     67 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     68 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     69 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     70 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     71 ------------------x--x---------------- expand
     72 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     73 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     74 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     75 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     76 ------------------x--x---------------- addExpanded
     77 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     78 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     79 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     80 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     81 ------------------x--x---------------- mark
     82 00:  seg/base=3/5 seg/base=7/19 MarkCoinStart
     83 01:  seg/base=3/20 seg/base=7/14 MarkCoinEnd
     84 02:  seg/base=1/17 seg/base=5/9 MarkCoinStart
     85 03:  seg/base=1/2 seg/base=5/18 MarkCoinEnd
     86 -------------------------------------- missing_coincidence
     87 -------------------------------------- expand
     88 -------------------------------------- expand
     89 -------------------------------------- apply
     90 SkOpSegment::markDone id=7 (1080,242 0,242) t=0.00462962963 [19] (1075,242) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
     91 SkOpSegment::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
     93 SkOpSegment::debugShowActiveSpans id=1 (-5,0 -8.8817842e-16,0) t=0 tEnd=0.00462962963 windSum=? windValue=1
     94 SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? oppSum=? windValue=1 oppValue=1
     95 SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
     96 SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=? oppSum=? windValue=1 oppValue=1
     97 SkOpSegment::debugShowActiveSpans id=3 (2.22044605e-14,242 -5,242) t=0.99537037 tEnd=1 windSum=? windValue=1
     98 SkOpSegment::debugShowActiveSpans id=4 (-5,242 -5,0) t=0 tEnd=1 windSum=? windValue=1
     99 SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
    100 SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
    101 SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
    102 SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=? windValue=1
    103 -------------------------------------- calc_angles
    104 SkOpSegment::sortAngles [1] tStart=0.00462962963 [17]
    105 SkOpAngle::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
    106 SkOpAngle::afterPart {{{0,0}, {-5,0}}} id=1
    107 SkOpAngle::afterPart {{{0,0}, {0,242}}} id=8
    108 SkOpAngle::afterPart {{{0,0}, {1075,0}}} id=1
    109 SkOpSegment::sortAngles [1] tStart=1 [2]
    110 SkOpAngle::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
    111 SkOpAngle::afterPart {{{1075,0}, {-8.8817842e-16,0}}} id=1
    112 SkOpAngle::afterPart {{{1075,0}, {1075,242}}} id=2
    113 SkOpAngle::afterPart {{{1075,0}, {1080,0}}} id=5
    114 SkOpSegment::sortAngles [2] tStart=0 [3]
    115 SkOpSegment::sortAngles [2] tStart=1 [4]
    116 SkOpAngle::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
    117 SkOpAngle::afterPart {{{1075,242}, {1075,0}}} id=2
    118 SkOpAngle::afterPart {{{1075,242}, {2.22044605e-14,242}}} id=3
    119 SkOpAngle::afterPart {{{1075,242}, {1080,242}}} id=7
    120 SkOpSegment::sortAngles [3] tStart=0 [5]
    121 SkOpSegment::sortAngles [3] tStart=0.99537037 [20]
    122 SkOpAngle::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
    123 SkOpAngle::afterPart {{{0,242}, {1075,242}}} id=3
    124 SkOpAngle::afterPart {{{0,242}, {0,0}}} id=8
    125 SkOpAngle::afterPart {{{0,242}, {-5,242}}} id=3
    126 SkOpSegment::sortAngles [5] tStart=0.99537037 [18]
    127 SkOpSegment::sortAngles [7] tStart=0.00462962963 [19]
    128 SkOpSegment::sortAngles [8] tStart=0 [15]
    129 SkOpSegment::sortAngles [8] tStart=1 [16]
    130 coinSpan - id=3 t=0 tEnd=0.99537037
    131 coinSpan + id=7 t=0.00462962963 tEnd=1
    132 coinSpan - id=1 t=0.00462962963 tEnd=1
    133 coinSpan + id=5 t=0 tEnd=0.99537037
    134 SkOpSpan::sortableTop dir=kTop seg=1 t=0.00231481481 pt=(-2.5,0)
    135 SkOpSpan::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)
    136 SkOpSegment::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
    137 SkOpSegment::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
    138 SkOpSegment::markWinding id=4 (-5,242 -5,0) t=0 [7] (-5,242) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
    139 SkOpSegment::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
    140 SkOpSegment::activeOp id=1 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=0 result=0
    141 SkOpSegment::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
    142 SkOpSegment::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
    143 SkOpSegment::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
    144 bridgeOp chase.append id=3 windSum=-1
    145 SkOpSegment::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
    146 SkOpSegment::markAngle last segment=3 span=5 windSum=-1 
    147 SkOpSegment::markWinding id=8 (0,242 0,0) t=0 [15] (0,242) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
    148 SkOpSegment::markAngle last segment=8 span=16 
    149 SkOpSegment::debugShowActiveSpans id=1 (-8.8817842e-16,0 1075,0) t=0.00462962963 tEnd=1 windSum=? oppSum=? windValue=1 oppValue=1
    150 SkOpSegment::debugShowActiveSpans id=2 (1075,0 1075,242) t=0 tEnd=1 windSum=? windValue=1
    151 SkOpSegment::debugShowActiveSpans id=3 (1075,242 2.22044605e-14,242) t=0 tEnd=0.99537037 windSum=-1 oppSum=-1 windValue=1 oppValue=1
    152 SkOpSegment::debugShowActiveSpans id=5 (1075,0 1080,0) t=0.99537037 tEnd=1 windSum=? windValue=1
    153 SkOpSegment::debugShowActiveSpans id=6 (1080,0 1080,242) t=0 tEnd=1 windSum=? windValue=1
    154 SkOpSegment::debugShowActiveSpans id=7 (1080,242 1075,242) t=0 tEnd=0.00462962963 windSum=? windValue=1
    155 SkOpSegment::debugShowActiveSpans id=8 (0,242 0,0) t=0 tEnd=1 windSum=-1 oppSum=-1 windValue=1 oppValue=0
    156 SkOpSegment::activeOp id=3 t=0.99537037 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
    157 SkOpSegment::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
    158 SkOpSegment::markWinding id=6 (1080,0 1080,242) t=0 [11] (1080,0) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
    159 SkOpSegment::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
    160 SkOpSegment::markAngle last segment=5 span=18 windSum=-1 
    161 SkOpSegment::markWinding id=2 (1075,0 1075,242) t=0 [3] (1075,0) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
    162 SkOpSegment::markAngle last segment=2 span=3 windSum=-1 
    163 SkOpSegment::findNextOp
    164 SkOpAngle::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
    165 SkOpAngle::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
    166 SkOpAngle::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
    167 SkOpSegment::activeOp id=7 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
    168 SkOpSegment::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
    169 SkOpSegment::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
    170 SkOpSegment::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
    171 SkOpSegment::findNextOp chase.append segment=5 span=18 windSum=-1
    172 SkOpSegment::activeOp id=2 t=1 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=1 suTo=1 result=1
    173 SkOpSegment::findNextOp chase.append segment=2 span=3 windSum=-1
    174 SkOpSegment::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
    175 SkOpSegment::findNextOp from:[3] to:[2] start=90366152 end=90366008
    176 bridgeOp current id=3 from=(2.22044605e-14,242) to=(1075,242)
    177 SkOpSegment::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
    178 SkOpSegment::markAngle last segment=1 span=17 windSum=-1 
    179 SkOpSegment::findNextOp
    180 SkOpAngle::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
    181 SkOpAngle::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
    182 SkOpAngle::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
    183 SkOpSegment::activeOp id=5 t=0.99537037 tEnd=1 op=sect miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
    184 SkOpSegment::activeOp id=1 t=1 tEnd=0.00462962963 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
    185 SkOpSegment::findNextOp chase.append segment=1 span=17 windSum=-1
    186 SkOpSegment::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
    187 SkOpSegment::findNextOp from:[2] to:[1] start=90365736 end=90368360
    188 bridgeOp current id=2 from=(1075,242) to=(1075,0)
    189 path.moveTo(2.22044605e-14,242);
    190 path.lineTo(1075,242);
    191 SkOpSegment::findNextOp
    192 SkOpAngle::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
    193 SkOpAngle::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
    194 SkOpAngle::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
    195 SkOpSegment::activeOp id=1 t=0.00462962963 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=0 result=0
    196 SkOpSegment::activeOp id=8 t=1 tEnd=0 op=sect miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
    197 SkOpSegment::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
    198 SkOpSegment::findNextOp from:[1] to:[8] start=90368192 end=90368048
    199 bridgeOp current id=1 from=(1075,0) to=(-8.8817842e-16,0)
    200 path.lineTo(1075,0);
    201 SkOpSegment::findNextOp
    202 SkOpAngle::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
    203 SkOpAngle::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
    204 SkOpAngle::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
    205 SkOpSegment::activeOp id=3 t=0.99537037 tEnd=1 op=sect miFrom=1 miTo=0 suFrom=0 suTo=0 result=0
    206 SkOpSegment::activeOp id=3 t=0.99537037 tEnd=0 op=sect miFrom=0 miTo=1 suFrom=0 suTo=1 result=1
    207 SkOpSegment::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
    208 SkOpSegment::findNextOp from:[8] to:[3] start=90368840 end=90366336
    209 bridgeOp current id=8 from=(0,0) to=(0,242)
    210 path.lineTo(-8.8817842e-16,0);
    211 path.lineTo(0,242);
    212 path.close();
    213 </div>
    214 
    215 </div>
    216 
    217 <script type="text/javascript">
    218 
    219     var testDivs = [
    220         android1,
    221     ];
    222 
    223 var decimal_places = 3; // make this 3 to show more precision
    224 
    225 var tests = [];
    226 var testLines = [];
    227 var testTitles = [];
    228 var testIndex = 0;
    229 var ctx;
    230 
    231 var xmin, xmax, focusXmin, focusXmax;
    232 var ymin, ymax, focusYmin, focusYmax;
    233 var scale;
    234 var mouseX, mouseY;
    235 var srcLeft, srcTop;
    236 var screenWidth, screenHeight;
    237 var drawnPts, drawnLines, drawnQuads, drawnConics, drawnCubics;
    238 var curveT = 0;
    239 
    240 var pt_labels = 2;
    241 var collect_bounds = false;
    242 var control_lines = 0;
    243 var curve_t = false;
    244 var debug_xy = 1;
    245 var focus_enabled = false;
    246 var focus_on_selection = false;
    247 var step_limit = 0;
    248 var draw_active = false;
    249 var draw_add = false;
    250 var draw_angle = 0;
    251 var draw_coincidence = false;
    252 var draw_deriviatives = 0;
    253 var draw_direction = false;
    254 var draw_hints = false;
    255 var draw_id = false;
    256 var draw_intersection = 0;
    257 var draw_intersectT = false;
    258 var draw_legend = true;
    259 var draw_log = false;
    260 var draw_mark = false;
    261 var draw_midpoint = false;
    262 var draw_op = 0;
    263 var draw_sequence = false;
    264 var draw_sort = 0;
    265 var draw_top = false;
    266 var draw_path = 3;
    267 var draw_computed = 0;
    268 var retina_scale = !!window.devicePixelRatio;
    269 
    270 var activeCount = 0;
    271 var addCount = 0;
    272 var angleCount = 0;
    273 var coinCount = 0;
    274 var opCount = 0;
    275 var sectCount = 0;
    276 var sortCount = 0;
    277 var topCount = 0;
    278 var markCount = 0;
    279 var activeMax = 0;
    280 var addMax = 0;
    281 var angleMax = 0;
    282 var coinMax = 0;
    283 var sectMax = 0;
    284 var sectMax2 = 0;
    285 var sortMax = 0;
    286 var topMax = 0;
    287 var markMax = 0;
    288 var opMax = 0;
    289 var stepMax = 0;
    290 var lastIndex = 0;
    291 var hasPath = false;
    292 var hasAlignedPath = false;
    293 var hasComputedPath = false;
    294 var angleBetween = false;
    295 var afterIndex = 0;
    296 
    297 var firstActiveSpan = -1;
    298 var logStart = -1;
    299 var logRange = 0;
    300 
    301 var SPAN_ID = 0;
    302 var SPAN_X1 = SPAN_ID + 1;
    303 var SPAN_Y1 = SPAN_X1 + 1;
    304 var SPAN_X2 = SPAN_Y1 + 1;
    305 var SPAN_Y2 = SPAN_X2 + 1;
    306 
    307 var SPAN_L_TX = SPAN_Y2 + 1;
    308 var SPAN_L_TY = SPAN_L_TX + 1;
    309 var SPAN_L_OTHER = SPAN_L_TY + 1;
    310 var SPAN_L_OTHERT = SPAN_L_OTHER + 1;
    311 var SPAN_L_OTHERI = SPAN_L_OTHERT + 1;
    312 var SPAN_L_SUM = SPAN_L_OTHERI + 1;
    313 var SPAN_L_VAL = SPAN_L_SUM + 1;
    314 var SPAN_L_OPP = SPAN_L_VAL + 1;
    315 
    316 var SPAN_X3 = SPAN_Y2 + 1;
    317 var SPAN_Y3 = SPAN_X3 + 1;
    318 
    319 var SPAN_Q_TX = SPAN_Y3 + 1;
    320 var SPAN_Q_TY = SPAN_Q_TX + 1;
    321 var SPAN_Q_OTHER = SPAN_Q_TY + 1;
    322 var SPAN_Q_OTHERT = SPAN_Q_OTHER + 1;
    323 var SPAN_Q_OTHERI = SPAN_Q_OTHERT + 1;
    324 var SPAN_Q_SUM = SPAN_Q_OTHERI + 1;
    325 var SPAN_Q_VAL = SPAN_Q_SUM + 1;
    326 var SPAN_Q_OPP = SPAN_Q_VAL + 1;
    327 
    328 var SPAN_K_W = SPAN_Y3 + 1;
    329 var SPAN_K_TX = SPAN_K_W + 1;
    330 var SPAN_K_TY = SPAN_K_TX + 1;
    331 var SPAN_K_OTHER = SPAN_K_TY + 1;
    332 var SPAN_K_OTHERT = SPAN_K_OTHER + 1;
    333 var SPAN_K_OTHERI = SPAN_K_OTHERT + 1;
    334 var SPAN_K_SUM = SPAN_K_OTHERI + 1;
    335 var SPAN_K_VAL = SPAN_K_SUM + 1;
    336 var SPAN_K_OPP = SPAN_K_VAL + 1;
    337 
    338 var SPAN_X4 = SPAN_Y3 + 1;
    339 var SPAN_Y4 = SPAN_X4 + 1;
    340 
    341 var SPAN_C_TX = SPAN_Y4 + 1;
    342 var SPAN_C_TY = SPAN_C_TX + 1;
    343 var SPAN_C_OTHER = SPAN_C_TY + 1;
    344 var SPAN_C_OTHERT = SPAN_C_OTHER + 1;
    345 var SPAN_C_OTHERI = SPAN_C_OTHERT + 1;
    346 var SPAN_C_SUM = SPAN_C_OTHERI + 1;
    347 var SPAN_C_VAL = SPAN_C_SUM + 1;
    348 var SPAN_C_OPP = SPAN_C_VAL + 1;
    349 
    350 var ACTIVE_LINE_SPAN =        1;
    351 var ACTIVE_QUAD_SPAN =        ACTIVE_LINE_SPAN + 1;
    352 var ACTIVE_CONIC_SPAN =       ACTIVE_QUAD_SPAN + 1;
    353 var ACTIVE_CUBIC_SPAN =       ACTIVE_CONIC_SPAN + 1;
    354 
    355 var ADD_MOVETO =              ACTIVE_CUBIC_SPAN + 1;
    356 var ADD_LINETO =              ADD_MOVETO + 1;
    357 var ADD_QUADTO =              ADD_LINETO + 1;
    358 var ADD_CONICTO =             ADD_QUADTO + 1;
    359 var ADD_CUBICTO =             ADD_CONICTO + 1;
    360 var ADD_CLOSE =               ADD_CUBICTO + 1;
    361 var ADD_FILL =                ADD_CLOSE + 1;
    362 
    363 var PATH_LINE =               ADD_FILL + 1;
    364 var PATH_QUAD =               PATH_LINE + 1;
    365 var PATH_CONIC =              PATH_QUAD + 1;
    366 var PATH_CUBIC =              PATH_CONIC + 1;
    367 
    368 var INTERSECT_LINE =          PATH_CUBIC + 1;
    369 var INTERSECT_LINE_2 =        INTERSECT_LINE + 1;
    370 var INTERSECT_LINE_NO =       INTERSECT_LINE_2 + 1;
    371 var INTERSECT_QUAD_LINE =     INTERSECT_LINE_NO + 1;
    372 var INTERSECT_QUAD_LINE_2 =   INTERSECT_QUAD_LINE + 1;
    373 var INTERSECT_QUAD_LINE_NO =  INTERSECT_QUAD_LINE_2 + 1;
    374 var INTERSECT_QUAD =          INTERSECT_QUAD_LINE_NO + 1;
    375 var INTERSECT_QUAD_2 =        INTERSECT_QUAD + 1;
    376 var INTERSECT_QUAD_NO =       INTERSECT_QUAD_2 + 1;
    377 var INTERSECT_CONIC_LINE =    INTERSECT_QUAD_NO + 1;
    378 var INTERSECT_CONIC_LINE_2 =  INTERSECT_CONIC_LINE + 1;
    379 var INTERSECT_CONIC_LINE_NO = INTERSECT_CONIC_LINE_2 + 1;
    380 var INTERSECT_CONIC_QUAD =    INTERSECT_CONIC_LINE_NO + 1;
    381 var INTERSECT_CONIC_QUAD_2 =  INTERSECT_CONIC_QUAD + 1;
    382 var INTERSECT_CONIC_QUAD_3 =  INTERSECT_CONIC_QUAD_2 + 1;
    383 var INTERSECT_CONIC_QUAD_4 =  INTERSECT_CONIC_QUAD_3 + 1;
    384 var INTERSECT_CONIC_QUAD_NO = INTERSECT_CONIC_QUAD_4 + 1;
    385 var INTERSECT_CONIC =         INTERSECT_CONIC_QUAD_NO + 1;
    386 var INTERSECT_CONIC_2 =       INTERSECT_CONIC + 1;
    387 var INTERSECT_CONIC_NO =      INTERSECT_CONIC_2 + 1;
    388 var INTERSECT_SELF_CUBIC =    INTERSECT_CONIC_NO + 1;
    389 var INTERSECT_SELF_CUBIC_NO = INTERSECT_SELF_CUBIC + 1;
    390 var INTERSECT_CUBIC_LINE =    INTERSECT_SELF_CUBIC_NO + 1;
    391 var INTERSECT_CUBIC_LINE_2 =  INTERSECT_CUBIC_LINE + 1;
    392 var INTERSECT_CUBIC_LINE_3 =  INTERSECT_CUBIC_LINE_2 + 1;
    393 var INTERSECT_CUBIC_LINE_NO = INTERSECT_CUBIC_LINE_3 + 1;
    394 var INTERSECT_CUBIC_QUAD =    INTERSECT_CUBIC_LINE_NO + 1;
    395 var INTERSECT_CUBIC_QUAD_2 =  INTERSECT_CUBIC_QUAD + 1;
    396 var INTERSECT_CUBIC_QUAD_3 =  INTERSECT_CUBIC_QUAD_2 + 1;
    397 var INTERSECT_CUBIC_QUAD_4 =  INTERSECT_CUBIC_QUAD_3 + 1;
    398 var INTERSECT_CUBIC_QUAD_NO = INTERSECT_CUBIC_QUAD_4 + 1;
    399 var INTERSECT_CUBIC =         INTERSECT_CUBIC_QUAD_NO + 1;
    400 var INTERSECT_CUBIC_2 =       INTERSECT_CUBIC + 1;
    401 var INTERSECT_CUBIC_3 =       INTERSECT_CUBIC_2 + 1;
    402 var INTERSECT_CUBIC_4 =       INTERSECT_CUBIC_3 + 1;
    403 // FIXME: add cubic 5- 9
    404 var INTERSECT_CUBIC_NO =      INTERSECT_CUBIC_4 + 1;
    405 
    406 var SORT_UNARY =              INTERSECT_CUBIC_NO + 1;
    407 var SORT_BINARY =             SORT_UNARY + 1;
    408 
    409 var OP_DIFFERENCE =           SORT_BINARY + 1;
    410 var OP_INTERSECT =            OP_DIFFERENCE + 1;
    411 var OP_UNION =                OP_INTERSECT + 1;
    412 var OP_XOR =                  OP_UNION + 1;
    413 
    414 var MARK_LINE =               OP_XOR + 1;
    415 var MARK_QUAD =               MARK_LINE + 1;
    416 var MARK_CONIC =              MARK_QUAD + 1;
    417 var MARK_CUBIC =              MARK_CONIC + 1;
    418 var MARK_DONE_LINE =          MARK_CUBIC + 1;
    419 var MARK_DONE_QUAD =          MARK_DONE_LINE + 1;
    420 var MARK_DONE_CONIC =         MARK_DONE_QUAD + 1;
    421 var MARK_DONE_CUBIC =         MARK_DONE_CONIC + 1;
    422 var MARK_UNSORTABLE_LINE =    MARK_DONE_CUBIC + 1;
    423 var MARK_UNSORTABLE_QUAD =    MARK_UNSORTABLE_LINE + 1;
    424 var MARK_UNSORTABLE_CONIC =   MARK_UNSORTABLE_QUAD + 1;
    425 var MARK_UNSORTABLE_CUBIC =   MARK_UNSORTABLE_CONIC + 1;
    426 var MARK_SIMPLE_LINE =        MARK_UNSORTABLE_CUBIC + 1;
    427 var MARK_SIMPLE_QUAD =        MARK_SIMPLE_LINE + 1;
    428 var MARK_SIMPLE_CONIC =       MARK_SIMPLE_QUAD + 1;
    429 var MARK_SIMPLE_CUBIC =       MARK_SIMPLE_CONIC + 1;
    430 var MARK_SIMPLE_DONE_LINE =   MARK_SIMPLE_CUBIC + 1;
    431 var MARK_SIMPLE_DONE_QUAD =   MARK_SIMPLE_DONE_LINE + 1;
    432 var MARK_SIMPLE_DONE_CONIC =  MARK_SIMPLE_DONE_QUAD + 1;
    433 var MARK_SIMPLE_DONE_CUBIC =  MARK_SIMPLE_DONE_CONIC + 1;
    434 var MARK_DONE_UNARY_LINE =    MARK_SIMPLE_DONE_CUBIC + 1;
    435 var MARK_DONE_UNARY_QUAD =    MARK_DONE_UNARY_LINE + 1;
    436 var MARK_DONE_UNARY_CONIC =   MARK_DONE_UNARY_QUAD + 1;
    437 var MARK_DONE_UNARY_CUBIC =   MARK_DONE_UNARY_CONIC + 1;
    438 var MARK_ANGLE_LAST =         MARK_DONE_UNARY_CUBIC + 1;
    439 
    440 var COMPUTED_SET_1 =          MARK_ANGLE_LAST + 1;
    441 var COMPUTED_SET_2 =          COMPUTED_SET_1 + 1;
    442 
    443 var ANGLE_AFTER =             COMPUTED_SET_2 + 1;
    444 var ANGLE_AFTERPART =         ANGLE_AFTER + 1;
    445 
    446 var ACTIVE_OP =               ANGLE_AFTERPART + 1;
    447 
    448 var COIN_MAIN_SPAN =          ACTIVE_OP + 1;
    449 var COIN_OPP_SPAN =           COIN_MAIN_SPAN + 1;
    450 
    451 var FRAG_TYPE_LAST =          COIN_OPP_SPAN;
    452 
    453 var REC_TYPE_UNKNOWN = -1;
    454 var REC_TYPE_PATH = 0;
    455 var REC_TYPE_PATH2 = 1;
    456 var REC_TYPE_SECT = 2;
    457 var REC_TYPE_ACTIVE = 3;
    458 var REC_TYPE_ADD = 4;
    459 var REC_TYPE_SORT = 5;
    460 var REC_TYPE_OP = 6;
    461 var REC_TYPE_MARK = 7;
    462 var REC_TYPE_COMPUTED = 8;
    463 var REC_TYPE_COIN = 9;
    464 var REC_TYPE_ANGLE = 10;
    465 var REC_TYPE_ACTIVE_OP = 11;
    466 var REC_TYPE_AFTERPART = 12;
    467 var REC_TYPE_TOP = 13;
    468 var REC_TYPE_COINCIDENCE = 14;
    469 var REC_TYPE_ALIGNED = 15;
    470 var REC_TYPE_LAST = REC_TYPE_ALIGNED;
    471 
    472 function 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 
    486 function 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 
    497 function 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 
    517 function 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 
    539 function 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 
    548 function endsWith(str, suffix) {
    549     return str.indexOf(suffix, str.length - suffix.length) !== -1;
    550 }
    551 
    552 function 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 
    899 function 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 
   1257 function 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 
   1291 function 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 
   1299 function 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 
   1333 function 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 
   1341 function 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 
   1393 function 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 
   1401 function 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 
   1413 function 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 
   1441 function 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 
   1498 function 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 
   1526 function 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 
   1540 function 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 
   1550 function 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 
   1571 function 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 
   1614 function 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 
   1625 function 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 
   1632 function 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 
   1639 function 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 
   1662 function 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 
   1674 function 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 
   1686 function 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 
   1703 function 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 
   1729 function 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 
   1743 function interp(A, B, t) {
   1744     return A + (B - A) * t;
   1745 }
   1746 
   1747 function 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 
   1755 function 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 
   1770 function 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 
   1791 function 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 
   1819 var kMaxConicToQuadPOW2 = 5;
   1820 
   1821 function 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 
   1838 function subdivide_w_value(w) {
   1839     return Math.sqrt(0.5 + w * 0.5);
   1840 }
   1841 
   1842 function 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 
   1858 function 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 
   1873 function chopIntoQuadsPOW2(curve, pow2, pts) {
   1874     subdivide(curve, pow2, pts);
   1875     return 1 << pow2;
   1876 }
   1877 
   1878 function 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 
   1898 function 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 
   1907 function 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 
   1914 function 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 
   1943 function 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 
   1955 function 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 
   1984 function 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 
   2000 function 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 
   2011 function 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 
   2034 function 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 
   2059 function 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 
   2076 function 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 
   2087 function 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 
   2098 function 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 
   2112 function 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 
   2123 function 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 
   2133 function 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 
   2158 function 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 
   2173 function 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 
   2180 function 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 
   2191 function 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 
   2229 function dpt_at_t(curve, t) {
   2230     var type = PATH_LINE + (curve.length / 2 - 2);
   2231     return dxy_at_t(curve, type, t);
   2232 }
   2233 
   2234 function 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 
   2248 function drawLabelX(ymin, num, loc) {
   2249     var px = (loc - srcLeft) * scale;
   2250     var py = (ymin - srcTop) * scale - 20;
   2251     drawLabel(num, px, py);
   2252 }
   2253 
   2254 function drawLabelY(xmin, num, loc) {
   2255     var px = (xmin - srcLeft) * scale - 20;
   2256     var py = (loc - srcTop) * scale;
   2257     drawLabel(num, px, py);
   2258 }
   2259 
   2260 function 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 
   2283 function scalexy(x, y, mag) {
   2284     var length = Math.sqrt(x * x + y * y);
   2285     return mag / length;
   2286 }
   2287 
   2288 function 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 
   2307 function 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 
   2328 function 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 
   2349 function 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 
   2356 function 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 
   2380 function 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 
   2422 function 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 
   2429 function 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 
   2441 function 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 
   2484 function 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 
   2492 function localToGlobal(local) {
   2493     var global = {};
   2494     global.x = (local.x - srcLeft) * scale;
   2495     global.y = (local.y - srcTop) * scale;
   2496     return global;
   2497 }
   2498 
   2499 function 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 
   2505 function 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 
   2541 function 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 
   2556 function drawLinePartialID(id, x1, y1, x2, y2, t1, t2) {
   2557     var curve = [x1, y1, x2, y2];
   2558     drawCurvePartialID(id, curve, t1, t2);
   2559 }
   2560 
   2561 function drawLineID(id, x1, y1, x2, y2) {
   2562     drawLinePartialID(id, x1, y1, x2, y2, 0, 1);
   2563 }
   2564 
   2565 function 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 
   2570 function drawQuadID(id, x1, y1, x2, y2, x3, y3) {
   2571     drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, 0, 1);
   2572 }
   2573 
   2574 function 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 
   2579 function drawConicID(id, x1, y1, x2, y2, x3, y3, w) {
   2580     drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, 0, 1);
   2581 }
   2582 
   2583 function 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 
   2588 function 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 
   2592 function  drawCurvePartialID(id, curve, t1, t2) {
   2593     drawVisibleID(curve, (t1 + t2) / 2, id);
   2594 }
   2595 
   2596 function 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 
   2654 function logCurves(test) {
   2655     for (curves in test) {
   2656         var curve = test[curves];
   2657         dumpCurve(curve);
   2658     }
   2659 }
   2660 
   2661 function 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 
   2681 function dumpCurve(curve) {
   2682     console.log(curveToString(curve));
   2683 }
   2684 
   2685 function 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 
   3788 function 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 
   3808 function 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 
   3828 function 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 
   3848 function 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 
   3859 function 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 
   3875 function 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 
   3883 function dumpAngleTest(test, id, t0, t1) {
   3884     var curve = curveByID(test, id);
   3885     console.log("    { {" + curveToString(curve) + "}, "
   3886             + curve.length / 2 + ", " + t0 + ", " + t1 + ", {} }, //");
   3887 }
   3888 
   3889 function 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 
   3928 var activeKey = 'a';
   3929 var pathKey = 'b';
   3930 var pathBackKey = 'B';
   3931 var centerKey = 'c';
   3932 var coincidenceKey = 'C';
   3933 var addKey = 'd';
   3934 var deriviativesKey = 'f';
   3935 var angleKey = 'g';
   3936 var angleBackKey = 'G';
   3937 var intersectionKey = 'i';
   3938 var intersectionBackKey = 'I';
   3939 var sequenceKey = 'j';
   3940 var midpointKey = 'k';
   3941 var logKey = 'l';
   3942 var logToConsoleKey = 'L';
   3943 var markKey = 'm';
   3944 var sortKey = 'o';
   3945 var opKey = 'p';
   3946 var opBackKey = 'P';
   3947 var computedKey = 'q';
   3948 var computedBackKey = 'Q';
   3949 var directionKey = 'r';
   3950 var stepKey = 's';
   3951 var stepBackKey = 'S';
   3952 var intersectTKey = 't';
   3953 var topKey = 'T';
   3954 var curveTKey = 'u';
   3955 var controlLinesBackKey = 'V';
   3956 var controlLinesKey = 'v';
   3957 var ptsKey = 'x';
   3958 var xyKey = 'y';
   3959 var logCurvesKey = 'z';
   3960 var focusKey = '`';
   3961 var idKey = '.';
   3962 var retinaKey = '\\';
   3963 
   3964 function 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 
   4170 function 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 
   4234 function 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 
   4243 function calcLeftTop() {
   4244     srcLeft = mouseX - screenWidth / 2 / scale;
   4245     srcTop = mouseY - screenHeight / 2 / scale;
   4246 }
   4247 
   4248 var disableClick = false;
   4249 
   4250 function 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 
   4265 function 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 
   4281 function 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