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