Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Forked from mbostock/.block
Last active July 26, 2016 17:15
Show Gist options
  • Save timelyportfolio/5149102 to your computer and use it in GitHub Desktop.
Save timelyportfolio/5149102 to your computer and use it in GitHub Desktop.
zui53 and d3 line chart

#zui53 Zoom Adaptation of Mike Bostock's Line Chart I have not seen anyone pair zui53 with so I thought I would experiment by adding the zoom and drag functionality to this line chart. I also wanted to test my skills by using a long form csv dataset with two series. zui53 zoom and drag does not work in Internet Explorer. Works best when opened in full window

Original Readme.md is below


A line chart with mouseover so that you can read the y-value based on the closest x-value. The x-value is found using d3.mouse and scale.invert. The y-value is then found by bisecting the data.

<!DOCTYPE html>
<html style='width: 100%; height:100%;'>
<head>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="zui53.js"></script>
<script>
function initZUI() {
console.log(ZUI53);
zui = new ZUI53.Viewport(document.getElementById('zui'));
zui.addSurface(new ZUI53.Surfaces.SVG($("#linechart")));
var pan_tool = new ZUI53.Tools.Pan(zui);
zui.toolset.add(pan_tool);
pan_tool.attach();
zui.showBounds({ width: 1000, height: 1000, x: 0, y: 0 });
}
function buildSVG() {
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
d3.selectAll("#zui").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g")
.attr("id", "linechart")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
drawd3line(width, height);
}
function drawd3line(width, height) {
var svg = d3.selectAll("#zui").selectAll("#linechart");
var parseDate = d3.time.format("%Y-%m-%d").parse,
bisectDate = d3.bisector(function (d) { return d.date; }).left;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.cumul); });
var color = d3.scale.category20();
d3.csv("indexdata.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
var indexkeys = d3.keys(d3.nest().key(function (d) { return d.indexname; }).map(data))
indexkeys.forEach(function (name) {
//ugly ugly but can't think of the best place to put this transform
//gets cumulative growth by dividing price by first price
var temp = data.filter(function (d) { return d.indexname == name; });
temp.forEach(function (d) { d.cumul = +d.price / temp[0].price });
})
indexdata = d3.nest().key(function(d) {return d.indexname;}).entries(data);
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain(d3.extent(data, function (d) { return d.cumul; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Cumulative Growth");
var indexGroup = svg.selectAll(".indexname")
.data(indexdata)
.enter().append("g")
.attr("class", "indexname")
.attr("id", function (d) { return d.key });
indexGroup.append("path")
.attr("class", "line")
.attr("d", function (d) { return line(d.values); })
.attr("stroke", function (d) { return color(d.key); });
//original ugly way of doing it until I found Mike Bostock's example
// var nodes = indexGroup.selectAll()
// .data(function (d) { return d.values; })
// .enter().append("g")
// .attr("class", "points")
// .attr("transform", function (d) { return "translate(" + x(d.date) + "," + y(d.cumul) + ")"; });
//nodes.append('circle')
// .attr("r", 10)
// .attr("fill", function (d) { return color(d.indexname); })
// nodes.append('text')
// .attr("text-anchor", "middle")
// .attr("dx", 12)
// .attr("dy", "-.35em")
// .text(function (d) { return d.indexname + "\n " + Math.round(d.cumul * 100) / 100; })
// .attr("fill", "gray");
var focus = svg.selectAll(".focus")
.data(indexkeys).enter().append("g")
.attr("class", "focus")
.attr("id", function (d) { return "focus-" + d; })
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function () { focus.style("display", null); })
.on("mouseout", function () { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]);
indexkeys.forEach(function (indexname, i1) {
var i = bisectDate(indexdata[i1].values, x0, 1),
d0 = indexdata[i1].values[i - 1],
d1 = indexdata[i1].values[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
d3.select("#focus-" + indexname)
.attr("transform", "translate(" + x(d.date) + "," + y(d.cumul) + ")")
.attr("fill", color(indexname));
d3.select("#focus-" + indexname).select("text").text(indexname + " " + Math.round(d.cumul*100)/100).attr("fill", color(indexname));
});
}
});
initZUI();
}
</script>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke-width: 5px;
}
/*ugly way before I found Mike Bostock's example
.points {opacity: 0;}
.points:hover {opacity: 1;}*/
.overlay {
fill: none;
pointer-events: all;
}
</style>
</head>
<body onload='buildSVG()' style='width:100%; height:100%; margin: 0; padding: 0;'>
<div id='zui'>
</div>
</body>
</html>
date indexname price
1976-01-30 SP500 100.86
1976-02-27 SP500 99.71
1976-03-31 SP500 102.77
1976-04-30 SP500 101.64
1976-05-31 SP500 100.18
1976-06-30 SP500 104.28
1976-07-30 SP500 103.44
1976-08-31 SP500 102.91
1976-09-30 SP500 105.24
1976-10-29 SP500 102.9
1976-11-30 SP500 102.1
1976-12-31 SP500 107.46
1977-01-31 SP500 102.03
1977-02-28 SP500 99.82
1977-03-31 SP500 98.42
1977-04-29 SP500 98.44
1977-05-31 SP500 96.12
1977-06-30 SP500 100.48
1977-07-29 SP500 98.85
1977-08-31 SP500 96.77
1977-09-30 SP500 96.53
1977-10-31 SP500 92.34
1977-11-30 SP500 94.83
1977-12-30 SP500 95.1
1978-01-31 SP500 89.25
1978-02-28 SP500 87.04
1978-03-31 SP500 89.21
1978-04-28 SP500 96.83
1978-05-31 SP500 97.29
1978-06-30 SP500 95.53
1978-07-31 SP500 100.68
1978-08-31 SP500 103.29
1978-09-29 SP500 102.54
1978-10-31 SP500 93.15
1978-11-30 SP500 94.7
1978-12-29 SP500 96.11
1979-01-31 SP500 99.93
1979-02-28 SP500 96.28
1979-03-30 SP500 101.59
1979-04-30 SP500 101.76
1979-05-31 SP500 99.08
1979-06-29 SP500 102.91
1979-07-31 SP500 103.81
1979-08-31 SP500 109.32
1979-09-28 SP500 109.32
1979-10-31 SP500 101.82
1979-11-30 SP500 106.16
1979-12-31 SP500 107.94
1980-01-31 SP500 114.16
1980-02-29 SP500 113.66
1980-03-31 SP500 102.09
1980-04-30 SP500 106.29
1980-05-30 SP500 111.24
1980-06-30 SP500 114.24
1980-07-31 SP500 121.67
1980-08-29 SP500 122.38
1980-09-30 SP500 125.46
1980-10-31 SP500 127.47
1980-11-28 SP500 140.52
1980-12-31 SP500 135.76
1981-01-30 SP500 129.55
1981-02-27 SP500 131.27
1981-03-31 SP500 136
1981-04-30 SP500 132.81
1981-05-29 SP500 132.59
1981-06-30 SP500 131.21
1981-07-31 SP500 130.92
1981-08-31 SP500 122.79
1981-09-30 SP500 116.18
1981-10-30 SP500 121.89
1981-11-30 SP500 126.35
1981-12-31 SP500 122.55
1982-01-29 SP500 120.4
1982-02-26 SP500 113.11
1982-03-31 SP500 111.96
1982-04-30 SP500 116.44
1982-05-31 SP500 111.88
1982-06-30 SP500 109.61
1982-07-30 SP500 107.09
1982-08-31 SP500 119.51
1982-09-30 SP500 120.42
1982-10-29 SP500 133.71
1982-11-30 SP500 138.54
1982-12-31 SP500 140.64
1983-01-31 SP500 145.3
1983-02-28 SP500 148.06
1983-03-31 SP500 152.96
1983-04-29 SP500 164.42
1983-05-31 SP500 162.39
1983-06-30 SP500 168.11
1983-07-29 SP500 162.56
1983-08-31 SP500 164.4
1983-09-30 SP500 166.07
1983-10-31 SP500 163.55
1983-11-30 SP500 166.4
1983-12-30 SP500 164.93
1984-01-31 SP500 163.41
1984-02-29 SP500 157.06
1984-03-30 SP500 159.18
1984-04-30 SP500 160.05
1984-05-31 SP500 150.55
1984-06-29 SP500 153.18
1984-07-31 SP500 150.66
1984-08-31 SP500 166.68
1984-09-28 SP500 166.1
1984-10-31 SP500 166.09
1984-11-30 SP500 163.58
1984-12-31 SP500 167.24
1985-01-31 SP500 179.63
1985-02-28 SP500 181.18
1985-03-29 SP500 180.66
1985-04-30 SP500 179.83
1985-05-31 SP500 189.55
1985-06-28 SP500 191.85
1985-07-31 SP500 190.92
1985-08-30 SP500 188.63
1985-09-30 SP500 182.08
1985-10-31 SP500 189.82
1985-11-29 SP500 202.17
1985-12-31 SP500 211.28
1986-01-31 SP500 211.78
1986-02-28 SP500 226.92
1986-03-31 SP500 238.9
1986-04-30 SP500 235.52
1986-05-30 SP500 247.35
1986-06-30 SP500 250.84
1986-07-31 SP500 236.12
1986-08-29 SP500 252.93
1986-09-30 SP500 231.32
1986-10-31 SP500 243.98
1986-11-28 SP500 249.22
1986-12-31 SP500 242.17
1987-01-30 SP500 274.08
1987-02-27 SP500 284.2
1987-03-31 SP500 291.7
1987-04-30 SP500 288.36
1987-05-29 SP500 290.1
1987-06-30 SP500 304
1987-07-31 SP500 318.66
1987-08-31 SP500 329.8
1987-09-30 SP500 321.83
1987-10-30 SP500 251.79
1987-11-30 SP500 230.3
1987-12-31 SP500 247.08
1988-01-29 SP500 257.07
1988-02-29 SP500 267.82
1988-03-31 SP500 258.89
1988-04-29 SP500 261.33
1988-05-31 SP500 262.16
1988-06-30 SP500 273.5
1988-07-29 SP500 272.02
1988-08-31 SP500 261.52
1988-09-30 SP500 271.91
1988-10-31 SP500 278.97
1988-11-30 SP500 273.7
1988-12-30 SP500 277.72
1989-01-31 SP500 297.47
1989-02-28 SP500 288.86
1989-03-31 SP500 294.87
1989-04-28 SP500 309.64
1989-05-31 SP500 320.52
1989-06-30 SP500 317.98
1989-07-31 SP500 346.08
1989-08-31 SP500 351.45
1989-09-29 SP500 349.15
1989-10-31 SP500 340.36
1989-11-30 SP500 345.99
1989-12-29 SP500 353.4
1990-01-31 SP500 329.08
1990-02-28 SP500 331.89
1990-03-30 SP500 339.94
1990-04-30 SP500 330.8
1990-05-31 SP500 361.23
1990-06-29 SP500 358.02
1990-07-31 SP500 356.15
1990-08-31 SP500 322.56
1990-09-28 SP500 306.05
1990-10-31 SP500 304
1990-11-30 SP500 322.22
1990-12-31 SP500 330.22
1991-01-31 SP500 343.93
1991-02-28 SP500 367.07
1991-03-29 SP500 375.22
1991-04-30 SP500 375.35
1991-05-31 SP500 389.83
1991-06-28 SP500 371.16
1991-07-31 SP500 387.81
1991-08-30 SP500 395.43
1991-09-30 SP500 387.86
1991-10-31 SP500 392.46
1991-11-29 SP500 375.22
1991-12-31 SP500 417.09
1992-01-31 SP500 408.79
1992-02-28 SP500 412.7
1992-03-31 SP500 403.69
1992-04-30 SP500 414.95
1992-05-29 SP500 415.35
1992-06-30 SP500 408.14
1992-07-31 SP500 424.21
1992-08-31 SP500 414.03
1992-09-30 SP500 417.8
1992-10-30 SP500 418.68
1992-11-30 SP500 431.35
1992-12-31 SP500 435.71
1993-01-29 SP500 438.78
1993-02-26 SP500 443.38
1993-03-31 SP500 451.67
1993-04-30 SP500 440.19
1993-05-31 SP500 450.19
1993-06-30 SP500 450.53
1993-07-30 SP500 448.13
1993-08-31 SP500 463.56
1993-09-30 SP500 458.93
1993-10-29 SP500 467.83
1993-11-30 SP500 461.79
1993-12-31 SP500 466.45
1994-01-31 SP500 481.61
1994-02-28 SP500 467.14
1994-03-31 SP500 445.77
1994-04-29 SP500 450.91
1994-05-31 SP500 456.5
1994-06-30 SP500 444.27
1994-07-29 SP500 458.26
1994-08-31 SP500 475.49
1994-09-30 SP500 462.69
1994-10-31 SP500 472.35
1994-11-30 SP500 453.69
1994-12-30 SP500 459.27
1995-01-31 SP500 470.42
1995-02-28 SP500 487.39
1995-03-31 SP500 500.71
1995-04-28 SP500 514.71
1995-05-31 SP500 533.4
1995-06-30 SP500 544.75
1995-07-31 SP500 562.06
1995-08-31 SP500 561.88
1995-09-29 SP500 584.41
1995-10-31 SP500 581.5
1995-11-30 SP500 605.37
1995-12-29 SP500 615.93
1996-01-31 SP500 636.02
1996-02-29 SP500 640.43
1996-03-29 SP500 645.5
1996-04-30 SP500 654.17
1996-05-31 SP500 669.12
1996-06-28 SP500 670.63
1996-07-31 SP500 639.95
1996-08-30 SP500 651.99
1996-09-30 SP500 687.31
1996-10-31 SP500 705.27
1996-11-29 SP500 757.02
1996-12-31 SP500 740.74
1997-01-31 SP500 786.16
1997-02-28 SP500 790.82
1997-03-31 SP500 757.12
1997-04-30 SP500 801.34
1997-05-30 SP500 848.28
1997-06-30 SP500 885.14
1997-07-31 SP500 954.29
1997-08-29 SP500 899.47
1997-09-30 SP500 947.28
1997-10-31 SP500 914.62
1997-11-28 SP500 955.4
1997-12-31 SP500 970.43
1998-01-30 SP500 980.28
1998-02-27 SP500 1049.34
1998-03-31 SP500 1101.75
1998-04-30 SP500 1111.75
1998-05-29 SP500 1090.82
1998-06-30 SP500 1133.84
1998-07-31 SP500 1120.67
1998-08-31 SP500 957.28
1998-09-30 SP500 1017.01
1998-10-30 SP500 1098.67
1998-11-30 SP500 1163.63
1998-12-31 SP500 1229.23
1999-01-29 SP500 1279.64
1999-02-26 SP500 1238.33
1999-03-31 SP500 1286.37
1999-04-30 SP500 1335.18
1999-05-31 SP500 1301.84
1999-06-30 SP500 1372.71
1999-07-30 SP500 1328.72
1999-08-31 SP500 1320.41
1999-09-30 SP500 1282.71
1999-10-29 SP500 1362.93
1999-11-30 SP500 1388.91
1999-12-31 SP500 1469.25
2000-01-31 SP500 1394.46
2000-02-29 SP500 1366.42
2000-03-31 SP500 1498.58
2000-04-28 SP500 1452.43
2000-05-31 SP500 1420.6
2000-06-30 SP500 1454.6
2000-07-31 SP500 1430.83
2000-08-31 SP500 1517.68
2000-09-29 SP500 1436.51
2000-10-31 SP500 1429.4
2000-11-30 SP500 1314.95
2000-12-29 SP500 1320.28
2001-01-31 SP500 1366.01
2001-02-28 SP500 1239.94
2001-03-30 SP500 1160.33
2001-04-30 SP500 1249.46
2001-05-31 SP500 1255.82
2001-06-29 SP500 1224.42
2001-07-31 SP500 1211.23
2001-08-31 SP500 1133.58
2001-09-28 SP500 1040.94
2001-10-31 SP500 1059.78
2001-11-30 SP500 1139.45
2001-12-31 SP500 1148.08
2002-01-31 SP500 1130.2
2002-02-28 SP500 1106.73
2002-03-29 SP500 1147.39
2002-04-30 SP500 1076.92
2002-05-31 SP500 1067.14
2002-06-28 SP500 989.81
2002-07-31 SP500 911.62
2002-08-30 SP500 916.07
2002-09-30 SP500 815.28
2002-10-31 SP500 885.76
2002-11-29 SP500 936.31
2002-12-31 SP500 879.82
2003-01-31 SP500 855.7
2003-02-28 SP500 841.15
2003-03-31 SP500 848.18
2003-04-30 SP500 916.92
2003-05-30 SP500 963.59
2003-06-30 SP500 974.5
2003-07-31 SP500 990.31
2003-08-29 SP500 1008.01
2003-09-30 SP500 995.97
2003-10-31 SP500 1050.71
2003-11-28 SP500 1058.2
2003-12-31 SP500 1111.92
2004-01-30 SP500 1131.13
2004-02-27 SP500 1144.94
2004-03-31 SP500 1126.21
2004-04-30 SP500 1107.3
2004-05-31 SP500 1120.68
2004-06-30 SP500 1140.84
2004-07-30 SP500 1101.72
2004-08-31 SP500 1104.24
2004-09-30 SP500 1114.58
2004-10-29 SP500 1130.2
2004-11-30 SP500 1173.82
2004-12-31 SP500 1211.92
2005-01-31 SP500 1181.27
2005-02-28 SP500 1203.6
2005-03-31 SP500 1180.59
2005-04-29 SP500 1156.85
2005-05-31 SP500 1191.5
2005-06-30 SP500 1191.33
2005-07-29 SP500 1234.18
2005-08-31 SP500 1220.33
2005-09-30 SP500 1228.81
2005-10-31 SP500 1207.01
2005-11-30 SP500 1249.48
2005-12-30 SP500 1248.29
2006-01-31 SP500 1280.08
2006-02-28 SP500 1280.66
2006-03-31 SP500 1294.83
2006-04-28 SP500 1310.61
2006-05-31 SP500 1270.09
2006-06-30 SP500 1270.2
2006-07-31 SP500 1276.66
2006-08-31 SP500 1303.82
2006-09-29 SP500 1335.85
2006-10-31 SP500 1377.94
2006-11-30 SP500 1400.63
2006-12-29 SP500 1418.3
2007-01-31 SP500 1438.24
2007-02-28 SP500 1406.82
2007-03-30 SP500 1420.86
2007-04-30 SP500 1482.37
2007-05-31 SP500 1530.62
2007-06-29 SP500 1503.35
2007-07-31 SP500 1455.27
2007-08-31 SP500 1473.99
2007-09-28 SP500 1526.75
2007-10-31 SP500 1549.38
2007-11-30 SP500 1481.14
2007-12-31 SP500 1468.36
2008-01-31 SP500 1378.55
2008-02-29 SP500 1330.63
2008-03-31 SP500 1322.7
2008-04-30 SP500 1385.59
2008-05-30 SP500 1400.38
2008-06-30 SP500 1280
2008-07-31 SP500 1267.38
2008-08-29 SP500 1282.83
2008-09-30 SP500 1166.36
2008-10-31 SP500 968.75
2008-11-28 SP500 896.24
2008-12-31 SP500 903.25
2009-01-30 SP500 825.88
2009-02-27 SP500 735.09
2009-03-31 SP500 797.87
2009-04-30 SP500 872.81
2009-05-29 SP500 919.14
2009-06-30 SP500 919.32
2009-07-31 SP500 987.48
2009-08-31 SP500 1020.62
2009-09-30 SP500 1057.08
2009-10-30 SP500 1036.19
2009-11-30 SP500 1095.63
2009-12-31 SP500 1115.1
2010-01-29 SP500 1073.87
2010-02-26 SP500 1104.49
2010-03-31 SP500 1169.43
2010-04-30 SP500 1186.69
2010-05-31 SP500 1089.41
2010-06-30 SP500 1030.71
2010-07-30 SP500 1101.6
2010-08-31 SP500 1049.33
2010-09-30 SP500 1141.2
2010-10-29 SP500 1183.26
2010-11-30 SP500 1180.55
2010-12-31 SP500 1257.64
2011-01-31 SP500 1286.12
2011-02-28 SP500 1327.22
2011-03-31 SP500 1325.83
2011-04-29 SP500 1363.61
2011-05-31 SP500 1345.2
2011-06-30 SP500 1320.64
2011-07-29 SP500 1292.28
2011-08-31 SP500 1218.89
2011-09-30 SP500 1131.42
2011-10-31 SP500 1253.3
2011-11-30 SP500 1246.96
2011-12-30 SP500 1257.6
2012-01-31 SP500 1312.41
2012-02-29 SP500 1365.68
2012-03-30 SP500 1408.47
2012-04-30 SP500 1397.91
2012-05-31 SP500 1310.33
2012-06-29 SP500 1362.16
2012-07-31 SP500 1379.32
2012-08-31 SP500 1406.58
2012-09-28 SP500 1440.67
2012-10-31 SP500 1412.16
2012-11-30 SP500 1416.18
2012-12-31 SP500 1426.19
2013-01-31 SP500 1498.11
2013-02-28 SP500 1514.68
1976-01-30 BarAgg 101.95
1976-02-27 BarAgg 102.72
1976-03-31 BarAgg 103.95
1976-04-30 BarAgg 104.53
1976-05-31 BarAgg 103.58
1976-06-30 BarAgg 105.05
1976-07-30 BarAgg 106.28
1976-08-31 BarAgg 108.18
1976-09-30 BarAgg 109.67
1976-10-29 BarAgg 110.85
1976-11-30 BarAgg 113.53
1976-12-31 BarAgg 115.6
1977-01-31 BarAgg 113.38
1977-02-28 BarAgg 113.81
1977-03-31 BarAgg 114.66
1977-04-29 BarAgg 115.7
1977-05-31 BarAgg 116.5
1977-06-30 BarAgg 118.15
1977-07-29 BarAgg 118.08
1977-08-31 BarAgg 119.21
1977-09-30 BarAgg 119.21
1977-10-31 BarAgg 118.58
1977-11-30 BarAgg 119.64
1977-12-30 BarAgg 119.11
1978-01-31 BarAgg 118.83
1978-02-28 BarAgg 119.29
1978-03-31 BarAgg 119.67
1978-04-28 BarAgg 119.84
1978-05-31 BarAgg 119.3
1978-06-30 BarAgg 119.27
1978-07-31 BarAgg 120.63
1978-08-31 BarAgg 122.54
1978-09-29 BarAgg 122.49
1978-10-31 BarAgg 120.76
1978-11-30 BarAgg 121.94
1978-12-29 BarAgg 120.77
1979-01-31 BarAgg 123.03
1979-02-28 BarAgg 122.51
1979-03-30 BarAgg 123.77
1979-04-30 BarAgg 123.57
1979-05-31 BarAgg 125.88
1979-06-29 BarAgg 128.62
1979-07-31 BarAgg 128.68
1979-08-31 BarAgg 128.35
1979-09-28 BarAgg 127.01
1979-10-31 BarAgg 119.29
1979-11-30 BarAgg 122.77
1979-12-31 BarAgg 123.1
1980-01-31 BarAgg 119.35
1980-02-29 BarAgg 112.29
1980-03-31 BarAgg 112.38
1980-04-30 BarAgg 125.12
1980-05-30 BarAgg 131.02
1980-06-30 BarAgg 133.49
1980-07-31 BarAgg 130.73
1980-08-29 BarAgg 126.05
1980-09-30 BarAgg 124.73
1980-10-31 BarAgg 123.3
1980-11-28 BarAgg 123.07
1980-12-31 BarAgg 126.43
1981-01-30 BarAgg 126.49
1981-02-27 BarAgg 124.09
1981-03-31 BarAgg 127.04
1981-04-30 BarAgg 122.51
1981-05-29 BarAgg 126.91
1981-06-30 BarAgg 126.62
1981-07-31 BarAgg 124.18
1981-08-31 BarAgg 121.56
1981-09-30 BarAgg 121.48
1981-10-30 BarAgg 128.59
1981-11-30 BarAgg 139.55
1981-12-31 BarAgg 134.33
1982-01-29 BarAgg 135.18
1982-02-26 BarAgg 137.82
1982-03-31 BarAgg 139.57
1982-04-30 BarAgg 143.48
1982-05-31 BarAgg 145.81
1982-06-30 BarAgg 143.52
1982-07-30 BarAgg 149.69
1982-08-31 BarAgg 157.86
1982-09-30 BarAgg 164.25
1982-10-29 BarAgg 172.95
1982-11-30 BarAgg 174.64
1982-12-31 BarAgg 178.15
1983-01-31 BarAgg 178.45
1983-02-28 BarAgg 183.57
1983-03-31 BarAgg 184.05
1983-04-29 BarAgg 189.18
1983-05-31 BarAgg 186.7
1983-06-30 BarAgg 186.9
1983-07-29 BarAgg 182.48
1983-08-31 BarAgg 183.75
1983-09-30 BarAgg 189.81
1983-10-31 BarAgg 190.49
1983-11-30 BarAgg 192.67
1983-12-30 BarAgg 193.04
1984-01-31 BarAgg 197.02
1984-02-29 BarAgg 196.01
1984-03-30 BarAgg 193.81
1984-04-30 BarAgg 193.43
1984-05-31 BarAgg 187.4
1984-06-29 BarAgg 189.8
1984-07-31 BarAgg 198.34
1984-08-31 BarAgg 201.68
1984-09-28 BarAgg 206.47
1984-10-31 BarAgg 215.23
1984-11-30 BarAgg 219.08
1984-12-31 BarAgg 222.28
1985-01-31 BarAgg 227.34
1985-02-28 BarAgg 222.69
1985-03-29 BarAgg 227.23
1985-04-30 BarAgg 231.93
1985-05-31 BarAgg 244.05
1985-06-28 BarAgg 246.63
1985-07-31 BarAgg 245.77
1985-08-30 BarAgg 250.39
1985-09-30 BarAgg 251.9
1985-10-31 BarAgg 257.18
1985-11-29 BarAgg 263.36
1985-12-31 BarAgg 271.41
1986-01-31 BarAgg 272.92
1986-02-28 BarAgg 283.68
1986-03-31 BarAgg 292.49
1986-04-30 BarAgg 294.04
1986-05-30 BarAgg 288.42
1986-06-30 BarAgg 295.97
1986-07-31 BarAgg 298.59
1986-08-29 BarAgg 306.01
1986-09-30 BarAgg 302.99
1986-10-31 BarAgg 307.37
1986-11-28 BarAgg 311.67
1986-12-31 BarAgg 312.84
1987-01-30 BarAgg 317.25
1987-02-27 BarAgg 319.45
1987-03-31 BarAgg 318.01
1987-04-30 BarAgg 309.29
1987-05-29 BarAgg 308.08
1987-06-30 BarAgg 312.32
1987-07-31 BarAgg 312.08
1987-08-31 BarAgg 310.41
1987-09-30 BarAgg 303.8
1987-10-30 BarAgg 314.62
1987-11-30 BarAgg 317.14
1987-12-31 BarAgg 321.46
1988-01-29 BarAgg 332.76
1988-02-29 BarAgg 336.71
1988-03-31 BarAgg 333.55
1988-04-29 BarAgg 331.75
1988-05-31 BarAgg 329.52
1988-06-30 BarAgg 337.47
1988-07-29 BarAgg 335.7
1988-08-31 BarAgg 336.58
1988-09-30 BarAgg 344.2
1988-10-31 BarAgg 350.68
1988-11-30 BarAgg 346.42
1988-12-30 BarAgg 346.81
1989-01-31 BarAgg 351.8
1989-02-28 BarAgg 349.25
1989-03-31 BarAgg 350.76
1989-04-28 BarAgg 358.1
1989-05-31 BarAgg 367.51
1989-06-30 BarAgg 378.7
1989-07-31 BarAgg 386.75
1989-08-31 BarAgg 381.02
1989-09-29 BarAgg 382.97
1989-10-31 BarAgg 392.4
1989-11-30 BarAgg 396.14
1989-12-29 BarAgg 397.2
1990-01-31 BarAgg 392.48
1990-02-28 BarAgg 393.75
1990-03-30 BarAgg 394.04
1990-04-30 BarAgg 390.43
1990-05-31 BarAgg 401.99
1990-06-29 BarAgg 408.44
1990-07-31 BarAgg 414.09
1990-08-31 BarAgg 408.56
1990-09-28 BarAgg 411.94
1990-10-31 BarAgg 417.17
1990-11-30 BarAgg 426.15
1990-12-31 BarAgg 432.79
1991-01-31 BarAgg 438.14
1991-02-28 BarAgg 441.88
1991-03-29 BarAgg 444.92
1991-04-30 BarAgg 449.74
1991-05-31 BarAgg 452.37
1991-06-28 BarAgg 452.14
1991-07-31 BarAgg 458.41
1991-08-30 BarAgg 468.33
1991-09-30 BarAgg 477.82
1991-10-31 BarAgg 483.14
1991-11-29 BarAgg 487.57
1991-12-31 BarAgg 502.05
1992-01-31 BarAgg 495.22
1992-02-28 BarAgg 498.44
1992-03-31 BarAgg 495.63
1992-04-30 BarAgg 499.21
1992-05-29 BarAgg 508.63
1992-06-30 BarAgg 515.63
1992-07-31 BarAgg 526.15
1992-08-31 BarAgg 531.48
1992-09-30 BarAgg 537.78
1992-10-30 BarAgg 530.65
1992-11-30 BarAgg 530.77
1992-12-31 BarAgg 539.21
1993-01-29 BarAgg 549.55
1993-02-26 BarAgg 559.17
1993-03-31 BarAgg 561.5
1993-04-30 BarAgg 565.41
1993-05-31 BarAgg 566.13
1993-06-30 BarAgg 576.39
1993-07-30 BarAgg 579.65
1993-08-31 BarAgg 589.81
1993-09-30 BarAgg 591.43
1993-10-29 BarAgg 593.64
1993-11-30 BarAgg 588.59
1993-12-31 BarAgg 591.78
1994-01-31 BarAgg 599.77
1994-02-28 BarAgg 589.35
1994-03-31 BarAgg 574.82
1994-04-29 BarAgg 570.23
1994-05-31 BarAgg 570.15
1994-06-30 BarAgg 568.89
1994-07-29 BarAgg 580.19
1994-08-31 BarAgg 580.91
1994-09-30 BarAgg 572.36
1994-10-31 BarAgg 571.85
1994-11-30 BarAgg 570.58
1994-12-30 BarAgg 574.52
1995-01-31 BarAgg 585.89
1995-02-28 BarAgg 599.82
1995-03-31 BarAgg 603.5
1995-04-28 BarAgg 611.93
1995-05-31 BarAgg 635.61
1995-06-30 BarAgg 640.27
1995-07-31 BarAgg 638.84
1995-08-31 BarAgg 646.55
1995-09-29 BarAgg 652.84
1995-10-31 BarAgg 661.33
1995-11-30 BarAgg 671.24
1995-12-29 BarAgg 680.66
1996-01-31 BarAgg 685.18
1996-02-29 BarAgg 673.27
1996-03-29 BarAgg 668.59
1996-04-30 BarAgg 664.83
1996-05-31 BarAgg 663.48
1996-06-28 BarAgg 672.39
1996-07-31 BarAgg 674.23
1996-08-30 BarAgg 673.1
1996-09-30 BarAgg 684.83
1996-10-31 BarAgg 700
1996-11-29 BarAgg 711.99
1996-12-31 BarAgg 705.37
1997-01-31 BarAgg 707.53
1997-02-28 BarAgg 709.29
1997-03-31 BarAgg 701.43
1997-04-30 BarAgg 711.93
1997-05-30 BarAgg 718.66
1997-06-30 BarAgg 727.19
1997-07-31 BarAgg 746.8
1997-08-29 BarAgg 740.43
1997-09-30 BarAgg 751.35
1997-10-31 BarAgg 762.25
1997-11-28 BarAgg 765.76
1997-12-31 BarAgg 773.47
1998-01-30 BarAgg 783.4
1998-02-27 BarAgg 782.81
1998-03-31 BarAgg 785.5
1998-04-30 BarAgg 789.6
1998-05-29 BarAgg 797.09
1998-06-30 BarAgg 803.85
1998-07-31 BarAgg 805.56
1998-08-31 BarAgg 818.67
1998-09-30 BarAgg 837.84
1998-10-30 BarAgg 833.42
1998-11-30 BarAgg 838.14
1998-12-31 BarAgg 840.66
1999-01-29 BarAgg 846.66
1999-02-26 BarAgg 831.88
1999-03-31 BarAgg 836.49
1999-04-30 BarAgg 839.14
1999-05-31 BarAgg 831.79
1999-06-30 BarAgg 829.14
1999-07-30 BarAgg 825.61
1999-08-31 BarAgg 825.19
1999-09-30 BarAgg 834.77
1999-10-29 BarAgg 837.85
1999-11-30 BarAgg 837.79
1999-12-31 BarAgg 833.75
2000-01-31 BarAgg 831.02
2000-02-29 BarAgg 841.07
2000-03-31 BarAgg 852.15
2000-04-28 BarAgg 849.71
2000-05-31 BarAgg 849.32
2000-06-30 BarAgg 866.99
2000-07-31 BarAgg 874.86
2000-08-31 BarAgg 887.54
2000-09-29 BarAgg 893.12
2000-10-31 BarAgg 899.03
2000-11-30 BarAgg 913.73
2000-12-29 BarAgg 930.68
2001-01-31 BarAgg 945.9
2001-02-28 BarAgg 954.14
2001-03-30 BarAgg 958.93
2001-04-30 BarAgg 954.95
2001-05-31 BarAgg 960.71
2001-06-29 BarAgg 964.34
2001-07-31 BarAgg 985.9
2001-08-31 BarAgg 997.19
2001-09-28 BarAgg 1008.81
2001-10-31 BarAgg 1029.92
2001-11-30 BarAgg 1015.72
2001-12-31 BarAgg 1009.27
2002-01-31 BarAgg 1017.44
2002-02-28 BarAgg 1027.3
2002-03-29 BarAgg 1010.21
2002-04-30 BarAgg 1029.8
2002-05-31 BarAgg 1038.55
2002-06-28 BarAgg 1047.53
2002-07-31 BarAgg 1060.17
2002-08-30 BarAgg 1078.0699
2002-09-30 BarAgg 1095.53
2002-10-31 BarAgg 1090.54
2002-11-29 BarAgg 1090.25
2002-12-31 BarAgg 1112.77
2003-01-31 BarAgg 1113.72
2003-02-28 BarAgg 1129.13
2003-03-31 BarAgg 1128.26
2003-04-30 BarAgg 1137.5699
2003-05-30 BarAgg 1158.78
2003-06-30 BarAgg 1156.48
2003-07-31 BarAgg 1117.6
2003-08-29 BarAgg 1125.02
2003-09-30 BarAgg 1154.8
2003-10-31 BarAgg 1144.03
2003-11-28 BarAgg 1146.77
2003-12-31 BarAgg 1158.4399
2004-01-30 BarAgg 1167.76
2004-02-27 BarAgg 1180.4
2004-03-31 BarAgg 1189.24
2004-04-30 BarAgg 1158.3
2004-05-31 BarAgg 1153.66
2004-06-30 BarAgg 1160.1801
2004-07-30 BarAgg 1171.6801
2004-08-31 BarAgg 1194.03
2004-09-30 BarAgg 1197.27
2004-10-29 BarAgg 1207.3101
2004-11-30 BarAgg 1197.6801
2004-12-31 BarAgg 1208.7
2005-01-31 BarAgg 1216.29
2005-02-28 BarAgg 1209.11
2005-03-31 BarAgg 1202.9
2005-04-29 BarAgg 1219.1801
2005-05-31 BarAgg 1232.37
2005-06-30 BarAgg 1239.09
2005-07-29 BarAgg 1227.8101
2005-08-31 BarAgg 1243.55
2005-09-30 BarAgg 1230.74
2005-10-31 BarAgg 1221
2005-11-30 BarAgg 1226.4
2005-12-30 BarAgg 1238.0601
2006-01-31 BarAgg 1238.13
2006-02-28 BarAgg 1242.24
2006-03-31 BarAgg 1230.05
2006-04-28 BarAgg 1227.8199
2006-05-31 BarAgg 1226.51
2006-06-30 BarAgg 1229.11
2006-07-31 BarAgg 1245.73
2006-08-31 BarAgg 1264.8
2006-09-29 BarAgg 1275.91
2006-10-31 BarAgg 1284.35
2006-11-30 BarAgg 1299.25
2006-12-29 BarAgg 1291.71
2007-01-31 BarAgg 1291.1801
2007-02-28 BarAgg 1311.09
2007-03-30 BarAgg 1311.13
2007-04-30 BarAgg 1318.2
2007-05-31 BarAgg 1308.21
2007-06-29 BarAgg 1304.34
2007-07-31 BarAgg 1315.22
2007-08-31 BarAgg 1331.34
2007-09-28 BarAgg 1341.4399
2007-10-31 BarAgg 1353.49
2007-11-30 BarAgg 1377.83
2007-12-31 BarAgg 1381.7
2008-01-31 BarAgg 1404.91
2008-02-29 BarAgg 1406.86
2008-03-31 BarAgg 1411.66
2008-04-30 BarAgg 1408.71
2008-05-30 BarAgg 1398.38
2008-06-30 BarAgg 1397.25
2008-07-31 BarAgg 1396.11
2008-08-29 BarAgg 1409.36
2008-09-30 BarAgg 1390.4301
2008-10-31 BarAgg 1357.61
2008-11-28 BarAgg 1401.8
2008-12-31 BarAgg 1454.1
2009-01-30 BarAgg 1441.27
2009-02-27 BarAgg 1435.83
2009-03-31 BarAgg 1455.79
2009-04-30 BarAgg 1462.75
2009-05-29 BarAgg 1473.36
2009-06-30 BarAgg 1481.74
2009-07-31 BarAgg 1505.64
2009-08-31 BarAgg 1521.23
2009-09-30 BarAgg 1537.21
2009-10-30 BarAgg 1544.8
2009-11-30 BarAgg 1564.8
2009-12-31 BarAgg 1540.34
2010-01-29 BarAgg 1563.87
2010-02-26 BarAgg 1569.71
2010-03-31 BarAgg 1567.78
2010-04-30 BarAgg 1584.1
2010-05-31 BarAgg 1597.4301
2010-06-30 BarAgg 1622.48
2010-07-30 BarAgg 1639.79
2010-08-31 BarAgg 1660.89
2010-09-30 BarAgg 1662.66
2010-10-29 BarAgg 1668.58
2010-11-30 BarAgg 1658.99
2010-12-31 BarAgg 1641.1
2011-01-31 BarAgg 1643.01
2011-02-28 BarAgg 1647.12
2011-03-31 BarAgg 1648.03
2011-04-29 BarAgg 1668.95
2011-05-31 BarAgg 1690.73
2011-06-30 BarAgg 1685.78
2011-07-29 BarAgg 1712.53
2011-08-31 BarAgg 1737.55
2011-09-30 BarAgg 1750.1899
2011-10-31 BarAgg 1752.0699
2011-11-30 BarAgg 1750.55
2011-12-30 BarAgg 1769.79
2012-01-31 BarAgg 1785.33
2012-02-29 BarAgg 1784.92
2012-03-30 BarAgg 1775.14
2012-04-30 BarAgg 1794.8199
2012-05-31 BarAgg 1811.0601
2012-06-29 BarAgg 1811.77
2012-07-31 BarAgg 1836.76
2012-08-31 BarAgg 1837.96
2012-09-28 BarAgg 1840.49
2012-10-31 BarAgg 1844.11
2012-11-30 BarAgg 1847.02
2012-12-31 BarAgg 1844.39
2013-01-31 BarAgg 1831.49
2013-02-28 BarAgg 1840.67
//please see https://github.com/florianguenther/zui53
//all of this zui53.js is copied from that repo
// # Sylvester
// Vector and Matrix mathematics modules for JavaScript
// Copyright (c) 2007 James Coglan
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
var Sylvester = {
version: '0.1.3',
precision: 1e-6
};
function Vector() {}
Vector.prototype = {
// Returns element i of the vector
e: function(i) {
return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
},
// Returns the number of elements the vector has
dimensions: function() {
return this.elements.length;
},
// Returns the modulus ('length') of the vector
modulus: function() {
return Math.sqrt(this.dot(this));
},
// Returns true iff the vector is equal to the argument
eql: function(vector) {
var n = this.elements.length;
var V = vector.elements || vector;
if (n != V.length) { return false; }
do {
if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }
} while (--n);
return true;
},
// Returns a copy of the vector
dup: function() {
return Vector.create(this.elements);
},
// Maps the vector to another vector according to the given function
map: function(fn) {
var elements = [];
this.each(function(x, i) {
elements.push(fn(x, i));
});
return Vector.create(elements);
},
// Calls the iterator for each element of the vector in turn
each: function(fn) {
var n = this.elements.length, k = n, i;
do { i = k - n;
fn(this.elements[i], i+1);
} while (--n);
},
// Returns a new vector created by normalizing the receiver
toUnitVector: function() {
var r = this.modulus();
if (r === 0) { return this.dup(); }
return this.map(function(x) { return x/r; });
},
// Returns the angle between the vector and the argument (also a vector)
angleFrom: function(vector) {
var V = vector.elements || vector;
var n = this.elements.length, k = n, i;
if (n != V.length) { return null; }
var dot = 0, mod1 = 0, mod2 = 0;
// Work things out in parallel to save time
this.each(function(x, i) {
dot += x * V[i-1];
mod1 += x * x;
mod2 += V[i-1] * V[i-1];
});
mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
if (mod1*mod2 === 0) { return null; }
var theta = dot / (mod1*mod2);
if (theta < -1) { theta = -1; }
if (theta > 1) { theta = 1; }
return Math.acos(theta);
},
// Returns true iff the vector is parallel to the argument
isParallelTo: function(vector) {
var angle = this.angleFrom(vector);
return (angle === null) ? null : (angle <= Sylvester.precision);
},
// Returns true iff the vector is antiparallel to the argument
isAntiparallelTo: function(vector) {
var angle = this.angleFrom(vector);
return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);
},
// Returns true iff the vector is perpendicular to the argument
isPerpendicularTo: function(vector) {
var dot = this.dot(vector);
return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);
},
// Returns the result of adding the argument to the vector
add: function(vector) {
var V = vector.elements || vector;
if (this.elements.length != V.length) { return null; }
return this.map(function(x, i) { return x + V[i-1]; });
},
// Returns the result of subtracting the argument from the vector
subtract: function(vector) {
var V = vector.elements || vector;
if (this.elements.length != V.length) { return null; }
return this.map(function(x, i) { return x - V[i-1]; });
},
// Returns the result of multiplying the elements of the vector by the argument
multiply: function(k) {
return this.map(function(x) { return x*k; });
},
x: function(k) { return this.multiply(k); },
// Returns the scalar product of the vector with the argument
// Both vectors must have equal dimensionality
dot: function(vector) {
var V = vector.elements || vector;
var i, product = 0, n = this.elements.length;
if (n != V.length) { return null; }
do { product += this.elements[n-1] * V[n-1]; } while (--n);
return product;
},
// Returns the vector product of the vector with the argument
// Both vectors must have dimensionality 3
cross: function(vector) {
var B = vector.elements || vector;
if (this.elements.length != 3 || B.length != 3) { return null; }
var A = this.elements;
return Vector.create([
(A[1] * B[2]) - (A[2] * B[1]),
(A[2] * B[0]) - (A[0] * B[2]),
(A[0] * B[1]) - (A[1] * B[0])
]);
},
// Returns the (absolute) largest element of the vector
max: function() {
var m = 0, n = this.elements.length, k = n, i;
do { i = k - n;
if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }
} while (--n);
return m;
},
// Returns the index of the first match found
indexOf: function(x) {
var index = null, n = this.elements.length, k = n, i;
do { i = k - n;
if (index === null && this.elements[i] == x) {
index = i + 1;
}
} while (--n);
return index;
},
// Returns a diagonal matrix with the vector's elements as its diagonal elements
toDiagonalMatrix: function() {
return Matrix.Diagonal(this.elements);
},
// Returns the result of rounding the elements of the vector
round: function() {
return this.map(function(x) { return Math.round(x); });
},
// Returns a copy of the vector with elements set to the given value if they
// differ from it by less than Sylvester.precision
snapTo: function(x) {
return this.map(function(y) {
return (Math.abs(y - x) <= Sylvester.precision) ? x : y;
});
},
// Returns the vector's distance from the argument, when considered as a point in space
distanceFrom: function(obj) {
if (obj.anchor) { return obj.distanceFrom(this); }
var V = obj.elements || obj;
if (V.length != this.elements.length) { return null; }
var sum = 0, part;
this.each(function(x, i) {
part = x - V[i-1];
sum += part * part;
});
return Math.sqrt(sum);
},
// Returns true if the vector is point on the given line
liesOn: function(line) {
return line.contains(this);
},
// Return true iff the vector is a point in the given plane
liesIn: function(plane) {
return plane.contains(this);
},
// Rotates the vector about the given object. The object should be a
// point if the vector is 2D, and a line if it is 3D. Be careful with line directions!
rotate: function(t, obj) {
var V, R, x, y, z;
switch (this.elements.length) {
case 2:
V = obj.elements || obj;
if (V.length != 2) { return null; }
R = Matrix.Rotation(t).elements;
x = this.elements[0] - V[0];
y = this.elements[1] - V[1];
return Vector.create([
V[0] + R[0][0] * x + R[0][1] * y,
V[1] + R[1][0] * x + R[1][1] * y
]);
break;
case 3:
if (!obj.direction) { return null; }
var C = obj.pointClosestTo(this).elements;
R = Matrix.Rotation(t, obj.direction).elements;
x = this.elements[0] - C[0];
y = this.elements[1] - C[1];
z = this.elements[2] - C[2];
return Vector.create([
C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
]);
break;
default:
return null;
}
},
// Returns the result of reflecting the point in the given point, line or plane
reflectionIn: function(obj) {
if (obj.anchor) {
// obj is a plane or line
var P = this.elements.slice();
var C = obj.pointClosestTo(P).elements;
return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
} else {
// obj is a point
var Q = obj.elements || obj;
if (this.elements.length != Q.length) { return null; }
return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
}
},
// Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added
to3D: function() {
var V = this.dup();
switch (V.elements.length) {
case 3: break;
case 2: V.elements.push(0); break;
default: return null;
}
return V;
},
// Returns a string representation of the vector
inspect: function() {
return '[' + this.elements.join(', ') + ']';
},
// Set vector's elements from an array
setElements: function(els) {
this.elements = (els.elements || els).slice();
return this;
}
};
// Constructor function
Vector.create = function(elements) {
var V = new Vector();
return V.setElements(elements);
};
// i, j, k unit vectors
Vector.i = Vector.create([1,0,0]);
Vector.j = Vector.create([0,1,0]);
Vector.k = Vector.create([0,0,1]);
// Random vector of size n
Vector.Random = function(n) {
var elements = [];
do { elements.push(Math.random());
} while (--n);
return Vector.create(elements);
};
// Vector filled with zeros
Vector.Zero = function(n) {
var elements = [];
do { elements.push(0);
} while (--n);
return Vector.create(elements);
};
function Matrix() {}
Matrix.prototype = {
// Returns element (i,j) of the matrix
e: function(i,j) {
if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
return this.elements[i-1][j-1];
},
// Returns row k of the matrix as a vector
row: function(i) {
if (i > this.elements.length) { return null; }
return Vector.create(this.elements[i-1]);
},
// Returns column k of the matrix as a vector
col: function(j) {
if (j > this.elements[0].length) { return null; }
var col = [], n = this.elements.length, k = n, i;
do { i = k - n;
col.push(this.elements[i][j-1]);
} while (--n);
return Vector.create(col);
},
// Returns the number of rows/columns the matrix has
dimensions: function() {
return {rows: this.elements.length, cols: this.elements[0].length};
},
// Returns the number of rows in the matrix
rows: function() {
return this.elements.length;
},
// Returns the number of columns in the matrix
cols: function() {
return this.elements[0].length;
},
// Returns true iff the matrix is equal to the argument. You can supply
// a vector as the argument, in which case the receiver must be a
// one-column matrix equal to the vector.
eql: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (this.elements.length != M.length ||
this.elements[0].length != M[0].length) { return false; }
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }
} while (--nj);
} while (--ni);
return true;
},
// Returns a copy of the matrix
dup: function() {
return Matrix.create(this.elements);
},
// Maps the matrix to another matrix (of the same dimensions) according to the given function
map: function(fn) {
var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
els[i] = [];
do { j = kj - nj;
els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
} while (--nj);
} while (--ni);
return Matrix.create(els);
},
// Returns true iff the argument has the same dimensions as the matrix
isSameSizeAs: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
return (this.elements.length == M.length &&
this.elements[0].length == M[0].length);
},
// Returns the result of adding the argument to the matrix
add: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.isSameSizeAs(M)) { return null; }
return this.map(function(x, i, j) { return x + M[i-1][j-1]; });
},
// Returns the result of subtracting the argument from the matrix
subtract: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.isSameSizeAs(M)) { return null; }
return this.map(function(x, i, j) { return x - M[i-1][j-1]; });
},
// Returns true iff the matrix can multiply the argument from the left
canMultiplyFromLeft: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
// this.columns should equal matrix.rows
return (this.elements[0].length == M.length);
},
// Returns the result of multiplying the matrix from the right by the argument.
// If the argument is a scalar then just multiply all the elements. If the argument is
// a vector, a vector is returned, which saves you having to remember calling
// col(1) on the result.
multiply: function(matrix) {
if (!matrix.elements) {
return this.map(function(x) { return x * matrix; });
}
var returnVector = matrix.modulus ? true : false;
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.canMultiplyFromLeft(M)) { return null; }
var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
var cols = this.elements[0].length, elements = [], sum, nc, c;
do { i = ki - ni;
elements[i] = [];
nj = kj;
do { j = kj - nj;
sum = 0;
nc = cols;
do { c = cols - nc;
sum += this.elements[i][c] * M[c][j];
} while (--nc);
elements[i][j] = sum;
} while (--nj);
} while (--ni);
var M = Matrix.create(elements);
return returnVector ? M.col(1) : M;
},
x: function(matrix) { return this.multiply(matrix); },
// Returns a submatrix taken from the matrix
// Argument order is: start row, start col, nrows, ncols
// Element selection wraps if the required index is outside the matrix's bounds, so you could
// use this to perform row/column cycling or copy-augmenting.
minor: function(a, b, c, d) {
var elements = [], ni = c, i, nj, j;
var rows = this.elements.length, cols = this.elements[0].length;
do { i = c - ni;
elements[i] = [];
nj = d;
do { j = d - nj;
elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];
} while (--nj);
} while (--ni);
return Matrix.create(elements);
},
// Returns the transpose of the matrix
transpose: function() {
var rows = this.elements.length, cols = this.elements[0].length;
var elements = [], ni = cols, i, nj, j;
do { i = cols - ni;
elements[i] = [];
nj = rows;
do { j = rows - nj;
elements[i][j] = this.elements[j][i];
} while (--nj);
} while (--ni);
return Matrix.create(elements);
},
// Returns true iff the matrix is square
isSquare: function() {
return (this.elements.length == this.elements[0].length);
},
// Returns the (absolute) largest element of the matrix
max: function() {
var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }
} while (--nj);
} while (--ni);
return m;
},
// Returns the indeces of the first match found by reading row-by-row from left to right
indexOf: function(x) {
var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }
} while (--nj);
} while (--ni);
return null;
},
// If the matrix is square, returns the diagonal elements as a vector.
// Otherwise, returns null.
diagonal: function() {
if (!this.isSquare) { return null; }
var els = [], n = this.elements.length, k = n, i;
do { i = k - n;
els.push(this.elements[i][i]);
} while (--n);
return Vector.create(els);
},
// Make the matrix upper (right) triangular by Gaussian elimination.
// This method only adds multiples of rows to other rows. No rows are
// scaled up or switched, and the determinant is preserved.
toRightTriangular: function() {
var M = this.dup(), els;
var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
do { i = k - n;
if (M.elements[i][i] == 0) {
for (j = i + 1; j < k; j++) {
if (M.elements[j][i] != 0) {
els = []; np = kp;
do { p = kp - np;
els.push(M.elements[i][p] + M.elements[j][p]);
} while (--np);
M.elements[i] = els;
break;
}
}
}
if (M.elements[i][i] != 0) {
for (j = i + 1; j < k; j++) {
var multiplier = M.elements[j][i] / M.elements[i][i];
els = []; np = kp;
do { p = kp - np;
// Elements with column numbers up to an including the number
// of the row that we're subtracting can safely be set straight to
// zero, since that's the point of this routine and it avoids having
// to loop over and correct rounding errors later
els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
} while (--np);
M.elements[j] = els;
}
}
} while (--n);
return M;
},
toUpperTriangular: function() { return this.toRightTriangular(); },
// Returns the determinant for square matrices
determinant: function() {
if (!this.isSquare()) { return null; }
var M = this.toRightTriangular();
var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
do { i = k - n + 1;
det = det * M.elements[i][i];
} while (--n);
return det;
},
det: function() { return this.determinant(); },
// Returns true iff the matrix is singular
isSingular: function() {
return (this.isSquare() && this.determinant() === 0);
},
// Returns the trace for square matrices
trace: function() {
if (!this.isSquare()) { return null; }
var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;
do { i = k - n + 1;
tr += this.elements[i][i];
} while (--n);
return tr;
},
tr: function() { return this.trace(); },
// Returns the rank of the matrix
rank: function() {
var M = this.toRightTriangular(), rank = 0;
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }
} while (--nj);
} while (--ni);
return rank;
},
rk: function() { return this.rank(); },
// Returns the result of attaching the given argument to the right-hand side of the matrix
augment: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
var T = this.dup(), cols = T.elements[0].length;
var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
if (ni != M.length) { return null; }
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
T.elements[i][cols + j] = M[i][j];
} while (--nj);
} while (--ni);
return T;
},
// Returns the inverse (if one exists) using Gauss-Jordan
inverse: function() {
if (!this.isSquare() || this.isSingular()) { return null; }
var ni = this.elements.length, ki = ni, i, j;
var M = this.augment(Matrix.I(ni)).toRightTriangular();
var np, kp = M.elements[0].length, p, els, divisor;
var inverse_elements = [], new_element;
// Matrix is non-singular so there will be no zeros on the diagonal
// Cycle through rows from last to first
do { i = ni - 1;
// First, normalise diagonal elements to 1
els = []; np = kp;
inverse_elements[i] = [];
divisor = M.elements[i][i];
do { p = kp - np;
new_element = M.elements[i][p] / divisor;
els.push(new_element);
// Shuffle of the current row of the right hand side into the results
// array as it will not be modified by later runs through this loop
if (p >= ki) { inverse_elements[i].push(new_element); }
} while (--np);
M.elements[i] = els;
// Then, subtract this row from those above it to
// give the identity matrix on the left hand side
for (j = 0; j < i; j++) {
els = []; np = kp;
do { p = kp - np;
els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
} while (--np);
M.elements[j] = els;
}
} while (--ni);
return Matrix.create(inverse_elements);
},
inv: function() { return this.inverse(); },
// Returns the result of rounding all the elements
round: function() {
return this.map(function(x) { return Math.round(x); });
},
// Returns a copy of the matrix with elements set to the given value if they
// differ from it by less than Sylvester.precision
snapTo: function(x) {
return this.map(function(p) {
return (Math.abs(p - x) <= Sylvester.precision) ? x : p;
});
},
// Returns a string representation of the matrix
inspect: function() {
var matrix_rows = [];
var n = this.elements.length, k = n, i;
do { i = k - n;
matrix_rows.push(Vector.create(this.elements[i]).inspect());
} while (--n);
return matrix_rows.join('\n');
},
// Set the matrix's elements from an array. If the argument passed
// is a vector, the resulting matrix will be a single column.
setElements: function(els) {
var i, elements = els.elements || els;
if (typeof(elements[0][0]) != 'undefined') {
var ni = elements.length, ki = ni, nj, kj, j;
this.elements = [];
do { i = ki - ni;
nj = elements[i].length; kj = nj;
this.elements[i] = [];
do { j = kj - nj;
this.elements[i][j] = elements[i][j];
} while (--nj);
} while(--ni);
return this;
}
var n = elements.length, k = n;
this.elements = [];
do { i = k - n;
this.elements.push([elements[i]]);
} while (--n);
return this;
}
};
// Constructor function
Matrix.create = function(elements) {
var M = new Matrix();
return M.setElements(elements);
};
// Identity matrix of size n
Matrix.I = function(n) {
var els = [], k = n, i, nj, j;
do { i = k - n;
els[i] = []; nj = k;
do { j = k - nj;
els[i][j] = (i == j) ? 1 : 0;
} while (--nj);
} while (--n);
return Matrix.create(els);
};
// Diagonal matrix - all off-diagonal elements are zero
Matrix.Diagonal = function(elements) {
var n = elements.length, k = n, i;
var M = Matrix.I(n);
do { i = k - n;
M.elements[i][i] = elements[i];
} while (--n);
return M;
};
// Rotation matrix about some axis. If no axis is
// supplied, assume we're after a 2D transform
Matrix.Rotation = function(theta, a) {
if (!a) {
return Matrix.create([
[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]
]);
}
var axis = a.dup();
if (axis.elements.length != 3) { return null; }
var mod = axis.modulus();
var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;
var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;
// Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
// That proof rotates the co-ordinate system so theta
// becomes -theta and sin becomes -sin here.
return Matrix.create([
[ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
[ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
[ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]
]);
};
// Special case rotations
Matrix.RotationX = function(t) {
var c = Math.cos(t), s = Math.sin(t);
return Matrix.create([
[ 1, 0, 0 ],
[ 0, c, -s ],
[ 0, s, c ]
]);
};
Matrix.RotationY = function(t) {
var c = Math.cos(t), s = Math.sin(t);
return Matrix.create([
[ c, 0, s ],
[ 0, 1, 0 ],
[ -s, 0, c ]
]);
};
Matrix.RotationZ = function(t) {
var c = Math.cos(t), s = Math.sin(t);
return Matrix.create([
[ c, -s, 0 ],
[ s, c, 0 ],
[ 0, 0, 1 ]
]);
};
// Random matrix of n rows, m columns
Matrix.Random = function(n, m) {
return Matrix.Zero(n, m).map(
function() { return Math.random(); }
);
};
// Matrix filled with zeros
Matrix.Zero = function(n, m) {
var els = [], ni = n, i, nj, j;
do { i = n - ni;
els[i] = [];
nj = m;
do { j = m - nj;
els[i][j] = 0;
} while (--nj);
} while (--ni);
return Matrix.create(els);
};
function Line() {}
Line.prototype = {
// Returns true if the argument occupies the same space as the line
eql: function(line) {
return (this.isParallelTo(line) && this.contains(line.anchor));
},
// Returns a copy of the line
dup: function() {
return Line.create(this.anchor, this.direction);
},
// Returns the result of translating the line by the given vector/array
translate: function(vector) {
var V = vector.elements || vector;
return Line.create([
this.anchor.elements[0] + V[0],
this.anchor.elements[1] + V[1],
this.anchor.elements[2] + (V[2] || 0)
], this.direction);
},
// Returns true if the line is parallel to the argument. Here, 'parallel to'
// means that the argument's direction is either parallel or antiparallel to
// the line's own direction. A line is parallel to a plane if the two do not
// have a unique intersection.
isParallelTo: function(obj) {
if (obj.normal) { return obj.isParallelTo(this); }
var theta = this.direction.angleFrom(obj.direction);
return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);
},
// Returns the line's perpendicular distance from the argument,
// which can be a point, a line or a plane
distanceFrom: function(obj) {
if (obj.normal) { return obj.distanceFrom(this); }
if (obj.direction) {
// obj is a line
if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }
var N = this.direction.cross(obj.direction).toUnitVector().elements;
var A = this.anchor.elements, B = obj.anchor.elements;
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
} else {
// obj is a point
var P = obj.elements || obj;
var A = this.anchor.elements, D = this.direction.elements;
var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];
var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);
if (modPA === 0) return 0;
// Assumes direction vector is normalized
var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;
var sin2 = 1 - cosTheta*cosTheta;
return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));
}
},
// Returns true iff the argument is a point on the line
contains: function(point) {
var dist = this.distanceFrom(point);
return (dist !== null && dist <= Sylvester.precision);
},
// Returns true iff the line lies in the given plane
liesIn: function(plane) {
return plane.contains(this);
},
// Returns true iff the line has a unique point of intersection with the argument
intersects: function(obj) {
if (obj.normal) { return obj.intersects(this); }
return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);
},
// Returns the unique intersection point with the argument, if one exists
intersectionWith: function(obj) {
if (obj.normal) { return obj.intersectionWith(this); }
if (!this.intersects(obj)) { return null; }
var P = this.anchor.elements, X = this.direction.elements,
Q = obj.anchor.elements, Y = obj.direction.elements;
var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];
var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];
var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;
var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;
var XdotX = X1*X1 + X2*X2 + X3*X3;
var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;
var XdotY = X1*Y1 + X2*Y2 + X3*Y3;
var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);
},
// Returns the point on the line that is closest to the given point or line
pointClosestTo: function(obj) {
if (obj.direction) {
// obj is a line
if (this.intersects(obj)) { return this.intersectionWith(obj); }
if (this.isParallelTo(obj)) { return null; }
var D = this.direction.elements, E = obj.direction.elements;
var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];
// Create plane containing obj and the shared normal and intersect this with it
// Thank you: http://www.cgafaq.info/wiki/Line-line_distance
var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);
var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);
var P = Plane.create(obj.anchor, N);
return P.intersectionWith(this);
} else {
// obj is a point
var P = obj.elements || obj;
if (this.contains(P)) { return Vector.create(P); }
var A = this.anchor.elements, D = this.direction.elements;
var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),
z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);
var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);
var k = this.distanceFrom(P) / V.modulus();
return Vector.create([
P[0] + V.elements[0] * k,
P[1] + V.elements[1] * k,
(P[2] || 0) + V.elements[2] * k
]);
}
},
// Returns a copy of the line rotated by t radians about the given line. Works by
// finding the argument's closest point to this line's anchor point (call this C) and
// rotating the anchor about C. Also rotates the line's direction about the argument's.
// Be careful with this - the rotation axis' direction affects the outcome!
rotate: function(t, line) {
// If we're working in 2D
if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }
var R = Matrix.Rotation(t, line.direction).elements;
var C = line.pointClosestTo(this.anchor).elements;
var A = this.anchor.elements, D = this.direction.elements;
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
return Line.create([
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
], [
R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],
R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],
R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]
]);
},
// Returns the line's reflection in the given point or line
reflectionIn: function(obj) {
if (obj.normal) {
// obj is a plane
var A = this.anchor.elements, D = this.direction.elements;
var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];
var newA = this.anchor.reflectionIn(obj).elements;
// Add the line's direction vector to its anchor, then mirror that in the plane
var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;
var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;
var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];
return Line.create(newA, newD);
} else if (obj.direction) {
// obj is a line - reflection obtained by rotating PI radians about obj
return this.rotate(Math.PI, obj);
} else {
// obj is a point - just reflect the line's anchor in it
var P = obj.elements || obj;
return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);
}
},
// Set the line's anchor point and direction.
setVectors: function(anchor, direction) {
// Need to do this so that line's properties are not
// references to the arguments passed in
anchor = Vector.create(anchor);
direction = Vector.create(direction);
if (anchor.elements.length == 2) {anchor.elements.push(0); }
if (direction.elements.length == 2) { direction.elements.push(0); }
if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }
var mod = direction.modulus();
if (mod === 0) { return null; }
this.anchor = anchor;
this.direction = Vector.create([
direction.elements[0] / mod,
direction.elements[1] / mod,
direction.elements[2] / mod
]);
return this;
}
};
// Constructor function
Line.create = function(anchor, direction) {
var L = new Line();
return L.setVectors(anchor, direction);
};
// Axes
Line.X = Line.create(Vector.Zero(3), Vector.i);
Line.Y = Line.create(Vector.Zero(3), Vector.j);
Line.Z = Line.create(Vector.Zero(3), Vector.k);
function Plane() {}
Plane.prototype = {
// Returns true iff the plane occupies the same space as the argument
eql: function(plane) {
return (this.contains(plane.anchor) && this.isParallelTo(plane));
},
// Returns a copy of the plane
dup: function() {
return Plane.create(this.anchor, this.normal);
},
// Returns the result of translating the plane by the given vector
translate: function(vector) {
var V = vector.elements || vector;
return Plane.create([
this.anchor.elements[0] + V[0],
this.anchor.elements[1] + V[1],
this.anchor.elements[2] + (V[2] || 0)
], this.normal);
},
// Returns true iff the plane is parallel to the argument. Will return true
// if the planes are equal, or if you give a line and it lies in the plane.
isParallelTo: function(obj) {
var theta;
if (obj.normal) {
// obj is a plane
theta = this.normal.angleFrom(obj.normal);
return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);
} else if (obj.direction) {
// obj is a line
return this.normal.isPerpendicularTo(obj.direction);
}
return null;
},
// Returns true iff the receiver is perpendicular to the argument
isPerpendicularTo: function(plane) {
var theta = this.normal.angleFrom(plane.normal);
return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);
},
// Returns the plane's distance from the given object (point, line or plane)
distanceFrom: function(obj) {
if (this.intersects(obj) || this.contains(obj)) { return 0; }
if (obj.anchor) {
// obj is a plane or line
var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
} else {
// obj is a point
var P = obj.elements || obj;
var A = this.anchor.elements, N = this.normal.elements;
return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);
}
},
// Returns true iff the plane contains the given point or line
contains: function(obj) {
if (obj.normal) { return null; }
if (obj.direction) {
return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));
} else {
var P = obj.elements || obj;
var A = this.anchor.elements, N = this.normal.elements;
var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));
return (diff <= Sylvester.precision);
}
},
// Returns true iff the plane has a unique point/line of intersection with the argument
intersects: function(obj) {
if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }
return !this.isParallelTo(obj);
},
// Returns the unique intersection with the argument, if one exists. The result
// will be a vector if a line is supplied, and a line if a plane is supplied.
intersectionWith: function(obj) {
if (!this.intersects(obj)) { return null; }
if (obj.direction) {
// obj is a line
var A = obj.anchor.elements, D = obj.direction.elements,
P = this.anchor.elements, N = this.normal.elements;
var multiplier = (N[0]*(P[0]-A[0]) + N[1]*(P[1]-A[1]) + N[2]*(P[2]-A[2])) / (N[0]*D[0] + N[1]*D[1] + N[2]*D[2]);
return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);
} else if (obj.normal) {
// obj is a plane
var direction = this.normal.cross(obj.normal).toUnitVector();
// To find an anchor point, we find one co-ordinate that has a value
// of zero somewhere on the intersection, and remember which one we picked
var N = this.normal.elements, A = this.anchor.elements,
O = obj.normal.elements, B = obj.anchor.elements;
var solver = Matrix.Zero(2,2), i = 0;
while (solver.isSingular()) {
i++;
solver = Matrix.create([
[ N[i%3], N[(i+1)%3] ],
[ O[i%3], O[(i+1)%3] ]
]);
}
// Then we solve the simultaneous equations in the remaining dimensions
var inverse = solver.inverse().elements;
var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];
var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];
var intersection = [
inverse[0][0] * x + inverse[0][1] * y,
inverse[1][0] * x + inverse[1][1] * y
];
var anchor = [];
for (var j = 1; j <= 3; j++) {
// This formula picks the right element from intersection by
// cycling depending on which element we set to zero above
anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);
}
return Line.create(anchor, direction);
}
},
// Returns the point in the plane closest to the given point
pointClosestTo: function(point) {
var P = point.elements || point;
var A = this.anchor.elements, N = this.normal.elements;
var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];
return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);
},
// Returns a copy of the plane, rotated by t radians about the given line
// See notes on Line#rotate.
rotate: function(t, line) {
var R = Matrix.Rotation(t, line.direction).elements;
var C = line.pointClosestTo(this.anchor).elements;
var A = this.anchor.elements, N = this.normal.elements;
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
return Plane.create([
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
], [
R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],
R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],
R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]
]);
},
// Returns the reflection of the plane in the given point, line or plane.
reflectionIn: function(obj) {
if (obj.normal) {
// obj is a plane
var A = this.anchor.elements, N = this.normal.elements;
var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];
var newA = this.anchor.reflectionIn(obj).elements;
// Add the plane's normal to its anchor, then mirror that in the other plane
var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;
var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;
var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];
return Plane.create(newA, newN);
} else if (obj.direction) {
// obj is a line
return this.rotate(Math.PI, obj);
} else {
// obj is a point
var P = obj.elements || obj;
return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);
}
},
// Sets the anchor point and normal to the plane. If three arguments are specified,
// the normal is calculated by assuming the three points should lie in the same plane.
// If only two are sepcified, the second is taken to be the normal. Normal vector is
// normalised before storage.
setVectors: function(anchor, v1, v2) {
anchor = Vector.create(anchor);
anchor = anchor.to3D(); if (anchor === null) { return null; }
v1 = Vector.create(v1);
v1 = v1.to3D(); if (v1 === null) { return null; }
if (typeof(v2) == 'undefined') {
v2 = null;
} else {
v2 = Vector.create(v2);
v2 = v2.to3D(); if (v2 === null) { return null; }
}
var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];
var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];
var normal, mod;
if (v2 !== null) {
var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];
normal = Vector.create([
(v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),
(v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),
(v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)
]);
mod = normal.modulus();
if (mod === 0) { return null; }
normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);
} else {
mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);
if (mod === 0) { return null; }
normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);
}
this.anchor = anchor;
this.normal = normal;
return this;
}
};
// Constructor function
Plane.create = function(anchor, v1, v2) {
var P = new Plane();
return P.setVectors(anchor, v1, v2);
};
// X-Y-Z planes
Plane.XY = Plane.create(Vector.Zero(3), Vector.k);
Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);
Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);
Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;
// Utility functions
var $V = Vector.create;
var $M = Matrix.create;
var $L = Line.create;
var $P = Plane.create;
/*!
* jQuery 2d Transform v0.9.3
* http://wiki.github.com/heygrady/transform/
*
* Copyright 2010, Grady Kuhnline
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Date: Sat Dec 4 15:46:09 2010 -0800
*/
///////////////////////////////////////////////////////
// Transform
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* @var Regex identify the matrix filter in IE
*/
var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
rperc = /%/;
// Steal some code from Modernizr
var m = document.createElement( 'modernizr' ),
m_style = m.style;
function stripUnits(arg) {
return parseFloat(arg);
}
/**
* Find the prefix that this browser uses
*/
function getVendorPrefix() {
var property = {
transformProperty : '',
MozTransform : '-moz-',
WebkitTransform : '-webkit-',
OTransform : '-o-',
msTransform : '-ms-'
};
for (var p in property) {
if (typeof m_style[p] != 'undefined') {
return property[p];
}
}
return null;
}
function supportCssTransforms() {
if (typeof(window.Modernizr) !== 'undefined') {
return Modernizr.csstransforms;
}
var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
for ( var i in props ) {
if ( m_style[ props[i] ] !== undefined ) {
return true;
}
}
}
// Capture some basic properties
var vendorPrefix = getVendorPrefix(),
transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
// store support in the jQuery Support object
$.support.csstransforms = supportCssTransforms();
// IE9 public preview 6 requires the DOM names
if (vendorPrefix == '-ms-') {
transformProperty = 'msTransform';
transformOriginProperty = 'msTransformOrigin';
}
/**
* Class for creating cross-browser transformations
* @constructor
*/
$.extend({
transform: function(elem) {
// Cache the transform object on the element itself
elem.transform = this;
/**
* The element we're working with
* @var jQueryCollection
*/
this.$elem = $(elem);
/**
* Remember the matrix we're applying to help the safeOuterLength func
*/
this.applyingMatrix = false;
this.matrix = null;
/**
* Remember the css height and width to save time
* This is only really used in IE
* @var Number
*/
this.height = null;
this.width = null;
this.outerHeight = null;
this.outerWidth = null;
/**
* We need to know the box-sizing in IE for building the outerHeight and outerWidth
* @var string
*/
this.boxSizingValue = null;
this.boxSizingProperty = null;
this.attr = null;
this.transformProperty = transformProperty;
this.transformOriginProperty = transformOriginProperty;
}
});
$.extend($.transform, {
/**
* @var Array list of all valid transform functions
*/
funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
});
/**
* Create Transform as a jQuery plugin
* @param Object funcs
* @param Object options
*/
$.fn.transform = function(funcs, options) {
return this.each(function() {
var t = this.transform || new $.transform(this);
if (funcs) {
t.exec(funcs, options);
}
});
};
$.transform.prototype = {
/**
* Applies all of the transformations
* @param Object funcs
* @param Object options
* forceMatrix - uses the matrix in all browsers
* preserve - tries to preserve the values from previous runs
*/
exec: function(funcs, options) {
// extend options
options = $.extend(true, {
forceMatrix: false,
preserve: false
}, options);
// preserve the funcs from the previous run
this.attr = null;
if (options.preserve) {
funcs = $.extend(true, this.getAttrs(true, true), funcs);
} else {
funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
}
// Record the custom attributes on the element itself
this.setAttrs(funcs);
// apply the funcs
if ($.support.csstransforms && !options.forceMatrix) {
// CSS3 is supported
return this.execFuncs(funcs);
} else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
// Internet Explorer or Forced matrix
return this.execMatrix(funcs);
}
return false;
},
/**
* Applies all of the transformations as functions
* @param Object funcs
*/
execFuncs: function(funcs) {
var values = [];
// construct a CSS string
for (var func in funcs) {
// handle origin separately
if (func == 'origin') {
this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
} else if ($.inArray(func, $.transform.funcs) !== -1) {
values.push(this.createTransformFunc(func, funcs[func]));
}
}
this.$elem.css(transformProperty, values.join(' '));
return true;
},
/**
* Applies all of the transformations as a matrix
* @param Object funcs
*/
execMatrix: function(funcs) {
var matrix,
tempMatrix,
args;
var elem = this.$elem[0],
_this = this;
function normalPixels(val, i) {
if (rperc.test(val)) {
// this really only applies to translation
return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
}
return toPx(elem, val);
}
var rtranslate = /translate[X|Y]?/,
trans = [];
for (var func in funcs) {
switch ($.type(funcs[func])) {
case 'array': args = funcs[func]; break;
case 'string': args = $.map(funcs[func].split(','), $.trim); break;
default: args = [funcs[func]];
}
if ($.matrix[func]) {
if ($.cssAngle[func]) {
// normalize on degrees
args = $.map(args, $.angle.toDegree);
} else if (!$.cssNumber[func]) {
// normalize to pixels
args = $.map(args, normalPixels);
} else {
// strip units
args = $.map(args, stripUnits);
}
tempMatrix = $.matrix[func].apply(this, args);
if (rtranslate.test(func)) {
//defer translation
trans.push(tempMatrix);
} else {
matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
}
} else if (func == 'origin') {
this[func].apply(this, args);
}
}
// check that we have a matrix
matrix = matrix || $.matrix.identity();
// Apply translation
$.each(trans, function(i, val) { matrix = matrix.x(val); });
// pull out the relevant values
var a = parseFloat(matrix.e(1,1).toFixed(6)),
b = parseFloat(matrix.e(2,1).toFixed(6)),
c = parseFloat(matrix.e(1,2).toFixed(6)),
d = parseFloat(matrix.e(2,2).toFixed(6)),
tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
//apply the transform to the element
if ($.support.csstransforms && vendorPrefix === '-moz-') {
// -moz-
this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
} else if ($.support.csstransforms) {
// -webkit, -o-, w3c
// NOTE: WebKit and Opera don't allow units on the translate variables
this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
} else if ($.browser.msie) {
// IE requires the special transform Filter
//TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
var style = this.$elem[0].style;
var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
', sizingMethod=\'auto expand\'' + filterType + ')';
var filter = style.filter || $.curCSS( this.$elem[0], "filter" ) || "";
style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
// Let's know that we're applying post matrix fixes and the height/width will be static for a bit
this.applyingMatrix = true;
this.matrix = matrix;
// IE can't set the origin or translate directly
this.fixPosition(matrix, tx, ty);
this.applyingMatrix = false;
this.matrix = null;
}
return true;
},
/**
* Sets the transform-origin
* This really needs to be percentages
* @param Number x length
* @param Number y length
*/
origin: function(x, y) {
// use CSS in supported browsers
if ($.support.csstransforms) {
if (typeof y === 'undefined') {
this.$elem.css(transformOriginProperty, x);
} else {
this.$elem.css(transformOriginProperty, x + ' ' + y);
}
return true;
}
// correct for keyword lengths
switch (x) {
case 'left': x = '0'; break;
case 'right': x = '100%'; break;
case 'center': // no break
case undefined: x = '50%';
}
switch (y) {
case 'top': y = '0'; break;
case 'bottom': y = '100%'; break;
case 'center': // no break
case undefined: y = '50%'; //TODO: does this work?
}
// store mixed values with units, assumed pixels
this.setAttr('origin', [
rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
]);
//console.log(this.getAttr('origin'));
return true;
},
/**
* Create a function suitable for a CSS value
* @param string func
* @param Mixed value
*/
createTransformFunc: function(func, value) {
if (func.substr(0, 7) === 'reflect') {
// let's fake reflection, false value
// falsey sets an identity matrix
var m = value ? $.matrix[func]() : $.matrix.identity();
return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
}
//value = _correctUnits(func, value);
if (func == 'matrix') {
if (vendorPrefix === '-moz-') {
value[4] = value[4] ? value[4] + 'px' : 0;
value[5] = value[5] ? value[5] + 'px' : 0;
}
}
return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
},
/**
* @param Matrix matrix
* @param Number tx
* @param Number ty
* @param Number height
* @param Number width
*/
fixPosition: function(matrix, tx, ty, height, width) {
// now we need to fix it!
var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
origin = this.getAttr('origin'); // mixed percentages and px
// translate a 0, 0 origin to the current origin
var offset = calc.originOffset(new $.matrix.V2(
rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
rperc.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
));
// IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
//TODO: This seems wrong in the calculations
var sides = calc.sides();
// Protect against an item that is already positioned
var cssPosition = this.$elem.css('position');
if (cssPosition == 'static') {
cssPosition = 'relative';
}
//TODO: if the element is already positioned, we should attempt to respect it (somehow)
//NOTE: we could preserve our offset top and left in an attr on the elem
var pos = {top: 0, left: 0};
// Approximates transform-origin, tx, and ty
var css = {
'position': cssPosition,
'top': (offset.top + ty + sides.top + pos.top) + 'px',
'left': (offset.left + tx + sides.left + pos.left) + 'px',
'zoom': 1
};
this.$elem.css(css);
}
};
/**
* Ensure that values have the appropriate units on them
* @param string func
* @param Mixed value
*/
function toPx(elem, val) {
var parts = rfxnum.exec($.trim(val));
if (parts[3] && parts[3] !== 'px') {
var prop = 'paddingBottom',
orig = $.style( elem, prop );
$.style( elem, prop, val );
val = cur( elem, prop );
$.style( elem, prop, orig );
return val;
}
return parseFloat( val );
}
function cur(elem, prop) {
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
return elem[ prop ];
}
var r = parseFloat( $.css( elem, prop ) );
return r && r > -10000 ? r : 0;
}
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Safe Outer Length
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
$.extend($.transform.prototype, {
/**
* @param void
* @return Number
*/
safeOuterHeight: function() {
return this.safeOuterLength('height');
},
/**
* @param void
* @return Number
*/
safeOuterWidth: function() {
return this.safeOuterLength('width');
},
/**
* Returns reliable outer dimensions for an object that may have been transformed.
* Only use this if the matrix isn't handy
* @param String dim height or width
* @return Number
*/
safeOuterLength: function(dim) {
var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');
if (!$.support.csstransforms && $.browser.msie) {
// make the variables more generic
dim = dim == 'width' ? 'width' : 'height';
// if we're transforming and have a matrix; we can shortcut.
// the true outerHeight is the transformed outerHeight divided by the ratio.
// the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
if (this.applyingMatrix && !this[funcName] && this.matrix) {
// calculate and return the correct size
var calc = new $.matrix.calc(this.matrix, 1, 1),
ratio = calc.offset(),
length = this.$elem[funcName]() / ratio[dim];
this[funcName] = length;
return length;
} else if (this.applyingMatrix && this[funcName]) {
// return the cached calculation
return this[funcName];
}
// map dimensions to box sides
var side = {
height: ['top', 'bottom'],
width: ['left', 'right']
};
// setup some variables
var elem = this.$elem[0],
outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
boxSizingProp = this.boxSizingProperty,
boxSizingValue = this.boxSizingValue;
// IE6 && IE7 will never have a box-sizing property, so fake it
if (!this.boxSizingProperty) {
boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
}
// return it immediately if we already know it
if (this[funcName] && this[dim] == outerLen) {
return this[funcName];
} else {
this[dim] = outerLen;
}
// add in the padding and border
if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
}
if (boxSizingProp && boxSizingValue == 'content-box') {
outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
}
// remember and return the outerHeight
this[funcName] = outerLen;
return outerLen;
}
return this.$elem[funcName]();
}
});
/**
* Determine the correct property for checking the box-sizing property
* @param void
* @return string
*/
var _boxSizingProperty = null;
function _findBoxSizingProperty () {
if (_boxSizingProperty) {
return _boxSizingProperty;
}
var property = {
boxSizing : 'box-sizing',
MozBoxSizing : '-moz-box-sizing',
WebkitBoxSizing : '-webkit-box-sizing',
OBoxSizing : '-o-box-sizing'
},
elem = document.body;
for (var p in property) {
if (typeof elem.style[p] != 'undefined') {
_boxSizingProperty = property[p];
return _boxSizingProperty;
}
}
return null;
}
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Attr
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
var rfuncvalue = /([\w\-]*?)\((.*?)\)/g, // with values
attr = 'data-transform',
rspace = /\s/,
rcspace = /,\s?/;
$.extend($.transform.prototype, {
/**
* This overrides all of the attributes
* @param Object funcs a list of transform functions to store on this element
* @return void
*/
setAttrs: function(funcs) {
var string = '',
value;
for (var func in funcs) {
value = funcs[func];
if ($.isArray(value)) {
value = value.join(', ');
}
string += ' ' + func + '(' + value + ')';
}
this.attr = $.trim(string);
this.$elem.attr(attr, this.attr);
},
/**
* This sets only a specific atribute
* @param string func name of a transform function
* @param mixed value with proper units
* @return void
*/
setAttr: function(func, value) {
// stringify the value
if ($.isArray(value)) {
value = value.join(', ');
}
// pull from a local variable to look it up
var transform = this.attr || this.$elem.attr(attr);
if (!transform || transform.indexOf(func) == -1) {
// we don't have any existing values, save it
// we don't have this function yet, save it
this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
this.$elem.attr(attr, this.attr);
} else {
// replace the existing value
var funcs = [], parts;
// regex split
rfuncvalue.lastIndex = 0; // reset the regex pointer
while (parts = rfuncvalue.exec(transform)) {
if (func == parts[1]) {
funcs.push(func + '(' + value + ')');
} else {
funcs.push(parts[0]);
}
}
this.attr = funcs.join(' ');
this.$elem.attr(attr, this.attr);
}
},
/**
* @return Object
*/
getAttrs: function() {
var transform = this.attr || this.$elem.attr(attr);
if (!transform) {
// We don't have any existing values, return empty object
return {};
}
// replace the existing value
var attrs = {}, parts, value;
rfuncvalue.lastIndex = 0; // reset the regex pointer
while ((parts = rfuncvalue.exec(transform)) !== null) {
if (parts) {
value = parts[2].split(rcspace);
attrs[parts[1]] = value.length == 1 ? value[0] : value;
}
}
return attrs;
},
/**
* @param String func
* @return mixed
*/
getAttr: function(func) {
var attrs = this.getAttrs();
if (typeof attrs[func] !== 'undefined') {
return attrs[func];
}
//TODO: move the origin to a function
if (func === 'origin' && $.support.csstransforms) {
// supported browsers return percentages always
return this.$elem.css(this.transformOriginProperty).split(rspace);
} else if (func === 'origin') {
// just force IE to also return a percentage
return ['50%', '50%'];
}
return $.cssDefault[func] || 0;
}
});
// Define
if (typeof($.cssAngle) == 'undefined') {
$.cssAngle = {};
}
$.extend($.cssAngle, {
rotate: true,
skew: true,
skewX: true,
skewY: true
});
// Define default values
if (typeof($.cssDefault) == 'undefined') {
$.cssDefault = {};
}
$.extend($.cssDefault, {
scale: [1, 1],
scaleX: 1,
scaleY: 1,
matrix: [1, 0, 0, 1, 0, 0],
origin: ['50%', '50%'], // TODO: allow this to be a function, like get
reflect: [1, 0, 0, 1, 0, 0],
reflectX: [1, 0, 0, 1, 0, 0],
reflectXY: [1, 0, 0, 1, 0, 0],
reflectY: [1, 0, 0, 1, 0, 0]
});
// Define functons with multiple values
if (typeof($.cssMultipleValues) == 'undefined') {
$.cssMultipleValues = {};
}
$.extend($.cssMultipleValues, {
matrix: 6,
origin: {
length: 2,
duplicate: true
},
reflect: 6,
reflectX: 6,
reflectXY: 6,
reflectY: 6,
scale: {
length: 2,
duplicate: true
},
skew: 2,
translate: 2
});
// specify unitless funcs
$.extend($.cssNumber, {
matrix: true,
reflect: true,
reflectX: true,
reflectXY: true,
reflectY: true,
scale: true,
scaleX: true,
scaleY: true
});
// override all of the css functions
$.each($.transform.funcs, function(i, func) {
$.cssHooks[func] = {
set: function(elem, value) {
var transform = elem.transform || new $.transform(elem),
funcs = {};
funcs[func] = value;
transform.exec(funcs, {preserve: true});
},
get: function(elem, computed) {
var transform = elem.transform || new $.transform(elem);
return transform.getAttr(func);
}
};
});
// Support Reflection animation better by returning a matrix
$.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.cssHooks[func].get = function(elem, computed) {
var transform = elem.transform || new $.transform(elem);
return transform.getAttr('matrix') || $.cssDefault[func];
};
});
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Animation
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* @var Regex looks for units on a string
*/
var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
/**
* Doctors prop values in the event that they contain spaces
* @param Object prop
* @param String speed
* @param String easing
* @param Function callback
* @return bool
*/
var _animate = $.fn.animate;
$.fn.animate = function( prop, speed, easing, callback ) {
var optall = $.speed(speed, easing, callback),
mv = $.cssMultipleValues;
// Speed always creates a complete function that must be reset
optall.complete = optall.old;
// Capture multiple values
if (!$.isEmptyObject(prop)) {
if (typeof optall.original === 'undefined') {
optall.original = {};
}
$.each( prop, function( name, val ) {
if (mv[name]
|| $.cssAngle[name]
|| (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {
// Handle special easing
var specialEasing = null;
if (jQuery.isArray(prop[name])) {
var mvlen = 1, len = val.length;
if (mv[name]) {
mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
}
if ( len > mvlen
|| (len < mvlen && len == 2)
|| (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {
specialEasing = val[len - 1];
val.splice(len - 1, 1);
}
}
// Store the original values onto the optall
optall.original[name] = val.toString();
// reduce to a unitless number (to trick animate)
prop[name] = parseFloat(val);
}
} );
}
//NOTE: we edited prop above to trick animate
//NOTE: we pre-convert to an optall so we can doctor it
return _animate.apply(this, [arguments[0], optall]);
};
var prop = 'paddingBottom';
function cur(elem, prop) {
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
//return elem[ prop ];
}
var r = parseFloat( $.css( elem, prop ) );
return r && r > -10000 ? r : 0;
}
var _custom = $.fx.prototype.custom;
$.fx.prototype.custom = function(from, to, unit) {
var multiple = $.cssMultipleValues[this.prop],
angle = $.cssAngle[this.prop];
//TODO: simply check for the existence of CSS Hooks?
if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
this.values = [];
if (!multiple) {
multiple = 1;
}
// Pull out the known values
var values = this.options.original[this.prop],
currentValues = $(this.elem).css(this.prop),
defaultValues = $.cssDefault[this.prop] || 0;
// make sure the current css value is an array
if (!$.isArray(currentValues)) {
currentValues = [currentValues];
}
// make sure the new values are an array
if (!$.isArray(values)) {
if ($.type(values) === 'string') {
values = values.split(',');
} else {
values = [values];
}
}
// make sure we have enough new values
var length = multiple.length || multiple, i = 0;
while (values.length < length) {
values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
i++;
}
// calculate a start, end and unit for each new value
var start, parts, end, //unit,
fx = this,
transform = fx.elem.transform;
orig = $.style(fx.elem, prop);
$.each(values, function(i, val) {
// find a sensible start value
if (currentValues[i]) {
start = currentValues[i];
} else if (defaultValues[i] && !multiple.duplicate) {
start = defaultValues[i];
} else if (multiple.duplicate) {
start = currentValues[0];
} else {
start = 0;
}
// Force the correct unit on the start
if (angle) {
start = $.angle.toDegree(start);
} else if (!$.cssNumber[fx.prop]) {
parts = rfxnum.exec($.trim(start));
if (parts[3] && parts[3] !== 'px') {
if (parts[3] === '%') {
start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
} else {
$.style( fx.elem, prop, start);
start = cur(fx.elem, prop);
$.style( fx.elem, prop, orig);
}
}
}
start = parseFloat(start);
// parse the value with a regex
parts = rfxnum.exec($.trim(val));
if (parts) {
// we found a sensible value and unit
end = parseFloat( parts[2] );
unit = parts[3] || "px"; //TODO: change to an appropriate default unit
if (angle) {
end = $.angle.toDegree(end + unit);
unit = 'deg';
} else if (!$.cssNumber[fx.prop] && unit === '%') {
start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
} else if (!$.cssNumber[fx.prop] && unit !== 'px') {
$.style( fx.elem, prop, (end || 1) + unit);
start = ((end || 1) / cur(fx.elem, prop)) * start;
$.style( fx.elem, prop, orig);
}
// If a +=/-= token was provided, we're doing a relative animation
if (parts[1]) {
end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
}
} else {
// I don't know when this would happen
end = val;
unit = '';
}
// Save the values
fx.values.push({
start: start,
end: end,
unit: unit
});
});
}
return _custom.apply(this, arguments);
};
/**
* Animates a multi value attribute
* @param Object fx
* @return null
*/
$.fx.multipleValueStep = {
_default: function(fx) {
$.each(fx.values, function(i, val) {
fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
});
}
};
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.fx.multipleValueStep[func] = function(fx) {
var d = fx.decomposed,
$m = $.matrix;
m = $m.identity();
d.now = {};
// increment each part of the decomposition and recompose it
$.each(d.start, function(k) {
// calculate the current value
d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);
// skip functions that won't affect the transform
if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
(k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
return true;
}
// calculating
m = m.x($m[k](d.now[k]));
});
// save the correct matrix values for the value of now
var val;
$.each(fx.values, function(i) {
switch (i) {
case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
}
fx.values[i].now = val;
});
};
});
/**
* Step for animating tranformations
*/
$.each($.transform.funcs, function(i, func) {
$.fx.step[func] = function(fx) {
var transform = fx.elem.transform || new $.transform(fx.elem),
funcs = {};
if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
funcs[fx.prop] = [];
$.each(fx.values, function(i, val) {
funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
});
} else {
funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
}
transform.exec(funcs, {preserve: true});
};
});
// Support matrix animation
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.fx.step[func] = function(fx) {
var transform = fx.elem.transform || new $.transform(fx.elem),
funcs = {};
if (!fx.initialized) {
fx.initialized = true;
// Reflections need a sensible end value set
if (func !== 'matrix') {
var values = $.matrix[func]().elements;
var val;
$.each(fx.values, function(i) {
switch (i) {
case 0: val = values[0]; break;
case 1: val = values[2]; break;
case 2: val = values[1]; break;
case 3: val = values[3]; break;
default: val = 0;
}
fx.values[i].end = val;
});
}
// Decompose the start and end
fx.decomposed = {};
var v = fx.values;
fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
}
($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
funcs.matrix = [];
$.each(fx.values, function(i, val) {
funcs.matrix.push(val.now);
});
transform.exec(funcs, {preserve: true});
};
});
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Angle
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* Converting a radian to a degree
* @const
*/
var RAD_DEG = 180/Math.PI;
/**
* Converting a radian to a grad
* @const
*/
var RAD_GRAD = 200/Math.PI;
/**
* Converting a degree to a radian
* @const
*/
var DEG_RAD = Math.PI/180;
/**
* Converting a degree to a grad
* @const
*/
var DEG_GRAD = 2/1.8;
/**
* Converting a grad to a degree
* @const
*/
var GRAD_DEG = 0.9;
/**
* Converting a grad to a radian
* @const
*/
var GRAD_RAD = Math.PI/200;
var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
/**
* Functions for converting angles
* @var Object
*/
$.extend({
angle: {
/**
* available units for an angle
* @var Regex
*/
runit: /(deg|g?rad)/,
/**
* Convert a radian into a degree
* @param Number rad
* @return Number
*/
radianToDegree: function(rad) {
return rad * RAD_DEG;
},
/**
* Convert a radian into a degree
* @param Number rad
* @return Number
*/
radianToGrad: function(rad) {
return rad * RAD_GRAD;
},
/**
* Convert a degree into a radian
* @param Number deg
* @return Number
*/
degreeToRadian: function(deg) {
return deg * DEG_RAD;
},
/**
* Convert a degree into a radian
* @param Number deg
* @return Number
*/
degreeToGrad: function(deg) {
return deg * DEG_GRAD;
},
/**
* Convert a grad into a degree
* @param Number grad
* @return Number
*/
gradToDegree: function(grad) {
return grad * GRAD_DEG;
},
/**
* Convert a grad into a radian
* @param Number grad
* @return Number
*/
gradToRadian: function(grad) {
return grad * GRAD_RAD;
},
/**
* Convert an angle with a unit to a degree
* @param String val angle with a unit
* @return Number
*/
toDegree: function (val) {