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