| OLD | NEW |
| (Empty) |
| 1 <html> | |
| 2 <head> | |
| 3 <div style="height:0"> | |
| 4 | |
| 5 <div id="cubic1"> | |
| 6 {{3.13,2.74}, {1.08,4.62}, {3.71,0.94}, {2.01,3.81}} | |
| 7 {{6.71,3.14}, {7.99,2.75}, {8.27,1.96}, {6.35,3.57}} | |
| 8 {{9.45,10.67}, {10.05,5.78}, {13.95,7.46}, {14.72,5.29}} | |
| 9 {{3.34,8.98}, {1.95,10.27}, {3.76,7.65}, {4.96,10.64}} | |
| 10 </div> | |
| 11 | |
| 12 </div> | |
| 13 | |
| 14 <script type="text/javascript"> | |
| 15 | |
| 16 var testDivs = [ | |
| 17 cubic1, | |
| 18 ]; | |
| 19 | |
| 20 var scale, columns, rows, xStart, yStart; | |
| 21 | |
| 22 var ticks = 10; | |
| 23 var at_x = 13 + 0.5; | |
| 24 var at_y = 23 + 0.5; | |
| 25 var decimal_places = 3; | |
| 26 var tests = []; | |
| 27 var testTitles = []; | |
| 28 var testIndex = 0; | |
| 29 var ctx; | |
| 30 var minScale = 1; | |
| 31 var subscale = 1; | |
| 32 var curveT = -1; | |
| 33 var xmin, xmax, ymin, ymax; | |
| 34 | |
| 35 var mouseX, mouseY; | |
| 36 var mouseDown = false; | |
| 37 | |
| 38 var draw_deriviatives = false; | |
| 39 var draw_endpoints = true; | |
| 40 var draw_hodo = false; | |
| 41 var draw_hodo2 = false; | |
| 42 var draw_hodo_origin = true; | |
| 43 var draw_midpoint = false; | |
| 44 var draw_tangents = true; | |
| 45 var draw_sequence = true; | |
| 46 | |
| 47 function parse(test, title) { | |
| 48 var curveStrs = test.split("{{"); | |
| 49 if (curveStrs.length == 1) | |
| 50 curveStrs = test.split("=("); | |
| 51 var pattern = /[a-z$=]?-?\d+\.*\d*e?-?\d*/g; | |
| 52 var curves = []; | |
| 53 for (var c in curveStrs) { | |
| 54 var curveStr = curveStrs[c]; | |
| 55 var points = curveStr.match(pattern); | |
| 56 var pts = []; | |
| 57 for (var wd in points) { | |
| 58 var num = parseFloat(points[wd]); | |
| 59 if (isNaN(num)) continue; | |
| 60 pts.push(num); | |
| 61 } | |
| 62 if (pts.length > 2) | |
| 63 curves.push(pts); | |
| 64 } | |
| 65 if (curves.length >= 1) { | |
| 66 tests.push(curves); | |
| 67 testTitles.push(title); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 function init(test) { | |
| 72 var canvas = document.getElementById('canvas'); | |
| 73 if (!canvas.getContext) return; | |
| 74 canvas.width = window.innerWidth - 20; | |
| 75 canvas.height = window.innerHeight - 20; | |
| 76 ctx = canvas.getContext('2d'); | |
| 77 xmin = Infinity; | |
| 78 xmax = -Infinity; | |
| 79 ymin = Infinity; | |
| 80 ymax = -Infinity; | |
| 81 for (var curves in test) { | |
| 82 var curve = test[curves]; | |
| 83 var last = curve.length; | |
| 84 for (var idx = 0; idx < last; idx += 2) { | |
| 85 xmin = Math.min(xmin, curve[idx]); | |
| 86 xmax = Math.max(xmax, curve[idx]); | |
| 87 ymin = Math.min(ymin, curve[idx + 1]); | |
| 88 ymax = Math.max(ymax, curve[idx + 1]); | |
| 89 } | |
| 90 } | |
| 91 xmin -= 1; | |
| 92 var testW = xmax - xmin; | |
| 93 var testH = ymax - ymin; | |
| 94 subscale = 1; | |
| 95 while (testW * subscale < 0.1 && testH * subscale < 0.1) { | |
| 96 subscale *= 10; | |
| 97 } | |
| 98 while (testW * subscale > 10 && testH * subscale > 10) { | |
| 99 subscale /= 10; | |
| 100 } | |
| 101 calcFromScale(); | |
| 102 } | |
| 103 | |
| 104 function hodograph(cubic) { | |
| 105 var hodo = []; | |
| 106 hodo[0] = 3 * (cubic[2] - cubic[0]); | |
| 107 hodo[1] = 3 * (cubic[3] - cubic[1]); | |
| 108 hodo[2] = 3 * (cubic[4] - cubic[2]); | |
| 109 hodo[3] = 3 * (cubic[5] - cubic[3]); | |
| 110 hodo[4] = 3 * (cubic[6] - cubic[4]); | |
| 111 hodo[5] = 3 * (cubic[7] - cubic[5]); | |
| 112 return hodo; | |
| 113 } | |
| 114 | |
| 115 function hodograph2(cubic) { | |
| 116 var quad = hodograph(cubic); | |
| 117 var hodo = []; | |
| 118 hodo[0] = 2 * (quad[2] - quad[0]); | |
| 119 hodo[1] = 2 * (quad[3] - quad[1]); | |
| 120 hodo[2] = 2 * (quad[4] - quad[2]); | |
| 121 hodo[3] = 2 * (quad[5] - quad[3]); | |
| 122 return hodo; | |
| 123 } | |
| 124 | |
| 125 function quadraticRootsReal(A, B, C, s) { | |
| 126 if (A == 0) { | |
| 127 if (B == 0) { | |
| 128 s[0] = 0; | |
| 129 return C == 0; | |
| 130 } | |
| 131 s[0] = -C / B; | |
| 132 return 1; | |
| 133 } | |
| 134 /* normal form: x^2 + px + q = 0 */ | |
| 135 var p = B / (2 * A); | |
| 136 var q = C / A; | |
| 137 var p2 = p * p; | |
| 138 if (p2 < q) { | |
| 139 return 0; | |
| 140 } | |
| 141 var sqrt_D = 0; | |
| 142 if (p2 > q) { | |
| 143 sqrt_D = sqrt(p2 - q); | |
| 144 } | |
| 145 s[0] = sqrt_D - p; | |
| 146 s[1] = -sqrt_D - p; | |
| 147 return 1 + s[0] != s[1]; | |
| 148 } | |
| 149 | |
| 150 function add_valid_ts(s, realRoots, t) { | |
| 151 var foundRoots = 0; | |
| 152 for (var index = 0; index < realRoots; ++index) { | |
| 153 var tValue = s[index]; | |
| 154 if (tValue >= 0 && tValue <= 1) { | |
| 155 for (var idx2 = 0; idx2 < foundRoots; ++idx2) { | |
| 156 if (t[idx2] != tValue) { | |
| 157 t[foundRoots++] = tValue; | |
| 158 } | |
| 159 } | |
| 160 } | |
| 161 } | |
| 162 return foundRoots; | |
| 163 } | |
| 164 | |
| 165 function quadraticRootsValidT(a, b, c, t) { | |
| 166 var s = []; | |
| 167 var realRoots = quadraticRootsReal(A, B, C, s); | |
| 168 var foundRoots = add_valid_ts(s, realRoots, t); | |
| 169 return foundRoots != 0; | |
| 170 } | |
| 171 | |
| 172 function find_cubic_inflections(cubic, tValues) | |
| 173 { | |
| 174 var Ax = src[2] - src[0]; | |
| 175 var Ay = src[3] - src[1]; | |
| 176 var Bx = src[4] - 2 * src[2] + src[0]; | |
| 177 var By = src[5] - 2 * src[3] + src[1]; | |
| 178 var Cx = src[6] + 3 * (src[2] - src[4]) - src[0]; | |
| 179 var Cy = src[7] + 3 * (src[3] - src[5]) - src[1]; | |
| 180 return quadraticRootsValidT(Bx * Cy - By * Cx, (Ax * Cy - Ay * Cx), | |
| 181 Ax * By - Ay * Bx, tValues); | |
| 182 } | |
| 183 | |
| 184 function dx_at_t(cubic, t) { | |
| 185 var one_t = 1 - t; | |
| 186 var a = cubic[0]; | |
| 187 var b = cubic[2]; | |
| 188 var c = cubic[4]; | |
| 189 var d = cubic[6]; | |
| 190 return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t
* t); | |
| 191 } | |
| 192 | |
| 193 function dy_at_t(cubic, t) { | |
| 194 var one_t = 1 - t; | |
| 195 var a = cubic[1]; | |
| 196 var b = cubic[3]; | |
| 197 var c = cubic[5]; | |
| 198 var d = cubic[7]; | |
| 199 return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t
* t); | |
| 200 } | |
| 201 | |
| 202 function x_at_t(cubic, t) { | |
| 203 var one_t = 1 - t; | |
| 204 var one_t2 = one_t * one_t; | |
| 205 var a = one_t2 * one_t; | |
| 206 var b = 3 * one_t2 * t; | |
| 207 var t2 = t * t; | |
| 208 var c = 3 * one_t * t2; | |
| 209 var d = t2 * t; | |
| 210 return a * cubic[0] + b * cubic[2] + c * cubic[4] + d * cubic[6]; | |
| 211 } | |
| 212 | |
| 213 function y_at_t(cubic, t) { | |
| 214 var one_t = 1 - t; | |
| 215 var one_t2 = one_t * one_t; | |
| 216 var a = one_t2 * one_t; | |
| 217 var b = 3 * one_t2 * t; | |
| 218 var t2 = t * t; | |
| 219 var c = 3 * one_t * t2; | |
| 220 var d = t2 * t; | |
| 221 return a * cubic[1] + b * cubic[3] + c * cubic[5] + d * cubic[7]; | |
| 222 } | |
| 223 | |
| 224 function calcFromScale() { | |
| 225 xStart = Math.floor(xmin * subscale) / subscale; | |
| 226 yStart = Math.floor(ymin * subscale) / subscale; | |
| 227 var xEnd = Math.ceil(xmin * subscale) / subscale; | |
| 228 var yEnd = Math.ceil(ymin * subscale) / subscale; | |
| 229 var cCelsW = Math.floor(ctx.canvas.width / 10); | |
| 230 var cCelsH = Math.floor(ctx.canvas.height / 10); | |
| 231 var testW = xEnd - xStart; | |
| 232 var testH = yEnd - yStart; | |
| 233 var scaleWH = 1; | |
| 234 while (cCelsW > testW * scaleWH * 10 && cCelsH > testH * scaleWH * 10) { | |
| 235 scaleWH *= 10; | |
| 236 } | |
| 237 while (cCelsW * 10 < testW * scaleWH && cCelsH * 10 < testH * scaleWH) { | |
| 238 scaleWH /= 10; | |
| 239 } | |
| 240 | |
| 241 columns = Math.ceil(xmax * subscale) - Math.floor(xmin * subscale) + 1; | |
| 242 rows = Math.ceil(ymax * subscale) - Math.floor(ymin * subscale) + 1; | |
| 243 | |
| 244 var hscale = ctx.canvas.width / columns / ticks; | |
| 245 var vscale = ctx.canvas.height / rows / ticks; | |
| 246 minScale = Math.floor(Math.min(hscale, vscale)); | |
| 247 scale = minScale * subscale; | |
| 248 } | |
| 249 | |
| 250 function drawLine(x1, y1, x2, y2) { | |
| 251 var unit = scale * ticks; | |
| 252 var xoffset = xStart * -unit + at_x; | |
| 253 var yoffset = yStart * -unit + at_y; | |
| 254 ctx.beginPath(); | |
| 255 ctx.moveTo(xoffset + x1 * unit, yoffset + y1 * unit); | |
| 256 ctx.lineTo(xoffset + x2 * unit, yoffset + y2 * unit); | |
| 257 ctx.stroke(); | |
| 258 } | |
| 259 | |
| 260 function drawPoint(px, py) { | |
| 261 var unit = scale * ticks; | |
| 262 var xoffset = xStart * -unit + at_x; | |
| 263 var yoffset = yStart * -unit + at_y; | |
| 264 var _px = px * unit + xoffset; | |
| 265 var _py = py * unit + yoffset; | |
| 266 ctx.beginPath(); | |
| 267 ctx.arc(_px, _py, 3, 0, Math.PI*2, true); | |
| 268 ctx.closePath(); | |
| 269 ctx.stroke(); | |
| 270 } | |
| 271 | |
| 272 function drawPointSolid(px, py) { | |
| 273 drawPoint(px, py); | |
| 274 ctx.fillStyle = "rgba(0,0,0, 0.4)"; | |
| 275 ctx.fill(); | |
| 276 } | |
| 277 | |
| 278 function drawLabel(num, px, py) { | |
| 279 ctx.beginPath(); | |
| 280 ctx.arc(px, py, 8, 0, Math.PI*2, true); | |
| 281 ctx.closePath(); | |
| 282 ctx.strokeStyle = "rgba(0,0,0, 0.4)"; | |
| 283 ctx.lineWidth = num == 0 || num == 3 ? 2 : 1; | |
| 284 ctx.stroke(); | |
| 285 ctx.fillStyle = "black"; | |
| 286 ctx.font = "normal 10px Arial"; | |
| 287 // ctx.rotate(0.001); | |
| 288 ctx.fillText(num, px - 2, py + 3); | |
| 289 // ctx.rotate(-0.001); | |
| 290 } | |
| 291 | |
| 292 function drawLabelX(ymin, num, loc) { | |
| 293 var unit = scale * ticks; | |
| 294 var xoffset = xStart * -unit + at_x; | |
| 295 var yoffset = yStart * -unit + at_y; | |
| 296 var px = loc * unit + xoffset; | |
| 297 var py = ymin * unit + yoffset - 20; | |
| 298 drawLabel(num, px, py); | |
| 299 } | |
| 300 | |
| 301 function drawLabelY(xmin, num, loc) { | |
| 302 var unit = scale * ticks; | |
| 303 var xoffset = xStart * -unit + at_x; | |
| 304 var yoffset = yStart * -unit + at_y; | |
| 305 var px = xmin * unit + xoffset - 20; | |
| 306 var py = loc * unit + yoffset; | |
| 307 drawLabel(num, px, py); | |
| 308 } | |
| 309 | |
| 310 function drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY) { | |
| 311 ctx.beginPath(); | |
| 312 ctx.moveTo(hx, hy - 100); | |
| 313 ctx.lineTo(hx, hy); | |
| 314 ctx.strokeStyle = hMinY < 0 ? "green" : "blue"; | |
| 315 ctx.stroke(); | |
| 316 ctx.beginPath(); | |
| 317 ctx.moveTo(hx, hy); | |
| 318 ctx.lineTo(hx, hy + 100); | |
| 319 ctx.strokeStyle = hMaxY > 0 ? "green" : "blue"; | |
| 320 ctx.stroke(); | |
| 321 ctx.beginPath(); | |
| 322 ctx.moveTo(hx - 100, hy); | |
| 323 ctx.lineTo(hx, hy); | |
| 324 ctx.strokeStyle = hMinX < 0 ? "green" : "blue"; | |
| 325 ctx.stroke(); | |
| 326 ctx.beginPath(); | |
| 327 ctx.moveTo(hx, hy); | |
| 328 ctx.lineTo(hx + 100, hy); | |
| 329 ctx.strokeStyle = hMaxX > 0 ? "green" : "blue"; | |
| 330 ctx.stroke(); | |
| 331 } | |
| 332 | |
| 333 function logCurves(test) { | |
| 334 for (curves in test) { | |
| 335 var curve = test[curves]; | |
| 336 if (curve.length != 8) { | |
| 337 continue; | |
| 338 } | |
| 339 var str = "{{"; | |
| 340 for (i = 0; i < 8; i += 2) { | |
| 341 str += curve[i].toFixed(2) + "," + curve[i + 1].toFixed(2); | |
| 342 if (i < 6) { | |
| 343 str += "}, {"; | |
| 344 } | |
| 345 } | |
| 346 str += "}}"; | |
| 347 console.log(str); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 function scalexy(x, y, mag) { | |
| 352 var length = Math.sqrt(x * x + y * y); | |
| 353 return mag / length; | |
| 354 } | |
| 355 | |
| 356 function drawArrow(x, y, dx, dy) { | |
| 357 var unit = scale * ticks; | |
| 358 var xoffset = xStart * -unit + at_x; | |
| 359 var yoffset = yStart * -unit + at_y; | |
| 360 var dscale = scalexy(dx, dy, 1); | |
| 361 dx *= dscale; | |
| 362 dy *= dscale; | |
| 363 ctx.beginPath(); | |
| 364 ctx.moveTo(xoffset + x * unit, yoffset + y * unit); | |
| 365 x += dx; | |
| 366 y += dy; | |
| 367 ctx.lineTo(xoffset + x * unit, yoffset + y * unit); | |
| 368 dx /= 10; | |
| 369 dy /= 10; | |
| 370 ctx.lineTo(xoffset + (x - dy) * unit, yoffset + (y + dx) * unit); | |
| 371 ctx.lineTo(xoffset + (x + dx * 2) * unit, yoffset + (y + dy * 2) * unit); | |
| 372 ctx.lineTo(xoffset + (x + dy) * unit, yoffset + (y - dx) * unit); | |
| 373 ctx.lineTo(xoffset + x * unit, yoffset + y * unit); | |
| 374 ctx.strokeStyle = "rgba(0,75,0, 0.4)"; | |
| 375 ctx.stroke(); | |
| 376 } | |
| 377 | |
| 378 function draw(test, title) { | |
| 379 ctx.fillStyle = "rgba(0,0,0, 0.1)"; | |
| 380 ctx.font = "normal 50px Arial"; | |
| 381 ctx.fillText(title, 50, 50); | |
| 382 ctx.font = "normal 10px Arial"; | |
| 383 var unit = scale * ticks; | |
| 384 // ctx.lineWidth = "1.001"; "0.999"; | |
| 385 var xoffset = xStart * -unit + at_x; | |
| 386 var yoffset = yStart * -unit + at_y; | |
| 387 | |
| 388 for (curves in test) { | |
| 389 var curve = test[curves]; | |
| 390 if (curve.length != 8) { | |
| 391 continue; | |
| 392 } | |
| 393 ctx.lineWidth = 1; | |
| 394 if (draw_tangents) { | |
| 395 ctx.strokeStyle = "rgba(0,0,255, 0.3)"; | |
| 396 drawLine(curve[0], curve[1], curve[2], curve[3]); | |
| 397 drawLine(curve[2], curve[3], curve[4], curve[5]); | |
| 398 drawLine(curve[4], curve[5], curve[6], curve[7]); | |
| 399 } | |
| 400 if (draw_deriviatives) { | |
| 401 var dx = dx_at_t(curve, 0); | |
| 402 var dy = dy_at_t(curve, 0); | |
| 403 drawArrow(curve[0], curve[1], dx, dy); | |
| 404 dx = dx_at_t(curve, 1); | |
| 405 dy = dy_at_t(curve, 1); | |
| 406 drawArrow(curve[6], curve[7], dx, dy); | |
| 407 if (draw_midpoint) { | |
| 408 var midX = x_at_t(curve, 0.5); | |
| 409 var midY = y_at_t(curve, 0.5); | |
| 410 dx = dx_at_t(curve, 0.5); | |
| 411 dy = dy_at_t(curve, 0.5); | |
| 412 drawArrow(midX, midY, dx, dy); | |
| 413 } | |
| 414 } | |
| 415 ctx.beginPath(); | |
| 416 ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit); | |
| 417 ctx.bezierCurveTo( | |
| 418 xoffset + curve[2] * unit, yoffset + curve[3] * unit, | |
| 419 xoffset + curve[4] * unit, yoffset + curve[5] * unit, | |
| 420 xoffset + curve[6] * unit, yoffset + curve[7] * unit); | |
| 421 ctx.strokeStyle = "black"; | |
| 422 ctx.stroke(); | |
| 423 if (draw_endpoints) { | |
| 424 drawPoint(curve[0], curve[1]); | |
| 425 drawPoint(curve[2], curve[3]); | |
| 426 drawPoint(curve[4], curve[5]); | |
| 427 drawPoint(curve[6], curve[7]); | |
| 428 } | |
| 429 if (draw_midpoint) { | |
| 430 var midX = x_at_t(curve, 0.5); | |
| 431 var midY = y_at_t(curve, 0.5); | |
| 432 drawPointSolid(midX, midY); | |
| 433 } | |
| 434 if (draw_hodo) { | |
| 435 var hodo = hodograph(curve); | |
| 436 var hMinX = Math.min(0, hodo[0], hodo[2], hodo[4]); | |
| 437 var hMinY = Math.min(0, hodo[1], hodo[3], hodo[5]); | |
| 438 var hMaxX = Math.max(0, hodo[0], hodo[2], hodo[4]); | |
| 439 var hMaxY = Math.max(0, hodo[1], hodo[3], hodo[5]); | |
| 440 var hScaleX = hMaxX - hMinX > 0 ? ctx.canvas.width / (hMaxX - hMinX)
: 1; | |
| 441 var hScaleY = hMaxY - hMinY > 0 ? ctx.canvas.height / (hMaxY - hMinY
) : 1; | |
| 442 var hUnit = Math.min(hScaleX, hScaleY); | |
| 443 hUnit /= 2; | |
| 444 var hx = xoffset - hMinX * hUnit; | |
| 445 var hy = yoffset - hMinY * hUnit; | |
| 446 ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit); | |
| 447 ctx.quadraticCurveTo( | |
| 448 hx + hodo[2] * hUnit, hy + hodo[3] * hUnit, | |
| 449 hx + hodo[4] * hUnit, hy + hodo[5] * hUnit); | |
| 450 ctx.strokeStyle = "red"; | |
| 451 ctx.stroke(); | |
| 452 if (draw_hodo_origin) { | |
| 453 drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY); | |
| 454 } | |
| 455 } | |
| 456 if (draw_hodo2) { | |
| 457 var hodo = hodograph2(curve); | |
| 458 var hMinX = Math.min(0, hodo[0], hodo[2]); | |
| 459 var hMinY = Math.min(0, hodo[1], hodo[3]); | |
| 460 var hMaxX = Math.max(0, hodo[0], hodo[2]); | |
| 461 var hMaxY = Math.max(0, hodo[1], hodo[3]); | |
| 462 var hScaleX = hMaxX - hMinX > 0 ? ctx.canvas.width / (hMaxX - hMinX)
: 1; | |
| 463 var hScaleY = hMaxY - hMinY > 0 ? ctx.canvas.height / (hMaxY - hMinY
) : 1; | |
| 464 var hUnit = Math.min(hScaleX, hScaleY); | |
| 465 hUnit /= 2; | |
| 466 var hx = xoffset - hMinX * hUnit; | |
| 467 var hy = yoffset - hMinY * hUnit; | |
| 468 ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit); | |
| 469 ctx.lineTo(hx + hodo[2] * hUnit, hy + hodo[3] * hUnit); | |
| 470 ctx.strokeStyle = "red"; | |
| 471 ctx.stroke(); | |
| 472 drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY); | |
| 473 } | |
| 474 if (draw_sequence) { | |
| 475 var ymin = Math.min(curve[1], curve[3], curve[5], curve[7]); | |
| 476 for (var i = 0; i < 8; i+= 2) { | |
| 477 drawLabelX(ymin, i >> 1, curve[i]); | |
| 478 } | |
| 479 var xmin = Math.min(curve[0], curve[2], curve[4], curve[6]); | |
| 480 for (var i = 1; i < 8; i+= 2) { | |
| 481 drawLabelY(xmin, i >> 1, curve[i]); | |
| 482 } | |
| 483 } | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 function drawTop() { | |
| 488 init(tests[testIndex]); | |
| 489 redraw(); | |
| 490 } | |
| 491 | |
| 492 function redraw() { | |
| 493 ctx.beginPath(); | |
| 494 ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height); | |
| 495 ctx.fillStyle="white"; | |
| 496 ctx.fill(); | |
| 497 draw(tests[testIndex], testTitles[testIndex]); | |
| 498 } | |
| 499 | |
| 500 function doKeyPress(evt) { | |
| 501 var char = String.fromCharCode(evt.charCode); | |
| 502 switch (char) { | |
| 503 case '2': | |
| 504 draw_hodo2 ^= true; | |
| 505 redraw(); | |
| 506 break; | |
| 507 case 'd': | |
| 508 draw_deriviatives ^= true; | |
| 509 redraw(); | |
| 510 break; | |
| 511 case 'e': | |
| 512 draw_endpoints ^= true; | |
| 513 redraw(); | |
| 514 break; | |
| 515 case 'h': | |
| 516 draw_hodo ^= true; | |
| 517 redraw(); | |
| 518 break; | |
| 519 case 'N': | |
| 520 testIndex += 9; | |
| 521 case 'n': | |
| 522 if (++testIndex >= tests.length) | |
| 523 testIndex = 0; | |
| 524 drawTop(); | |
| 525 break; | |
| 526 case 'l': | |
| 527 logCurves(tests[testIndex]); | |
| 528 break; | |
| 529 case 'm': | |
| 530 draw_midpoint ^= true; | |
| 531 redraw(); | |
| 532 break; | |
| 533 case 'o': | |
| 534 draw_hodo_origin ^= true; | |
| 535 redraw(); | |
| 536 break; | |
| 537 case 'P': | |
| 538 testIndex -= 9; | |
| 539 case 'p': | |
| 540 if (--testIndex < 0) | |
| 541 testIndex = tests.length - 1; | |
| 542 drawTop(); | |
| 543 break; | |
| 544 case 's': | |
| 545 draw_sequence ^= true; | |
| 546 redraw(); | |
| 547 break; | |
| 548 case 't': | |
| 549 draw_tangents ^= true; | |
| 550 redraw(); | |
| 551 break; | |
| 552 } | |
| 553 } | |
| 554 | |
| 555 function calcXY() { | |
| 556 var e = window.event; | |
| 557 var tgt = e.target || e.srcElement; | |
| 558 var left = tgt.offsetLeft; | |
| 559 var top = tgt.offsetTop; | |
| 560 var unit = scale * ticks; | |
| 561 mouseX = (e.clientX - left - Math.ceil(at_x) + 1) / unit + xStart; | |
| 562 mouseY = (e.clientY - top - Math.ceil(at_y)) / unit + yStart; | |
| 563 } | |
| 564 | |
| 565 var lastX, lastY; | |
| 566 var activeCurve = []; | |
| 567 var activePt; | |
| 568 | |
| 569 function handleMouseClick() { | |
| 570 calcXY(); | |
| 571 } | |
| 572 | |
| 573 function initDown() { | |
| 574 var unit = scale * ticks; | |
| 575 var xoffset = xStart * -unit + at_x; | |
| 576 var yoffset = yStart * -unit + at_y; | |
| 577 var test = tests[testIndex]; | |
| 578 var bestDistance = 1000000; | |
| 579 activePt = -1; | |
| 580 for (curves in test) { | |
| 581 var testCurve = test[curves]; | |
| 582 if (testCurve.length != 8) { | |
| 583 continue; | |
| 584 } | |
| 585 for (var i = 0; i < 8; i += 2) { | |
| 586 var testX = testCurve[i]; | |
| 587 var testY = testCurve[i + 1]; | |
| 588 var dx = testX - mouseX; | |
| 589 var dy = testY - mouseY; | |
| 590 var dist = dx * dx + dy * dy; | |
| 591 if (dist > bestDistance) { | |
| 592 continue; | |
| 593 } | |
| 594 activeCurve = testCurve; | |
| 595 activePt = i; | |
| 596 bestDistance = dist; | |
| 597 } | |
| 598 } | |
| 599 if (activePt >= 0) { | |
| 600 lastX = mouseX; | |
| 601 lastY = mouseY; | |
| 602 } | |
| 603 } | |
| 604 | |
| 605 function handleMouseOver() { | |
| 606 if (!mouseDown) { | |
| 607 activePt = -1; | |
| 608 return; | |
| 609 } | |
| 610 calcXY(); | |
| 611 if (activePt < 0) { | |
| 612 initDown(); | |
| 613 return; | |
| 614 } | |
| 615 var unit = scale * ticks; | |
| 616 var deltaX = (mouseX - lastX) /* / unit */; | |
| 617 var deltaY = (mouseY - lastY) /*/ unit */; | |
| 618 lastX = mouseX; | |
| 619 lastY = mouseY; | |
| 620 activeCurve[activePt] += deltaX; | |
| 621 activeCurve[activePt + 1] += deltaY; | |
| 622 redraw(); | |
| 623 } | |
| 624 | |
| 625 function start() { | |
| 626 for (i = 0; i < testDivs.length; ++i) { | |
| 627 var title = testDivs[i].id.toString(); | |
| 628 var str = testDivs[i].firstChild.data; | |
| 629 parse(str, title); | |
| 630 } | |
| 631 drawTop(); | |
| 632 window.addEventListener('keypress', doKeyPress, true); | |
| 633 window.onresize = function() { | |
| 634 drawTop(); | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 </script> | |
| 639 </head> | |
| 640 | |
| 641 <body onLoad="start();"> | |
| 642 <canvas id="canvas" width="750" height="500" | |
| 643 onmousedown="mouseDown = true" | |
| 644 onmouseup="mouseDown = false" | |
| 645 onmousemove="handleMouseOver()" | |
| 646 onclick="handleMouseClick()" | |
| 647 ></canvas > | |
| 648 </body> | |
| 649 </html> | |
| OLD | NEW |