1 <html> 2 <head> 3 <div style="height:0"> 4 5 <div id="test1"> 6 computeDelta 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 7 cubicTangent t=0.0166500365 tangent=(-2.85263545,-12.6745554 2.95089079,15.1559166) pt=(0.0491276698 1.24068063) dxy=(2.90176312 13.915236) 8 cubicTangent t=0.126935168 tangent=(-0.852150487,0.242871519 0.961097194,2.2532568) pt=(0.0544733534 1.24806416) dxy=(0.90662384 1.00519264) 9 cubicDelta tangent=(-0.00039510851,-0.00189471984 0.0495227783,1.24257535) intersectLen=0.00193547772 tangentLen=14.2145708 scale=0.00390625 result=0.00404241153 10 cubicDelta 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"> 14 computeDelta 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 15 cubicTangent t=0.121215914 tangent=(-0.810112087,0.159501524 0.908958243,2.32468734) pt=(0.0494230781 1.24209443) dxy=(0.859535165 1.08259291) 16 cubicTangent t=0.0167515231 tangent=(-2.85175241,-12.6666182 2.95059667,15.1508033) pt=(0.0494221303 1.24209251) dxy=(2.90117454 13.9087108) 17 cubicDelta 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 18 cubicDelta 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"> 22 computeDelta 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 23 cubicTangent t=0.0167458976 tangent=(-2.85180136,-12.6670582 2.95061297,15.1510867) pt=(0.0494058095 1.24201427) dxy=(2.90120716 13.9090724) 24 cubicTangent t=0.121141872 tangent=(-0.809569955,0.158411583 0.908288874,2.32561689) pt=(0.0493594591 1.24201424) dxy=(0.858929414 1.08360265) 25 cubicDelta 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 26 cubicDelta 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 33 var testDivs = [ 34 test3, 35 test2, 36 test1, 37 ]; 38 39 var scale, columns, rows, xStart, yStart; 40 41 var ticks = 10; 42 var at_x = 13 + 0.5; 43 var at_y = 23 + 0.5; 44 var decimal_places = 3; 45 var tests = []; 46 var testTitles = []; 47 var testIndex = 0; 48 var ctx; 49 var minScale = 1; 50 var subscale = 1; 51 var curveT = -1; 52 var drawCubics = true; 53 var drawQuads = true; 54 var drawControlLines = true; 55 var drawT = true; 56 57 var xmin, xmax, ymin, ymax; 58 59 function 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 73 function 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 82 var COMPUTE_DELTA = 1; 83 var CUBIC_TANGENT = 2; 84 var CUBIC_DATA = 3; 85 86 var DELTA_C1_X1 = 1; 87 var DELTA_C1_Y1 = 2; 88 var DELTA_C1_X2 = 3; 89 var DELTA_C1_Y2 = 4; 90 var DELTA_C1_X3 = 5; 91 var DELTA_C1_Y3 = 6; 92 var DELTA_C1_X4 = 7; 93 var DELTA_C1_Y4 = 8; 94 var DELTA_T1 = 9; 95 var DELTA_SCALE1 = 10; 96 var DELTA_C2_X1 = 11; 97 var DELTA_C2_Y1 = 12; 98 var DELTA_C2_X2 = 13; 99 var DELTA_C2_Y2 = 14; 100 var DELTA_C2_X3 = 15; 101 var DELTA_C2_Y3 = 16; 102 var DELTA_C2_X4 = 17; 103 var DELTA_C2_Y4 = 18; 104 var DELTA_T2 = 19; 105 var DELTA_SCALE2 = 20; 106 107 var TANGENT_T = 1; 108 var TANGENT_TANGENT_X1 = 2; 109 var TANGENT_TANGENT_Y1 = 3; 110 var TANGENT_TANGENT_X2 = 4; 111 var TANGENT_TANGENT_Y2 = 5; 112 var TANGENT_PT_X = 6; 113 var TANGENT_PT_Y = 7; 114 var TANGENT_DXY_X = 8; 115 var TANGENT_DXY_Y = 9; 116 117 var CUBIC_TANGENT_X1 = 1; 118 var CUBIC_TANGENT_Y1 = 2; 119 var CUBIC_TANGENT_X2 = 3; 120 var CUBIC_TANGENT_Y2 = 4; 121 var CUBIC_INTERSECTION_LEN = 5; 122 var CUBIC_TANGENT_LEN = 6; 123 var CUBIC_SCALE = 7; 124 var CUBIC_RESULT = 8; 125 126 function 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 179 function 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 222 function 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 248 function 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 259 function 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 273 function 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 377 function drawTop() { 378 init(tests[testIndex]); 379 redraw(); 380 } 381 382 function 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 390 function 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 466 function 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 476 function 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 488 function 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 501 function handleMouseClick() { 502 start(); 503 } 504 505 function 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