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