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