block by enjalot 200cd9a890f98b82d6f3

positive definite

Full Screen

Built with blockbuilder.org

index.html

<!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="//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>

matrix.js

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