runs some sample animations side by side in canvas and svg

TBR=fmalita@chromium.org

Review URL: https://codereview.chromium.org/1342523002
This commit is contained in:
caryclark 2015-11-16 13:36:08 -08:00 committed by Commit bot
parent 73d882e13f
commit 3257c12220
9 changed files with 3196 additions and 0 deletions

View File

@ -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]);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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" }
]},
]
};

View File

@ -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;
}

View File

@ -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>

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}