aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/docs
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2015-11-16 13:36:08 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-11-16 13:36:08 -0800
commit3257c12220c411cc9db564e6bf02baba4c1bc061 (patch)
treede67bc0cbdcaba91f0a3ff18ddfc406e08bc17c7 /experimental/docs
parent73d882e13fc54ff9ecc53a83838c2d900ebf8397 (diff)
runs some sample animations side by side in canvas and svg
TBR=fmalita@chromium.org Review URL: https://codereview.chromium.org/1342523002
Diffstat (limited to 'experimental/docs')
-rw-r--r--experimental/docs/animationCommon.js314
-rw-r--r--experimental/docs/backend.js44
-rw-r--r--experimental/docs/canvasBackend.js167
-rw-r--r--experimental/docs/exampleSlides.js564
-rw-r--r--experimental/docs/interpolatorFunctions.js84
-rw-r--r--experimental/docs/jsonbaseddoc.htm41
-rw-r--r--experimental/docs/svgBackend.js246
-rw-r--r--experimental/docs/svgbaseddoc.htm1712
-rw-r--r--experimental/docs/utilities.js24
9 files changed, 3196 insertions, 0 deletions
diff --git a/experimental/docs/animationCommon.js b/experimental/docs/animationCommon.js
new file mode 100644
index 0000000000..1733ec2032
--- /dev/null
+++ b/experimental/docs/animationCommon.js
@@ -0,0 +1,314 @@
+var animationState = {};
+animationState.reset = function (engine) {
+ if ('string' === typeof engine) {
+ this.defaultEngine = engine;
+ }
+ this.defaults = {};
+ this.displayList = [];
+ this.displayDict = {};
+ this.start = null;
+ this.time = 0;
+ this.timeline = [];
+ this.timelineIndex = 0;
+ this.requestID = null;
+ this.paused = false;
+ this.displayEngine = 'undefined' === typeof engine ? this.defaultEngine : engine;
+}
+
+function addActions(frame, timeline) {
+ var keyframe = keyframes[frame];
+ var len = keyframe.length;
+ for (var i = 0; i < len; ++i) {
+ var action = keyframe[i];
+ loopOver(action, timeline);
+ }
+}
+
+function animateList(now) {
+ if (animationState.paused) {
+ return;
+ }
+ if (animationState.start == null) {
+ animationState.start = now - animationState.time;
+ }
+ animationState.time = now - animationState.start;
+ var stillAnimating = false;
+ for (var index = animationState.timelineIndex; index < animationState.timeline.length; ++index) {
+ var animation = animationState.timeline[index];
+ if (animation.time > animationState.time) {
+ stillAnimating = true;
+ break;
+ }
+ if (animation.time + animation.duration < animationState.time) {
+ if (animation.finalized) {
+ continue;
+ }
+ animation.finalized = true;
+ }
+ stillAnimating = true;
+ var actions = animation.actions;
+ for (var aIndex = 0; aIndex < actions.length; ++aIndex) {
+ var action = actions[aIndex];
+ var hasDraw = 'draw' in action;
+ var hasRef = 'ref' in action;
+ var displayIndex;
+ if (hasDraw) {
+ var ref = hasRef ? action.ref : "anonymous_" + index + "_" + aIndex;
+ assert('string' == typeof(ref));
+ if (ref in animationState.displayDict) {
+ displayIndex = animationState.displayDict[ref];
+ } else {
+ assert('string' == typeof(action.draw));
+ var draw = (new Function("return " + action.draw))();
+ assert('object' == typeof(draw));
+ var paint;
+ if ('paint' in action) {
+ assert('string' == typeof(action.paint));
+ paint = (new Function("return " + action.paint))();
+ assert('object' == typeof(paint) && !isArray(paint));
+ } else {
+ paint = animationState.defaults.paint;
+ }
+ displayIndex = animationState.displayList.length;
+ animationState.displayList.push( { "ref":ref, "draw":draw, "paint":paint,
+ "drawSpec":action.draw, "paintSpec":action.paint,
+ "drawCopied":false, "paintCopied":false,
+ "drawDirty":true, "paintDirty":true, "once":false } );
+ animationState.displayDict[ref] = displayIndex;
+ }
+ } else if (hasRef) {
+ assert('string' == typeof(action.ref));
+ displayIndex = animationState.displayDict[action.ref];
+ } else {
+ assert(actions.length == 1);
+ for (var prop in action) {
+ if ('paint' == prop) {
+ assert('string' == typeof(action[prop]));
+ var obj = (new Function("return " + action[prop]))();
+ assert('object' == typeof(obj) && !isArray(obj));
+ animationState.defaults[prop] = obj;
+ } else {
+ animationState.defaults[prop] = action[prop];
+ }
+ }
+ continue;
+ }
+ var targetSpec = 'target' in action ? action.target : animationState.defaults.target;
+ assert(targetSpec);
+ assert('string' == typeof(targetSpec));
+ assert(displayIndex < animationState.displayList.length);
+ var display = animationState.displayList[displayIndex];
+ var modDraw = targetSpec.startsWith('draw');
+ assert(modDraw || targetSpec.startsWith('paint'));
+ var modType = modDraw ? "draw" : "paint";
+ var copied = modDraw ? display.drawCopied : action.paintCopied;
+ if (!copied) {
+ var copy;
+ if (!modDraw || display.drawSpec.startsWith("text")) {
+ copy = {};
+ var original = modDraw ? display.draw : display.paint;
+ for (var p in original) {
+ copy[p] = original[p];
+ }
+ } else if (display.drawSpec.startsWith("paths")) {
+ copy = [];
+ for (var i = 0; i < display.draw.length; ++i) {
+ var curves = display.draw[i];
+ var curve = Object.keys(curves)[0];
+ copy[i] = {};
+ copy[i][curve] = curves[curve].slice(0); // clone the array of curves
+ }
+ } else {
+ assert(display.drawSpec.startsWith("pictures"));
+ copy = [];
+ for (var i = 0; i < display.draw.length; ++i) {
+ var entry = display.draw[i];
+ copy[i] = { "draw":entry.draw, "paint":entry.paint };
+ }
+ }
+ display[modType] = copy;
+ display[modType + "Copied"] = true;
+ }
+ var targetField, targetObject, fieldOffset;
+ if (targetSpec.endsWith("]")) {
+ fieldOffset = targetSpec.lastIndexOf("[");
+ assert(fieldOffset >= 0);
+ targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length - 1);
+ var arrayIndex = +targetField;
+ if (!isNaN(arrayIndex) && targetField.length > 0) {
+ targetField = arrayIndex;
+ }
+
+ } else {
+ fieldOffset = targetSpec.lastIndexOf(".");
+ if (fieldOffset >= 0) {
+ targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length);
+ } else {
+ targetObject = display;
+ targetField = targetSpec;
+ }
+ }
+ if (fieldOffset >= 0) {
+ var sub = targetSpec.substring(0, fieldOffset);
+ targetObject = (new Function('display', "return display." + sub))(display);
+ }
+ assert(null != targetObject[targetField]);
+ if (!('start' in action) || action.start < animation.time) {
+ for (var p in animationState.defaults) {
+ if ('draw' == p || 'paint' == p || 'ref' == p) {
+ continue;
+ }
+ assert('range' == p || 'target' == p || 'formula' == p || 'params' == p);
+ if (!(p in action)) {
+ action[p] = animationState.defaults[p];
+ }
+ }
+ if ('number' == typeof(action.formula)) {
+ targetObject[targetField] = action.formula;
+ action.once = true;
+ }
+ action.start = animation.time;
+ }
+ if (action.once) {
+ continue;
+ }
+ var value = Math.min(1, (animationState.time - animation.time) / animation.duration);
+ var scaled = action.range[0] + (action.range[1] - action.range[0]) * value;
+ if ('params' in action) {
+ if (!('func' in action)) {
+ if (isArray(action.params)) {
+ action.funcParams = [];
+ var len = action.params.length;
+ for (var i = 0; i < len; ++i) {
+ action.funcParams[i] = 'target' == action.params[i]
+ ? targetObject[targetField]
+ : (new Function("return " + action.params[i]))();
+ }
+ } else {
+ action.funcParams = 'target' == action.params
+ ? targetObject[targetField]
+ : (new Function("return " + action.params))();
+ }
+ assert('formula' in action && 'string' == typeof(action.formula));
+ // evaluate inline function to get value
+ action.func = new Function('value', 'params', "return " + action.formula);
+ }
+ scaled = action.func(scaled, action.funcParams);
+ }
+ if (targetObject[targetField] != scaled) {
+ if (modDraw) {
+ display.drawDirty = true;
+ } else {
+ display.paintDirty = true;
+ }
+ targetObject[targetField] = scaled;
+ }
+ }
+ }
+ displayBackend(animationState.displayEngine, animationState.displayList);
+
+ if (stillAnimating) {
+ animationState.requestID = requestAnimationFrame(animateList);
+ }
+}
+
+function flattenPaint(paint) {
+ if (!paint.paint) {
+ return;
+ }
+ var parent = paints[paint.paint];
+ flattenPaint(parent);
+ for (var prop in parent) {
+ if (!(prop in paint)) {
+ paint[prop] = parent[prop];
+ }
+ }
+ paint.paint = null;
+}
+
+function init(engine, keyframe) {
+ animationState.reset(engine);
+ setupPaint();
+ setupBackend(animationState.displayEngine);
+ keyframeInit(keyframe);
+}
+
+function keyframeInit(frame) {
+ animationState.reset();
+ addActions("_default", animationState.timeline);
+ addActions(frame, animationState.timeline);
+ for (var index = 0; index < animationState.timeline.length; ++index) {
+ animationState.timeline[index].position = index;
+ }
+ animationState.timeline.sort(function(a, b) {
+ if (a.time == b.time) {
+ return a.position - b.position;
+ }
+ return a.time - b.time;
+ });
+ keyframeBackendInit(animationState.displayEngine, animationState.displayList,
+ keyframes[frame][0]);
+ animationState.requestID = requestAnimationFrame(animateList);
+}
+
+function loopAddProp(action, propName) {
+ var funcStr = "";
+ var prop = action[propName];
+ if ('draw' != propName && isArray(prop)) {
+ funcStr += '[';
+ for (var index = 0; index < prop.length; ++index) {
+ funcStr += loopAddProp(prop, index);
+ if (index + 1 < prop.length) {
+ funcStr += ", ";
+ }
+ }
+ funcStr += ']';
+ return funcStr;
+ }
+ assert("object" != typeof(prop));
+ var useString = "string" == typeof(prop) && isAlpha(prop.charCodeAt(0));
+ if (useString) {
+ funcStr += "'";
+ }
+ funcStr += prop;
+ if (useString) {
+ funcStr += "'";
+ }
+ return funcStr;
+}
+
+function loopOver(rec, timeline) {
+ var funcStr = "";
+ if (rec.for) {
+ funcStr += "for (" + rec.for[0] + "; " + rec.for[1] + "; " + rec.for[2] + ") {\n";
+ }
+ funcStr += " var time = " + ('time' in rec ? rec.time : 0) + ";\n";
+ funcStr += " var duration = " + ('duration' in rec ? rec.duration : 0) + ";\n";
+ funcStr += " var actions = [];\n";
+ var len = rec.actions.length;
+ for (var i = 0; i < len; ++i) {
+ funcStr += " var action" + i + " = {\n";
+ var action = rec.actions[i];
+ for (var p in action) {
+ funcStr += " '" + p + "':";
+ funcStr += loopAddProp(action, p);
+ funcStr += ",\n";
+ }
+ funcStr = funcStr.substring(0, funcStr.length - 2);
+ funcStr += "\n };\n";
+ funcStr += " actions.push(action" + i + ");\n";
+ }
+ funcStr += " timeline.push( { 'time':time, 'duration':duration, 'actions':actions,"
+ + "'finalized':false } );\n";
+ if (rec.for) {
+ funcStr += "}\n";
+ }
+ var func = new Function('rec', 'timeline', funcStr);
+ func(rec, timeline);
+}
+
+function setupPaint() {
+ for (var prop in paints) {
+ flattenPaint(paints[prop]);
+ }
+}
diff --git a/experimental/docs/backend.js b/experimental/docs/backend.js
new file mode 100644
index 0000000000..efb8e52ef0
--- /dev/null
+++ b/experimental/docs/backend.js
@@ -0,0 +1,44 @@
+function displayBackend(displayEngine, displayList) {
+ switch (displayEngine) {
+ case 'all':
+ displayCanvas(displayList);
+ displaySvg(displayList);
+ break;
+ case 'Canvas':
+ displayCanvas(displayList);
+ break;
+ case 'SVG':
+ displaySvg(displayList);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+function keyframeBackendInit(displayEngine, displayList, first) {
+ switch (displayEngine) {
+ case 'all':
+ case 'Canvas':
+ keyframeCanvasInit(displayList, first);
+ break;
+ case 'SVG':
+ break;
+ default:
+ assert(0);
+ }
+}
+
+function setupBackend(displayEngine) {
+ switch (displayEngine) {
+ case 'all':
+ case 'Canvas':
+ setupCanvas();
+ setupSvg();
+ break;
+ case 'SVG':
+ setupSvg();
+ break;
+ default:
+ assert(0);
+ }
+}
diff --git a/experimental/docs/canvasBackend.js b/experimental/docs/canvasBackend.js
new file mode 100644
index 0000000000..0574813f22
--- /dev/null
+++ b/experimental/docs/canvasBackend.js
@@ -0,0 +1,167 @@
+var canvas;
+var ctx;
+var canvasGradients = {};
+
+function canvas_rbga(color) {
+ var a = canvas_opacity(color);
+ var r = (color >> 16) & 0xFF;
+ var g = (color >> 8) & 0xFF;
+ var b = (color >> 0) & 0xFF;
+ return "rgba(" + r + "," + g + "," + b + "," + a + ")";
+}
+
+function canvas_opacity(color) {
+ var a = (color >> 24) & 0xFF;
+ return a / 255.;
+}
+
+function displayCanvas(displayList) {
+ if (displayList.clear) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
+ for (var index = 0; index < displayList.length; ++index) {
+ drawToCanvas(displayList[index]);
+ }
+}
+
+function drawToCanvas(action) {
+ ctx.save();
+ var paint = paintToCanvas(action.paint);
+ var draw = action.draw;
+ if ('string' == typeof(draw)) {
+ draw = (new Function("return " + draw))();
+ }
+ if (isArray(draw)) {
+ assert(draw.length > 0);
+ var picture = 'draw' in draw[0];
+ if (picture) {
+ for (var index = 0; index < draw.length; ++index) {
+ drawToCanvas(draw[index]);
+ }
+ return;
+ }
+ ctx.beginPath();
+ for (var index = 0; index < draw.length; ++index) {
+ for (var prop in draw[index]) {
+ var v = draw[index][prop];
+ switch (prop) {
+ case 'arcTo':
+ ctx.arcTo(v[0], v[1], v[2], v[3], v[4]);
+ break;
+ case 'close':
+ ctx.closePath();
+ break;
+ case 'cubic':
+ ctx.moveTo(v[0], v[1]);
+ ctx.bezierCurveTo(v[2], v[3], v[4], v[5], v[6], v[7]);
+ break;
+ case 'line':
+ ctx.moveTo(v[0], v[1]);
+ ctx.lineTo(v[2], v[3]);
+ break;
+ case 'quad':
+ ctx.moveTo(v[0], v[1]);
+ ctx.quadraticCurveTo(v[2], v[3], v[4], v[5]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ if ('fill' == paint.style) {
+ ctx.fill();
+ } else {
+ assert('stroke' == paint.style);
+ ctx.stroke();
+ }
+ } else {
+ assert('string' in draw);
+ if ('fill' == paint.style) {
+ ctx.fillText(draw.string, draw.x, draw.y);
+ } else {
+ assert('stroke' == paint.style);
+ ctx.strokeText(draw.string, draw.x, draw.y);
+ }
+ }
+ ctx.restore();
+}
+
+function keyframeCanvasInit(displayList, first) {
+ if ('canvas' in first && 'clear' == first.canvas) {
+ displayList.clear = true;
+ }
+}
+
+function paintToCanvas(paint) {
+ var color;
+ var inPicture = 'string' == typeof(paint);
+ if (inPicture) {
+ paint = (new Function("return " + paint))();
+ assert('object' == typeof(paint) && !isArray(paint));
+ }
+ if ('gradient' in paint) {
+ var gradient = paint.gradient.split('.');
+ var gradName = gradient[1];
+ if (!canvasGradients[gradName]) {
+ var g = window[gradient[0]][gradient[1]];
+ var grad = ctx.createRadialGradient(g.cx, g.cy, 0, g.cx, g.cy, g.r);
+ var stopLen = g.stops.length;
+ for (var index = 0; index < stopLen; ++index) {
+ var stop = g.stops[index];
+ var color = canvas_rbga(stop.color);
+ grad.addColorStop(index, color);
+ }
+ canvasGradients[gradName] = grad;
+ }
+ color = canvasGradients[gradName];
+ if (!inPicture) {
+ ctx.globalAlpha = canvas_opacity(paint.color);
+ }
+ } else {
+ color = canvas_rbga(paint.color);
+ }
+ if ('fill' == paint.style) {
+ ctx.fillStyle = color;
+ } else if ('stroke' == paint.style) {
+ ctx.strokeStyle = color;
+ } else {
+ ctx.globalAlpha = canvas_opacity(paint.color);
+ }
+ if ('strokeWidth' in paint) {
+ ctx.lineWidth = paint.strokeWidth;
+ }
+ if ('typeface' in paint) {
+ var typeface = typefaces[paint.typeface];
+ var font = typeface.style;
+ if ('textSize' in paint) {
+ font += " " + paint.textSize;
+ }
+ if ('family' in typeface) {
+ font += " " + typeface.family;
+ }
+ ctx.font = font;
+ if ('textAlign' in paint) {
+ ctx.textAlign = paint.textAlign;
+ }
+ if ('textBaseline' in paint) {
+ ctx.textBaseline = paint.textBaseline;
+ }
+ }
+ return paint;
+}
+
+function setupCanvas() {
+ canvas = document.getElementById("canvas");
+ ctx = canvas ? canvas.getContext("2d") : null;
+ assert(ctx);
+ var 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);
+ }
+}
diff --git a/experimental/docs/exampleSlides.js b/experimental/docs/exampleSlides.js
new file mode 100644
index 0000000000..d1a10b6985
--- /dev/null
+++ b/experimental/docs/exampleSlides.js
@@ -0,0 +1,564 @@
+var circle = {
+ "center":{ "x":200, "y":200 },
+ "radius":100
+}
+
+var gradients = {
+ "grad1": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,0,0,255) },
+ { "offset":1, "color": argb( 0,0,0,255) }
+ ]
+ },
+ "grad2": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,0,255,0) },
+ { "offset":1, "color": argb( 0,0,255,0) }
+ ]
+ },
+ "grad3": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,255,0,0) },
+ { "offset":1, "color": argb( 0,255,0,0) }
+ ]
+ },
+ "grad4": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,192,63,192) },
+ { "offset":1, "color": argb( 0,192,63,192) }
+ ]
+ },
+ "grad5": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,127,127,0) },
+ { "offset":1, "color": argb( 0,127,127,0) }
+ ]
+ },
+ "grad6": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,127,0,127) },
+ { "offset":1, "color": argb( 0,127,0,127) }
+ ]
+ },
+ "grad7": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,0,127,127) },
+ { "offset":1, "color": argb( 0,0,127,127) }
+ ]
+ },
+ "grad8": { "cx":200, "cy":200, "r":300,
+ "stops": [
+ { "offset":0, "color": argb(76,63,192,63) },
+ { "offset":1, "color": argb( 0,63,192,63) }
+ ]
+ }
+};
+
+var paths = {
+ "cubicSegment1": [
+ { "cubic": [ 200,200, 200,200, 200,200, 200,200 ] }
+ ],
+ "cubicSegment2": [
+ { "cubic": [ 200,200, 250,200, 300,200, 300,100 ] }
+ ],
+ "curveSegment1": [
+ { "cubic": [ 200,200, 250,200, 300,150, 300,100 ] }
+ ],
+ "curveSegment2": [
+ { "cubic": [ 200,200, 250,200, 300,150, 200,100 ] }
+ ],
+ "curveSegment3": [
+ { "cubic": [ 200,200, 350,200, 250,-150, 170,300 ] }
+ ],
+ "diagSegment": [
+ { "line": [ 200,200, 100,100 ] }
+ ],
+ "horzSegment": [
+ { "line": [ 200,200, 341.4,200 ] }
+ ],
+ "lineSegment": [
+ { "line": [ 200,200, 200 + circle.radius * Math.cos(-22.5 * Math.PI / 180),
+ 200 + circle.radius * Math.sin(-22.5 * Math.PI / 180) ] }
+ ],
+ "span1": [
+ { "quad": [ 200,200, 300,300, 200,300 ] }
+ ],
+ "span2": [
+ { "cubic": [ 200,200, 100,300, 100,400, 200,300 ] }
+ ],
+ "span3": [
+ { "cubic": [ 200,200, 300,100, 100,400, 300,200 ] }
+ ],
+ "span4": [
+ { "quad": [ 200,200, 300,300, 400,300 ] }
+ ],
+ "span5": [
+ { "quad": [ 200,200, 280,320, 200,400 ] }
+ ],
+ "span6": [
+ { "quad": [ 200,200, 60,340, 100,400 ] }
+ ],
+ "vertSegment": [
+ { "line": [ 200,200, 200,341.4 ] }
+ ],
+ "wedge1": [
+ { "line": [ 200,200, 500,500 ] },
+ { "arcTo": [ 375.74,624.36, 200,624.26, 424.26 ] },
+ { "close": null }
+ ],
+ "wedge2": [
+ { "line": [ 200,200, 200,624.26 ] },
+ { "arcTo": [ 24.265,624.26, -100,500, 424.26 ] },
+ { "close": null }
+ ],
+ "wedge3": [
+ { "line": [ 200,200, 500,-100 ] },
+ { "arcTo": [ 1138.22,537.70, 240,622.5, 424.26 ] },
+ { "close": null }
+ ],
+ "wedge4": [
+ { "line": [ 200,200, 500,500 ] },
+ { "arcTo": [ 530.79,438.42, 579.47,389.74, 424.26 ] },
+ { "close": null }
+ ],
+ "wedge5": [
+ { "line": [ 200,200, 389.74,579.47 ] },
+ { "arcTo": [ 284.94,563.441, 200,500, 424.26 ] },
+ { "close": null }
+ ],
+ "wedge6": [
+ { "line": [ 200,200, 10.26,579.47 ] },
+ { "arcTo": [ -51.318,548.68, -100,500, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY1": [
+ { "line": [ 200,200, 500,-100 ] },
+ { "arcTo": [ 624.26,24.265, 624.26,200, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY2": [
+ { "line": [ 200,200, 200,-175.74 ] },
+ { "arcTo": [ 364.83,-196.61, 500,-100, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY3": [
+ { "line": [ 200,200, -100,-100 ] },
+ { "arcTo": [ 35.170,-196.61, 200,-175.74, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY4": [
+ { "line": [ 200,200, -175.74,200 ] },
+ { "arcTo": [ -196.61,35.170, -100,-100, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY5": [
+ { "line": [ 200,200, -100,500 ] },
+ { "arcTo": [ -196.61,364.83, -175.74,200, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY6": [
+ { "line": [ 200,200, -100,500 ] },
+ { "arcTo": [ 75.735,500, 200,624.26, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY7": [
+ { "line": [ 200,200, 200,624.26 ] },
+ { "arcTo": [ 324.26,500, 500,500, 424.26 ] },
+ { "close": null }
+ ],
+ "wedgeXY8": [
+ { "line": [ 200,200, 500,500 ] },
+ { "arcTo": [ 500,324.26, 624.26,200, 424.26 ] },
+ { "close": null }
+ ],
+ "xaxis": [
+ { "line": [ 100,200, 300,200 ] }
+ ],
+ "yaxis": [
+ { "line": [ 200,100, 200,300 ] }
+ ]
+};
+
+var text = {
+ "curve1d1": {
+ "string":"Some curves initially occupy", "x":400, "y":200
+ },
+ "curve1d2": {
+ "string":"one-dimensional sectors, then diverge.", "x":400, "y":240
+ },
+ "curveMultiple1": {
+ "string":"A curve span may cover more", "x":400, "y":200
+ },
+ "curveMultiple2": {
+ "string":"than one sector.", "x":400, "y":240
+ },
+ "line1DDest1": {
+ "string":"Some lines occupy one-dimensional", "x":400, "y":200
+ },
+ "line1DDest2": {
+ "string":"sectors.", "x":400, "y":240
+ },
+ "lineSingle": {
+ "string":"Line spans are contained by a single sector.", "x":400, "y":200
+ },
+ "sector1": {
+ "string":"A sector is a wedge of a circle", "x":400, "y":200
+ },
+ "sector2": {
+ "string":"containing a range of points.", "x":400, "y":240
+ },
+ "sectorXY1": {
+ "string":"X > 0 Y < 0 -Y < X", "x":500, "y":460
+ },
+ "sectorXY2": {
+ "string":"X > 0 Y < 0 -Y > X", "x":500, "y":460
+ },
+ "sectorXY3": {
+ "string":"X < 0 Y < 0 Y < X", "x":500, "y":460
+ },
+ "sectorXY4": {
+ "string":"X < 0 Y < 0 Y > X", "x":500, "y":460
+ },
+ "sectorXY5": {
+ "string":"X < 0 Y > 0 -Y > X", "x":500, "y":460
+ },
+ "sectorXY6": {
+ "string":"X < 0 Y > 0 -Y < X", "x":500, "y":460
+ },
+ "sectorXY7": {
+ "string":"X > 0 Y > 0 Y > X", "x":500, "y":460
+ },
+ "sectorXY8": {
+ "string":"X > 0 Y > 0 Y < X", "x":500, "y":460
+ },
+ "sectorXY9": {
+ "string":"X > 0 Y == 0", "x":500, "y":460
+ },
+ "sectorXY10": {
+ "string":"Y > 0 0 == X", "x":500, "y":460
+ },
+ "sectorXY11": {
+ "string":"X < 0 Y == X", "x":500, "y":460
+ },
+ "sectorXYA": {
+ "string":"X > 0 Y > 0 Y < X", "x":500, "y":310
+ },
+ "sectorXYB": {
+ "string":"X < 0 Y > 0 -Y < X", "x":500, "y":360
+ },
+ "sectorXYC": {
+ "string":"X < 0 Y < 0 Y < X", "x":500, "y":410
+ },
+ "spanWedge": {
+ "string":"All spans are contained by a wedge", "x":400, "y":200
+ },
+ "trivialWedge1": {
+ "string":"Wedges that don't overlap can be", "x":400, "y":200
+ },
+ "trivialWedge2": {
+ "string":"easily sorted.", "x":400, "y":240
+ },
+ "xaxis1": {
+ "string":"-X", "x":100, "y":220
+ },
+ "xaxis2": {
+ "string":"+X", "x":300, "y":220
+ },
+ "yaxis1": {
+ "string":"-Y", "x":205, "y":100
+ },
+ "yaxis2": {
+ "string":"+Y", "x":205, "y":300
+ }
+};
+
+var typefaces = {
+ "description": { "style":"normal", "family":"Helvetica,Arial" }
+};
+
+var paints = {
+ "axisStroke": { "style":"stroke", "color":rgb(191,191,191) },
+ "axisTextDesc": { "paint":"textBase", "color":rgb(191,191,191) },
+ "axisTextRight": { "paint":"axisTextDesc", "textAlign":"right" },
+ "axisTextTop": { "paint":"axisTextDesc", "textBaseline":"hanging" },
+ "diagSegment": { "style":"stroke", "color":rgb(127,63,127), "strokeWidth":2 },
+ "gradient1": { "style":"fill", "gradient":"gradients.grad1", "color":alpha(255) },
+ "gradient2": { "paint":"gradient1", "gradient":"gradients.grad2" },
+ "gradient3": { "paint":"gradient1", "gradient":"gradients.grad3" },
+ "gradient4": { "paint":"gradient1", "gradient":"gradients.grad4" },
+ "gradient5": { "paint":"gradient1", "gradient":"gradients.grad5" },
+ "gradient6": { "paint":"gradient1", "gradient":"gradients.grad6" },
+ "gradient7": { "paint":"gradient1", "gradient":"gradients.grad7" },
+ "gradient8": { "paint":"gradient1", "gradient":"gradients.grad8" },
+ "horzSegment": { "paint":"diagSegment", "color":rgb(192,92,31) },
+ "picture": { "color":alpha(255) },
+ "sectorADesc": { "paint":"textBase", "color":rgb(0,0,255) },
+ "sectorBDesc": { "paint":"textBase", "color":rgb(0,127,0) },
+ "sectorCDesc": { "paint":"textBase", "color":rgb(255,0,0) },
+ "sectorXY1": { "paint":"textBase", "color":rgb(192,63,192) },
+ "sectorXY2": { "paint":"textBase", "color":rgb(127,127,0) },
+ "sectorXY3": { "paint":"textBase", "color":rgb(255,0,0) },
+ "sectorXY4": { "paint":"textBase", "color":rgb(127,0,127) },
+ "sectorXY5": { "paint":"textBase", "color":rgb(0,127,127) },
+ "sectorXY6": { "paint":"textBase", "color":rgb(0,127,0) },
+ "sectorXY7": { "paint":"textBase", "color":rgb(63,192,63) },
+ "sectorXY8": { "paint":"textBase", "color":rgb(0,0,255) },
+ "sectorXY9": { "paint":"textBase", "color":rgb(192,92,31) },
+ "sectorXY10": { "paint":"textBase", "color":rgb(31,92,192) },
+ "sectorXY11": { "paint":"textBase", "color":rgb(127,63,127) },
+
+ "stroke": { "style":"stroke", "color":rgb(0,0,0) },
+ "textBase": { "style":"fill", "color":rgb(0,0,0), "typeface":"description",
+ "textSize":"1.3rem" },
+ "vertSegment": { "paint":"diagSegment", "color":rgb(31,92,192) },
+};
+
+var pictures = {
+ "curve1DDestText": [
+ { "draw":"text.curve1d1", "paint":"paints.textBase" },
+ { "draw":"text.curve1d2", "paint":"paints.textBase" }
+ ],
+ "curveMultipleText": [
+ { "draw":"text.curveMultiple1", "paint":"paints.textBase" },
+ { "draw":"text.curveMultiple2", "paint":"paints.textBase" }
+ ],
+ "line1DDestText": [
+ { "draw":"text.line1DDest1", "paint":"paints.textBase" },
+ { "draw":"text.line1DDest2", "paint":"paints.textBase" }
+ ],
+ "sectorXYA": [
+ { "draw":"text.sectorXYA", "paint":"paints.sectorADesc" },
+ { "draw":"paths.wedgeXY8", "paint":"paints.gradient1" }
+ ],
+ "sectorXYB": [
+ { "draw":"text.sectorXYB", "paint":"paints.sectorBDesc" },
+ { "draw":"paths.wedgeXY6", "paint":"paints.gradient2" }
+ ],
+ "sectorXYC": [
+ { "draw":"text.sectorXYC", "paint":"paints.sectorCDesc" },
+ { "draw":"paths.wedgeXY3", "paint":"paints.gradient3" }
+ ],
+ "sectorText": [
+ { "draw":"text.sector1", "paint":"paints.textBase" },
+ { "draw":"text.sector2", "paint":"paints.textBase" }
+ ],
+ "trivialWedgeSpans": [
+ { "draw":"paths.span4", "paint":"paints.stroke" },
+ { "draw":"paths.wedge4", "paint":"paints.gradient4" },
+ { "draw":"paths.span5", "paint":"paints.stroke" },
+ { "draw":"paths.wedge5", "paint":"paints.gradient5" },
+ { "draw":"paths.span6", "paint":"paints.stroke" },
+ { "draw":"paths.wedge6", "paint":"paints.gradient6" }
+ ],
+ "trivialWedgeText": [
+ { "draw":"text.trivialWedge1", "paint":"paints.textBase" },
+ { "draw":"text.trivialWedge2", "paint":"paints.textBase" }
+ ],
+ "xaxis": [
+ { "draw":"paths.xaxis", "paint":"paints.axisStroke" },
+ { "draw":"text.xaxis1", "paint":"paints.axisTextDesc" },
+ { "draw":"text.xaxis2", "paint":"paints.axisTextRight" }
+ ],
+ "yaxis": [
+ { "draw":"paths.yaxis", "paint":"paints.axisStroke" },
+ { "draw":"text.yaxis1", "paint":"paints.axisTextTop" },
+ { "draw":"text.yaxis2", "paint":"paints.axisTextDesc" }
+ ],
+ "axes": [
+ { "draw":"pictures.xaxis", "paint":"paints.picture" },
+ { "draw":"pictures.yaxis", "paint":"paints.picture" }
+ ]
+};
+
+var gradientLookup = [
+ 0, 4, 5, 3, 6, 7, 2, 8, 1
+];
+
+var keyframes = {
+ "_default": [
+ { "actions": [
+ { "range":[0,255], "paint":"paints.picture", "target":"paint.color",
+ "params":"target", "formula":"alpha(value, params)" }
+ ]}
+ ],
+ "keyframe1": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"text.spanWedge", "paint":"paints.textBase" }
+ ]},
+ { "time":1000, "duration":1000, "actions": [
+ { "ref":"span1", "draw":"paths.span1", "paint":"paints.stroke" }
+ ]},
+ { "time":1500, "duration":1500, "actions": [
+ { "ref":"wedge1", "draw":"paths.wedge1", "paint":"paints.gradient1" }
+ ]},
+ { "time":3500, "duration": 500, "actions": [
+ { "ref":"span1", "range":[255,0] },
+ { "ref":"wedge1", "range":[255,0] }
+ ]},
+ { "time":4000, "duration":1000, "actions": [
+ { "ref":"span2", "draw":"paths.span2", "paint":"paints.stroke" }
+ ]},
+ { "time":4500, "duration":1500, "actions": [
+ { "ref":"wedge2", "draw":"paths.wedge2", "paint":"paints.gradient2" }
+ ]},
+ { "time":6500, "duration": 500, "actions": [
+ { "ref":"span2", "range":[255,0] },
+ { "ref":"wedge2", "range":[255,0] }
+ ]},
+ { "time":7000, "duration":1000, "actions": [
+ { "draw":"paths.span3", "paint":"paints.stroke" }
+ ]},
+ { "time":7500, "duration":1500, "actions": [
+ { "draw":"paths.wedge3", "paint":"paints.gradient3" }
+ ]}
+ ],
+ "keyframe2": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"pictures.trivialWedgeText", "paint":"paints.picture" }
+ ]},
+ { "time":2000, "duration":1500, "actions": [
+ { "draw":"pictures.trivialWedgeSpans", "paint":"paints.picture" }
+ ]}
+ ],
+ "keyframe3": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"pictures.sectorText" },
+ { "draw":"pictures.xaxis" }
+ ]},
+ { "time": 500, "duration":1000, "actions": [
+ { "draw":"pictures.yaxis" }
+ ]},
+ { "time":2000, "duration":1500, "actions": [
+ { "draw":"pictures.sectorXYA" }
+ ]},
+ { "time":3000, "duration":1500, "actions": [
+ { "draw":"pictures.sectorXYB" }
+ ]},
+ { "time":4000, "duration":1500, "actions": [
+ { "draw":"pictures.sectorXYC" }
+ ]}
+ ],
+ "keyframe4": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"text.lineSingle", "paint":"paints.textBase" },
+ { "draw":"pictures.axes" }
+ ]},
+ { "time":1000, "duration":1000, "actions": [
+ { "ref":"line", "draw":"paths.lineSegment", "paint":"paints.stroke" }
+ ]},
+ { "time":1850, "duration":1000, "actions": [
+ { "ref":"sectorXY1", "draw":"text.sectorXY1", "paint":"paints.sectorXY1" },
+ { "ref":"sectorXY1", "target":"draw.y", "formula":260 },
+ { "ref":"wedgeXY1", "draw":"paths.wedgeXY1", "paint":"paints.gradient4" }
+ ]},
+ { "time":3000, "duration":4000, "actions": [
+ { "ref":"line", "target":"draw[0].line[2]",
+ "range":[-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], "params":"circle",
+ "formula":"params.center.x + params.radius * Math.cos(value)"
+ },
+ { "ref":"line", "target":"draw[0].line[3]",
+ "range":[-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], "params":"circle",
+ "formula":"params.center.y + params.radius * Math.sin(value)"
+ }
+ ]},
+ { "for":["i=2", "i<=8", "++i"], "time":"2250 + 500 * i", "duration":100, "actions": [
+ { "ref":"'sectorXY' + i", "draw":"'text.sectorXY' + i",
+ "paint":"'paints.sectorXY' + i" },
+ { "ref":"'sectorXY' + i", "target":"draw.y", "formula":260 },
+ { "ref":"'wedgeXY' + i", "draw":"'paths.wedgeXY' + i",
+ "paint":"'paints.gradient' + gradientLookup[i]" },
+ { "ref":"'sectorXY' + (i - 1)", "range":[255,0] },
+ { "ref":"'wedgeXY' + (i - 1)", "range":[255,0] }
+ ]},
+ { "time":2250 + 500 * 9, "duration":100, "actions": [
+ { "ref":"sectorXY1" },
+ { "ref":"wedgeXY1" },
+ { "ref":"sectorXY8", "range":[255,0] },
+ { "ref":"wedgeXY8", "range":[255,0] }
+ ]}
+ ],
+ "keyframe5": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"pictures.curveMultipleText" },
+ { "draw":"pictures.axes" }
+ ]},
+ { "time":1000, "duration":1000, "actions": [
+ { "ref":"curve", "draw":"paths.curveSegment1", "paint":"paints.stroke" }
+ ]},
+ { "time":2000, "duration":1000, "actions": [
+ { "draw":"text.sectorXY1", "paint":"paints.sectorXY1",
+ "target":"draw.y", "formula":260 + 1 * 25},
+ { "draw":"paths.wedgeXY1", "paint":"paints.gradient4" }
+ ]},
+ { "time":3000, "duration":1000, "actions": [
+ { "ref":"curve", "range":[0,1], "target":"draw",
+ "params":["paths.curveSegment1","paths.curveSegment2"],
+ "formula":"interp_paths(value, params)"
+ }
+ ]},
+ { "time":4000, "duration":1000, "actions": [
+ { "draw":"text.sectorXY2", "paint":"paints.sectorXY2",
+ "target":"draw.y", "formula":260 + 2 * 25},
+ { "draw":"paths.wedgeXY2", "paint":"paints.gradient5" }
+ ]},
+ { "time":5000, "duration":1000, "actions": [
+ { "ref":"curve", "range":[0,1], "target":"draw",
+ "params":["paths.curveSegment2","paths.curveSegment3"],
+ "formula":"interp_paths(value, params)"
+ }
+ ]},
+ { "for":["i=3", "i<=6", "++i"], "time":"6000", "actions": [
+ { "ref":"'text' + i", "draw":"'text.sectorXY' + i", "paint":"'paints.sectorXY' + i",
+ "target":"draw.y", "formula":"260 + i * 25" },
+ ]},
+ { "for":["i=3", "i<=6", "++i"], "time":"6000", "duration":1000, "actions": [
+ { "ref":"'text' + i" },
+ ]},
+ { "time":6000, "duration":1000, "actions": [
+ { "draw":"paths.wedgeXY3", "paint":"paints.gradient3" },
+ { "draw":"paths.wedgeXY4", "paint":"paints.gradient6" },
+ { "draw":"paths.wedgeXY5", "paint":"paints.gradient7" },
+ { "draw":"paths.wedgeXY6", "paint":"paints.gradient2" },
+ ]}
+ ],
+ "keyframe6": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"pictures.line1DDestText" },
+ { "draw":"pictures.axes" }
+ ]},
+ { "time":2000, "duration":1000, "actions": [
+ { "ref":"xy9", "draw":"text.sectorXY9", "paint":"paints.sectorXY9" },
+ { "ref":"xy9", "target":"draw.y", "formula":260 + 25},
+ { "draw":"paths.horzSegment", "paint":"paints.horzSegment" }
+ ]},
+ { "time":3000, "duration":1000, "actions": [
+ { "ref":"xy10", "draw":"text.sectorXY10", "paint":"paints.sectorXY10" },
+ { "ref":"xy10", "target":"draw.y", "formula":260 + 50 },
+ { "draw":"paths.vertSegment", "paint":"paints.vertSegment" }
+ ]},
+ { "time":4000, "duration":1000, "actions": [
+ { "ref":"xy11", "draw":"text.sectorXY11", "paint":"paints.sectorXY11" },
+ { "ref":"xy11", "target":"draw.y", "formula":260 + 75 },
+ { "draw":"paths.diagSegment", "paint":"paints.diagSegment" }
+ ]}
+ ],
+ "keyframe7": [
+ { "time": 0, "duration":1000, "canvas":"clear", "actions": [
+ { "draw":"pictures.curve1DDestText" },
+ { "draw":"pictures.axes" }
+ ]},
+ { "time":2000, "duration":1000, "actions": [
+ { "ref":"cubic", "draw":"paths.cubicSegment1", "paint":"paints.stroke" },
+ { "ref":"cubic", "range":[0,1], "target":"draw",
+ "params":"paths.cubicSegment2", "formula":"path_partial(value, params)" },
+ { "ref":"xy9", "draw":"text.sectorXY9", "paint":"paints.sectorXY9" },
+ { "ref":"xy9", "target":"draw.y", "formula":260 + 25},
+ { "draw":"paths.horzSegment", "paint":"paints.horzSegment" }
+ ]},
+ { "time":3000, "duration":1000, "actions": [
+ { "ref":"xy1", "draw":"text.sectorXY1", "paint":"paints.sectorXY1" },
+ { "ref":"xy1", "target":"draw.y", "formula":260 + 60},
+ { "draw":"paths.wedgeXY1", "paint":"paints.gradient4" }
+ ]},
+ ]
+};
diff --git a/experimental/docs/interpolatorFunctions.js b/experimental/docs/interpolatorFunctions.js
new file mode 100644
index 0000000000..d51969ff11
--- /dev/null
+++ b/experimental/docs/interpolatorFunctions.js
@@ -0,0 +1,84 @@
+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;
+}
+
+// FIXME : only works for path with single cubic
+function path_partial(value, path) {
+ assert(isArray(path));
+ var out = [];
+ for (var cIndex = 0; cIndex < path.length; ++cIndex) {
+ out[cIndex] = {};
+ var curveKey = Object.keys(path[cIndex])[0];
+ var curve = path[cIndex][curveKey];
+ var outArray;
+ switch (curveKey) {
+ case "cubic":
+ var x1 = curve[0], y1 = curve[1], x2 = curve[2], y2 = curve[3];
+ var x3 = curve[4], y3 = curve[5], x4 = curve[6], y4 = curve[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;
+ outArray = [
+ ax, ay, bx, by, cx, cy, dx, dy
+ ];
+ break;
+ default:
+ assert(0); // unimplemented
+ }
+ out[cIndex][curveKey] = outArray;
+ }
+ return out;
+}
+
+function interp_paths(value, paths) {
+ assert(isArray(paths));
+ assert(paths.length == 2);
+ var curves0 = paths[0];
+ assert(isArray(curves0));
+ var curves1 = paths[1];
+ assert(isArray(curves1));
+ assert(curves0.length == curves1.length);
+ var out = [];
+ for (var cIndex = 0; cIndex < curves0.length; ++cIndex) {
+ out[cIndex] = {};
+ var curve0Key = Object.keys(curves0[cIndex])[0];
+ var curve1Key = Object.keys(curves1[cIndex])[0];
+ assert(curve0Key == curve1Key);
+ var curve0 = curves0[cIndex][curve0Key];
+ var curve1 = curves1[cIndex][curve1Key];
+ assert(isArray(curve0));
+ assert(isArray(curve1));
+ assert(curve0.length == curve1.length);
+ var outArray = [];
+ for (var i = 0; i < curve1.length; ++i) {
+ outArray[i] = curve0[i] + (curve1[i] - curve0[i]) * value;
+ }
+ out[cIndex][curve0Key] = outArray;
+ }
+ return out;
+}
diff --git a/experimental/docs/jsonbaseddoc.htm b/experimental/docs/jsonbaseddoc.htm
new file mode 100644
index 0000000000..bceabaebc6
--- /dev/null
+++ b/experimental/docs/jsonbaseddoc.htm
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+
+<script src="utilities.js"></script>
+
+<script src="animationCommon.js"></script>
+<script src="backend.js"></script>
+<script src="canvasBackend.js"></script>
+<script src="exampleSlides.js"></script>
+<script src="interpolatorFunctions.js"></script>
+<script src="svgBackend.js"></script>
+
+<script>
+
+var frame = 1;
+
+function keypress() {
+ init('all', 'keyframe' + frame);
+ if (++frame > 7) {
+ frame = 1;
+ }
+}
+
+function onload() {
+ init('all', 'keyframe6');
+}
+
+</script>
+
+</head>
+
+<body onLoad="onload()" onKeypress="keypress()">
+
+<canvas id="canvas" width="770" height="500" ></canvas>
+
+<svg id="svg" width="770" height="500"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" />
+
+</body>
+</html>
diff --git a/experimental/docs/svgBackend.js b/experimental/docs/svgBackend.js
new file mode 100644
index 0000000000..b5907f0340
--- /dev/null
+++ b/experimental/docs/svgBackend.js
@@ -0,0 +1,246 @@
+var svgCache;
+var svgDefs;
+var svgGradients;
+var svgNS = "http://www.w3.org/2000/svg";
+var svgRoot;
+
+function displaySvg(displayList) {
+ for (var index = 0; index < displayList.length; ++index) {
+ drawToSvg(displayList[index]);
+ }
+}
+
+function drawToSvg(display) {
+ assert('string' == typeof(display.ref));
+ var cache;
+ if (display.ref in svgCache) {
+ cache = svgCache[display.ref];
+ if (display.drawDirty) {
+ switch (cache.spec) {
+ case "paths":
+ svgSetPathData(cache.element, display.draw);
+ break;
+ case "pictures":
+ svgSetPictureData(cache.element, display.draw);
+ break;
+ case "text":
+ svgCreateText(cache.element, display.draw);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ } else {
+ cache = {};
+ cache.action = display;
+ cache.spec = display.drawSpec;
+ var dot = cache.spec.indexOf(".");
+ if (dot > 0) {
+ cache.spec = cache.spec.substring(0, dot);
+ }
+ switch (cache.spec) {
+ case "paths":
+ cache.element = svgCreatePath(display.ref, display.draw);
+ break;
+ case "pictures":
+ cache.element = svgCreatePicture(display.ref, display.draw);
+ break;
+ case "text":
+ cache.element = svgCreateText(display.ref, display.draw);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ display.drawDirty = false;
+ if (display.paintDirty) {
+ svgSetPaintData(cache.element, display.paint);
+ var opacity = svg_opacity(display.paint.color);
+ cache.element.setAttribute("fill-opacity", opacity);
+ cache.element.setAttribute("stroke-opacity", opacity);
+ display.paintDirty = false;
+ }
+ assert('object' == typeof(cache));
+ if (!(display.ref in svgCache)) {
+ svgRoot.appendChild(cache.element);
+ svgCache[display.ref] = cache;
+ }
+}
+
+function setupSvg() {
+ svgCache = { "paths":{}, "pictures":{}, "text":{} };
+ svgDefs = document.createElementNS(svgNS, "defs");
+ svgGradients = {};
+ svgRoot = document.getElementById("svg");
+ while (svgRoot.lastChild) {
+ svgRoot.removeChild(svgRoot.lastChild);
+ }
+ svgRoot.appendChild(svgDefs);
+}
+
+function svg_rbg(color) {
+ return "rgb(" + ((color >> 16) & 0xFF)
+ + "," + ((color >> 8) & 0xFF)
+ + "," + ((color >> 0) & 0xFF) + ")";
+}
+
+function svg_opacity(color) {
+ return ((color >> 24) & 0xFF) / 255.0;
+}
+
+function svgCreatePath(key, path) {
+ var svgPath = document.createElementNS(svgNS, "path");
+ svgPath.setAttribute("id", key);
+ svgSetPathData(svgPath, path);
+ return svgPath;
+}
+
+function svgCreatePicture(key, picture) {
+ var svgPicture = document.createElementNS(svgNS, "g");
+ svgPicture.setAttribute("id", key);
+ svgSetPictureData(svgPicture, picture);
+ return svgPicture;
+}
+
+function svgCreateRadialGradient(key) {
+ var g = gradients[key];
+ var e = document.createElementNS(svgNS, "radialGradient");
+ e.setAttribute("id", key);
+ e.setAttribute("cx", g.cx);
+ e.setAttribute("cy", g.cy);
+ e.setAttribute("r", g.r);
+ e.setAttribute("gradientUnits", "userSpaceOnUse");
+ var stopLen = g.stops.length;
+ for (var index = 0; index < stopLen; ++index) {
+ var stop = g.stops[index];
+ var color = svg_rbg(stop.color);
+ var s = document.createElementNS(svgNS, 'stop');
+ s.setAttribute("offset", stop.offset);
+ var style = "stop-color:" + svg_rbg(stop.color) + "; stop-opacity:"
+ + svg_opacity(stop.color);
+ s.setAttribute("style", style);
+ e.appendChild(s);
+ }
+ svgGradients[key] = e;
+ svgDefs.appendChild(e);
+}
+
+function svgCreateText(key, text) {
+ var svgText = document.createElementNS(svgNS, "text");
+ svgText.setAttribute("id", key);
+ var textNode = document.createTextNode(text.string);
+ svgText.appendChild(textNode);
+ svgSetTextData(svgText, text);
+ return svgText;
+}
+
+function svgSetPathData(svgPath, path) {
+ var dString = "";
+ for (var cIndex = 0; cIndex < path.length; ++cIndex) {
+ var curveKey = Object.keys(path[cIndex])[0];
+ var v = path[cIndex][curveKey];
+ switch (curveKey) {
+ case 'arcTo':
+ var clockwise = 1; // to do; work in general case
+ dString += " A" + v[4] + "," + v[4] + " 0 0," + clockwise + " "
+ + v[2] + "," + v[3];
+ break;
+ case 'close':
+ dString += " z";
+ break;
+ case 'cubic':
+ dString += " M" + v[0] + "," + v[1];
+ dString += " C" + v[2] + "," + v[3]
+ + " " + v[4] + "," + v[5]
+ + " " + v[6] + "," + v[7];
+ break;
+ case 'line':
+ dString += " M" + v[0] + "," + v[1];
+ dString += " L" + v[2] + "," + v[3];
+ break;
+ case 'quad':
+ dString += " M" + v[0] + "," + v[1];
+ dString += " Q" + v[2] + "," + v[3]
+ + " " + v[4] + "," + v[5];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ svgPath.setAttribute("d", dString);
+}
+
+function svgSetPaintData(svgElement, paint) {
+ var color;
+ var inPicture = 'string' == typeof(paint);
+ if (inPicture) {
+ paint = (new Function("return " + paint))();
+ assert('object' == typeof(paint) && !isArray(paint));
+ }
+ if ('gradient' in paint) {
+ var gradient = paint.gradient.split('.');
+ var gradName = gradient[1];
+ if (!svgGradients[gradName]) {
+ svgCreateRadialGradient(gradName);
+ }
+ color = "url(#" + gradName + ")";
+ } else {
+ color = svg_rbg(paint.color);
+ }
+ svgElement.setAttribute("fill", 'fill' == paint.style ? color : "none");
+ if ('stroke' == paint.style) {
+ svgElement.setAttribute("stroke", color);
+ }
+ if ('strokeWidth' in paint) {
+ svgElement.setAttribute("stroke-width", paint.strokeWidth);
+ }
+ if ('typeface' in paint) {
+ var typeface = typefaces[paint.typeface];
+ var font = typeface.style;
+ if ('textSize' in paint) {
+ svgElement.setAttribute("font-size", paint.textSize);
+ }
+ if ('family' in typeface) {
+ svgElement.setAttribute("font-family", typeface.family);
+ }
+ if ('textAlign' in paint) {
+ svgElement.setAttribute("text-anchor", paint.textAlign == "right" ? "end" : assert(0));
+ }
+ if ('textBaseline' in paint) {
+ svgElement.setAttribute("alignment-baseline", paint.textBaseline);
+ }
+ }
+}
+
+function svgSetPictureData(svgPicture, picture) {
+ while (svgPicture.lastChild) {
+ svgPicture.removeChild(svgPicture.lastChild);
+ }
+ for (var index = 0; index < picture.length; ++index) {
+ var entry = picture[index];
+ var drawObj = (new Function("return " + entry.draw))();
+ var drawSpec = entry.draw.split('.');
+ var svgElement;
+ switch (drawSpec[0]) {
+ case 'paths':
+ svgElement = svgCreatePath(drawSpec[1], drawObj);
+ break;
+ case 'pictures':
+ svgElement = svgCreatePicture(drawSpec[1], drawObj);
+ break;
+ case 'text':
+ svgElement = svgCreateText(drawSpec[1], drawObj);
+ break;
+ default:
+ assert(0);
+ }
+ var paintObj = (new Function("return " + entry.paint))();
+ svgSetPaintData(svgElement, paintObj);
+ svgPicture.appendChild(svgElement);
+ }
+}
+
+function svgSetTextData(svgElement, text) {
+ svgElement.setAttribute('x', text.x);
+ svgElement.setAttribute('y', text.y);
+}
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
diff --git a/experimental/docs/utilities.js b/experimental/docs/utilities.js
new file mode 100644
index 0000000000..e3261c604e
--- /dev/null
+++ b/experimental/docs/utilities.js
@@ -0,0 +1,24 @@
+function alpha(value, color) {
+ return value << 24 | (color & 0x00FFFFFF);
+}
+
+function argb(a, r, g, b) {
+ return a << 24 | r << 16 | g << 8 | b;
+}
+
+function assert(condition) {
+ if (!condition) debugger;
+}
+
+function isAlpha(code) {
+ return (code > 64 && code < 91) // upper alpha (A-Z)
+ || (code > 96 && code < 123); // lower alpha (a-z)
+}
+
+function isArray(a) {
+ return a.constructor === Array;
+}
+
+function rgb(r, g, b) {
+ return 0xFF << 24 | r << 16 | g << 8 | b;
+}