mirror of
https://github.com/dataarts/dat.gui.git
synced 2024-12-12 04:08:27 +00:00
512 lines
12 KiB
JavaScript
512 lines
12 KiB
JavaScript
|
/**
|
||
|
* @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;
|
||
|
}
|