diff options
Diffstat (limited to 'experimental/docs/svgbaseddoc.htm')
-rw-r--r-- | experimental/docs/svgbaseddoc.htm | 1712 |
1 files changed, 1712 insertions, 0 deletions
diff --git a/experimental/docs/svgbaseddoc.htm b/experimental/docs/svgbaseddoc.htm new file mode 100644 index 0000000000..c96edcc4f6 --- /dev/null +++ b/experimental/docs/svgbaseddoc.htm @@ -0,0 +1,1712 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + + +<style> +html { + font-family: Helvetica, Arial, sans-serif; + font-size: 100%; +} + +.controls { + margin: 1em 0; +} + +button { + display: inline-block; + border-radius: 3px; + border: none; + font-size: 0.9rem; + padding: 0.4rem 0.8em; + background: #69c773; + border-bottom: 1px solid #498b50; + color: white; + -webkit-font-smoothing: antialiased; + font-weight: bold; + margin: 0 0.25rem; + text-align: center; +} + +button:hover, button:focus { + opacity: 0.75; + cursor: pointer; +} + +button:active { + opacity: 1; + box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset; +} + +</style> + +<! set height back to 500 /> +<svg id="svg" width="800" height="500" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + +<defs> + <radialGradient id="grad1" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(0,0,255); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad2" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(0,255,0); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(0,255,0); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad3" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(255,0,0); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad4" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(192,63,192); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(192,63,192); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad5" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(127,127,0); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(127,127,0); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad6" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(127,0,127); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(127,0,127); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad7" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(0,127,127); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(0,127,127); stop-opacity:0" /> + </radialGradient> + <radialGradient id="grad8" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> + <stop offset="0%" style="stop-color:rgb(63,192,63); stop-opacity:0.3" /> + <stop offset="100%" style="stop-color:rgb(63,192,63); stop-opacity:0" /> + </radialGradient> +</defs> + +<path id="circleFill" d="M300,200 A 100,100 0,0,0 300,200" fill="#777" fill-opacity="0" /> +<path id="circle" d="M300,200 A 100,100 0,0,0 300,200" fill="none" stroke="black" /> + +<! elements for keyframe 1 /> +<text id="spanWedgeDesc" fill-opacity="0" > +All spans are contained by a wedge. +</text> +<path id="span1" d="M200,200 Q300,300 200,300" fill="none" stroke="black" stroke-opacity="0"/> +<path id="span2" d="M200,200 C100,300 100,400 200,300" fill="none" stroke="black" stroke-opacity="0"/> +<path id="span3" d="M200,200 C300,100 100,400 300,200" fill="none" stroke="black" stroke-opacity="0"/> +<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"/> +<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"/> +<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"/> + +<! keyframe 2 /> +<text id="trivialWedgeDesc1" fill-opacity="0" > +Wedges that don't overlap can be +</text> +<text id="trivialWedgeDesc2" y="240" fill-opacity="0" > +easily sorted. +</text> +<path id="span4" d="M200,200 Q300,300 400,300" fill="none" stroke="black" stroke-opacity="0"/> +<path id="span5" d="M200,200 Q280,320 200,400" fill="none" stroke="black" stroke-opacity="0"/> +<path id="span6" d="M200,200 Q60,340 100,400" fill="none" stroke="black" stroke-opacity="0"/> +<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"/> +<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"/> +<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"/> + + +<! keyframe 3 /> +<text id="sectorDesc1" fill-opacity="0" > +A sector is a wedge of a circle +</text> +<text id="sectorDesc2" y="240" fill-opacity="0" > +containing a range of points. +</text> +<g id="xaxis" stroke-opacity="0" fill-opacity="0"> + <path d="M100,200 L300,200" fill="none" stroke="rgb(191,191,191)"/> + <text x="100" y="220" fill="rgb(191,191,191)">-X</text> + <text x="300" y="220" text-anchor="end" fill="rgb(191,191,191)">+X</text> +</g> +<g id="yaxis" stroke-opacity="0" fill-opacity="0"> + <path d="M200,100 L200,300" fill="none" stroke="rgb(191,191,191)"/> + <text x="205" y="100" alignment-baseline="hanging" fill="rgb(191,191,191)">-Y</text> + <text x="205" y="300" fill="rgb(191,191,191)">+Y</text> +</g> +<text id="sectorDescXYA" x="500" y="310" fill="rgb(0,0,255)" fill-opacity="0"> +X > 0> Y > 0 Y < X</text> +<text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0"> +X < 0 Y > 0 -Y < X</text> +<text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0"> +X < 0 Y < 0 Y < X</text> +<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"/> +<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"/> +<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"/> + +<! keyframe 4 /> +<text id="lineSingleDesc" fill-opacity="0" > +Line spans are contained by a single sector. +</text> +<text id="sectorDescXY1" x="500" y="460" fill="rgb(192,63,192)" fill-opacity="0"> +X > 0 Y < 0 -Y < X</text> +<text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0"> +X > 0 Y < 0 -Y > X</text> +<text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0"> +X < 0 Y < 0 Y < X</text> +<text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0"> +X < 0 Y < 0 Y > X</text> +<text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0"> +X < 0 Y > 0 -Y < X</text> +<text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0"> +X < 0 Y > 0 -Y < X</text> +<text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0"> +X > 0 Y > 0 Y > X</text> +<text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0"> +X > 0 Y > 0 Y < X</text> +<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"/> +<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"/> +<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"/> +<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"/> +<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"/> +<path id="lineSegment" d="M200,200 L200,624.26" fill="none" stroke="black" stroke-opacity="0"/> + +<! keyframe 5 /> +<text id="curveMultipleDesc1" fill-opacity="0" > +A curve span may cover more +</text> +<text id="curveMultipleDesc2" y="240" fill-opacity="0" > +than one sector. +</text> +<path id="curveSegment" d="M200,200 C250,200 300,150 300,100" fill="none" stroke="black" stroke-opacity="0"/> +<path id="curveSegment1" d="M200,200 C250,200 300,150 300,100" fill="none"/> +<path id="curveSegment2" d="M200,200 C250,200 300,150 200,100" fill="none"/> +<path id="curveSegment3" d="M200,200 C350,200 250,-150 170,300" fill="none"/> + +<! keyframe 6 /> +<text id="line1DDest1" fill-opacity="0" > +Some lines occupy one-dimensional +</text> +<text id="line1DDest2" y="240" fill-opacity="0" > +sectors. +</text> +<text id="sectorDescXY9" x="500" y="460" fill="rgb(192,92,31)" fill-opacity="0"> +X > 0 Y == 0</text> +<text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0"> +Y > 0 0 == X</text> +<text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0"> +X < 0 Y == X</text> +<path id="horzSegment" d="M200,200 L341.4,200" fill="none" stroke="rgb(192,92,31)" stroke-width="2" stroke-opacity="0"/> +<path id="vertSegment" d="M200,200 L200,341.4" fill="none" stroke="rgb(31,92,192)" stroke-width="2" stroke-opacity="0"/> +<path id="diagSegment" d="M200,200 L100,100" fill="none" stroke="rgb(127,63,127)" stroke-width="2" stroke-opacity="0"/> + +<! keyframe 7 /> +<text id="curve1dDesc1" fill-opacity="0" > +Some curves initially occupy +</text> +<text id="curve1dDesc2" y="240" fill-opacity="0" > +one-dimensional sectors, then diverge. +</text> +<path id="cubicSegment" fill="none" stroke="black" /> +<path id="cubicSegment1" d="M200,200 C200,200 200,200 200,200" fill="none" /> +<path id="cubicSegment2" d="M200,200 C250,200 300,200 300,100" fill="none"/> + +<text id="sectorNumberDesc" fill-opacity="0" > +Each sector is assigned a number. +</text> +<text id="spanSectorDesc" fill-opacity="0" > +Each span has a bit set for one or more sectors. +</text> +<text id="bitOverDesc" fill-opacity="0" > +Span sets allow rough sorting without angle computation. +</text> + +</svg> + +<! canvas support /> +<script> + +var keyFrameQueue = []; +var animationsPending = []; +var animationsActive = []; +var displayList = []; +var visibleFinished = []; + +var animationState = {}; +animationState.reset = function () { + this.start = null; + this.time = 0; + this.requestID = null; + this.paused = false; + this.displayEngine = 'Canvas'; +} + +circle.center = { x: 200, y: 200 } +circle.radius = 100; + +function assert(condition) { + if (!condition) debugger; +} + +function CanvasGrads(ctx) { + var grad1 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad1.addColorStop(0, "rgba(0,0,255, 0.3)"); + grad1.addColorStop(1, "rgba(0,0,255, 0)"); + var grad2 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad2.addColorStop(0, "rgba(0,255,0, 0.3)"); + grad2.addColorStop(1, "rgba(0,255,0, 0)"); + var grad3 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad3.addColorStop(0, "rgba(255,0,0, 0.3)"); + grad3.addColorStop(1, "rgba(255,0,0, 0)"); + var grad4 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad4.addColorStop(0, "rgba(192,63,192, 0.3)"); + grad4.addColorStop(1, "rgba(192,63,192, 0)"); + var grad5 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad5.addColorStop(0, "rgba(127,127,0, 0.3)"); + grad5.addColorStop(1, "rgba(127,127,0, 0)"); + var grad6 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad6.addColorStop(0, "rgba(127,0,127, 0.3)"); + grad6.addColorStop(1, "rgba(127,0,127, 0)"); + var grad7 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad7.addColorStop(0, "rgba(0,127,127, 0.3)"); + grad7.addColorStop(1, "rgba(0,127,127, 0)"); + var grad8 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); + grad8.addColorStop(0, "rgba(63,192,63, 0.3)"); + grad8.addColorStop(1, "rgba(63,192,63, 0)"); + var data = { + grad1: grad1, + grad2: grad2, + grad3: grad3, + grad4: grad4, + grad5: grad5, + grad6: grad6, + grad7: grad7, + grad8: grad8, + }; + return data; +} + +function skip_sep(data) { + if (!data.length) { + return data; + } + while (data[0] == ' ' || data[0] == ',') { + data = data.substring(1); + } + return data; +} + +function find_points(str, value, count, isRelative, relative) { + var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; + var match; + for (var index = 0; index < count; ++index) { + str = skip_sep(str); + match = numRegEx.exec(str); + assert(match); + var x = Number(match[0]); + str = skip_sep(str); + match = numRegEx.exec(str); + assert(match); + var y = Number(match[0]); + value[index] = { x: x, y : y }; + } + if (isRelative) { + for (var index = 0; index < count; index++) { + value[index].x += relative.x; + value[index].y += relative.y; + } + } + return str.substring(match.index + match[0].length); +} + +function find_scalar(str, obj, isRelative, relative) { + var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; + str = skip_sep(str); + var match = numRegEx.exec(str); + obj.value = Number(match[0]); + if (isRelative) { + obj.value += relative; + } + return str.substring(match.index + match[0].length); +} + +function parse_path(data) { + var path = "ctx.beginPath();\n"; + var f = {x:0, y:0}; + var c = {x:0, y:0}; + var lastc = {x:0, y:0}; + var points = []; + var op = '\0'; + var previousOp = '\0'; + var relative = false; + for (;;) { + data = skip_sep(data); + if (!data.length) { + break; + } + var ch = data[0]; + if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+') { + assert(op != '\0'); + } else if (ch == ' ' || ch == ',') { + data = skip_sep(data); + } else { + op = ch; + relative = false; + if ('a' <= op && op <= 'z') { + op = op.toUpperCase(); + relative = true; + } + data = data.substring(1); + data = skip_sep(data); + } + switch (op) { + case 'A': + var radii = []; + data = find_points(data, radii, 1, false, null); + var xaxisObj = {}; + data = find_scalar(data, xaxisObj, false, null); + var largeArcObj = {}; + data = find_scalar(data, largeArcObj, false, null); + var sweepObj = {}; + data = find_scalar(data, sweepObj, false, null); + data = find_points(data, points, 1, relative, c); + var mid = { x: (c.x + points[0].x) / 2, y: (c.y + points[0].y) / 2 }; + var midVec = { x: mid.x - c.x, y: mid.y - c.y }; + var midLenSqr = midVec.x * midVec.x + midVec.y * midVec.y; + var radius = radii[0].x; + var scale = Math.sqrt(midLenSqr) / Math.sqrt(radius * radius - midLenSqr); + var tangentPt = { x: mid.x + midVec.y * scale, + y: mid.y - midVec.x * scale }; + path += "ctx.arcTo(" + tangentPt.x + "," + tangentPt.y + "," + + points[0].x + "," + points[0].y + "," + radius + ");\n"; + c = points[0]; + break; + case 'M': + data = find_points(data, points, 1, relative, c); + path += "ctx.moveTo(" + points[0].x + "," + points[0].y + ");\n"; + op = 'L'; + c = points[0]; + break; + case 'L': + data = find_points(data, points, 1, relative, c); + path += "ctx.lineTo(" + points[0].x + "," + points[0].y + ");\n"; + c = points[0]; + break; + case 'H': { + var xObj = {}; + data = find_scalar(data, xObj, relative, c.x); + path += "ctx.lineTo(" + xObj.value + "," + c.y + ");\n"; + c.x = xObj.value; + } break; + case 'V': { + var yObj = {}; + data = find_scalar(data, y, relative, c.y); + path += "ctx.lineTo(" + c.x + "," + yObj.value+ ");\n"; + c.y = yObj.value; + } break; + case 'C': + data = find_points(data, points, 3, relative, c); + path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," + + points[1].x + "," + points[1].y + "," + + points[2].x + "," + points[2].y + ");\n"; + lastc = points[1]; + c = points[2]; + break; + case 'S': + var pts2_3 = []; + data = find_points(data, pts2_3, 2, relative, c); + points[0] = c; + points[1] = pts2_3[0]; + points[2] = pts2_3[1]; + if (previousOp == 'C' || previousOp == 'S') { + points[0].x -= lastc.x - c.x; + points[0].y -= lastc.y - c.y; + } + path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," + + points[1].x + "," + points[1].y + "," + + points[2].x + "," + points[2].y + ");\n"; + lastc = points[1]; + c = points[2]; + break; + case 'Q': // Quadratic Bezier Curve + data = find_points(data, points, 2, relative, c); + path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," + + points[1].x + "," + points[1].y + ");\n"; + lastc = points[0]; + c = points[1]; + break; + case 'T': + var pts2 = []; + data = find_points(data, pts2, 1, relative, c); + points[0] = pts2[0]; + points[1] = pts2[0]; + if (previousOp == 'Q' || previousOp == 'T') { + points[0].x = c.x * 2 - lastc.x; + points[0].y = c.y * 2 - lastc.y; + } + path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," + + points[1].x + "," + points[1].y + ");\n"; + path.quadTo(points[0], points[1]); + lastc = points[0]; + c = points[1]; + break; + case 'Z': + path += "ctx.closePath();\n"; + c = f; + op = '\0'; + break; + case '~': + var args = []; + data = find_points(data, args, 2, false, null); + path += "moveTo(" + args[0].x + "," + args[0].y + ");\n"; + path += "lineTo(" + args[1].x + "," + args[1].y + ");\n"; + break; + default: + return false; + } + if (previousOp == 0) { + f = c; + } + previousOp = op; + } + return path; +} + +function CanvasPaths(ctx) { + var svgStrs = { + // keyframe 1 + span1: "M200,200 Q300,300 200,300", + span2: "M200,200 C100,300 100,400 200,300", + span3: "M200,200 C300,100 100,400 300,200", + wedge1: "M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z", + wedge2: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z", + wedge3: "M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z", + // keyframe 2 + span4: "M200,200 Q300,300 400,300", + span5: "M200,200 Q280,320 200,400", + span6: "M200,200 Q60,340 100,400", + wedge4: "M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z", + wedge5: "M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z", + wedge6: "M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z", + // keyframe 3 + xaxis: "M100,200 L300,200", + yaxis: "M200,100 L200,300", + wedgeXY8: "M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z", + wedgeXY6: "M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z", + wedgeXY3: "M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z", + // keyframe 4 + wedgeXY1: "M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z", + wedgeXY2: "M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z", + wedgeXY4: "M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z", + wedgeXY5: "M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z", + wedgeXY7: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z", + lineSegment: "M200,200 L200,624.26", + // keyframe 5 + curveSegment: "M200,200 C250,200 300,150 300,100", + curveSegment1: "M200,200 C250,200 300,150 300,100", + curveSegment2: "M200,200 C250,200 300,150 200,100", + curveSegment3: "M200,200 C350,200 250,-150 170,300", + // keyframe 6 + horzSegment: "M200,200 L341.4,200", + vertSegment: "M200,200 L200,341.4", + diagSegment: "M200,200 L100,100", + // keyframe 7 + cubicSegment: "M200,200 C200,200 200,200 200,200", + cubicSegment1: "M200,200 C200,200 200,200 200,200", + cubicSegment2: "M200,200 C250,200 300,200 300,100", + }; + var paths = []; + var keys = Object.keys(svgStrs); + for (var index in keys) { + var key = keys[index]; + var str = svgStrs[key]; + var path = parse_path(str); + var record = []; + paths[key] = { + str: str, + funcBody: path, + }; + } + return paths; +} + +function canvas_fill_font(record) { + assert(record); + var str = 'ctx.font = "normal 1.3rem Helvetica,Arial";\n'; + if (record.fillStyle) { + str += 'ctx.fillStyle = ' + record.fillStyle + ';\n'; + } + return str; +} + +function canvas_fill_text(record) { + assert(record); + assert(typeof record.fillText == 'string'); + return 'ctx.fillText("' + record.fillText + '"'; +} + +function canvas_xy(record) { + var x = typeof record.x == "number" ? record.x : 400; + var y = typeof record.y == "number" ? record.y : 200; + return ', ' + x + ', ' + y + ');\n'; +} + +function canvas_text_xy(record) { + return canvas_fill_text(record) + canvas_xy(record); +} + +function add_canvas_stroke(paths, data, id, strokeStyle) { + var record = {}; + record.data = paths[id].funcBody; + record.style = 'ctx.strokeStyle = ' + (strokeStyle ? strokeStyle : '"black"') + ';\n'; + record.draw = 'ctx.stroke();\n'; + record.func = new Function('ctx', record.data + record.style + record.draw); + return data[id] = record; +} + +function add_canvas_style(record, style) { + record.style += style; + record.func = new Function('ctx', record.data + record.style + record.draw); +} + +function add_canvas_fill(paths, data, id, fillStyle) { + var record = {}; + record.data = paths[id].funcBody; + record.style = 'ctx.fillStyle = ' + (fillStyle ? fillStyle : '"black"') + ';\n'; + record.draw = 'ctx.fill();\n'; + record.func = new Function('ctx', record.data + record.style + record.draw); + return data[id] = record; +} + +function add_canvas_text(data, id, params) { + var record = {}; + record.style = canvas_fill_font(params); + record.draw = canvas_fill_text(params); + record.position = canvas_xy(params); + record.x = params.x; + record.y = params.y; + record.func = new Function('ctx', record.style + record.draw + record.position); + return data[id] = record; +} + +function keyframe1(grads, paths) { + var data = []; + add_canvas_text(data, "spanWedgeDesc", { fillText:"All spans are contained by a wedge" } ); + add_canvas_stroke(paths, data, "span1"); + add_canvas_stroke(paths, data, "span2"); + add_canvas_stroke(paths, data, "span3"); + add_canvas_fill(paths, data, "wedge1", "grads.grad1"); + add_canvas_fill(paths, data, "wedge2", "grads.grad2"); + add_canvas_fill(paths, data, "wedge3", "grads.grad3"); + return data; +} + +function keyframe2(grads, paths) { + var data = []; + add_canvas_text(data, "trivialWedgeDesc1", { fillText:"Wedges that don't overlap can be" } ); + add_canvas_text(data, "trivialWedgeDesc2", { fillText:"easily sorted.", y:240 } ); + add_canvas_stroke(paths, data, "span4").debug = true; + add_canvas_stroke(paths, data, "span5"); + add_canvas_stroke(paths, data, "span6"); + add_canvas_fill(paths, data, "wedge4", "grads.grad1"); + add_canvas_fill(paths, data, "wedge5", "grads.grad2"); + add_canvas_fill(paths, data, "wedge6", "grads.grad3"); + return data; +} + +function setup_axes(paths, data) { + var color = '"rgb(191,191,191)"'; + var xaxis = add_canvas_stroke(paths, data, "xaxis", color); + xaxis.funcBody = canvas_fill_font( { fillStyle:color } ); + xaxis.funcBody += canvas_text_xy( { fillText:"-X", x:100, y:220 } ); + xaxis.funcBody += "ctx.textAlign = 'right';\n"; + xaxis.funcBody += canvas_text_xy( { fillText:"+X", x:300, y:220 } ); + xaxis.func = new Function('ctx', xaxis.data + xaxis.style + xaxis.draw + xaxis.funcBody); + var yaxis = add_canvas_stroke(paths, data, "yaxis", color); + yaxis.funcBody = canvas_fill_font( { fillStyle:color } ); + yaxis.funcBody += "ctx.textBaseline = 'hanging';\n"; + yaxis.funcBody += canvas_text_xy( { fillText:"-Y", x:205, y:100 } ); + yaxis.funcBody += "ctx.textBaseline = 'alphabetic';\n"; + yaxis.funcBody += canvas_text_xy( { fillText:"+Y", x:205, y:300 } ); + yaxis.func = new Function('ctx', yaxis.data + yaxis.style + yaxis.draw + yaxis.funcBody); +} + +function keyframe3(grads, paths) { + var data = []; + add_canvas_text(data, "sectorDesc1", { fillText:"A sector is a wedge of a circle" } ); + add_canvas_text(data, "sectorDesc2", { fillText:"containing a range of points.", y:240 } ); + setup_axes(paths, data); + add_canvas_text(data, "sectorDescXYA", + { fillText:"X > 0 Y > 0 Y < X", x:500, y:310, fillStyle:'"rgb(0,0,255)"'} ); + add_canvas_text(data, "sectorDescXYB", + { fillText:"X < 0 Y > 0 -Y < X", x:500, y:360, fillStyle:'"rgb(0,127,0)"'} ); + add_canvas_text(data, "sectorDescXYC", + { fillText:"X < 0 Y < 0 Y < X", x:500, y:410, fillStyle:'"rgb(255,0,0)"'} ); + add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); + add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); + add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); + return data; +} + +function keyframe4(grads, paths) { + var data = []; + setup_axes(paths, data); + add_canvas_text(data, "lineSingleDesc", + { fillText:"Line spans are contained by a single sector." } ); + add_canvas_text(data, "sectorDescXY1", + { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); + add_canvas_text(data, "sectorDescXY2", + { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); + add_canvas_text(data, "sectorDescXY3", + { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); + add_canvas_text(data, "sectorDescXY4", + { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); + add_canvas_text(data, "sectorDescXY5", + { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); + add_canvas_text(data, "sectorDescXY6", + { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); + add_canvas_text(data, "sectorDescXY7", + { fillText:"X > 0 Y > 0 Y > X", x:500, y:460, fillStyle:'"rgb(63,192,63)"'} ); + add_canvas_text(data, "sectorDescXY8", + { fillText:"X > 0 Y > 0 Y < X", x:500, y:460, fillStyle:'"rgb(0,0,255)"'} ); + add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); + add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); + add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); + add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); + add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); + add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); + add_canvas_fill(paths, data, "wedgeXY7", "grads.grad8"); + add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); + add_canvas_stroke(paths, data, "lineSegment"); + return data; +} + +function keyframe5(grads, paths) { + var data = []; + setup_axes(paths, data); + add_canvas_text(data, "curveMultipleDesc1", + { fillText:"A curve span may cover more" } ); + add_canvas_text(data, "curveMultipleDesc2", + { fillText:"than one sector.", y:240 } ); + add_canvas_stroke(paths, data, "curveSegment"); + add_canvas_text(data, "sectorDescXY1", + { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); + add_canvas_text(data, "sectorDescXY2", + { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); + add_canvas_text(data, "sectorDescXY3", + { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); + add_canvas_text(data, "sectorDescXY4", + { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); + add_canvas_text(data, "sectorDescXY5", + { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); + add_canvas_text(data, "sectorDescXY6", + { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); + add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); + add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); + add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); + add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); + add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); + add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); + return data; +} + +function keyframe6(grads, paths) { + var data = []; + setup_axes(paths, data); + + add_canvas_text(data, "line1DDest1", + { fillText:"Some lines occupy one-dimensional" } ); + add_canvas_text(data, "line1DDest2", + { fillText:"sectors.", y:240 } ); + add_canvas_text(data, "sectorDescXY9", + { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); + add_canvas_text(data, "sectorDescXY10", + { fillText:"Y > 0 0 == X", x:500, y:460, fillStyle:'"rgb(31,92,192)"' } ); + add_canvas_text(data, "sectorDescXY11", + { fillText:"X < 0 Y == X", x:500, y:460, fillStyle:'"rgb(127,63,127)"' } ); + var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); + add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); + var vert = add_canvas_stroke(paths, data, "vertSegment", '"rgb(31,92,192)"'); + add_canvas_style(vert, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); + var diag = add_canvas_stroke(paths, data, "diagSegment", '"rgb(127,63,127)"'); + add_canvas_style(diag, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); + return data; +} + +function keyframe7(grads, paths) { + var data = []; + setup_axes(paths, data); + add_canvas_text(data, "curve1dDesc1", + { fillText:"Some curves initially occupy" } ); + add_canvas_text(data, "curve1dDesc2", + { fillText:"one-dimensional sectors, then diverge.", y:240 } ); + add_canvas_stroke(paths, data, "cubicSegment"); + add_canvas_text(data, "sectorDescXY1", + { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); + add_canvas_text(data, "sectorDescXY9", + { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); + var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); + add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); + add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); + return data; +} + +var canvasData = null; + +function CanvasInit(keyframe) { + canvasData = window[keyframe](grads, paths); +} + +</script> + +<script> + +function interp(A, B, t) { + return A + (B - A) * t; +} + +function interp_cubic_coords(x1, x2, x3, x4, t) +{ + var ab = interp(x1, x2, t); + var bc = interp(x2, x3, t); + var cd = interp(x3, x4, t); + var abc = interp(ab, bc, t); + var bcd = interp(bc, cd, t); + var abcd = interp(abc, bcd, t); + return abcd; +} + +function cubic_partial(value, p) { + var x1 = p[0], y1 = p[1], x2 = p[2], y2 = p[3]; + var x3 = p[4], y3 = p[5], x4 = p[6], y4 = p[7]; + var t1 = 0, t2 = value; + var ax = interp_cubic_coords(x1, x2, x3, x4, t1); + var ay = interp_cubic_coords(y1, y2, y3, y4, t1); + var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3); + var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3); + var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3); + var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3); + var dx = interp_cubic_coords(x1, x2, x3, x4, t2); + var dy = interp_cubic_coords(y1, y2, y3, y4, t2); + var mx = ex * 27 - ax * 8 - dx; + var my = ey * 27 - ay * 8 - dy; + var nx = fx * 27 - ax - dx * 8; + var ny = fy * 27 - ay - dy * 8; + var bx = (mx * 2 - nx) / 18; + var by = (my * 2 - ny) / 18; + var cx = (nx * 2 - mx) / 18; + var cy = (ny * 2 - my) / 18; + var array = [ + ax, ay, bx, by, cx, cy, dx, dy + ]; + return array; +} + +function evaluate_at(value, p) { + var array = []; + for (var index = 0; index < p.length; ++index) { + var func = new Function('value', 'return ' + p[index] + ';'); + array[index] = func(value); + } + return array; +} + +function interpolate_at(value, p) { + var array = []; + var start = p[0]; + var end = p[1]; + assert(typeof end == typeof start); + switch (typeof start) { + case 'object': + for (var index = 0; index < start.length; ++index) { + array[index] = interp(start[index], end[index], value); + } + break; + case 'number': + array[index] = interp(start, end, value); + break; + default: + debugger; + } + return array; +} + +function AnimationAddCommon(timing, range, attr, inParams) { + var animation = { + timing: timing, + range: range, + attr: attr, + inParams: inParams, + duration: timing[1] - timing[0], + remaining: timing[1] - timing[0], + firstStep: true, + } + animationsPending.push(animation); + return animation; +} + +function AnimationAddSVG(timing, element, range, attr, inParams) { + var animation = AnimationAddCommon(timing, range, attr, inParams); + animation.element = element; + return animation; +} + +function AnimationAddCanvas(timing, element, range, attr, inParams) { + var animation = AnimationAddCommon(timing, range, attr, inParams); + animation.element = canvasData[element]; + assert(animation.element); + animation.firstElement = null; + return animation; +} + +function AnimationAdd(timing, e, range, attr, funct, inParams) { + if (!range) { + range = [0, 1]; + } + if (!attr) { + attr = 'opacity'; + } + if (!funct) { + funct = interpolate_at; + } + var element; + switch (animationState.displayEngine) { + case 'SVG': + element = typeof e == 'string' ? document.getElementById(e) : e; + break; + case 'Canvas': + element = typeof e == 'string' ? e : e.id; + break; + default: + debugger; + } + assert(element); + switch (attr) { + case 'path': + if (!inParams) { + inParams = PathDataArray(element); + } + break; + case 'opacity': + if (!inParams) { + inParams = [0, 1]; + } + break; + default: + debugger; + } + var funcBody = 'var outParams = ' + funct.name + '(value, inParams);\n'; + switch (animationState.displayEngine) { + case 'SVG': + switch (attr) { + case 'path': + var verbArray = PathVerbArray(element); + funcBody += 'return '; + for (var index = 0; index < inParams.length; ++index) { + funcBody += '"' + verbArray[index] + '"'; + funcBody += 'outParams[' + index + '];\n'; + } + if (verbArray.length > inParams.length) { + funcBody += '"' + verbArray[verbArray.length - 1] + '"'; + } + funcBody += ';\n'; + var animation = AnimationAddSVG(timing, element, range, "d", inParams); + animation.func = new Function('value', 'inParams', funcBody); + break; + case 'opacity': + if (animation.element.getAttribute("stroke-opacity")) { + animation = AnimationAddSVG(timing, element, range, "stroke-opacity", inParams); + } + if (animation.element.getAttribute("fill-opacity")) { + animation = AnimationAddSVG(timing, element, range, "fill-opacity", inParams); + } + break; + default: + debugger; + } + case 'Canvas': + switch (attr) { + case 'path': + var verbArray = PathVerbArray(element); + for (var index = 0; index < inParams.length; ++index) { + funcBody += verbArray[index]; + funcBody += 'outParams[' + index + ']'; + } + if (verbArray.length > inParams.length) { + funcBody += verbArray[verbArray.length - 1]; + } + animation = AnimationAddCanvas(timing, element, range, attr, inParams); + funcBody += animation.element.style + animation.element.draw; + animation.func = new Function('ctx', 'value', 'inParams', funcBody); + break; + case 'opacity': + animation = AnimationAddCanvas(timing, element, range, attr, inParams); + break; + default: + debugger; + } + break; + default: + debugger; + } + return animation; +} + +function path_data_common(element, getValues) { + var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; + var data = []; + var match; + var path; + switch (animationState.displayEngine) { + case 'SVG': path = element.getAttribute("d"); break; + case 'Canvas': path = paths[element].funcBody; break; + default: debugger; + } + if (getValues) { + while ((match = numRegEx.exec(path))) { + data.push(Number(match[0])); + } + } else { + var sIndex = 0; + while ((match = numRegEx.exec(path))) { + if (sIndex < match.index) { + data.push(path.substring(sIndex, match.index)); + } + sIndex = match.index + match[0].length; + } + if (sIndex < path.length) { + data.push(path.substring(sIndex, path.length)); + } + } + return data; +} + +function PathDataArray(element) { + return path_data_common(element, true); +} + +function PathVerbArray(element) { + return path_data_common(element, false); +} + +function PathSet(element, funct, value, params) { + var pathVerbs = PathVerbArray(element); + if (funct) { + params = funct(value, params); + } + var setValue = ''; + for (var index = 0; index < params.length; ++index) { + setValue += pathVerbs[index]; + setValue += params[index]; + } + if (pathVerbs.length > params.length) { + setValue += pathVerbs[pathVerbs.length - 1]; + } + switch (animationState.displayEngine) { + case 'SVG': + element.setAttribute('d', setValue); + break; + case 'Canvas': + element.func = new Function('ctx', setValue + element.style + element.draw); + break; + default: + debugger; + } +} + +function RemoveFromArray(array, element) { + for (var index in array) { + var record = array[index]; + if (record.element == element) { + array.splice(index, 1); + break; + } + } +} + +function EndAnimationCanvas(animation, visibleFinished) { + var changeAlpha = "opacity" == animation.attr; + if (!changeAlpha || animation.range[1] > 0) { + if (changeAlpha) { + ctx.save(); + ctx.globalAlpha = animation.range[1]; + } + if (animation.func) { + animation.func(ctx, animation.range[animation.range.length - 1], animation.inParams); + } else { + animation.element.func(ctx); + } + if (changeAlpha) { + ctx.restore(); + } +// if (visibleFinished) { +// visibleFinished.push(animation); +// } + } else { + // if (visibleFinished) { + // RemoveFromArray(visibleFinished, animation.element); + // } + } +} + +/* start here +canvas: + +display list : + for each element (canvas) + save + set global alpha (override) + create geometry (override) + create style (override) + draw + restore + +maybe each action should have an override slot +animations write to the slot +each element in display list then iterates overrides once the animations complete the frame + +so, first -- + active animations update the display list + +next -- + active animations install themselves in override slots + +finally + display list is iterated, calling override slots + +---------------- + +svg: + display list is implicit + + active animations write element attributes + */ + +function EndAnimationSVG(animation, visibleFinished) { + switch (animation.attr) { + case "opacity": + animation.element.setAttribute(animation.attribute, animation.range[1]); + if (animation.range[1] > 0) { + visibleFinished.push(animation); + } else { + RemoveFromArray(visibleFinished, animation.element); + } + break; + case "path": + var attrStr = animation.func(animation.range[1], animation.inParams); + animation.element.setAttribute(animation.attribute, attrStr); + break; + default: + debugger; + } +} + +function StepAnimationCanvas(animation, value) { + var endValue = animation.range[animation.range.length - 1]; + var interp = animation.range[0] + (endValue - animation.range[0]) * (1 - value); + if (animation.firstStep) { + RemoveFromArray(visibleFinished, animation.element); + animation.firstStep = false; + } + var changeAlpha = "opacity" == animation.attr; + if (changeAlpha) { + ctx.save(); + ctx.globalAlpha = interp; + } + if (animation.func) { + animation.func(ctx, interp, animation.inParams); + } else { + animation.element.func(ctx); + } + if (changeAlpha) { + ctx.restore(); + } +} + +function StepAnimationSVG(animation, value) { + var interp = animation.range[0] + (animation.range[1] - animation.range[0]) * (1 - value); + switch (animation.attr) { + case "opacity": + animation.element.setAttribute(animation.attribute, interp); + break; + case "path": + var attrStr = animation.func(interp, animation.inParams); + animation.element.setAttribute(animation.attribute, attrStr); + break; + default: + debugger; + } +} + +var animate_frame = 0; + +function AnimateList(now) { + ++animate_frame; + if (animationState.paused) { + return; + } + if (animationState.start == null) { + animationState.start = now - animationState.time; + } + animationState.time = now - animationState.start; + var stillPending = []; + for (var index in animationsPending) { + var animation = animationsPending[index]; + var interval = animationState.time - animation.timing[0]; + if (interval <= 0) { + stillPending.push(animation); + continue; + } + animationsActive.push(animation); + var inList = false; + for (var dlIndex in displayList) { + var displayable = displayList[dlIndex]; + if (displayable == animation.element) { + inList = true; + break; + } + } + if (!inList) { + displayList.push(animation.element); + } + } + animationsPending = stillPending; + var stillAnimating = []; + if ('Canvas' == animationState.displayEngine) { + ctx.clearRect(0, 0, canvas.width, canvas.height); +// for (var index in visibleFinished) { +// var animation = visibleFinished[index]; +// animation.endAnimation = false; +// } + } + for (var index in animationsActive) { + var animation = animationsActive[index]; + var interval = animationState.time - animation.timing[0]; + animation.remaining = animation.duration > interval ? animation.duration - interval : 0; + animation.endAnimation = animation.remaining <= 0; + if (animation.endAnimation) { + switch (animationState.displayEngine) { + case 'SVG': EndAnimationSVG(animation, visibleFinished); break; + case 'Canvas': EndAnimationCanvas(animation, visibleFinished); break; + default: debugger; + } + continue; + } + var value = animation.remaining / animation.duration; + switch (animationState.displayEngine) { + case 'SVG': StepAnimationSVG(animation, value); break; + case 'Canvas': + if (!animation.firstElement || !animation.firstElement.endAnimation) { + StepAnimationCanvas(animation, value); + } + break; + default: debugger; + } + stillAnimating.push(animation); + } + if ('Canvas' == animationState.displayEngine) { + for (var index in visibleFinished) { + var animation = visibleFinished[index]; + if (!animation.endAnimation) { + EndAnimationCanvas(animation, null); + } + } + } + animationsActive = stillAnimating; + if (animationsPending.length || animationsActive.length) { + animationState.requestID = requestAnimationFrame(AnimateList); + } +} + +function CancelAnimate(now) { + if (animationState.start == null) { + animationState.start = now; + } + var time = now - animationState.start; + var stillAnimating = []; + for (var index in animationsActive) { + var animation = animationsActive[index]; + var remaining = animation.remaining - time; + var value = remaining / animation.duration; + switch (animationState.displayEngine) { + case 'SVG': animation.element.setAttribute(animation.attribute, value); break; + case 'Canvas': break; + } + if (remaining <= 0) { + continue; + } + stillAnimating.push(animation); + } + animationsActive = stillAnimating; + if (animationsActive.length) { + animationState.requestID = requestAnimationFrame(CancelAnimate); + return; + } + animationsPending = []; + animationState.reset(); + if (keyFrameQueue.length > 0) { + var animationFunc = keyFrameQueue.pop(); + animationFunc(); + } +} + +function CancelAnimation() { + cancelAnimationFrame(animationState.requestID); + for (var index in animationsActive) { + var animation = animationsActive[index]; + switch (animation.attr) { + case "opacity": + var tmp = animation.range[0]; animation.range[0] = animation.range[1]; animation[1] = tmp; + animation.remaining = animation.duration - animation.remaining; + animation.remaining /= animation.duration / 1000; + animation.duration = 1000; + break; + case "fadeOut": + RemoveFromArray(visibleFinished, animation.element); + break; + case "path": + break; + default: + debugger; + + } + } + for (var index in visibleFinished) { + var animation = visibleFinished[index]; + animation.action = "fadeOut"; + animation.remaining = animation.duration = 1000; + animationsActive.push(animation); + } + visibleFinished = []; + animationState.reset(); + animationState.requestID = requestAnimationFrame(CancelAnimate); +} + +function PauseAnimation() { + animationState.paused = true; +} + +function QueueAnimation(animationFunc) { + if (null == animationState.requestID) { + animationFunc(); + return; + } + keyFrameQueue.push(animationFunc); +} + +function UnpauseAnimation() { + animationState.paused = false; + animationState.start = performance.now() - animationState.time; + animationState.requestID = requestAnimationFrame(AnimateList); +} + +function SetupTextSVG(t, x, y) { + var text; + if (typeof t == "string") { + text = document.getElementById(t); + } else { + text = t; + } + text.setAttribute("font-family", "Helvetica,Arial"); + text.setAttribute("font-size", "1.3rem"); + if (typeof x == 'number') { + text.setAttribute("x", x); + } else if (null == text.getAttribute("x")) { + text.setAttribute("x", 400); + } + if (typeof y == 'number') { + text.setAttribute("y", y); + } else if (null == text.getAttribute("y")) { + text.setAttribute("y", 200); + } +} + +function SetupTextCanvas(t, x, y) { + var text = typeof t == 'string' ? t : t.id; + var record = canvasData[text]; + if (typeof x == 'number') { + record.x = x; + } + if (typeof y == 'number') { + record.y = y; + } + record.position = canvas_xy(record); + record.func = new Function('ctx', record.style + record.draw + record.position); +} + +function SetupText(t, x, y) { + switch (animationState.displayEngine) { + case 'SVG': + SetupTextSVG(t, x, y); + break; + case 'Canvas': + SetupTextCanvas(t, x, y); + break; + default: + debugger; + } +} + +function FirstText(text) { + SetupText(text); + AnimationAdd([0, 1000], text); +} + + +function EngineInit(keyframe) { + displayList = []; + switch (animationState.displayEngine) { + case 'SVG': break; + case 'Canvas': CanvasInit(keyframe); break; + default: debugger; + } +} + +function EngineStart() { + switch (animationState.displayEngine) { + case 'SVG': break; + case 'Canvas': + // associate fadeIn and fadeOut + for (var outerIndex in animationsPending) { + var outer = animationsPending[outerIndex]; + for (var innerIndex in animationsPending) { + if (outerIndex == innerIndex) { + continue; + } + var inner = animationsPending[innerIndex]; + if (inner.element == outer.element) { + inner.firstElement = outer; + continue; + } + } + } + break; + default: debugger; + } + animationState.reset(); + animationState.requestID = requestAnimationFrame(AnimateList); +} + +function AnimateSpanWedge() { + EngineInit('keyframe1'); + FirstText(spanWedgeDesc); + AnimationAdd([1000, 2000], span1); + AnimationAdd([1500, 3000], wedge1); + AnimationAdd([3500, 4000], span1, [1, 0]); + AnimationAdd([3500, 4000], wedge1, [1, 0]); + AnimationAdd([4000, 5000], span2); + AnimationAdd([4500, 6000], wedge2); + AnimationAdd([6500, 7000], span2, [1, 0]); + AnimationAdd([6500, 7000], wedge2, [1, 0]); + AnimationAdd([7000, 8000], span3); + AnimationAdd([7500, 9000], wedge3); + EngineStart(); +} + +function AnimateTrivialWedge() { + EngineInit('keyframe2'); + FirstText(trivialWedgeDesc1); + FirstText(trivialWedgeDesc2); + AnimationAdd([2000, 3500], span4); + AnimationAdd([2000, 3500], wedge4); + AnimationAdd([2000, 3500], span5); + AnimationAdd([2000, 3500], wedge5); + AnimationAdd([2000, 3500], span6); + AnimationAdd([2000, 3500], wedge6); + EngineStart(); +} + +function AnimateSectorDesc() { + EngineInit('keyframe3'); + FirstText(sectorDesc1); + FirstText(sectorDesc2); + AnimationAdd([ 0, 1000], xaxis); + AnimationAdd([ 500, 1500], yaxis); + AnimationAdd([2000, 3500], sectorDescXYA); + AnimationAdd([2000, 3500], wedgeXY8); + AnimationAdd([3000, 4500], sectorDescXYB); + AnimationAdd([3000, 4500], wedgeXY6); + AnimationAdd([4000, 5500], sectorDescXYC); + AnimationAdd([4000, 5500], wedgeXY3); + EngineStart(); +} + +function AnimateLineSingle() { + EngineInit('keyframe4'); + FirstText(lineSingleDesc); + for (var i = 1; i <= 8; ++i) { + SetupText("sectorDescXY" + i, 500, 260); + } + AnimationAdd([ 0, 1000], xaxis); + AnimationAdd([ 0, 1000], yaxis); + AnimationAdd([1000, 2000], lineSegment); + AnimationAdd([1000, 3000], lineSegment, [-22.5 * Math.PI / 180], "path", evaluate_at, + [ circle.center.x, circle.center.y, + circle.center.x + " + " + circle.radius + " * Math.cos(value)", + circle.center.y + " + " + circle.radius + " * Math.sin(value)", + ]); + AnimationAdd([2000, 3000], sectorDescXY1); + AnimationAdd([2000, 3000], wedgeXY1); + AnimationAdd([3000, 7000], lineSegment, [-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], + "path", evaluate_at, + [ circle.center.x, circle.center.y, + circle.center.x + " + " + circle.radius + " * Math.cos(value)", + circle.center.y + " + " + circle.radius + " * Math.sin(value)", + ]); + for (var i = 1; i < 8; ++i) { + AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + (i + 1)); + AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + (i + 1)); + AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + i, [1, 0]); + AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + i, [1, 0]); + } + AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY1); + AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY1); + AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY8, [1, 0]); + AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY8, [1, 0]); + EngineStart(); +} + +function AnimateCurveMultiple() { + EngineInit('keyframe5'); + var cubicStart = PathDataArray(curveSegment1); + var cubicMid = PathDataArray(curveSegment2); + var cubicEnd = PathDataArray(curveSegment3); + FirstText(curveMultipleDesc1); + FirstText(curveMultipleDesc2); + for (var i = 1; i <= 6; ++i) { + SetupText("sectorDescXY" + i, 500, 260 + i * 25); + } + AnimationAdd([ 0, 1000], xaxis); + AnimationAdd([ 0, 1000], yaxis); + AnimationAdd([1000, 2000], curveSegment); + AnimationAdd([2000, 3000], sectorDescXY1); + AnimationAdd([2000, 3000], wedgeXY1); + AnimationAdd([3000, 4000], curveSegment, [0, 1], "path", interpolate_at, [cubicStart, cubicMid]); + AnimationAdd([4000, 5000], sectorDescXY2); + AnimationAdd([4000, 5000], wedgeXY2); + AnimationAdd([5000, 6000], curveSegment, [0, 1], "path", interpolate_at, [cubicMid, cubicEnd]); + AnimationAdd([6000, 7000], sectorDescXY3); + AnimationAdd([6000, 7000], wedgeXY3); + AnimationAdd([6000, 7000], sectorDescXY4); + AnimationAdd([6000, 7000], wedgeXY4); + AnimationAdd([6000, 7000], sectorDescXY5); + AnimationAdd([6000, 7000], wedgeXY5); + AnimationAdd([6000, 7000], sectorDescXY6); + AnimationAdd([6000, 7000], wedgeXY6); + EngineStart(); +} + +function AnimateOneDLines() { + EngineInit('keyframe6'); + FirstText(line1DDest1); + FirstText(line1DDest2); + for (var i = 9; i <= 11; ++i) { + SetupText("sectorDescXY" + i, 500, 260 + (i - 8) * 25); + } + AnimationAdd([ 0, 1000], xaxis); + AnimationAdd([ 0, 1000], yaxis); + AnimationAdd([2000, 3000], sectorDescXY9); + AnimationAdd([2000, 3000], horzSegment); + AnimationAdd([3000, 4000], sectorDescXY10); + AnimationAdd([3000, 4000], vertSegment); + AnimationAdd([4000, 5000], sectorDescXY11); + AnimationAdd([4000, 5000], diagSegment); + EngineStart(); +} + +function AnimateDiverging() { + EngineInit('keyframe7'); + var cubicData = PathDataArray(cubicSegment2); + FirstText(curve1dDesc1); + FirstText(curve1dDesc2); + SetupText("sectorDescXY9", 500, 285); + SetupText("sectorDescXY1", 500, 320); + AnimationAdd([ 0, 1000], xaxis); + AnimationAdd([ 0, 1000], yaxis); + AnimationAdd([1900, 1900], cubicSegment); + AnimationAdd([2000, 3000], cubicSegment, [0, 1], "path", cubic_partial, cubicData); + AnimationAdd([2000, 3000], sectorDescXY9); + AnimationAdd([2000, 3000], horzSegment); + AnimationAdd([3000, 4000], sectorDescXY1); + AnimationAdd([3000, 4000], wedgeXY1); + EngineStart(); +} + +circle.animate = AnimateCircle; +circle.start = null; + +function AngleToPt(center, radius, degrees) { + var radians = degrees * Math.PI / 180.0; + return { + x: center.x + (radius * Math.cos(radians)), + y: center.y - (radius * Math.sin(radians)) + }; +} + +function PtsToSweep(pt1, pt2, center) { // unused + return { + start: 180 / Math.PI * Math.atan2(pt1.y - center.y, pt1.x - center.x), + end: 180 / Math.PI * Math.atan2(pt2.y - center.y, pt2.x - center.x) + }; +} + + +function ArcStr(center, radius, startAngle, endAngle) { + var endPt = AngleToPt(center, radius, endAngle); + var arcSweep = endAngle - startAngle <= 180 ? "0" : "1"; + return ["A", radius, radius, 0, arcSweep, 0, endPt.x, endPt.y].join(" "); +} + +function ArcStart(center, radius, startAngle, endAngle) { + var startPt = AngleToPt(center, radius, startAngle); + return [ startPt.x, startPt.y, ArcStr(center, radius, startAngle, endAngle) ].join(" "); +} + +function MakeArc(arcStart) { + return "M" + arcStart; +} + +function MakeWedge(center, arcStart) { + return ["M", center.x, center.y, "L", arcStart, "z"].join(" "); +} + +function Animate(path, now, dur) { + if (path.start == null) { + path.start = now; +// console.log("start=" + now); + } + if (now - path.start < dur) { + requestAnimationFrame(path.animate); + return true; + } + return false; +} + +function AnimateCircle(now) { + if (circle.start == null) { + circleFill.setAttribute("fill-opacity", "0.3"); + } + var dur = 2 * 1000; + var animating = Animate(circle, now, dur); +// console.log("now=" + now + "circle.start=" + circle.start ) + var pathStr = ArcStart(circle.center, circle.radius, 0, (now - circle.start) / (dur / 359.9)); + + circle.setAttribute("d", MakeArc(pathStr)); + circleFill.setAttribute("d", MakeWedge(circle.center, pathStr)); + if (!animating) { + var delay = dur - (now - circle.start); + setTimeout(CircleFinal, delay); + } +} + +function CircleFinal() { + var firstHalf = ArcStart(circle.center, circle.radius, 0, 180); + var secondHalf = ArcStr(circle.center, circle.radius, 180, 360); + circle.setAttribute("d", "M" + firstHalf + secondHalf + "z"); + circleFill.setAttribute("d", "M" + firstHalf + secondHalf + "z"); +} + +var svgNS = "http://www.w3.org/2000/svg"; + +function CreateTextLabels() +{ + for (var i = 0; i < 32; ++i) { + var text = document.createElementNS(svgNS, "text"); + var pt = AngleToPt(circle.center, circle.radius + 80, i * 360 / 32); + text.setAttribute("id", "t" + i); + text.setAttribute("x", pt.x); + text.setAttribute("y", pt.y); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("alignment-baseline", "mathematical"); + var textNode = document.createTextNode(i); + text.appendChild(textNode); + document.getElementById("svg").appendChild(text); + } +} + +// CreateTextLabels(); + +var keyframeArray = [ + AnimateSpanWedge, + AnimateTrivialWedge, + AnimateSectorDesc, + AnimateLineSingle, + AnimateCurveMultiple, + AnimateOneDLines, + AnimateDiverging, +]; + +var keyframeIndex = 3; // keyframeArray.length - 1; // normally 0 ; set to debug a particular frame + +function QueueKeyframe() { + QueueAnimation(keyframeArray[keyframeIndex]); + if (keyframeIndex < keyframeArray.length - 1) { + ++keyframeIndex; + } +} + +var grads; +var paths; +var canvas; +var ctx; + +function canvasSetup() { + canvas = document.getElementById("canvas"); + ctx = canvas ? canvas.getContext("2d") : null; + assert(ctx); + var resScale = animationState.resScale = window.devicePixelRatio ? window.devicePixelRatio : 1; + var unscaledWidth = canvas.width; + var unscaledHeight = canvas.height; + canvas.width = unscaledWidth * resScale; + canvas.height = unscaledHeight * resScale; + canvas.style.width = unscaledWidth + 'px'; + canvas.style.height = unscaledHeight + 'px'; + if (resScale != 1) { + ctx.scale(resScale, resScale); + } + + grads = CanvasGrads(ctx); + paths = CanvasPaths(ctx); +} + +function Onload() { + canvasSetup(); + var startBtn = document.getElementById('startBtn'); + var stopBtn = document.getElementById('stopBtn'); + var resetBtn = document.getElementById('resetBtn'); + + startBtn.addEventListener('click', function(e) { + e.preventDefault(); + e.srcElement.innerText = "Next"; + CancelAnimation(); + QueueKeyframe(); + }); + + stopBtn.addEventListener('click', function(e) { + e.preventDefault(); + + if (!animationState.paused) { + PauseAnimation(); + e.srcElement.innerText = "Resume"; + } else { + UnpauseAnimation(); + e.srcElement.innerText = "Pause"; + } + }); + + resetBtn.addEventListener('click', function(e) { + e.preventDefault(); + CancelAnimation(); + keyframeIndex = 0; + startBtn.innerText = "Start"; + QueueKeyframe(); + }); +} + +</script> + +</head> + +<body onLoad="Onload()"> + +<div class="controls"> + <button type="button" id="startBtn">Start</button> + <button type="button" id="stopBtn">Pause</button> + <button type="button" id="resetBtn">Restart</button> +</div> + +<canvas id="canvas" width="800" height="500" /> + +</body> +</html>
\ No newline at end of file |