You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
7.9 KiB

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