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
247 lines
7.9 KiB
4 months ago
|
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);
|
||
|
}
|