1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 5 6<style> 7html { 8 font-family: Helvetica, Arial, sans-serif; 9 font-size: 100%; 10} 11 12.controls { 13 margin: 1em 0; 14} 15 16button { 17 display: inline-block; 18 border-radius: 3px; 19 border: none; 20 font-size: 0.9rem; 21 padding: 0.4rem 0.8em; 22 background: #69c773; 23 border-bottom: 1px solid #498b50; 24 color: white; 25 -webkit-font-smoothing: antialiased; 26 font-weight: bold; 27 margin: 0 0.25rem; 28 text-align: center; 29} 30 31button:hover, button:focus { 32 opacity: 0.75; 33 cursor: pointer; 34} 35 36button:active { 37 opacity: 1; 38 box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset; 39} 40 41</style> 42 43<! set height back to 500 /> 44<svg id="svg" width="800" height="500" 45 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 46 47<defs> 48 <radialGradient id="grad1" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 49 <stop offset="0%" style="stop-color:rgb(0,0,255); stop-opacity:0.3" /> 50 <stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:0" /> 51 </radialGradient> 52 <radialGradient id="grad2" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 53 <stop offset="0%" style="stop-color:rgb(0,255,0); stop-opacity:0.3" /> 54 <stop offset="100%" style="stop-color:rgb(0,255,0); stop-opacity:0" /> 55 </radialGradient> 56 <radialGradient id="grad3" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 57 <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:0.3" /> 58 <stop offset="100%" style="stop-color:rgb(255,0,0); stop-opacity:0" /> 59 </radialGradient> 60 <radialGradient id="grad4" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 61 <stop offset="0%" style="stop-color:rgb(192,63,192); stop-opacity:0.3" /> 62 <stop offset="100%" style="stop-color:rgb(192,63,192); stop-opacity:0" /> 63 </radialGradient> 64 <radialGradient id="grad5" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 65 <stop offset="0%" style="stop-color:rgb(127,127,0); stop-opacity:0.3" /> 66 <stop offset="100%" style="stop-color:rgb(127,127,0); stop-opacity:0" /> 67 </radialGradient> 68 <radialGradient id="grad6" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 69 <stop offset="0%" style="stop-color:rgb(127,0,127); stop-opacity:0.3" /> 70 <stop offset="100%" style="stop-color:rgb(127,0,127); stop-opacity:0" /> 71 </radialGradient> 72 <radialGradient id="grad7" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 73 <stop offset="0%" style="stop-color:rgb(0,127,127); stop-opacity:0.3" /> 74 <stop offset="100%" style="stop-color:rgb(0,127,127); stop-opacity:0" /> 75 </radialGradient> 76 <radialGradient id="grad8" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> 77 <stop offset="0%" style="stop-color:rgb(63,192,63); stop-opacity:0.3" /> 78 <stop offset="100%" style="stop-color:rgb(63,192,63); stop-opacity:0" /> 79 </radialGradient> 80</defs> 81 82<path id="circleFill" d="M300,200 A 100,100 0,0,0 300,200" fill="#777" fill-opacity="0" /> 83<path id="circle" d="M300,200 A 100,100 0,0,0 300,200" fill="none" stroke="black" /> 84 85<! elements for keyframe 1 /> 86<text id="spanWedgeDesc" fill-opacity="0" > 87All spans are contained by a wedge. 88</text> 89<path id="span1" d="M200,200 Q300,300 200,300" fill="none" stroke="black" stroke-opacity="0"/> 90<path id="span2" d="M200,200 C100,300 100,400 200,300" fill="none" stroke="black" stroke-opacity="0"/> 91<path id="span3" d="M200,200 C300,100 100,400 300,200" fill="none" stroke="black" stroke-opacity="0"/> 92<path id="wedge1" d="M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad1)" fill-opacity="0"/> 93<path id="wedge2" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad2)" fill-opacity="0"/> 94<path id="wedge3" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z" fill="url(#grad3)" fill-opacity="0"/> 95 96<! keyframe 2 /> 97<text id="trivialWedgeDesc1" fill-opacity="0" > 98Wedges that don't overlap can be 99</text> 100<text id="trivialWedgeDesc2" y="240" fill-opacity="0" > 101easily sorted. 102</text> 103<path id="span4" d="M200,200 Q300,300 400,300" fill="none" stroke="black" stroke-opacity="0"/> 104<path id="span5" d="M200,200 Q280,320 200,400" fill="none" stroke="black" stroke-opacity="0"/> 105<path id="span6" d="M200,200 Q60,340 100,400" fill="none" stroke="black" stroke-opacity="0"/> 106<path id="wedge4" d="M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z" fill="url(#grad1)" fill-opacity="0"/> 107<path id="wedge5" d="M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z" fill="url(#grad2)" fill-opacity="0"/> 108<path id="wedge6" d="M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad3)" fill-opacity="0"/> 109 110 111<! keyframe 3 /> 112<text id="sectorDesc1" fill-opacity="0" > 113A sector is a wedge of a circle 114</text> 115<text id="sectorDesc2" y="240" fill-opacity="0" > 116containing a range of points. 117</text> 118<g id="xaxis" stroke-opacity="0" fill-opacity="0"> 119 <path d="M100,200 L300,200" fill="none" stroke="rgb(191,191,191)"/> 120 <text x="100" y="220" fill="rgb(191,191,191)">-X</text> 121 <text x="300" y="220" text-anchor="end" fill="rgb(191,191,191)">+X</text> 122</g> 123<g id="yaxis" stroke-opacity="0" fill-opacity="0"> 124 <path d="M200,100 L200,300" fill="none" stroke="rgb(191,191,191)"/> 125 <text x="205" y="100" alignment-baseline="hanging" fill="rgb(191,191,191)">-Y</text> 126 <text x="205" y="300" fill="rgb(191,191,191)">+Y</text> 127</g> 128<text id="sectorDescXYA" x="500" y="310" fill="rgb(0,0,255)" fill-opacity="0"> 129X > 0> Y > 0 Y < X</text> 130<text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0"> 131X < 0 Y > 0 -Y < X</text> 132<text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0"> 133X < 0 Y < 0 Y < X</text> 134<path id="wedgeXY8" d="M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad1)" fill-opacity="0"/> 135<path id="wedgeXY6" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad2)" fill-opacity="0"/> 136<path id="wedgeXY3" d="M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z" fill="url(#grad3)" fill-opacity="0"/> 137 138<! keyframe 4 /> 139<text id="lineSingleDesc" fill-opacity="0" > 140Line spans are contained by a single sector. 141</text> 142<text id="sectorDescXY1" x="500" y="460" fill="rgb(192,63,192)" fill-opacity="0"> 143X > 0 Y < 0 -Y < X</text> 144<text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0"> 145X > 0 Y < 0 -Y > X</text> 146<text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0"> 147X < 0 Y < 0 Y < X</text> 148<text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0"> 149X < 0 Y < 0 Y > X</text> 150<text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0"> 151X < 0 Y > 0 -Y < X</text> 152<text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0"> 153X < 0 Y > 0 -Y < X</text> 154<text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0"> 155X > 0 Y > 0 Y > X</text> 156<text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0"> 157X > 0 Y > 0 Y < X</text> 158<path id="wedgeXY1" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad4)" fill-opacity="0"/> 159<path id="wedgeXY2" d="M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z" fill="url(#grad5)" fill-opacity="0"/> 160<path id="wedgeXY4" d="M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z" fill="url(#grad6)" fill-opacity="0"/> 161<path id="wedgeXY5" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z" fill="url(#grad7)" fill-opacity="0"/> 162<path id="wedgeXY7" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z" fill="url(#grad8)" fill-opacity="0"/> 163<path id="lineSegment" d="M200,200 L200,624.26" fill="none" stroke="black" stroke-opacity="0"/> 164 165<! keyframe 5 /> 166<text id="curveMultipleDesc1" fill-opacity="0" > 167A curve span may cover more 168</text> 169<text id="curveMultipleDesc2" y="240" fill-opacity="0" > 170than one sector. 171</text> 172<path id="curveSegment" d="M200,200 C250,200 300,150 300,100" fill="none" stroke="black" stroke-opacity="0"/> 173<path id="curveSegment1" d="M200,200 C250,200 300,150 300,100" fill="none"/> 174<path id="curveSegment2" d="M200,200 C250,200 300,150 200,100" fill="none"/> 175<path id="curveSegment3" d="M200,200 C350,200 250,-150 170,300" fill="none"/> 176 177<! keyframe 6 /> 178<text id="line1DDest1" fill-opacity="0" > 179Some lines occupy one-dimensional 180</text> 181<text id="line1DDest2" y="240" fill-opacity="0" > 182sectors. 183</text> 184<text id="sectorDescXY9" x="500" y="460" fill="rgb(192,92,31)" fill-opacity="0"> 185X > 0 Y == 0</text> 186<text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0"> 187Y > 0 0 == X</text> 188<text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0"> 189X < 0 Y == X</text> 190<path id="horzSegment" d="M200,200 L341.4,200" fill="none" stroke="rgb(192,92,31)" stroke-width="2" stroke-opacity="0"/> 191<path id="vertSegment" d="M200,200 L200,341.4" fill="none" stroke="rgb(31,92,192)" stroke-width="2" stroke-opacity="0"/> 192<path id="diagSegment" d="M200,200 L100,100" fill="none" stroke="rgb(127,63,127)" stroke-width="2" stroke-opacity="0"/> 193 194<! keyframe 7 /> 195<text id="curve1dDesc1" fill-opacity="0" > 196Some curves initially occupy 197</text> 198<text id="curve1dDesc2" y="240" fill-opacity="0" > 199one-dimensional sectors, then diverge. 200</text> 201<path id="cubicSegment" fill="none" stroke="black" /> 202<path id="cubicSegment1" d="M200,200 C200,200 200,200 200,200" fill="none" /> 203<path id="cubicSegment2" d="M200,200 C250,200 300,200 300,100" fill="none"/> 204 205<text id="sectorNumberDesc" fill-opacity="0" > 206Each sector is assigned a number. 207</text> 208<text id="spanSectorDesc" fill-opacity="0" > 209Each span has a bit set for one or more sectors. 210</text> 211<text id="bitOverDesc" fill-opacity="0" > 212Span sets allow rough sorting without angle computation. 213</text> 214 215</svg> 216 217<! canvas support /> 218<script> 219 220var keyFrameQueue = []; 221var animationsPending = []; 222var animationsActive = []; 223var displayList = []; 224var visibleFinished = []; 225 226var animationState = {}; 227animationState.reset = function () { 228 this.start = null; 229 this.time = 0; 230 this.requestID = null; 231 this.paused = false; 232 this.displayEngine = 'Canvas'; 233} 234 235circle.center = { x: 200, y: 200 } 236circle.radius = 100; 237 238function assert(condition) { 239 if (!condition) debugger; 240} 241 242function CanvasGrads(ctx) { 243 var grad1 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 244 grad1.addColorStop(0, "rgba(0,0,255, 0.3)"); 245 grad1.addColorStop(1, "rgba(0,0,255, 0)"); 246 var grad2 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 247 grad2.addColorStop(0, "rgba(0,255,0, 0.3)"); 248 grad2.addColorStop(1, "rgba(0,255,0, 0)"); 249 var grad3 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 250 grad3.addColorStop(0, "rgba(255,0,0, 0.3)"); 251 grad3.addColorStop(1, "rgba(255,0,0, 0)"); 252 var grad4 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 253 grad4.addColorStop(0, "rgba(192,63,192, 0.3)"); 254 grad4.addColorStop(1, "rgba(192,63,192, 0)"); 255 var grad5 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 256 grad5.addColorStop(0, "rgba(127,127,0, 0.3)"); 257 grad5.addColorStop(1, "rgba(127,127,0, 0)"); 258 var grad6 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 259 grad6.addColorStop(0, "rgba(127,0,127, 0.3)"); 260 grad6.addColorStop(1, "rgba(127,0,127, 0)"); 261 var grad7 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 262 grad7.addColorStop(0, "rgba(0,127,127, 0.3)"); 263 grad7.addColorStop(1, "rgba(0,127,127, 0)"); 264 var grad8 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); 265 grad8.addColorStop(0, "rgba(63,192,63, 0.3)"); 266 grad8.addColorStop(1, "rgba(63,192,63, 0)"); 267 var data = { 268 grad1: grad1, 269 grad2: grad2, 270 grad3: grad3, 271 grad4: grad4, 272 grad5: grad5, 273 grad6: grad6, 274 grad7: grad7, 275 grad8: grad8, 276 }; 277 return data; 278} 279 280function skip_sep(data) { 281 if (!data.length) { 282 return data; 283 } 284 while (data[0] == ' ' || data[0] == ',') { 285 data = data.substring(1); 286 } 287 return data; 288} 289 290function find_points(str, value, count, isRelative, relative) { 291 var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; 292 var match; 293 for (var index = 0; index < count; ++index) { 294 str = skip_sep(str); 295 match = numRegEx.exec(str); 296 assert(match); 297 var x = Number(match[0]); 298 str = skip_sep(str); 299 match = numRegEx.exec(str); 300 assert(match); 301 var y = Number(match[0]); 302 value[index] = { x: x, y : y }; 303 } 304 if (isRelative) { 305 for (var index = 0; index < count; index++) { 306 value[index].x += relative.x; 307 value[index].y += relative.y; 308 } 309 } 310 return str.substring(match.index + match[0].length); 311} 312 313function find_scalar(str, obj, isRelative, relative) { 314 var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; 315 str = skip_sep(str); 316 var match = numRegEx.exec(str); 317 obj.value = Number(match[0]); 318 if (isRelative) { 319 obj.value += relative; 320 } 321 return str.substring(match.index + match[0].length); 322} 323 324function parse_path(data) { 325 var path = "ctx.beginPath();\n"; 326 var f = {x:0, y:0}; 327 var c = {x:0, y:0}; 328 var lastc = {x:0, y:0}; 329 var points = []; 330 var op = '\0'; 331 var previousOp = '\0'; 332 var relative = false; 333 for (;;) { 334 data = skip_sep(data); 335 if (!data.length) { 336 break; 337 } 338 var ch = data[0]; 339 if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+') { 340 assert(op != '\0'); 341 } else if (ch == ' ' || ch == ',') { 342 data = skip_sep(data); 343 } else { 344 op = ch; 345 relative = false; 346 if ('a' <= op && op <= 'z') { 347 op = op.toUpperCase(); 348 relative = true; 349 } 350 data = data.substring(1); 351 data = skip_sep(data); 352 } 353 switch (op) { 354 case 'A': 355 var radii = []; 356 data = find_points(data, radii, 1, false, null); 357 var xaxisObj = {}; 358 data = find_scalar(data, xaxisObj, false, null); 359 var largeArcObj = {}; 360 data = find_scalar(data, largeArcObj, false, null); 361 var sweepObj = {}; 362 data = find_scalar(data, sweepObj, false, null); 363 data = find_points(data, points, 1, relative, c); 364 var mid = { x: (c.x + points[0].x) / 2, y: (c.y + points[0].y) / 2 }; 365 var midVec = { x: mid.x - c.x, y: mid.y - c.y }; 366 var midLenSqr = midVec.x * midVec.x + midVec.y * midVec.y; 367 var radius = radii[0].x; 368 var scale = Math.sqrt(midLenSqr) / Math.sqrt(radius * radius - midLenSqr); 369 var tangentPt = { x: mid.x + midVec.y * scale, 370 y: mid.y - midVec.x * scale }; 371 path += "ctx.arcTo(" + tangentPt.x + "," + tangentPt.y + "," 372 + points[0].x + "," + points[0].y + "," + radius + ");\n"; 373 c = points[0]; 374 break; 375 case 'M': 376 data = find_points(data, points, 1, relative, c); 377 path += "ctx.moveTo(" + points[0].x + "," + points[0].y + ");\n"; 378 op = 'L'; 379 c = points[0]; 380 break; 381 case 'L': 382 data = find_points(data, points, 1, relative, c); 383 path += "ctx.lineTo(" + points[0].x + "," + points[0].y + ");\n"; 384 c = points[0]; 385 break; 386 case 'H': { 387 var xObj = {}; 388 data = find_scalar(data, xObj, relative, c.x); 389 path += "ctx.lineTo(" + xObj.value + "," + c.y + ");\n"; 390 c.x = xObj.value; 391 } break; 392 case 'V': { 393 var yObj = {}; 394 data = find_scalar(data, y, relative, c.y); 395 path += "ctx.lineTo(" + c.x + "," + yObj.value+ ");\n"; 396 c.y = yObj.value; 397 } break; 398 case 'C': 399 data = find_points(data, points, 3, relative, c); 400 path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," 401 + points[1].x + "," + points[1].y + "," 402 + points[2].x + "," + points[2].y + ");\n"; 403 lastc = points[1]; 404 c = points[2]; 405 break; 406 case 'S': 407 var pts2_3 = []; 408 data = find_points(data, pts2_3, 2, relative, c); 409 points[0] = c; 410 points[1] = pts2_3[0]; 411 points[2] = pts2_3[1]; 412 if (previousOp == 'C' || previousOp == 'S') { 413 points[0].x -= lastc.x - c.x; 414 points[0].y -= lastc.y - c.y; 415 } 416 path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," 417 + points[1].x + "," + points[1].y + "," 418 + points[2].x + "," + points[2].y + ");\n"; 419 lastc = points[1]; 420 c = points[2]; 421 break; 422 case 'Q': // Quadratic Bezier Curve 423 data = find_points(data, points, 2, relative, c); 424 path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," 425 + points[1].x + "," + points[1].y + ");\n"; 426 lastc = points[0]; 427 c = points[1]; 428 break; 429 case 'T': 430 var pts2 = []; 431 data = find_points(data, pts2, 1, relative, c); 432 points[0] = pts2[0]; 433 points[1] = pts2[0]; 434 if (previousOp == 'Q' || previousOp == 'T') { 435 points[0].x = c.x * 2 - lastc.x; 436 points[0].y = c.y * 2 - lastc.y; 437 } 438 path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," 439 + points[1].x + "," + points[1].y + ");\n"; 440 path.quadTo(points[0], points[1]); 441 lastc = points[0]; 442 c = points[1]; 443 break; 444 case 'Z': 445 path += "ctx.closePath();\n"; 446 c = f; 447 op = '\0'; 448 break; 449 case '~': 450 var args = []; 451 data = find_points(data, args, 2, false, null); 452 path += "moveTo(" + args[0].x + "," + args[0].y + ");\n"; 453 path += "lineTo(" + args[1].x + "," + args[1].y + ");\n"; 454 break; 455 default: 456 return false; 457 } 458 if (previousOp == 0) { 459 f = c; 460 } 461 previousOp = op; 462 } 463 return path; 464} 465 466function CanvasPaths(ctx) { 467 var svgStrs = { 468 // keyframe 1 469 span1: "M200,200 Q300,300 200,300", 470 span2: "M200,200 C100,300 100,400 200,300", 471 span3: "M200,200 C300,100 100,400 300,200", 472 wedge1: "M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z", 473 wedge2: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z", 474 wedge3: "M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z", 475 // keyframe 2 476 span4: "M200,200 Q300,300 400,300", 477 span5: "M200,200 Q280,320 200,400", 478 span6: "M200,200 Q60,340 100,400", 479 wedge4: "M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z", 480 wedge5: "M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z", 481 wedge6: "M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z", 482 // keyframe 3 483 xaxis: "M100,200 L300,200", 484 yaxis: "M200,100 L200,300", 485 wedgeXY8: "M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z", 486 wedgeXY6: "M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z", 487 wedgeXY3: "M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z", 488 // keyframe 4 489 wedgeXY1: "M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z", 490 wedgeXY2: "M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z", 491 wedgeXY4: "M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z", 492 wedgeXY5: "M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z", 493 wedgeXY7: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z", 494 lineSegment: "M200,200 L200,624.26", 495 // keyframe 5 496 curveSegment: "M200,200 C250,200 300,150 300,100", 497 curveSegment1: "M200,200 C250,200 300,150 300,100", 498 curveSegment2: "M200,200 C250,200 300,150 200,100", 499 curveSegment3: "M200,200 C350,200 250,-150 170,300", 500 // keyframe 6 501 horzSegment: "M200,200 L341.4,200", 502 vertSegment: "M200,200 L200,341.4", 503 diagSegment: "M200,200 L100,100", 504 // keyframe 7 505 cubicSegment: "M200,200 C200,200 200,200 200,200", 506 cubicSegment1: "M200,200 C200,200 200,200 200,200", 507 cubicSegment2: "M200,200 C250,200 300,200 300,100", 508 }; 509 var paths = []; 510 var keys = Object.keys(svgStrs); 511 for (var index in keys) { 512 var key = keys[index]; 513 var str = svgStrs[key]; 514 var path = parse_path(str); 515 var record = []; 516 paths[key] = { 517 str: str, 518 funcBody: path, 519 }; 520 } 521 return paths; 522} 523 524function canvas_fill_font(record) { 525 assert(record); 526 var str = 'ctx.font = "normal 1.3rem Helvetica,Arial";\n'; 527 if (record.fillStyle) { 528 str += 'ctx.fillStyle = ' + record.fillStyle + ';\n'; 529 } 530 return str; 531} 532 533function canvas_fill_text(record) { 534 assert(record); 535 assert(typeof record.fillText == 'string'); 536 return 'ctx.fillText("' + record.fillText + '"'; 537} 538 539function canvas_xy(record) { 540 var x = typeof record.x == "number" ? record.x : 400; 541 var y = typeof record.y == "number" ? record.y : 200; 542 return ', ' + x + ', ' + y + ');\n'; 543} 544 545function canvas_text_xy(record) { 546 return canvas_fill_text(record) + canvas_xy(record); 547} 548 549function add_canvas_stroke(paths, data, id, strokeStyle) { 550 var record = {}; 551 record.data = paths[id].funcBody; 552 record.style = 'ctx.strokeStyle = ' + (strokeStyle ? strokeStyle : '"black"') + ';\n'; 553 record.draw = 'ctx.stroke();\n'; 554 record.func = new Function('ctx', record.data + record.style + record.draw); 555 return data[id] = record; 556} 557 558function add_canvas_style(record, style) { 559 record.style += style; 560 record.func = new Function('ctx', record.data + record.style + record.draw); 561} 562 563function add_canvas_fill(paths, data, id, fillStyle) { 564 var record = {}; 565 record.data = paths[id].funcBody; 566 record.style = 'ctx.fillStyle = ' + (fillStyle ? fillStyle : '"black"') + ';\n'; 567 record.draw = 'ctx.fill();\n'; 568 record.func = new Function('ctx', record.data + record.style + record.draw); 569 return data[id] = record; 570} 571 572function add_canvas_text(data, id, params) { 573 var record = {}; 574 record.style = canvas_fill_font(params); 575 record.draw = canvas_fill_text(params); 576 record.position = canvas_xy(params); 577 record.x = params.x; 578 record.y = params.y; 579 record.func = new Function('ctx', record.style + record.draw + record.position); 580 return data[id] = record; 581} 582 583function keyframe1(grads, paths) { 584 var data = []; 585 add_canvas_text(data, "spanWedgeDesc", { fillText:"All spans are contained by a wedge" } ); 586 add_canvas_stroke(paths, data, "span1"); 587 add_canvas_stroke(paths, data, "span2"); 588 add_canvas_stroke(paths, data, "span3"); 589 add_canvas_fill(paths, data, "wedge1", "grads.grad1"); 590 add_canvas_fill(paths, data, "wedge2", "grads.grad2"); 591 add_canvas_fill(paths, data, "wedge3", "grads.grad3"); 592 return data; 593} 594 595function keyframe2(grads, paths) { 596 var data = []; 597 add_canvas_text(data, "trivialWedgeDesc1", { fillText:"Wedges that don't overlap can be" } ); 598 add_canvas_text(data, "trivialWedgeDesc2", { fillText:"easily sorted.", y:240 } ); 599 add_canvas_stroke(paths, data, "span4").debug = true; 600 add_canvas_stroke(paths, data, "span5"); 601 add_canvas_stroke(paths, data, "span6"); 602 add_canvas_fill(paths, data, "wedge4", "grads.grad1"); 603 add_canvas_fill(paths, data, "wedge5", "grads.grad2"); 604 add_canvas_fill(paths, data, "wedge6", "grads.grad3"); 605 return data; 606} 607 608function setup_axes(paths, data) { 609 var color = '"rgb(191,191,191)"'; 610 var xaxis = add_canvas_stroke(paths, data, "xaxis", color); 611 xaxis.funcBody = canvas_fill_font( { fillStyle:color } ); 612 xaxis.funcBody += canvas_text_xy( { fillText:"-X", x:100, y:220 } ); 613 xaxis.funcBody += "ctx.textAlign = 'right';\n"; 614 xaxis.funcBody += canvas_text_xy( { fillText:"+X", x:300, y:220 } ); 615 xaxis.func = new Function('ctx', xaxis.data + xaxis.style + xaxis.draw + xaxis.funcBody); 616 var yaxis = add_canvas_stroke(paths, data, "yaxis", color); 617 yaxis.funcBody = canvas_fill_font( { fillStyle:color } ); 618 yaxis.funcBody += "ctx.textBaseline = 'hanging';\n"; 619 yaxis.funcBody += canvas_text_xy( { fillText:"-Y", x:205, y:100 } ); 620 yaxis.funcBody += "ctx.textBaseline = 'alphabetic';\n"; 621 yaxis.funcBody += canvas_text_xy( { fillText:"+Y", x:205, y:300 } ); 622 yaxis.func = new Function('ctx', yaxis.data + yaxis.style + yaxis.draw + yaxis.funcBody); 623} 624 625function keyframe3(grads, paths) { 626 var data = []; 627 add_canvas_text(data, "sectorDesc1", { fillText:"A sector is a wedge of a circle" } ); 628 add_canvas_text(data, "sectorDesc2", { fillText:"containing a range of points.", y:240 } ); 629 setup_axes(paths, data); 630 add_canvas_text(data, "sectorDescXYA", 631 { fillText:"X > 0 Y > 0 Y < X", x:500, y:310, fillStyle:'"rgb(0,0,255)"'} ); 632 add_canvas_text(data, "sectorDescXYB", 633 { fillText:"X < 0 Y > 0 -Y < X", x:500, y:360, fillStyle:'"rgb(0,127,0)"'} ); 634 add_canvas_text(data, "sectorDescXYC", 635 { fillText:"X < 0 Y < 0 Y < X", x:500, y:410, fillStyle:'"rgb(255,0,0)"'} ); 636 add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); 637 add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); 638 add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); 639 return data; 640} 641 642function keyframe4(grads, paths) { 643 var data = []; 644 setup_axes(paths, data); 645 add_canvas_text(data, "lineSingleDesc", 646 { fillText:"Line spans are contained by a single sector." } ); 647 add_canvas_text(data, "sectorDescXY1", 648 { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); 649 add_canvas_text(data, "sectorDescXY2", 650 { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); 651 add_canvas_text(data, "sectorDescXY3", 652 { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); 653 add_canvas_text(data, "sectorDescXY4", 654 { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); 655 add_canvas_text(data, "sectorDescXY5", 656 { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); 657 add_canvas_text(data, "sectorDescXY6", 658 { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); 659 add_canvas_text(data, "sectorDescXY7", 660 { fillText:"X > 0 Y > 0 Y > X", x:500, y:460, fillStyle:'"rgb(63,192,63)"'} ); 661 add_canvas_text(data, "sectorDescXY8", 662 { fillText:"X > 0 Y > 0 Y < X", x:500, y:460, fillStyle:'"rgb(0,0,255)"'} ); 663 add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); 664 add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); 665 add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); 666 add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); 667 add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); 668 add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); 669 add_canvas_fill(paths, data, "wedgeXY7", "grads.grad8"); 670 add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); 671 add_canvas_stroke(paths, data, "lineSegment"); 672 return data; 673} 674 675function keyframe5(grads, paths) { 676 var data = []; 677 setup_axes(paths, data); 678 add_canvas_text(data, "curveMultipleDesc1", 679 { fillText:"A curve span may cover more" } ); 680 add_canvas_text(data, "curveMultipleDesc2", 681 { fillText:"than one sector.", y:240 } ); 682 add_canvas_stroke(paths, data, "curveSegment"); 683 add_canvas_text(data, "sectorDescXY1", 684 { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); 685 add_canvas_text(data, "sectorDescXY2", 686 { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); 687 add_canvas_text(data, "sectorDescXY3", 688 { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); 689 add_canvas_text(data, "sectorDescXY4", 690 { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); 691 add_canvas_text(data, "sectorDescXY5", 692 { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); 693 add_canvas_text(data, "sectorDescXY6", 694 { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); 695 add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); 696 add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); 697 add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); 698 add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); 699 add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); 700 add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); 701 return data; 702} 703 704function keyframe6(grads, paths) { 705 var data = []; 706 setup_axes(paths, data); 707 708 add_canvas_text(data, "line1DDest1", 709 { fillText:"Some lines occupy one-dimensional" } ); 710 add_canvas_text(data, "line1DDest2", 711 { fillText:"sectors.", y:240 } ); 712 add_canvas_text(data, "sectorDescXY9", 713 { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); 714 add_canvas_text(data, "sectorDescXY10", 715 { fillText:"Y > 0 0 == X", x:500, y:460, fillStyle:'"rgb(31,92,192)"' } ); 716 add_canvas_text(data, "sectorDescXY11", 717 { fillText:"X < 0 Y == X", x:500, y:460, fillStyle:'"rgb(127,63,127)"' } ); 718 var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); 719 add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); 720 var vert = add_canvas_stroke(paths, data, "vertSegment", '"rgb(31,92,192)"'); 721 add_canvas_style(vert, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); 722 var diag = add_canvas_stroke(paths, data, "diagSegment", '"rgb(127,63,127)"'); 723 add_canvas_style(diag, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); 724 return data; 725} 726 727function keyframe7(grads, paths) { 728 var data = []; 729 setup_axes(paths, data); 730 add_canvas_text(data, "curve1dDesc1", 731 { fillText:"Some curves initially occupy" } ); 732 add_canvas_text(data, "curve1dDesc2", 733 { fillText:"one-dimensional sectors, then diverge.", y:240 } ); 734 add_canvas_stroke(paths, data, "cubicSegment"); 735 add_canvas_text(data, "sectorDescXY1", 736 { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); 737 add_canvas_text(data, "sectorDescXY9", 738 { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); 739 var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); 740 add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); 741 add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); 742 return data; 743} 744 745var canvasData = null; 746 747function CanvasInit(keyframe) { 748 canvasData = window[keyframe](grads, paths); 749} 750 751</script> 752 753<script> 754 755function interp(A, B, t) { 756 return A + (B - A) * t; 757} 758 759function interp_cubic_coords(x1, x2, x3, x4, t) 760{ 761 var ab = interp(x1, x2, t); 762 var bc = interp(x2, x3, t); 763 var cd = interp(x3, x4, t); 764 var abc = interp(ab, bc, t); 765 var bcd = interp(bc, cd, t); 766 var abcd = interp(abc, bcd, t); 767 return abcd; 768} 769 770function cubic_partial(value, p) { 771 var x1 = p[0], y1 = p[1], x2 = p[2], y2 = p[3]; 772 var x3 = p[4], y3 = p[5], x4 = p[6], y4 = p[7]; 773 var t1 = 0, t2 = value; 774 var ax = interp_cubic_coords(x1, x2, x3, x4, t1); 775 var ay = interp_cubic_coords(y1, y2, y3, y4, t1); 776 var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3); 777 var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3); 778 var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3); 779 var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3); 780 var dx = interp_cubic_coords(x1, x2, x3, x4, t2); 781 var dy = interp_cubic_coords(y1, y2, y3, y4, t2); 782 var mx = ex * 27 - ax * 8 - dx; 783 var my = ey * 27 - ay * 8 - dy; 784 var nx = fx * 27 - ax - dx * 8; 785 var ny = fy * 27 - ay - dy * 8; 786 var bx = (mx * 2 - nx) / 18; 787 var by = (my * 2 - ny) / 18; 788 var cx = (nx * 2 - mx) / 18; 789 var cy = (ny * 2 - my) / 18; 790 var array = [ 791 ax, ay, bx, by, cx, cy, dx, dy 792 ]; 793 return array; 794} 795 796function evaluate_at(value, p) { 797 var array = []; 798 for (var index = 0; index < p.length; ++index) { 799 var func = new Function('value', 'return ' + p[index] + ';'); 800 array[index] = func(value); 801 } 802 return array; 803} 804 805function interpolate_at(value, p) { 806 var array = []; 807 var start = p[0]; 808 var end = p[1]; 809 assert(typeof end == typeof start); 810 switch (typeof start) { 811 case 'object': 812 for (var index = 0; index < start.length; ++index) { 813 array[index] = interp(start[index], end[index], value); 814 } 815 break; 816 case 'number': 817 array[index] = interp(start, end, value); 818 break; 819 default: 820 debugger; 821 } 822 return array; 823} 824 825function AnimationAddCommon(timing, range, attr, inParams) { 826 var animation = { 827 timing: timing, 828 range: range, 829 attr: attr, 830 inParams: inParams, 831 duration: timing[1] - timing[0], 832 remaining: timing[1] - timing[0], 833 firstStep: true, 834 } 835 animationsPending.push(animation); 836 return animation; 837} 838 839function AnimationAddSVG(timing, element, range, attr, inParams) { 840 var animation = AnimationAddCommon(timing, range, attr, inParams); 841 animation.element = element; 842 return animation; 843} 844 845function AnimationAddCanvas(timing, element, range, attr, inParams) { 846 var animation = AnimationAddCommon(timing, range, attr, inParams); 847 animation.element = canvasData[element]; 848 assert(animation.element); 849 animation.firstElement = null; 850 return animation; 851} 852 853function AnimationAdd(timing, e, range, attr, funct, inParams) { 854 if (!range) { 855 range = [0, 1]; 856 } 857 if (!attr) { 858 attr = 'opacity'; 859 } 860 if (!funct) { 861 funct = interpolate_at; 862 } 863 var element; 864 switch (animationState.displayEngine) { 865 case 'SVG': 866 element = typeof e == 'string' ? document.getElementById(e) : e; 867 break; 868 case 'Canvas': 869 element = typeof e == 'string' ? e : e.id; 870 break; 871 default: 872 debugger; 873 } 874 assert(element); 875 switch (attr) { 876 case 'path': 877 if (!inParams) { 878 inParams = PathDataArray(element); 879 } 880 break; 881 case 'opacity': 882 if (!inParams) { 883 inParams = [0, 1]; 884 } 885 break; 886 default: 887 debugger; 888 } 889 var funcBody = 'var outParams = ' + funct.name + '(value, inParams);\n'; 890 switch (animationState.displayEngine) { 891 case 'SVG': 892 switch (attr) { 893 case 'path': 894 var verbArray = PathVerbArray(element); 895 funcBody += 'return '; 896 for (var index = 0; index < inParams.length; ++index) { 897 funcBody += '"' + verbArray[index] + '"'; 898 funcBody += 'outParams[' + index + '];\n'; 899 } 900 if (verbArray.length > inParams.length) { 901 funcBody += '"' + verbArray[verbArray.length - 1] + '"'; 902 } 903 funcBody += ';\n'; 904 var animation = AnimationAddSVG(timing, element, range, "d", inParams); 905 animation.func = new Function('value', 'inParams', funcBody); 906 break; 907 case 'opacity': 908 if (animation.element.getAttribute("stroke-opacity")) { 909 animation = AnimationAddSVG(timing, element, range, "stroke-opacity", inParams); 910 } 911 if (animation.element.getAttribute("fill-opacity")) { 912 animation = AnimationAddSVG(timing, element, range, "fill-opacity", inParams); 913 } 914 break; 915 default: 916 debugger; 917 } 918 case 'Canvas': 919 switch (attr) { 920 case 'path': 921 var verbArray = PathVerbArray(element); 922 for (var index = 0; index < inParams.length; ++index) { 923 funcBody += verbArray[index]; 924 funcBody += 'outParams[' + index + ']'; 925 } 926 if (verbArray.length > inParams.length) { 927 funcBody += verbArray[verbArray.length - 1]; 928 } 929 animation = AnimationAddCanvas(timing, element, range, attr, inParams); 930 funcBody += animation.element.style + animation.element.draw; 931 animation.func = new Function('ctx', 'value', 'inParams', funcBody); 932 break; 933 case 'opacity': 934 animation = AnimationAddCanvas(timing, element, range, attr, inParams); 935 break; 936 default: 937 debugger; 938 } 939 break; 940 default: 941 debugger; 942 } 943 return animation; 944} 945 946function path_data_common(element, getValues) { 947 var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; 948 var data = []; 949 var match; 950 var path; 951 switch (animationState.displayEngine) { 952 case 'SVG': path = element.getAttribute("d"); break; 953 case 'Canvas': path = paths[element].funcBody; break; 954 default: debugger; 955 } 956 if (getValues) { 957 while ((match = numRegEx.exec(path))) { 958 data.push(Number(match[0])); 959 } 960 } else { 961 var sIndex = 0; 962 while ((match = numRegEx.exec(path))) { 963 if (sIndex < match.index) { 964 data.push(path.substring(sIndex, match.index)); 965 } 966 sIndex = match.index + match[0].length; 967 } 968 if (sIndex < path.length) { 969 data.push(path.substring(sIndex, path.length)); 970 } 971 } 972 return data; 973} 974 975function PathDataArray(element) { 976 return path_data_common(element, true); 977} 978 979function PathVerbArray(element) { 980 return path_data_common(element, false); 981} 982 983function PathSet(element, funct, value, params) { 984 var pathVerbs = PathVerbArray(element); 985 if (funct) { 986 params = funct(value, params); 987 } 988 var setValue = ''; 989 for (var index = 0; index < params.length; ++index) { 990 setValue += pathVerbs[index]; 991 setValue += params[index]; 992 } 993 if (pathVerbs.length > params.length) { 994 setValue += pathVerbs[pathVerbs.length - 1]; 995 } 996 switch (animationState.displayEngine) { 997 case 'SVG': 998 element.setAttribute('d', setValue); 999 break; 1000 case 'Canvas': 1001 element.func = new Function('ctx', setValue + element.style + element.draw); 1002 break; 1003 default: 1004 debugger; 1005 } 1006} 1007 1008function RemoveFromArray(array, element) { 1009 for (var index in array) { 1010 var record = array[index]; 1011 if (record.element == element) { 1012 array.splice(index, 1); 1013 break; 1014 } 1015 } 1016} 1017 1018function EndAnimationCanvas(animation, visibleFinished) { 1019 var changeAlpha = "opacity" == animation.attr; 1020 if (!changeAlpha || animation.range[1] > 0) { 1021 if (changeAlpha) { 1022 ctx.save(); 1023 ctx.globalAlpha = animation.range[1]; 1024 } 1025 if (animation.func) { 1026 animation.func(ctx, animation.range[animation.range.length - 1], animation.inParams); 1027 } else { 1028 animation.element.func(ctx); 1029 } 1030 if (changeAlpha) { 1031 ctx.restore(); 1032 } 1033// if (visibleFinished) { 1034// visibleFinished.push(animation); 1035// } 1036 } else { 1037 // if (visibleFinished) { 1038 // RemoveFromArray(visibleFinished, animation.element); 1039 // } 1040 } 1041} 1042 1043/* start here 1044canvas: 1045 1046display list : 1047 for each element (canvas) 1048 save 1049 set global alpha (override) 1050 create geometry (override) 1051 create style (override) 1052 draw 1053 restore 1054 1055maybe each action should have an override slot 1056animations write to the slot 1057each element in display list then iterates overrides once the animations complete the frame 1058 1059so, first -- 1060 active animations update the display list 1061 1062next -- 1063 active animations install themselves in override slots 1064 1065finally 1066 display list is iterated, calling override slots 1067 1068---------------- 1069 1070svg: 1071 display list is implicit 1072 1073 active animations write element attributes 1074 */ 1075 1076function EndAnimationSVG(animation, visibleFinished) { 1077 switch (animation.attr) { 1078 case "opacity": 1079 animation.element.setAttribute(animation.attribute, animation.range[1]); 1080 if (animation.range[1] > 0) { 1081 visibleFinished.push(animation); 1082 } else { 1083 RemoveFromArray(visibleFinished, animation.element); 1084 } 1085 break; 1086 case "path": 1087 var attrStr = animation.func(animation.range[1], animation.inParams); 1088 animation.element.setAttribute(animation.attribute, attrStr); 1089 break; 1090 default: 1091 debugger; 1092 } 1093} 1094 1095function StepAnimationCanvas(animation, value) { 1096 var endValue = animation.range[animation.range.length - 1]; 1097 var interp = animation.range[0] + (endValue - animation.range[0]) * (1 - value); 1098 if (animation.firstStep) { 1099 RemoveFromArray(visibleFinished, animation.element); 1100 animation.firstStep = false; 1101 } 1102 var changeAlpha = "opacity" == animation.attr; 1103 if (changeAlpha) { 1104 ctx.save(); 1105 ctx.globalAlpha = interp; 1106 } 1107 if (animation.func) { 1108 animation.func(ctx, interp, animation.inParams); 1109 } else { 1110 animation.element.func(ctx); 1111 } 1112 if (changeAlpha) { 1113 ctx.restore(); 1114 } 1115} 1116 1117function StepAnimationSVG(animation, value) { 1118 var interp = animation.range[0] + (animation.range[1] - animation.range[0]) * (1 - value); 1119 switch (animation.attr) { 1120 case "opacity": 1121 animation.element.setAttribute(animation.attribute, interp); 1122 break; 1123 case "path": 1124 var attrStr = animation.func(interp, animation.inParams); 1125 animation.element.setAttribute(animation.attribute, attrStr); 1126 break; 1127 default: 1128 debugger; 1129 } 1130} 1131 1132var animate_frame = 0; 1133 1134function AnimateList(now) { 1135 ++animate_frame; 1136 if (animationState.paused) { 1137 return; 1138 } 1139 if (animationState.start == null) { 1140 animationState.start = now - animationState.time; 1141 } 1142 animationState.time = now - animationState.start; 1143 var stillPending = []; 1144 for (var index in animationsPending) { 1145 var animation = animationsPending[index]; 1146 var interval = animationState.time - animation.timing[0]; 1147 if (interval <= 0) { 1148 stillPending.push(animation); 1149 continue; 1150 } 1151 animationsActive.push(animation); 1152 var inList = false; 1153 for (var dlIndex in displayList) { 1154 var displayable = displayList[dlIndex]; 1155 if (displayable == animation.element) { 1156 inList = true; 1157 break; 1158 } 1159 } 1160 if (!inList) { 1161 displayList.push(animation.element); 1162 } 1163 } 1164 animationsPending = stillPending; 1165 var stillAnimating = []; 1166 if ('Canvas' == animationState.displayEngine) { 1167 ctx.clearRect(0, 0, canvas.width, canvas.height); 1168// for (var index in visibleFinished) { 1169// var animation = visibleFinished[index]; 1170// animation.endAnimation = false; 1171// } 1172 } 1173 for (var index in animationsActive) { 1174 var animation = animationsActive[index]; 1175 var interval = animationState.time - animation.timing[0]; 1176 animation.remaining = animation.duration > interval ? animation.duration - interval : 0; 1177 animation.endAnimation = animation.remaining <= 0; 1178 if (animation.endAnimation) { 1179 switch (animationState.displayEngine) { 1180 case 'SVG': EndAnimationSVG(animation, visibleFinished); break; 1181 case 'Canvas': EndAnimationCanvas(animation, visibleFinished); break; 1182 default: debugger; 1183 } 1184 continue; 1185 } 1186 var value = animation.remaining / animation.duration; 1187 switch (animationState.displayEngine) { 1188 case 'SVG': StepAnimationSVG(animation, value); break; 1189 case 'Canvas': 1190 if (!animation.firstElement || !animation.firstElement.endAnimation) { 1191 StepAnimationCanvas(animation, value); 1192 } 1193 break; 1194 default: debugger; 1195 } 1196 stillAnimating.push(animation); 1197 } 1198 if ('Canvas' == animationState.displayEngine) { 1199 for (var index in visibleFinished) { 1200 var animation = visibleFinished[index]; 1201 if (!animation.endAnimation) { 1202 EndAnimationCanvas(animation, null); 1203 } 1204 } 1205 } 1206 animationsActive = stillAnimating; 1207 if (animationsPending.length || animationsActive.length) { 1208 animationState.requestID = requestAnimationFrame(AnimateList); 1209 } 1210} 1211 1212function CancelAnimate(now) { 1213 if (animationState.start == null) { 1214 animationState.start = now; 1215 } 1216 var time = now - animationState.start; 1217 var stillAnimating = []; 1218 for (var index in animationsActive) { 1219 var animation = animationsActive[index]; 1220 var remaining = animation.remaining - time; 1221 var value = remaining / animation.duration; 1222 switch (animationState.displayEngine) { 1223 case 'SVG': animation.element.setAttribute(animation.attribute, value); break; 1224 case 'Canvas': break; 1225 } 1226 if (remaining <= 0) { 1227 continue; 1228 } 1229 stillAnimating.push(animation); 1230 } 1231 animationsActive = stillAnimating; 1232 if (animationsActive.length) { 1233 animationState.requestID = requestAnimationFrame(CancelAnimate); 1234 return; 1235 } 1236 animationsPending = []; 1237 animationState.reset(); 1238 if (keyFrameQueue.length > 0) { 1239 var animationFunc = keyFrameQueue.pop(); 1240 animationFunc(); 1241 } 1242} 1243 1244function CancelAnimation() { 1245 cancelAnimationFrame(animationState.requestID); 1246 for (var index in animationsActive) { 1247 var animation = animationsActive[index]; 1248 switch (animation.attr) { 1249 case "opacity": 1250 var tmp = animation.range[0]; animation.range[0] = animation.range[1]; animation[1] = tmp; 1251 animation.remaining = animation.duration - animation.remaining; 1252 animation.remaining /= animation.duration / 1000; 1253 animation.duration = 1000; 1254 break; 1255 case "fadeOut": 1256 RemoveFromArray(visibleFinished, animation.element); 1257 break; 1258 case "path": 1259 break; 1260 default: 1261 debugger; 1262 1263 } 1264 } 1265 for (var index in visibleFinished) { 1266 var animation = visibleFinished[index]; 1267 animation.action = "fadeOut"; 1268 animation.remaining = animation.duration = 1000; 1269 animationsActive.push(animation); 1270 } 1271 visibleFinished = []; 1272 animationState.reset(); 1273 animationState.requestID = requestAnimationFrame(CancelAnimate); 1274} 1275 1276function PauseAnimation() { 1277 animationState.paused = true; 1278} 1279 1280function QueueAnimation(animationFunc) { 1281 if (null == animationState.requestID) { 1282 animationFunc(); 1283 return; 1284 } 1285 keyFrameQueue.push(animationFunc); 1286} 1287 1288function UnpauseAnimation() { 1289 animationState.paused = false; 1290 animationState.start = performance.now() - animationState.time; 1291 animationState.requestID = requestAnimationFrame(AnimateList); 1292} 1293 1294function SetupTextSVG(t, x, y) { 1295 var text; 1296 if (typeof t == "string") { 1297 text = document.getElementById(t); 1298 } else { 1299 text = t; 1300 } 1301 text.setAttribute("font-family", "Helvetica,Arial"); 1302 text.setAttribute("font-size", "1.3rem"); 1303 if (typeof x == 'number') { 1304 text.setAttribute("x", x); 1305 } else if (null == text.getAttribute("x")) { 1306 text.setAttribute("x", 400); 1307 } 1308 if (typeof y == 'number') { 1309 text.setAttribute("y", y); 1310 } else if (null == text.getAttribute("y")) { 1311 text.setAttribute("y", 200); 1312 } 1313} 1314 1315function SetupTextCanvas(t, x, y) { 1316 var text = typeof t == 'string' ? t : t.id; 1317 var record = canvasData[text]; 1318 if (typeof x == 'number') { 1319 record.x = x; 1320 } 1321 if (typeof y == 'number') { 1322 record.y = y; 1323 } 1324 record.position = canvas_xy(record); 1325 record.func = new Function('ctx', record.style + record.draw + record.position); 1326} 1327 1328function SetupText(t, x, y) { 1329 switch (animationState.displayEngine) { 1330 case 'SVG': 1331 SetupTextSVG(t, x, y); 1332 break; 1333 case 'Canvas': 1334 SetupTextCanvas(t, x, y); 1335 break; 1336 default: 1337 debugger; 1338 } 1339} 1340 1341function FirstText(text) { 1342 SetupText(text); 1343 AnimationAdd([0, 1000], text); 1344} 1345 1346 1347function EngineInit(keyframe) { 1348 displayList = []; 1349 switch (animationState.displayEngine) { 1350 case 'SVG': break; 1351 case 'Canvas': CanvasInit(keyframe); break; 1352 default: debugger; 1353 } 1354} 1355 1356function EngineStart() { 1357 switch (animationState.displayEngine) { 1358 case 'SVG': break; 1359 case 'Canvas': 1360 // associate fadeIn and fadeOut 1361 for (var outerIndex in animationsPending) { 1362 var outer = animationsPending[outerIndex]; 1363 for (var innerIndex in animationsPending) { 1364 if (outerIndex == innerIndex) { 1365 continue; 1366 } 1367 var inner = animationsPending[innerIndex]; 1368 if (inner.element == outer.element) { 1369 inner.firstElement = outer; 1370 continue; 1371 } 1372 } 1373 } 1374 break; 1375 default: debugger; 1376 } 1377 animationState.reset(); 1378 animationState.requestID = requestAnimationFrame(AnimateList); 1379} 1380 1381function AnimateSpanWedge() { 1382 EngineInit('keyframe1'); 1383 FirstText(spanWedgeDesc); 1384 AnimationAdd([1000, 2000], span1); 1385 AnimationAdd([1500, 3000], wedge1); 1386 AnimationAdd([3500, 4000], span1, [1, 0]); 1387 AnimationAdd([3500, 4000], wedge1, [1, 0]); 1388 AnimationAdd([4000, 5000], span2); 1389 AnimationAdd([4500, 6000], wedge2); 1390 AnimationAdd([6500, 7000], span2, [1, 0]); 1391 AnimationAdd([6500, 7000], wedge2, [1, 0]); 1392 AnimationAdd([7000, 8000], span3); 1393 AnimationAdd([7500, 9000], wedge3); 1394 EngineStart(); 1395} 1396 1397function AnimateTrivialWedge() { 1398 EngineInit('keyframe2'); 1399 FirstText(trivialWedgeDesc1); 1400 FirstText(trivialWedgeDesc2); 1401 AnimationAdd([2000, 3500], span4); 1402 AnimationAdd([2000, 3500], wedge4); 1403 AnimationAdd([2000, 3500], span5); 1404 AnimationAdd([2000, 3500], wedge5); 1405 AnimationAdd([2000, 3500], span6); 1406 AnimationAdd([2000, 3500], wedge6); 1407 EngineStart(); 1408} 1409 1410function AnimateSectorDesc() { 1411 EngineInit('keyframe3'); 1412 FirstText(sectorDesc1); 1413 FirstText(sectorDesc2); 1414 AnimationAdd([ 0, 1000], xaxis); 1415 AnimationAdd([ 500, 1500], yaxis); 1416 AnimationAdd([2000, 3500], sectorDescXYA); 1417 AnimationAdd([2000, 3500], wedgeXY8); 1418 AnimationAdd([3000, 4500], sectorDescXYB); 1419 AnimationAdd([3000, 4500], wedgeXY6); 1420 AnimationAdd([4000, 5500], sectorDescXYC); 1421 AnimationAdd([4000, 5500], wedgeXY3); 1422 EngineStart(); 1423} 1424 1425function AnimateLineSingle() { 1426 EngineInit('keyframe4'); 1427 FirstText(lineSingleDesc); 1428 for (var i = 1; i <= 8; ++i) { 1429 SetupText("sectorDescXY" + i, 500, 260); 1430 } 1431 AnimationAdd([ 0, 1000], xaxis); 1432 AnimationAdd([ 0, 1000], yaxis); 1433 AnimationAdd([1000, 2000], lineSegment); 1434 AnimationAdd([1000, 3000], lineSegment, [-22.5 * Math.PI / 180], "path", evaluate_at, 1435 [ circle.center.x, circle.center.y, 1436 circle.center.x + " + " + circle.radius + " * Math.cos(value)", 1437 circle.center.y + " + " + circle.radius + " * Math.sin(value)", 1438 ]); 1439 AnimationAdd([2000, 3000], sectorDescXY1); 1440 AnimationAdd([2000, 3000], wedgeXY1); 1441 AnimationAdd([3000, 7000], lineSegment, [-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], 1442 "path", evaluate_at, 1443 [ circle.center.x, circle.center.y, 1444 circle.center.x + " + " + circle.radius + " * Math.cos(value)", 1445 circle.center.y + " + " + circle.radius + " * Math.sin(value)", 1446 ]); 1447 for (var i = 1; i < 8; ++i) { 1448 AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + (i + 1)); 1449 AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + (i + 1)); 1450 AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + i, [1, 0]); 1451 AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + i, [1, 0]); 1452 } 1453 AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY1); 1454 AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY1); 1455 AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY8, [1, 0]); 1456 AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY8, [1, 0]); 1457 EngineStart(); 1458} 1459 1460function AnimateCurveMultiple() { 1461 EngineInit('keyframe5'); 1462 var cubicStart = PathDataArray(curveSegment1); 1463 var cubicMid = PathDataArray(curveSegment2); 1464 var cubicEnd = PathDataArray(curveSegment3); 1465 FirstText(curveMultipleDesc1); 1466 FirstText(curveMultipleDesc2); 1467 for (var i = 1; i <= 6; ++i) { 1468 SetupText("sectorDescXY" + i, 500, 260 + i * 25); 1469 } 1470 AnimationAdd([ 0, 1000], xaxis); 1471 AnimationAdd([ 0, 1000], yaxis); 1472 AnimationAdd([1000, 2000], curveSegment); 1473 AnimationAdd([2000, 3000], sectorDescXY1); 1474 AnimationAdd([2000, 3000], wedgeXY1); 1475 AnimationAdd([3000, 4000], curveSegment, [0, 1], "path", interpolate_at, [cubicStart, cubicMid]); 1476 AnimationAdd([4000, 5000], sectorDescXY2); 1477 AnimationAdd([4000, 5000], wedgeXY2); 1478 AnimationAdd([5000, 6000], curveSegment, [0, 1], "path", interpolate_at, [cubicMid, cubicEnd]); 1479 AnimationAdd([6000, 7000], sectorDescXY3); 1480 AnimationAdd([6000, 7000], wedgeXY3); 1481 AnimationAdd([6000, 7000], sectorDescXY4); 1482 AnimationAdd([6000, 7000], wedgeXY4); 1483 AnimationAdd([6000, 7000], sectorDescXY5); 1484 AnimationAdd([6000, 7000], wedgeXY5); 1485 AnimationAdd([6000, 7000], sectorDescXY6); 1486 AnimationAdd([6000, 7000], wedgeXY6); 1487 EngineStart(); 1488} 1489 1490function AnimateOneDLines() { 1491 EngineInit('keyframe6'); 1492 FirstText(line1DDest1); 1493 FirstText(line1DDest2); 1494 for (var i = 9; i <= 11; ++i) { 1495 SetupText("sectorDescXY" + i, 500, 260 + (i - 8) * 25); 1496 } 1497 AnimationAdd([ 0, 1000], xaxis); 1498 AnimationAdd([ 0, 1000], yaxis); 1499 AnimationAdd([2000, 3000], sectorDescXY9); 1500 AnimationAdd([2000, 3000], horzSegment); 1501 AnimationAdd([3000, 4000], sectorDescXY10); 1502 AnimationAdd([3000, 4000], vertSegment); 1503 AnimationAdd([4000, 5000], sectorDescXY11); 1504 AnimationAdd([4000, 5000], diagSegment); 1505 EngineStart(); 1506} 1507 1508function AnimateDiverging() { 1509 EngineInit('keyframe7'); 1510 var cubicData = PathDataArray(cubicSegment2); 1511 FirstText(curve1dDesc1); 1512 FirstText(curve1dDesc2); 1513 SetupText("sectorDescXY9", 500, 285); 1514 SetupText("sectorDescXY1", 500, 320); 1515 AnimationAdd([ 0, 1000], xaxis); 1516 AnimationAdd([ 0, 1000], yaxis); 1517 AnimationAdd([1900, 1900], cubicSegment); 1518 AnimationAdd([2000, 3000], cubicSegment, [0, 1], "path", cubic_partial, cubicData); 1519 AnimationAdd([2000, 3000], sectorDescXY9); 1520 AnimationAdd([2000, 3000], horzSegment); 1521 AnimationAdd([3000, 4000], sectorDescXY1); 1522 AnimationAdd([3000, 4000], wedgeXY1); 1523 EngineStart(); 1524} 1525 1526circle.animate = AnimateCircle; 1527circle.start = null; 1528 1529function AngleToPt(center, radius, degrees) { 1530 var radians = degrees * Math.PI / 180.0; 1531 return { 1532 x: center.x + (radius * Math.cos(radians)), 1533 y: center.y - (radius * Math.sin(radians)) 1534 }; 1535} 1536 1537function PtsToSweep(pt1, pt2, center) { // unused 1538 return { 1539 start: 180 / Math.PI * Math.atan2(pt1.y - center.y, pt1.x - center.x), 1540 end: 180 / Math.PI * Math.atan2(pt2.y - center.y, pt2.x - center.x) 1541 }; 1542} 1543 1544 1545function ArcStr(center, radius, startAngle, endAngle) { 1546 var endPt = AngleToPt(center, radius, endAngle); 1547 var arcSweep = endAngle - startAngle <= 180 ? "0" : "1"; 1548 return ["A", radius, radius, 0, arcSweep, 0, endPt.x, endPt.y].join(" "); 1549} 1550 1551function ArcStart(center, radius, startAngle, endAngle) { 1552 var startPt = AngleToPt(center, radius, startAngle); 1553 return [ startPt.x, startPt.y, ArcStr(center, radius, startAngle, endAngle) ].join(" "); 1554} 1555 1556function MakeArc(arcStart) { 1557 return "M" + arcStart; 1558} 1559 1560function MakeWedge(center, arcStart) { 1561 return ["M", center.x, center.y, "L", arcStart, "z"].join(" "); 1562} 1563 1564function Animate(path, now, dur) { 1565 if (path.start == null) { 1566 path.start = now; 1567// console.log("start=" + now); 1568 } 1569 if (now - path.start < dur) { 1570 requestAnimationFrame(path.animate); 1571 return true; 1572 } 1573 return false; 1574} 1575 1576function AnimateCircle(now) { 1577 if (circle.start == null) { 1578 circleFill.setAttribute("fill-opacity", "0.3"); 1579 } 1580 var dur = 2 * 1000; 1581 var animating = Animate(circle, now, dur); 1582// console.log("now=" + now + "circle.start=" + circle.start ) 1583 var pathStr = ArcStart(circle.center, circle.radius, 0, (now - circle.start) / (dur / 359.9)); 1584 1585 circle.setAttribute("d", MakeArc(pathStr)); 1586 circleFill.setAttribute("d", MakeWedge(circle.center, pathStr)); 1587 if (!animating) { 1588 var delay = dur - (now - circle.start); 1589 setTimeout(CircleFinal, delay); 1590 } 1591} 1592 1593function CircleFinal() { 1594 var firstHalf = ArcStart(circle.center, circle.radius, 0, 180); 1595 var secondHalf = ArcStr(circle.center, circle.radius, 180, 360); 1596 circle.setAttribute("d", "M" + firstHalf + secondHalf + "z"); 1597 circleFill.setAttribute("d", "M" + firstHalf + secondHalf + "z"); 1598} 1599 1600var svgNS = "http://www.w3.org/2000/svg"; 1601 1602function CreateTextLabels() 1603{ 1604 for (var i = 0; i < 32; ++i) { 1605 var text = document.createElementNS(svgNS, "text"); 1606 var pt = AngleToPt(circle.center, circle.radius + 80, i * 360 / 32); 1607 text.setAttribute("id", "t" + i); 1608 text.setAttribute("x", pt.x); 1609 text.setAttribute("y", pt.y); 1610 text.setAttribute("text-anchor", "middle"); 1611 text.setAttribute("alignment-baseline", "mathematical"); 1612 var textNode = document.createTextNode(i); 1613 text.appendChild(textNode); 1614 document.getElementById("svg").appendChild(text); 1615 } 1616} 1617 1618// CreateTextLabels(); 1619 1620var keyframeArray = [ 1621 AnimateSpanWedge, 1622 AnimateTrivialWedge, 1623 AnimateSectorDesc, 1624 AnimateLineSingle, 1625 AnimateCurveMultiple, 1626 AnimateOneDLines, 1627 AnimateDiverging, 1628]; 1629 1630var keyframeIndex = 3; // keyframeArray.length - 1; // normally 0 ; set to debug a particular frame 1631 1632function QueueKeyframe() { 1633 QueueAnimation(keyframeArray[keyframeIndex]); 1634 if (keyframeIndex < keyframeArray.length - 1) { 1635 ++keyframeIndex; 1636 } 1637} 1638 1639var grads; 1640var paths; 1641var canvas; 1642var ctx; 1643 1644function canvasSetup() { 1645 canvas = document.getElementById("canvas"); 1646 ctx = canvas ? canvas.getContext("2d") : null; 1647 assert(ctx); 1648 var resScale = animationState.resScale = window.devicePixelRatio ? window.devicePixelRatio : 1; 1649 var unscaledWidth = canvas.width; 1650 var unscaledHeight = canvas.height; 1651 canvas.width = unscaledWidth * resScale; 1652 canvas.height = unscaledHeight * resScale; 1653 canvas.style.width = unscaledWidth + 'px'; 1654 canvas.style.height = unscaledHeight + 'px'; 1655 if (resScale != 1) { 1656 ctx.scale(resScale, resScale); 1657 } 1658 1659 grads = CanvasGrads(ctx); 1660 paths = CanvasPaths(ctx); 1661} 1662 1663function Onload() { 1664 canvasSetup(); 1665 var startBtn = document.getElementById('startBtn'); 1666 var stopBtn = document.getElementById('stopBtn'); 1667 var resetBtn = document.getElementById('resetBtn'); 1668 1669 startBtn.addEventListener('click', function(e) { 1670 e.preventDefault(); 1671 e.srcElement.innerText = "Next"; 1672 CancelAnimation(); 1673 QueueKeyframe(); 1674 }); 1675 1676 stopBtn.addEventListener('click', function(e) { 1677 e.preventDefault(); 1678 1679 if (!animationState.paused) { 1680 PauseAnimation(); 1681 e.srcElement.innerText = "Resume"; 1682 } else { 1683 UnpauseAnimation(); 1684 e.srcElement.innerText = "Pause"; 1685 } 1686 }); 1687 1688 resetBtn.addEventListener('click', function(e) { 1689 e.preventDefault(); 1690 CancelAnimation(); 1691 keyframeIndex = 0; 1692 startBtn.innerText = "Start"; 1693 QueueKeyframe(); 1694 }); 1695} 1696 1697</script> 1698 1699</head> 1700 1701<body onLoad="Onload()"> 1702 1703<div class="controls"> 1704 <button type="button" id="startBtn">Start</button> 1705 <button type="button" id="stopBtn">Pause</button> 1706 <button type="button" id="resetBtn">Restart</button> 1707</div> 1708 1709<canvas id="canvas" width="800" height="500" /> 1710 1711</body> 1712</html>