From ed9065551436203a9908b762b3035cf4a092fd03 Mon Sep 17 00:00:00 2001 From: jonobr1 Date: Tue, 25 Jan 2011 14:21:15 -0800 Subject: [PATCH] added georges svg library --- demo/swell.js | 512 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 demo/swell.js diff --git a/demo/swell.js b/demo/swell.js new file mode 100644 index 0000000..62933dd --- /dev/null +++ b/demo/swell.js @@ -0,0 +1,512 @@ +/** + * @author george michael brower / http://georgemichaelbrower.com/ + */ + +function loadSVG(loc, success, fail) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange=function() { + if (xmlhttp.readyState==4) { + if (xmlhttp.status == 200) { + if (xmlhttp.responseXML == null) { + if (fail != undefined) fail.call(this, loc); + } else { + var node = xmlhttp.responseXML.getElementsByTagName("svg").item(0); + var svg = new SVG(node); + svg.filename = loc; + success.call(this, svg, loc); + } + } else { + if (fail != undefined) fail.call(this, loc); + } + + } + } + xmlhttp.open("GET", loc, true); + xmlhttp.send(null); +} + +function createSVG(svgText) { + var node = document.createElement("svg"); + node.innerHTML = svgText; + return new SVG(node); +} + +var SVG = function(node) { + this.filename = ""; + + this.children = []; + + // TODO interpret things other than pixels. + + var w = node.getAttribute("width"); + var h = node.getAttribute("height"); + + this.width = w == null ? 0 : parseFloat(w.replace("px", "")); + this.height = h == null ? 0 : parseFloat(h.replace("px", "")); + + for (var i = 0; i < node.childNodes.length; i++) { + + if (!node.childNodes.item(i).getAttribute) continue; + + var toAdd; + if (node.childNodes.item(i).nodeName == "g") { + toAdd = new SVG(node.childNodes.item(i)); + } else { + toAdd = new Path(node.childNodes.item(i)); + } + + this.children.push(toAdd); + + } + + // Draws every path in this SVG to the specified context. + this.draw = function(context) { + for (var i = 0; i < this.children.length; i++) { + this.children[i].draw(context); + } + }; + + +} + +var Path = function(element) { + + this.element = element; + this.commands = commands(element); + this.lineWidth = parseLineWidth(element); + this.strokeStyle = parseStrokeStyle(element); + this.fillStyle = parseFillStyle(element); + +// for at + this.totalLength = 0; + this.lengths = []; + this.tlengths = [] + + + var turtle = function() { + this.x; + this.y; + this.x1; + this.y1; + this.x2; + this.y2; + this.reset = function() { + this.x = this.y = this.x1 = this.y1 = this.x2 = this.y2 = 0; + } + this.reset(); + } + + // Draws this entire path to the specified context. + this.draw = function(context) { + this.style(context); + context.beginPath(); + this.shape(context); + this.end(context); + + }; + + // Calls canvas shape methods such as moveTo(), lineTo(), bezierCurveTo() based on the commands in this path. + this.shape = function(context) { + for (var i = 0; i < this.commands.length; i++) { + this.commands[i].shape(turtle, context); + } + }; + + this.lerp = function(a,b,c,d,t) { + var t1 = 1.0 - t; + return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t; + + }; + + this.at = function(t, c) { + var rx, ry; + if (this.lengths.length == 0) { + this.calcLengths(c); + } + + var tt = this.tlengths[0]; + var i = 0; + + while (t > tt) { + i++; + tt += this.tlengths[i]; + } + + pt = tt - this.tlengths[i]; + + + + var it = this.map(t, pt, tt, 0, 1); + + + for (var j = 0; j <= i; j++) { + this.commands[j].shape(turtle, c); + } + var px = turtle.x; + var py = turtle.y; + + this.commands[i+1].shape(turtle, c); + + + rx = this.lerp(px, turtle.x1, turtle.x2, turtle.x, it); + ry = this.lerp(py, turtle.y1, turtle.y2, turtle.y, it); + + return {x:rx, y:ry}; + }; + + this.map = function(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); + } + + + this.calcLengths = function(c) { + var rx,ry; + var prx, pry; + + // go through and get the length of the entire path + // as well as the lengths of each indiv. path + + var curLength = 0; + + var lengthAccuracy = 0.001; + + this.commands[0].shape(turtle, c); + + var px = prx = turtle.x; + var py = pry = turtle.y; + + for (var i = 1; i < this.commands.length; i++) { + + curLength = 0; + + px = turtle.x; + py = turtle.y; + this.commands[i].shape(turtle, c); + + for (var tt = 0; tt <=1; tt+= lengthAccuracy) { + + rx = this.lerp(px, turtle.x1, turtle.x2, turtle.x, tt); + ry = this.lerp(py, turtle.y1, turtle.y2, turtle.y, tt); + + + curLength += this.dist(rx, ry, prx, pry); + + + prx = rx; + pry = ry; + + } + + this.lengths.push(curLength); + this.totalLength += curLength; + + + } + + for (var j = 0; j < this.lengths.length; j++) { + this.tlengths.push(this.lengths[j]/this.totalLength); + } + } + + this.dist = function (x, y, xx, yy) { + return Math.sqrt((x - xx) * (x - xx) + (y - yy) * (y - yy)); + }; + + // Sets the drawing style of the canvas context based on the styles in this Path. + this.style = function(context) { + + if (this.lineWidth != null) { + context.lineWidth = this.lineWidth; + } + + if (this.strokeStyle != null) { + context.strokeStyle = this.strokeStyle; + if (this.lineWidth == undefined) { + context.lineWidth = 1; + } + } + + if (this.fillStyle != null) { + context.fillStyle = this.fillStyle; + } + + }; + + // Calls context.fill() and/or context.stroke() depending on the styles in this Path. + this.end = function(context) { + if (this.fillStyle != null) context.fill(); + if (this.strokeStyle != null) context.stroke(); + } + +} + +var parseLineWidth = function(element) { + var a = element.attributes.getNamedItem("stroke-width"); + return a == null ? null : parseFloat(a.nodeValue); +} + +var parseStrokeStyle = function(element) { + var a = element.attributes.getNamedItem("stroke"); + return a == null ? null : a.nodeValue; +} + +var parseFillStyle = function(element) { + var a = element.attributes.getNamedItem("fill"); + if (a == null) { + var s = element.attributes.getNamedItem("stroke"); + if (s != null) { + return null; + } else { + return "#000000"; + } + } else { + if (a.nodeValue == "none") return null; + return a.nodeValue; + } +} + +var Command = function(type, data) { + this.type = type; + this.data = data; + this.debug = false; + + // Calls context shape methods such as moveTo(), lineTo(), bezierCurveTo(), etc. + this.shape = function(turtle, c) { + + var px = turtle.x; + var py = turtle.y; + + if (this.type == "M") { + + turtle.x = this.data[0]; + turtle.y = this.data[1]; + if (c) c.moveTo(turtle.x, turtle.y); + + + } else if (this.type == "C") { + + turtle.x = this.data[4]; + turtle.y = this.data[5]; + if (c) c.bezierCurveTo(turtle.x1 = this.data[0], + turtle.y1 = this.data[1], + turtle.x2 = this.data[2], + turtle.y2 = this.data[3], + turtle.x, + turtle.y); + + } else if (this.type == "c") { + + if (c) c.bezierCurveTo(turtle.x1 = turtle.x+this.data[0], + turtle.y1 = turtle.y+this.data[1], + turtle.x2 = turtle.x+this.data[2], + turtle.y2 = turtle.y+this.data[3], + turtle.x += this.data[4], + turtle.y += this.data[5]); + + } else if (this.type == "S") { + + turtle.x = this.data[2]; + turtle.y = this.data[3]; + var dx = turtle.x - turtle.x2; + var dy = turtle.y - turtle.y2; + if (c) c.bezierCurveTo(turtle.x1 = turtle.x+dx, + turtle.y1 = turtle.y+dy, + turtle.x2 = this.data[0], + turtle.y2 = this.data[1], + turtle.x, + turtle.y); + + } else if (this.type == "s") { + + var dx = turtle.x - turtle.x2; + var dy = turtle.y - turtle.y2; + if (c) c.bezierCurveTo(turtle.x1 = turtle.x+dx, + turtle.y1 = turtle.y+dy, + turtle.x2 = turtle.x+this.data[0], + turtle.y2 = turtle.y+this.data[1], + turtle.x += this.data[2], + turtle.y += this.data[3]); + + } else if (this.type == "L") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x = this.data[0], + turtle.y = this.data[1]); + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "l") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x+=this.data[0], turtle.y+=this.data[1]); + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "H") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x = this.data[0], turtle.y) + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "h") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x += this.data[0], turtle.y) + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "V") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x, turtle.y = this.data[0]); + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "v") { + + turtle.x1 = turtle.x; + turtle.y1 = turtle.y; + if (c) c.lineTo(turtle.x, turtle.y += this.data[0]); + turtle.x2 = turtle.x; + turtle.y2 = turtle.y; + + } else if (this.type == "z") { + + c.closePath(); + + } else { + + alert("unrecognized command " + this.type); + + } + + if (c){ + c.strokeStyle = "#000000"; + c.lineWidth = 1; + if (this.debug) { + c.strokeRect(turtle.x-1.5, turtle.y-1.5, 3, 3); + c.beginPath(); + c.moveTo(turtle.px, turtle.py); + c.lineTo(turtle.x1, turtle.y1); + c.closePath(); + c.stroke(); + } + } + + } + +} + +// Utility functions + +var commands = function(element) { + + if (element.nodeName.toLowerCase() == "path") { + return commandsFromD(element.getAttribute("d")); + } + + if (element.nodeName.toLowerCase() == "polygon") { + return commandsFromPoints(element.getAttribute("points")); + } + + if (element.nodeName.toLowerCase() == "line") { + return commandsFromLine(element); + } + + if (element.nodeName.toLowerCase() == "rect") { + return commandsFromRect(element); + } + + return []; + +} + +// Returns an array of commands as interpreted by the "d" attribute of a path. +var commandsFromD = function(d) { + + var toReturn = []; + var commands = d.match(/[a-zA-Z][0-9\.\-\,]+/g); + + for (var i = 0; i < commands.length; i++) { + + var type = commands[i].charAt(0); + + // Dirty time. + var commandData = commands[i].substr(1); + commandData = commandData.replace(/\-/g, ",-") + + if (commandData.charAt(0) == ",") { + commandData = commandData.substr(1); + } + commandData = commandData.split(","); + for (var j = 0; j < commandData.length; j++) { + commandData[j] = parseFloat(commandData[j]); + } + + toReturn.push(new Command(type, commandData)); + + } + + return toReturn; +} + +var commandsFromLine = function(element) { + var toReturn = []; + var x1 = parseFloat(element.getAttribute("x1")); + var x2 = parseFloat(element.getAttribute("x2")); + var y1 = parseFloat(element.getAttribute("y1")); + var y2 = parseFloat(element.getAttribute("y2")); + toReturn.push(new Command("M", [x1,y1])); + toReturn.push(new Command("L", [x2,y2])); + return toReturn; +} + +// Returns an array of commands as interpreted by the "points" attribute of a polygon. +var commandsFromPoints = function(pointAttribute) { + //pointAttribute = pointAttribute.replace(/\,\-/g, "-"); + + + var shouldBeComma = true; + if (pointAttribute.indexOf(",") == -1) { + for (var i = 0; i < pointAttribute.length; i++) { + var c = pointAttribute.charAt(i); + if (c == " ") { + if (shouldBeComma) { + pointAttribute = pointAttribute.setCharAt(i, ","); + } + shouldBeComma = !shouldBeComma; + } + } + } + + pointAttribute = "M"+pointAttribute; + pointAttribute = pointAttribute.replace(/ /g, "L") + "z"; + var toReturn = commandsFromD(pointAttribute); + return toReturn; +} + +String.prototype.setCharAt = function(index,chr) { + if(index > this.length-1) return str; + return this.substr(0,index) + chr + this.substr(index+1); +} + +var commandsFromRect = function(element) { + + var toReturn = []; + var x = parseFloat(element.getAttribute("x")); + var y = parseFloat(element.getAttribute("y")); + var w = parseFloat(element.getAttribute("width")); + var h = parseFloat(element.getAttribute("height")); + toReturn.push(new Command("M", [x,y])); + toReturn.push(new Command("h", [w])); + toReturn.push(new Command("v", [h])); + toReturn.push(new Command("h", [-w])); + toReturn.push(new Command("v", [-h])); + return toReturn; +} \ No newline at end of file