Skip to content

Instantly share code, notes, and snippets.

@enjalot
Last active November 2, 2015 00:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enjalot/200cd9a890f98b82d6f3 to your computer and use it in GitHub Desktop.
Save enjalot/200cd9a890f98b82d6f3 to your computer and use it in GitHub Desktop.
positive definite
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="http://acko.net/files/mathbox2/mathbox-bundle.min.js"></script>
<script src="matrix.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg {
position: absolute;
left: 440px;
width: 500px;
height: 200;
}
#mathbox1 {
position: absolute;
left:20px;
top: 20px;
width: 400px;
height: 400px;
}
.number {
cursor: col-resize;
}
</style>
</head>
<script type="application/glsl" id="vertex-xyz">
// Enable STPQ mapping
#define POSITION_STPQ
void getPosition(inout vec4 xyzw, inout vec4 stpq) {
// Store XYZ per vertex in STPQ
stpq = xyzw;
}
</script>
<script type="application/glsl" id="fragment-shader">
// Enable STPQ mapping
#define POSITION_STPQ
vec4 getColor(vec4 rgba, inout vec4 stpq) {
float z = stpq.z/3.0;
if(z < 0.0) {
return vec4(0.4, 0, 0, 0.9);
}
return vec4(0.0, 0.4*z - 0.6, (0.4/z + 0.4), 0.9);
}
</script>
<body>
<svg>
<text x=140 y=55 id="eqn"></text>
<text x=140 y=80 id="result">Positive Definite? <tspan id="pd"></tspan></text>
</svg>
<div id="mathbox1"></div>
<script>
var svg = d3.select("svg");
var A = [
[1, 0],
[0, 1]
]
var matrix = new d3.svg.matrix()
.data(A).mapping([
["a", "b"],
["b", "c"]
])
.cellWidth(40)
.cellHeight(40)
.margin([10, 10])
var green = '#6ffe5c';
var orange = '#fe732d';
var matrixg = svg.append("g")
.attr("transform", "translate(20, 30)")
matrix.update(matrixg) // render the matrix
matrix.on("change", function(){
updateAnnotations();
})
function updateAnnotations() {
var a = A[0][0];
var b = A[0][1];
var c = A[1][1];
var isPD = a*c > b*b
d3.select("#pd").text( isPD ? "yes" : "no")
matrixg.selectAll("path.bracket").style({
stroke: (isPD ? "green" : orange)
})
d3.select("#eqn")
.text(a + " * x^2 + 2*" + b + "* x*y + " + c + " * y^2")
}
updateAnnotations();
// Load mathbox with controls
var mathbox = mathBox({
element: d3.select("#mathbox1").node(),
plugins: ['core', 'cursor', 'controls'],
controls: {
klass: THREE.OrbitControls,
},
});
if (mathbox.fallback) throw "WebGL error";
// mathbox.resize({viewWidth:100, viewHeight:100})
// Set renderer background
var three = mathbox.three;
three.renderer.setClearColor(new THREE.Color(0xffffff), 1.0);
// Set mathbox units and place camera
mathbox.set({ scale: 720, focus: 3 });
var camera = mathbox.camera({ proxy: true, position: [0, -1.0,1.1] });
// Create cartesian view
var view = mathbox
.cartesian({
range: [[-10, 10], [-10, 10], [-10, 15]],
scale: [1,1,1],
});
// 2D axes / grid
var axesWidth = 0.5
view.axis({ axis: 1, width: axesWidth });
view.axis({ axis: 2, width: axesWidth });
view.axis({ axis: 3, width: axesWidth });
view.grid({ width: axesWidth, divideX: 20, divideY: 20, opacity:1 });
var r = 3;
var ai = document.getElementById("a")
var bi = document.getElementById("b")
var ci = document.getElementById("c")
var area = view.area({
id: "main",
width: 20,
height: 20,
axes: [0, 2],
rangeX: [-r, r],
rangeY: [-r, r],
expr: function (emit, x, y, i, j, time, delta) {
var a = A[0][0];
var b = A[0][1];
var c = A[1][1];
var z = (a * x*x + 2*b*x*y + c * y*y) * 0.2;
emit(x, y, z);
},
channels:3
})
area
.shader({
code: "#vertex-xyz"
})
.vertex({
// Work in data XYZ instead of view XYZ
pass: 'data'
})
.shader({
code: "#fragment-shader",
})
.fragment({gamma: true })
// Fragment shaded shapes
.surface({
fill: true,
lineX: false,
lineY: false,
width: 1,
zBias: 1,
/*
shaded: true,
lineX: true,
lineY: true,
//color: "#51e4ff",
width: 0.5,
opacity: 1
*/
})
area.surface({
fill: false,
lineX: true,
lineY: true,
width: 1.5,
zBias: 1,
color: "#c6cfd1",
width: 0.5,
opacity: 1
})
view
.interval({
length: 32,
channels: 2,
expr: function (emit, x, i, t) {
var a = A[0][0];
var b = A[0][1];
var c = A[1][1];
emit(x, -x); //TODO line up with trough
},
})
.line({
width: 1,
color: orange,
})
view
.interval({
length: 32,
channels: 3,
expr: function (emit, x, i, t) {
var a = A[0][0];
var b = A[0][1];
var c = A[1][1];
var y = -x;
var z = (a * x*x + 2*b*x*y + c * y*y) * 0.2;
emit(x,y, z);//TODO line up with trough
},
})
.line({
width: 1,
color: green,
})
var view = mathbox.select("")
var surface = mathbox.select('surface')
</script>
</body>
d3.layout.matrix = matrixLayout;
d3.svg.matrix = matrixComponent;
function matrixComponent() {
var g;
var data = [[]];
var mapping = [[]];
var nodes = [];
var layout = d3.layout.matrix();
var margin = layout.margin();
var cellWidth = layout.cellWidth();
var cellHeight = layout.cellHeight();
/*
TODO
make scrubbing configurable, per-cell
*/
var dispatch = d3.dispatch("change")
this.update = function(group) {
if(group) g = group;
nodes = layout.nodes(data);
var line = d3.svg.line()
.x(function(d) { return d[0] })
.y(function(d) { return d[1] })
var brackets = g.selectAll("path.bracket")
.data([1, -1])
brackets.enter().append("path").classed("bracket", true)
.attr("d", function(d) {
var nRows = data.length;
var x0 = d * cellWidth/4;
var x1 = -margin[0]/2;
var y0 = -margin[1]/2;
var y1 = (cellHeight + margin[1]) * nRows - margin[1]/2
if(d === 1) {
return line([
[x0, y0],
[x1, y0],
[x1, y1],
[x0, y1]
])
} else {
var dx = (cellWidth + margin[0]) * data[0].length - margin[0]/2
x0 -= margin[0]/2
return line([
[x0 + dx, y0],
[dx, y0],
[dx, y1],
[x0 + dx, y1]
])
}
}).attr({
stroke: "#111",
fill: "none"
})
var cells = g.selectAll("g.number").data(nodes)
var enter = cells.enter().append("g").classed("number", true)
enter.append("rect").classed("bg", true)
cells.select("rect.bg")
.attr({
width: cellWidth,
height: cellHeight,
x: function(d) { return d.x },
y: function(d) { return d.y },
fill: "#fff"
})
enter.append("text")
cells.select("text").attr({
x: function(d) { return d.x + cellWidth/2 },
y: function(d) { return d.y + cellHeight/2 },
"alignment-baseline": "middle",
"text-anchor": "middle",
"line-height": cellHeight,
"fill": "#091242"
}).text(function(d) { return d.data })
var step = 0.1;
var that = this;
var drag = d3.behavior.drag()
.on("drag", function(d) {
var oldData = d.data;
var val = d.data + d3.event.dx * step
val = +(Math.round(val*10)/10).toFixed(1)
set(val, d.i, d.j);
//data[d.i][d.j] = val;
that.update()
dispatch.change(d, oldData)
})
cells.call(drag)
return this;
}
function set(val, i, j) {
var m = mapping[i][j];
if(m){
mapping.forEach(function(row, mi) {
row.forEach(function(col, mj) {
if(col === m) {
data[mi][mj] = val;
}
})
})
}
data[i][j] = val;
}
this.mapping = function(val) {
if(val) {
// TODO make sure dims match
mapping = val;
return this;
}
return mapping;
}
this.data = function(val) {
if(val) {
data = val;
nodes = layout.nodes(data);
return this;
}
return data;
}
this.margin = function(val) {
if(val) {
margin = val;
layout.margin(margin);
return this;
}
return margin;
}
this.cellWidth = function(val) {
if(val) {
cellWidth = val;
layout.cellWidth(cellWidth);
return this;
}
return cellWidth;
}
this.cellHeight = function(val) {
if(val) {
cellHeight = val;
layout.cellHeight(cellHeight);
return this;
}
return cellHeight;
}
d3.rebind(this, dispatch, "on")
return this;
}
function matrixLayout() {
/*
We accept our matrix data as a list of rows:
[ [a, b],
[c, d] ]
*/
var data = [[]];
var nodes;
var margin = [0, 0];
var cellWidth = 20;
var cellHeight = 20;
var nRows;
function getX(i) {
return i * (cellWidth + margin[0])
}
function getY(j) {
return j * (cellHeight + margin[1])
}
function newNodes() {
nRows = data.length;
nodes = [];
data.forEach(function(rows,i) {
rows.forEach(function(col, j) {
var node = {
x: getX(j),
y: getY(i),
data: col,
i: i,
j: j,
index: i * nRows + j
}
nodes.push(node);
})
})
}
function calculate() {
nRows = data.length;
data.forEach(function(rows,i) {
rows.forEach(function(col, j) {
var node = nodes[i * nRows + j];
if(!node) return;
node.data = col;
node.x = getX(j);
node.y = getY(i);
})
})
}
this.nodes = function(val) {
if(val) {
this.data(val);
}
return nodes;
}
this.data = function(val) {
if(val) {
if(val.length === data.length && val[0].length === data[0].length) {
// if the same size matrix is being updated,
// just update the values by reference
// the positions shouldn't change
data = val;
calculate();
} else {
data = val;
newNodes();
}
nRows = data.length;
return this;
}
return data;
}
this.margin = function(val) {
if(val) {
margin = val;
calculate();
return this;
}
return margin;
}
this.cellWidth = function(val) {
if(val) {
cellWidth = val;
calculate();
return this;
}
return cellWidth;
}
this.cellHeight = function(val) {
if(val) {
cellHeight = val
calculate();
return this;
}
return cellHeight;
}
return this;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment