1<html>
2<head>
3<div style="height:0">
4
5<div id="test1">
6computeDelta c1=(0,1 1,6 1,0 2,0) t1=0.0166500365 scale1=1 c2=(0,1 0,2 1,0 6,1) t2=0.126935168 scale2=1
7cubicTangent t=0.0166500365 tangent=(-2.85263545,-12.6745554 2.95089079,15.1559166) pt=(0.0491276698 1.24068063) dxy=(2.90176312 13.915236)
8cubicTangent t=0.126935168 tangent=(-0.852150487,0.242871519 0.961097194,2.2532568) pt=(0.0544733534 1.24806416) dxy=(0.90662384 1.00519264)
9cubicDelta tangent=(-0.00039510851,-0.00189471984 0.0495227783,1.24257535) intersectLen=0.00193547772 tangentLen=14.2145708 scale=0.00390625 result=0.00404241153
10cubicDelta tangent=(0.00495057512,0.00548880522 0.0495227783,1.24257535) intersectLen=0.00739156118 tangentLen=1.35365395 scale=0.00390625 result=0.00936670107
11</div>
12
13<div id="test2">
14computeDelta c1=(0,1 0,2 1,0 6,1) t1=0.121215914 scale1=0.0187334021 c2=(0,1 1,6 1,0 2,0) t2=0.0167515231 scale2=0.00808482306
15cubicTangent t=0.121215914 tangent=(-0.810112087,0.159501524 0.908958243,2.32468734) pt=(0.0494230781 1.24209443) dxy=(0.859535165 1.08259291)
16cubicTangent t=0.0167515231 tangent=(-2.85175241,-12.6666182 2.95059667,15.1508033) pt=(0.0494221303 1.24209251) dxy=(2.90117454 13.9087108)
17cubicDelta tangent=(7.4284882e-07,9.35625319e-07 0.0494223352,1.2420935) intersectLen=1.19466276e-06 tangentLen=1.38231983 scale=7.31773521e-05 result=7.40415969e-05
18cubicDelta tangent=(-2.04951629e-07,-9.82572016e-07 0.0494223352,1.2420935) intersectLen=1.00371955e-06 tangentLen=14.2080628 scale=3.15813401e-05 result=3.16519844e-05
19</div>
20
21<div id="test3">
22computeDelta c1=(0,1 1,6 1,0 2,0) t1=0.0167458976 scale1=6.33039689e-05 c2=(0,1 0,2 1,0 6,1) t2=0.121141872 scale2=0.000148083194
23cubicTangent t=0.0167458976 tangent=(-2.85180136,-12.6670582 2.95061297,15.1510867) pt=(0.0494058095 1.24201427) dxy=(2.90120716 13.9090724)
24cubicTangent t=0.121141872 tangent=(-0.809569955,0.158411583 0.908288874,2.32561689) pt=(0.0493594591 1.24201424) dxy=(0.858929414 1.08360265)
25cubicDelta tangent=(-1.65436799e-05,-7.93143093e-05 0.0494223532,1.24209358) intersectLen=8.1021312e-05 tangentLen=14.2084235 scale=2.47281129e-07 result=5.94962466e-06
26cubicDelta tangent=(-6.28940702e-05,-7.93454971e-05 0.0494223532,1.24209358) intersectLen=0.000101249059 tangentLen=1.38273441 scale=5.78449976e-07 result=7.38022436e-05
27</div>
28
29</div>
30
31<script type="text/javascript">
32
33var testDivs = [
34    test3,
35    test2,
36    test1,
37];
38
39var scale, columns, rows, xStart, yStart;
40
41var ticks = 10;
42var at_x = 13 + 0.5;
43var at_y = 23 + 0.5;
44var decimal_places = 3;
45var tests = [];
46var testTitles = [];
47var testIndex = 0;
48var ctx;
49var minScale = 1;
50var subscale = 1;
51var curveT = -1;
52var drawCubics = true;
53var drawQuads = true;
54var drawControlLines = true;
55var drawT = true;
56
57var xmin, xmax, ymin, ymax;
58
59function strs_to_nums(strs) {
60    var result = [];
61    for (var idx in strs) {
62        var str = strs[idx];
63        var num = parseFloat(str);
64        if (isNaN(num)) {
65            result.push(str);
66        } else {
67            result.push(num);
68        }
69    }
70    return result;
71}
72
73function construct_regexp(pattern) {
74    var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
75    escape = escape.replace(/PT_VAL/g, "(-?\\d+\\.?\\d*e?-?\\d*),(-?\\d+\\.?\\d*e?-?\\d*)");
76    escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*e?-?\\d*)");
77    escape = escape.replace(/IDX/g, "(\\d+)");
78    escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
79    return new RegExp(escape, 'i');
80}
81
82var COMPUTE_DELTA = 1;
83var CUBIC_TANGENT = 2;
84var CUBIC_DATA = 3;
85
86var DELTA_C1_X1 = 1;
87var DELTA_C1_Y1 = 2;
88var DELTA_C1_X2 = 3;
89var DELTA_C1_Y2 = 4;
90var DELTA_C1_X3 = 5;
91var DELTA_C1_Y3 = 6;
92var DELTA_C1_X4 = 7;
93var DELTA_C1_Y4 = 8;
94var DELTA_T1 = 9;
95var DELTA_SCALE1 = 10;
96var DELTA_C2_X1 = 11;
97var DELTA_C2_Y1 = 12;
98var DELTA_C2_X2 = 13;
99var DELTA_C2_Y2 = 14;
100var DELTA_C2_X3 = 15;
101var DELTA_C2_Y3 = 16;
102var DELTA_C2_X4 = 17;
103var DELTA_C2_Y4 = 18;
104var DELTA_T2 = 19;
105var DELTA_SCALE2 = 20;
106
107var TANGENT_T = 1;
108var TANGENT_TANGENT_X1 = 2;
109var TANGENT_TANGENT_Y1 = 3;
110var TANGENT_TANGENT_X2 = 4;
111var TANGENT_TANGENT_Y2 = 5;
112var TANGENT_PT_X = 6;
113var TANGENT_PT_Y = 7;
114var TANGENT_DXY_X = 8;
115var TANGENT_DXY_Y = 9;
116
117var CUBIC_TANGENT_X1 = 1;
118var CUBIC_TANGENT_Y1 = 2;
119var CUBIC_TANGENT_X2 = 3;
120var CUBIC_TANGENT_Y2 = 4;
121var CUBIC_INTERSECTION_LEN = 5;
122var CUBIC_TANGENT_LEN = 6;
123var CUBIC_SCALE = 7;
124var CUBIC_RESULT = 8;
125
126function parse(test, title) {
127    var compute_delta = construct_regexp(" c1=(PT_VAL PT_VAL PT_VAL PT_VAL)"
128        + " t1=T_VAL scale1=T_VAL c2=(PT_VAL PT_VAL PT_VAL PT_VAL) t2=T_VAL scale2=T_VAL");
129    var cubic_tangent = construct_regexp(" t=T_VAL tangent=(PT_VAL PT_VAL)"
130        + " pt=(T_VAL T_VAL) dxy=(T_VAL T_VAL)");
131    var cubic_data = construct_regexp(" tangent=(PT_VAL PT_VAL)"
132        + " intersectLen=T_VAL tangentLen=T_VAL scale=T_VAL result=T_VAL");
133
134    var cStrs = test.split("computeDelta");
135    var data = [];
136    for (var cs in cStrs) {
137        var str = cStrs[cs];
138        if (str == "\n") {
139            continue;
140        }
141        var tStrs = str.split("cubicTangent");
142        for (var ts in tStrs) {
143            str = tStrs[ts];
144            if (str == "\n") {
145                continue;
146            }
147            var dStrs = str.split("cubicDelta");
148            var dataStrs;
149            for (var ds in dStrs) {
150                str = dStrs[ds];
151                if (str == "\n") {
152                    continue;
153                }
154                var lineMatch, lineStrs;
155                if (compute_delta.test(str)) {
156                    lineMatch = COMPUTE_DELTA;
157                    lineStrs = compute_delta.exec(str);
158                } else if (cubic_tangent.test(str)) {
159                    lineMatch = CUBIC_TANGENT;
160                    lineStrs = cubic_tangent.exec(str);
161                } else if (cubic_data.test(str)) {
162                    lineMatch = CUBIC_DATA;
163                    lineStrs = cubic_data.exec(str);
164                } else {
165                    continue;
166                }
167                var line = strs_to_nums(lineStrs);
168                data.push(lineMatch);
169                data.push(line);
170            }
171        }
172    }
173    if (data.length >= 1) {
174        tests.push(data);
175        testTitles.push(title);
176    }
177}
178
179function init(test) {
180    var canvas = document.getElementById('canvas');
181    if (!canvas.getContext) return;
182    canvas.width = window.innerWidth - at_x;
183    canvas.height = window.innerHeight - at_y;
184    ctx = canvas.getContext('2d');
185    xmin = Infinity;
186    xmax = -Infinity;
187    ymin = Infinity;
188    ymax = -Infinity;
189    var scanType = -1;
190    for (var scansStr in test) {
191        var scans = parseInt(scansStr);
192        var scan = test[scans];
193        if (scanType == -1) {
194            scanType = scan;
195            continue;
196        }
197        if (scanType == CUBIC_TANGENT) {
198            for (var idx = TANGENT_TANGENT_X1; idx < TANGENT_PT_X; idx += 2) {
199                xmin = Math.min(xmin, scan[idx]);
200                xmax = Math.max(xmax, scan[idx]);
201                ymin = Math.min(ymin, scan[idx + 1]);
202                ymax = Math.max(ymax, scan[idx + 1]);
203            }
204        }
205        scanType = -1;
206    }
207    var testW = xmax - xmin;
208    var testH = ymax - ymin;
209    subscale = 1;
210    if (testW > 1e10 || testH > 1e10) {
211        return;
212    }
213    while (testW * subscale < 0.1 && testH * subscale < 0.1) {
214        subscale *= 10;
215    }
216    while (testW * subscale > 10 && testH * subscale > 10) {
217        subscale /= 10;
218    }
219    calcFromScale();
220}
221
222function calcFromScale() {
223    xStart = Math.floor(xmin * subscale) / subscale;
224    yStart = Math.floor(ymin * subscale) / subscale;
225    var xEnd = Math.ceil(xmin * subscale) / subscale;
226    var yEnd = Math.ceil(ymin * subscale) / subscale;
227    var cCelsW = Math.floor(ctx.canvas.width / 10);
228    var cCelsH = Math.floor(ctx.canvas.height / 10);
229    var testW = xEnd - xStart;
230    var testH = yEnd - yStart; 
231    var scaleWH = 1;
232    while (cCelsW > testW * scaleWH * 10 && cCelsH > testH * scaleWH * 10) {
233        scaleWH *= 10;
234    }
235    while (cCelsW * 10 < testW * scaleWH && cCelsH * 10 < testH * scaleWH) {
236        scaleWH /= 10;
237    }
238    
239    columns = Math.ceil(xmax * subscale) - Math.floor(xmin * subscale) + 1;
240    rows = Math.ceil(ymax * subscale) - Math.floor(ymin * subscale) + 1;
241    
242    var hscale = ctx.canvas.width / columns / ticks;
243    var vscale = ctx.canvas.height / rows / ticks;
244    minScale = Math.floor(Math.min(hscale, vscale));
245    scale = minScale * subscale;
246}
247
248function drawPoint(px, py, xoffset, yoffset, unit) {
249    var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
250    var _px = px * unit + xoffset;
251    var _py = py * unit + yoffset;
252    ctx.beginPath();
253    ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
254    ctx.closePath();
255    ctx.fill();
256    ctx.fillText(label, _px + 5, _py);
257}
258
259function drawTPt(scan, cIdx, tIdx, xoffset, yoffset, unit) {
260    var t = scan[tIdx];
261    var one_t = 1 - t;
262    var one_t2 = one_t * one_t;
263    var a = one_t2 * one_t;
264    var b = 3 * one_t2 * t;
265    var t2 = t * t;
266    var c = 3 * one_t * t2;
267    var d = t2 * t;
268    var x = a * scan[cIdx + 0] + b * scan[cIdx + 2] + c * scan[cIdx + 4] + d * scan[cIdx + 6];
269    var y = a * scan[cIdx + 1] + b * scan[cIdx + 3] + c * scan[cIdx + 5] + d * scan[cIdx + 7];
270    drawPoint(x, y, xoffset, yoffset, unit);
271}
272
273function draw(test, title, scale) {
274    ctx.fillStyle = "rgba(0,0,0, 0.1)";
275    ctx.font = "normal 50px Arial";
276    ctx.fillText(title, 50, 50);
277    ctx.font = "normal 10px Arial";
278
279    var unit = scale * ticks;
280    ctx.lineWidth = 1;
281    var i;
282    for (i = 0; i <= rows * ticks; ++i) {
283        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(200,200,200)" : "black";
284        ctx.beginPath();
285        ctx.moveTo(at_x + 0, at_y + i * minScale);
286        ctx.lineTo(at_x + ticks * columns * minScale, at_y + i * minScale);
287        ctx.stroke();
288    }
289    for (i = 0; i <= columns * ticks; ++i) {
290        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(200,200,200)" : "black";
291        ctx.beginPath();
292        ctx.moveTo(at_x + i * minScale, at_y + 0);
293        ctx.lineTo(at_x + i * minScale, at_y + ticks * rows * minScale);
294        ctx.stroke();
295    }
296 
297    var xoffset = xStart * -unit + at_x;
298    var yoffset = yStart * -unit + at_y;
299
300    ctx.fillStyle = "rgb(40,80,60)"
301    for (i = 0; i <= columns; i += 1)
302    {
303        num = xStart + i / subscale; 
304        ctx.fillText(num.toFixed(decimal_places), xoffset + num * unit - 5, 10);
305    }
306    for (i = 0; i <= rows; i += 1)
307    {
308        num = yStart + i / subscale; 
309        ctx.fillText(num.toFixed(decimal_places), 0, yoffset + num * unit + 0);
310    }
311    var scanType = -1;
312    var partIndex = 0;
313    for (var scans in test) {
314        var scan = test[scans];
315        if (scanType == -1) {
316            scanType = scan;
317            continue;
318        }
319        partIndex++;
320        if (scanType == COMPUTE_DELTA) {
321            ctx.beginPath();
322            ctx.moveTo(xoffset + scan[DELTA_C1_X1] * unit, yoffset + scan[DELTA_C1_Y1] * unit);
323            ctx.bezierCurveTo(
324                xoffset + scan[DELTA_C1_X2] * unit, yoffset + scan[DELTA_C1_Y2] * unit,
325                xoffset + scan[DELTA_C1_X3] * unit, yoffset + scan[DELTA_C1_Y3] * unit,
326                xoffset + scan[DELTA_C1_X4] * unit, yoffset + scan[DELTA_C1_Y4] * unit);
327            ctx.strokeStyle = "red"; // "rgba(0,0,0, 1.0)";
328            ctx.stroke();
329            ctx.beginPath();
330            ctx.moveTo(xoffset + scan[DELTA_C2_X1] * unit, yoffset + scan[DELTA_C2_Y1] * unit);
331            ctx.bezierCurveTo(
332                xoffset + scan[DELTA_C2_X2] * unit, yoffset + scan[DELTA_C2_Y2] * unit,
333                xoffset + scan[DELTA_C2_X3] * unit, yoffset + scan[DELTA_C2_Y3] * unit,
334                xoffset + scan[DELTA_C2_X4] * unit, yoffset + scan[DELTA_C2_Y4] * unit);
335            ctx.strokeStyle = "blue"; // "rgba(0,0,0, 1.0)";
336            ctx.stroke();
337        }
338        if (scanType == COMPUTE_DELTA && drawControlLines) {
339            ctx.beginPath();
340            ctx.moveTo(xoffset + scan[DELTA_C1_X1] * unit, yoffset + scan[DELTA_C1_Y1] * unit);
341            ctx.lineTo(xoffset + scan[DELTA_C1_X2] * unit, yoffset + scan[DELTA_C1_Y2] * unit);
342            ctx.lineTo(xoffset + scan[DELTA_C1_X3] * unit, yoffset + scan[DELTA_C1_Y3] * unit);
343            ctx.lineTo(xoffset + scan[DELTA_C1_X4] * unit, yoffset + scan[DELTA_C1_Y4] * unit);
344            ctx.strokeStyle = "rgba(0,0,0, 0.3)";
345            ctx.stroke();
346            ctx.beginPath();
347            ctx.moveTo(xoffset + scan[DELTA_C2_X1] * unit, yoffset + scan[DELTA_C2_Y1] * unit);
348            ctx.lineTo(xoffset + scan[DELTA_C2_X2] * unit, yoffset + scan[DELTA_C2_Y2] * unit);
349            ctx.lineTo(xoffset + scan[DELTA_C2_X3] * unit, yoffset + scan[DELTA_C2_Y3] * unit);
350            ctx.lineTo(xoffset + scan[DELTA_C2_X4] * unit, yoffset + scan[DELTA_C2_Y4] * unit);
351            ctx.strokeStyle = "rgba(0,0,0, 0.3)";
352            ctx.stroke();
353        }
354        if (scanType == COMPUTE_DELTA && drawT) {
355            drawTPt(scan, DELTA_C1_X1, DELTA_T1, xoffset, yoffset, unit);
356            drawTPt(scan, DELTA_C2_X1, DELTA_T2, xoffset, yoffset, unit);
357            var num = "c1=" + scan[DELTA_T1].toFixed(decimal_places)
358                    + " c2=" + scan[DELTA_T2].toFixed(decimal_places);
359            ctx.beginPath();
360            ctx.rect(200,10,200,10);
361            ctx.fillStyle="white";
362            ctx.fill();
363            ctx.fillStyle="black";
364            ctx.fillText(num, 230, 18);
365        }
366        if (scanType == CUBIC_TANGENT) {
367            ctx.beginPath();
368            ctx.moveTo(xoffset + scan[TANGENT_TANGENT_X1] * unit, yoffset + scan[TANGENT_TANGENT_Y1] * unit);
369            ctx.lineTo(xoffset + scan[TANGENT_TANGENT_X2] * unit, yoffset + scan[TANGENT_TANGENT_Y2] * unit);
370            ctx.strokeStyle = partIndex > 2 ? "rgba(0,0,255, 0.7)" : "rgba(255,0,0, 0.7)";
371            ctx.stroke();
372        }
373        scanType = -1;
374    }
375}
376
377function drawTop() {
378    init(tests[testIndex]);
379    redraw();
380}
381
382function redraw() {
383    ctx.beginPath();
384    ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
385    ctx.fillStyle="white";
386    ctx.fill();
387    draw(tests[testIndex], testTitles[testIndex], scale);
388}
389
390function doKeyPress(evt) {
391    var char = String.fromCharCode(evt.charCode);
392    switch (char) {
393    case 'c':
394        drawCubics ^= true;
395        redraw();
396        break;
397    case 'd':
398        decimal_places++;
399        redraw();
400        break;
401    case 'D':
402        decimal_places--;
403        if (decimal_places < 1) {
404            decimal_places = 1;
405        }
406        redraw();
407        break;
408    case 'l':
409        drawControlLines ^= true;
410        redraw();
411        break;
412    case 'N':
413        testIndex += 9;
414    case 'n':
415        if (++testIndex >= tests.length)
416            testIndex = 0;
417        mouseX = Infinity;
418        drawTop();
419        break;
420    case 'P':
421        testIndex -= 9;
422    case 'p':
423        if (--testIndex < 0)
424            testIndex = tests.length - 1;
425        mouseX = Infinity;
426        drawTop();
427        break;
428    case 'q':
429        drawQuads ^= true;
430        redraw();
431        break;
432    case 't':
433        drawT ^= true;
434        redraw();
435        break;
436    case 'x':
437        drawCubics ^= true;
438        drawQuads ^= true;
439        redraw();
440        break;
441    case '-':
442    case '_':
443        subscale /= 2;
444        calcFromScale();
445        redraw();
446        break;
447    case '+':
448    case '=':
449        subscale *= 2;
450        calcFromScale();
451        redraw();
452        break;
453    }
454}
455
456/*
457    var e = window.event;
458	var tgt = e.target || e.srcElement;
459    var min = tgt.offsetTop + Math.ceil(at_y);
460    var max = min + ticks * rows * minScale;
461    curveT = (e.clientY - min) / (max - min);
462    redraw();
463}
464*/
465
466function calcXY() {
467    var e = window.event;
468	var tgt = e.target || e.srcElement;
469    var left = tgt.offsetLeft;
470    var top = tgt.offsetTop;
471    var unit = scale * ticks;
472    mouseX = (e.clientX - left - Math.ceil(at_x) + 1) / unit + xStart;
473    mouseY = (e.clientY - top - Math.ceil(at_y)) / unit + yStart;
474}
475
476function handleMouseOver() {
477 /*   calcXY();
478    var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
479    ctx.beginPath();
480    ctx.rect(30,10,200,10);
481    ctx.fillStyle="white";
482    ctx.fill();
483    ctx.fillStyle="black";
484    ctx.fillText(num, 30, 18);
485*/
486}
487
488function start() {
489    for (i = 0; i < testDivs.length; ++i) {
490        var title = testDivs[i].id.toString();
491        var str = testDivs[i].firstChild.data;
492        parse(str, title);
493    }
494    drawTop();
495    window.addEventListener('keypress', doKeyPress, true);
496    window.onresize = function() {
497        drawTop();
498    }
499}
500
501function handleMouseClick() {
502    start();
503}
504
505function startx() {
506}
507
508</script>
509</head>
510
511<body onLoad="startx();">
512<canvas id="canvas" width="750" height="500"
513    onmousemove="handleMouseOver()"
514    onclick="handleMouseClick()"
515    ></canvas >
516</body>
517</html>
518