text: <!DOCTYPE html>
  <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>VisIdeal2D</title>
         <!-- <script type="text/javascript" src="js/d3.v3.js"></script>-->
          <link href="css/bootstrap.min.css" rel="stylesheet">
      <link href="css/BootSideMenu.css" rel="stylesheet">
      <link href="css/nouislider.min.css" rel="stylesheet">
          <style>
             #svgMain {margin-left:auto; margin-right: auto; display:block}
              .axis path,
              .axis line {
                  fill: none;
                  stroke: black;
                  stroke-width: 2;
                  shape-rendering: crispEdges;
              }
  
              .axis text {
                  font-family: sans-serif;
                  font-size: 11px;
              }
  
  
  
  
        .noselect {
          -webkit-touch-callout: none;
          -webkit-user-select: none;
          -khtml-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
        }
  
        a { cursor: pointer; cursor: hand; }
  
        #canvasElement2d {
          background-color: #FFF;
          cursor: default;
          display:inline;
          z-index:2;
          padding-bottom: 10px;
        }
  
        #canvasElement2d:not(.active):not(.shift) {
          cursor: crosshair;
        }
  
        /* CSS for sliders */
        #charge-slider{
           margin: 10px;
           display: inline-block;
           height: 10px;
           width: 120px;
        }
      
        #linkdist-slider{
           margin: 10px;
           display: inline-block;
           height: 10px;
           width: 120px;
        }
      
        .noUi-horizontal .noUi-handle {
           width: 24px;
           height: 18px;
           left: -17px;
           top: -6px;
        }
      
        .noUi-horizontal {
           height: 14px;
        }
  
        /* Vertical lines on the handles */
        .noUi-handle:before,
        .noUi-handle:after {
           content: "";
           display: block;
           position: absolute;
           height: 10px;
           width: 1px;
           background: #E8E7E6;
           left: 10px;
           top: 3px;
        }
        .noUi-handle:after {
           left: 12px;
        }
          
        .noUi-horizontal.noUi-extended {
           padding-right: 20px;
        }
        .noUi-horizontal.noUi-extended .noUi-handle {
           left: -2px;
        }
        .noUi-horizontal.noUi-extended .noUi-origin  {
           right: -32px;
        }
          
        path.link {
          fill: none;
          stroke: #B6B6B6;
          stroke-width: 2px;
          cursor: default;
        }
  
        #canvasElement2d:not(.active):not(.shift) path.link {
          cursor: pointer;
        }
  
        path.link.selected {
          stroke-dasharray: 10,2;
        }
          
        path.link.highlighted {
          fill: none;
          /* stroke: #B6B6B6; */
          stroke: #FF0000;
          stroke-width: 4px;
          cursor: default;
        }
  
        path.link.dragline {
          pointer-events: none;
        }
  
        path.link.hidden {
          stroke-width: 0;
        }
  
        circle.node {
          stroke-width: 0px;
          cursor: pointer;
        }
  
        circle.node.reflexive {
          stroke: #000 !important;
          stroke-width: 2.5px;
        }
       
        circle.node.highlighted {
          stroke: #000 !important;    
          stroke-width: 2.5px;
        }
  
        text {
          font: 12px sans-serif;
          pointer-events: none;
        }
  
        text.id {
          text-anchor: middle;
          font-weight: bold;
        }
  
        #canvasElement3d {
          background-color: #FFF;
          cursor: default;
          display:none;
          z-index: 0;
          padding-bottom: 10px;
        }
  
        #constructorString {
          -o-user-select: text;
          -moz-user-select: text;
          -webkit-user-select: text;
          user-select: text;
          z-index: 100;
          padding-bottom: 5px;
        }
  
        #incString {
          -o-user-select: text;
          -moz-user-select: text;
          -webkit-user-select: text;
          user-select: text;
          z-index: 100;
          padding-bottom: 5px;
        }
        #adjString {
          -o-user-select: text;
          -moz-user-select: text;
          -webkit-user-select: text;
          user-select: text;
          z-index: 100;
          padding-bottom: 5px;
        }
          </style>
          <script type="text/javascript">
        //global variables
        curConvex = true;
        h = window.innerHeight-10;//600;
        w = window.innerWidth-10;//900;
        dataset = [[1, 3], [4, 0], [0, 5]];
      </script>
      </head>
      <body>
  
  
      <div id="side">
            <h2>&#8202;&#8202;Menu</h2>
            <div class="list-group" id="menuList">
              </div>
              <a id="convexToggle" href="#" class="list-group-item">Convex Hull: off</a>
              <a id="idealGens" href="#" class="list-group-item">Ideal Generators</a>
              <!-- we can put these options in later
              <a id="exportTikz" href="#" class="list-group-item">Generate TikZ code</a>
              <a id="endSession" href="#" class="list-group-item">End session</a>            
              -->
            </div>
          </div>
    
  
      <div class="container">
      <div id="canvasElement2d"></div>
    </div>
  
    <script src="js/jquery-1.11.3.min.js"></script>
    <script src="js/BootSideMenu.js"></script>
    <script src="js/d3.v3.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
  <!--  <script src="js/clipboard.min.js"></script>
    <script src="js/nouislider.min.js"></script>
  -->
  
  
      <script type="text/javascript">
  
      $('#side').BootSideMenu({side:"right", closeOnClick: false, width: "230px"});
  
      $(document).ready(function(){
  
      // display convex hull when clicked
      $("#convexToggle").on("click", function(){
          if(curConvex) {
            $(this).html("Convex Hull: on");
            curConvex = !curConvex;
              hull.attr("opacity",1);
          } else {
            $(this).html("Convex Hull: off");
            curConvex = !curConvex;
              hull.attr("opacity",0);
          }
        });
  
      // show the ideal generators when clicked
      $("#idealGens").on("click", function(){
            $(this).html("Ideal Generators: "  + labels);
        });
      });
       
          dataset.sort(function(a,b) {return a[0]-b[0]});
  
          svg = d3.select("body")
                          .append("svg")
                          .attr("height", h)
                          .attr("width", w)
                          .attr("id", "svgMain");
  
          // find largest x and y exponents
          xMax = 0;
          yMax = 0;
          for (i = 0; i < dataset.length; i++) {
                  if (dataset[i][0] > xMax) {
                          xMax = dataset[i][0];
                  }
                  if (dataset[i][1] > yMax) {
                          yMax = dataset[i][1];
                  }
          }
  
          // make the lattice go one unit beyond max x and y values
          // looks like chart is an array of arrays of 1's
          chart = [];
          for (i = 0; i < yMax+1; i++) {
                  chart.push([]);
                  for (j = 0; j < xMax+1; j++) {
                          chart[i].push(1);
                  }
          }
  
          // determine the size of each unit square
          sq = Math.ceil(Math.min(h/(yMax+2), w/(xMax+2)));
  
          // datum will be a coordinate in the plane and blankout will 
          // put a 0 in every lattice point above and to the right
          var blankOut = function(datum, chart) {
                  for (i = 0; i < chart.length; i++) {
                          if (i >= datum[1]) {
                                  for (j = 0; j < chart[0].length; j++) {
                                          if (j >= datum[0]) {
                                                  chart[i][j] = 0;
                                          }
                                  }
                          }
                  }
                  return chart;
          }
  
          // put 0 in coordinates for elements of ideal
          for (k = 0; k < dataset.length; k++) {
                  chart = blankOut(dataset[k], chart);
                  }
  
  
          // the lattice points not in the ideal
          var makeDatFromChart = function(chart) {
                  dat = [];
                  for (i = 0; i < chart.length; i++) {
                          for (j = 0; j < chart[0].length; j++) {
                                  if (chart[i][j] === 1) {
                                          dat.push([i, j]);
                                  }
                          }
                  }
                  return dat;
          }
  
          // the coordinates of the points not in the ideal
          dat = makeDatFromChart(chart);
  
  
          
          // i'm not sure but i think the lines we get when switching
          // between convex hull are coming from the scale
          var xScale = d3.scale.linear();
          xScale.domain([0,xMax+1]);
          xScale.range([sq/2, (xMax+1.5)*sq]);
  
          var yScale = d3.scale.linear();
          yScale.domain([0, yMax+1]);
          yScale.range([h-1.5*sq, h-(yMax+2.5)*sq]);
  
  
  
  
          // list of triangles from generator to another generator to
          // the point with (x max, y max)
          tri = [];
  
          for (i = 0; i < dataset.length-1; i++) {
                  for(j = i+1; j < dataset.length; j++) {
                          tri.push(xScale(dataset[i][0]+.01).toString() + "," +
                          yScale(dataset[i][1]-1).toString() + " " +
                          xScale(dataset[j][0]+.01).toString() + "," +
                          yScale(dataset[j][1]-1).toString() + " " +
                          xScale(Math.max(dataset[i][0]+.01,
                          dataset[j][0]+.01)).toString() + "," +
                          yScale(Math.max(dataset[i][1],
                          dataset[j][1])-1).toString());
                  }
          }
  
  
  
          console.log(tri);
  
          var xAxis = d3.svg.axis()
                          .scale(xScale)
                          .ticks(xMax)
                          .orient("bottom");
  
          var yAxis = d3.svg.axis()
                          .scale(yScale) 
                          .ticks(yMax)
                          .orient("left");
  
          var latticePoints= [];
          for (i = 0; i <= yMax; i++) {
                  for (j = 1; j <= xMax+1; j++) {
                          latticePoints.push([i,j]);
                  }
          }
  
  
          // shades all the squares not in the ideal
          ideal = svg.selectAll("rect")
                          .data(dat)
                          .enter()
                          .append("rect")
                          .attr("x", function(d) { return Math.ceil(xScale(d[1])); })
                          .attr("y", function(d) { return Math.ceil(yScale(d[0])); })
                          .attr("width", sq)
                          .attr("height", sq) 
                          .attr("fill", "#f5deb3");
  
          // shades all the triangles. default is transparent
          hull = svg.selectAll("polygon")
                          .data(tri)
                          .enter()
                          .append("polygon")
                          .attr("points", function(d) { return d; })
                          .attr("fill", "#FFFFFF")
                          .attr("opacity", 0);
  
          // shades all the lattice points
          lattice = svg.selectAll("circle.lattice")
                          .data(latticePoints)
                          .enter()
                          .append("circle")
                          .attr("cx", function(d) { return Math.floor(xScale(d[1])); })
                          .attr("cy", function(d) { return Math.floor(yScale(d[0])); })
                          .attr("r", 4) 
                          .attr("fill", "#b3caf5");
  
  
          svg.append("g")
                  .attr("class", "axis")
                  .attr("transform", "translate(0," + (h-sq/2) + ")")
                  .call(xAxis);
  
          svg.append("g")
                  .attr("class", "axis")
                  .attr("transform", "translate(" + sq/2 + "," + sq + ")")
                  .call(yAxis);
  
  
          var makeXYstring = function(x,y) {
                  if (x === 0) { xstr = ""; }
                  else if (x === 1) { xstr = "x"; }
                  else { xstr = "x<sup>" + x.toString() + "</sup>"; }
                  if (y === 0) { ystr = ""; }
                  else if (y === 1) { ystr = "y"; }
                  else { ystr = "y<sup>" + y.toString() + "</sup>"; }
                  xystr = xstr + ystr;
                  return xystr;
          }
  
          var labels = []
          for (i = 0; i < dataset.length; i++) {
                  labels.push(makeXYstring(dataset[i][0],dataset[i][1]));
          }
  
  
          var pointsBelow = function (point1, point2, extX, extY) {
                  if (point1[0] < point2[0]) {
                          first = point1;
                          second = point2;
                  }
                  else { first = point2; second = point1; }
                  xMin = first[0];
                  xMax = second[0];
                  yMin = 0;
                  yMax = Math.max(point1[1], point2[1]);
                  points = []
                  for (x = xMin; x <= xMax; x++) {
                          for (y = yMin; y <= yMax; y++) {
                                  t = (x - xMin)/(xMax - xMin);
                                  l = first[1]*(1-t) + second[1]*(t);
                                  if (y < l) { points.push([x,y]); }
                          }
                  }
                  for (x = 0; x < xMin; x++) {
                          for (y = 0; y <= extY; y++) {
                                  points.push([x,y]);
                          }
                  }
                  for (x = xMax+1; x <= extX; x++) {
                          for (y = 0; y <= extY; y++) {
                                  points.push([x,y]);
                          }
                  }
                  return points;
          }
  
          var comparePoints = function(point1, point2) {
                  if (point1[0] === point2[0] && point1[1] === point2[1]) { return true; }
                  else { return false; }
          }
  
          extX = 0
          extY = 0
          for (i = 0; i < dataset.length; i++) {
                  if (dataset[i][0] > extX) { extX = dataset[i][0]; }
                  if (dataset[i][1] > extY) { extY = dataset[i][1]; }
          }
  
          pointList = []
          for (i = 0; i <= extX; i++) {
                  for (j = 0; j <= extY; j++) {
                          pointList.push([i,j])
                  }
          }
  
          for (i = 0; i < dataset.length-1; i++) {
                  for (j = i+1; j < dataset.length; j++) {
                          pointsUnder = pointsBelow(dataset[i], dataset[j], extX, extY);
                          newPointList = []
                          for (a = 0; a < pointsUnder.length; a++) {
                                  for (b = 0; b < pointList.length; b++) {
                                          if (comparePoints(pointsUnder[a], pointList[b])) { newPointList.push(pointsUnder[a]); }
                                  }
                          }
                          pointList = newPointList;
                  }
          }
  
          innerLattice = svg.selectAll("circle.inner")
                          .data(pointList)
                          .enter()
                          .append("circle")
                          .attr("cx", function(d) { return Math.floor(xScale(d[0])); })
                          .attr("cy", function(d) { return Math.floor(yScale(d[1]-1)); })
                          .attr("r", 2) 
                          .attr("fill", "#FFFFFF");
  
  
          </script>
  
  
  </body>
  </html>