aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/docs/svgbaseddoc.htm
diff options
context:
space:
mode:
Diffstat (limited to 'experimental/docs/svgbaseddoc.htm')
-rw-r--r--experimental/docs/svgbaseddoc.htm1712
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 &gt; 0>&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &lt; X</text>
+<text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;-Y &lt; X</text>
+<text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &lt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &lt; 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 &gt; 0&nbsp;&nbsp;&nbsp;Y &lt; 0&nbsp;&nbsp;&nbsp;-Y &lt; X</text>
+<text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0">
+X &gt; 0&nbsp;&nbsp;&nbsp;Y &lt; 0&nbsp;&nbsp;&nbsp;-Y &gt; X</text>
+<text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &lt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &lt; X</text>
+<text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &lt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &gt; X</text>
+<text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;-Y &lt; X</text>
+<text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;-Y &lt; X</text>
+<text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0">
+X &gt; 0&nbsp;&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &gt; X</text>
+<text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0">
+X &gt; 0&nbsp;&nbsp;&nbsp;Y &gt; 0&nbsp;&nbsp;&nbsp;&nbsp;Y &lt; 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 &gt; 0&nbsp;&nbsp;&nbsp;Y == 0</text>
+<text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0">
+Y &gt; 0&nbsp;&nbsp;&nbsp;0 == X</text>
+<text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0">
+X &lt; 0&nbsp;&nbsp;&nbsp;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