<!DOCTYPE html>
<meta charset="utf-8">
<style>
.polygon {
  fill: none;
  stroke: #000;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src='https://npmcdn.com/babel-core@5.8.34/browser.min.js'></script>
<script src="obj_parse.js"></script>
<script lang='babel' type='text/babel'>
  const width = 960;
  const height = 500;
  const margin = 20;

  const squareLength = d3.min([width, height]) - 2 * margin;

  const xScale = d3.scale.linear()
      .range([width / 2 - squareLength / 2, width / 2 + squareLength / 2]);

  const yScale = d3.scale.linear()
      .range([height / 2 + squareLength / 2, height / 2 - squareLength / 2]);

  const zScale = d3.scale.linear()
      .range([0, 1]);

  const camera = {
    inclination: Math.PI / 2,
    azimuth: 0,
    center: {
      x: 0,
      y: 0,
      z: 0
    }
  };

  let panMode = false;
  let surfaces;
  let obj;

  const zoom = d3.behavior.zoom()
      .scaleExtent([1, 10])
      .on('zoom', zoomed); // eslint-disable-line

  const svg = d3.select('body').append('svg')
      .attr('width', width)
      .attr('height', height)
      .call(zoom);

  const container = svg.append('g');

  function translatePT(pt, center) {
    return {
      x: pt.x - center.x,
      y: pt.y - center.y,
      z: pt.z - center.z
    };
  }

  function rotatePT(pt, camera) { // eslint-disable-line
    // first rotate around y-axis to the azimuth angle
    const xp2 = pt.x * Math.cos(camera.azimuth) - pt.z * Math.sin(camera.azimuth);
    const zp2 = pt.x * Math.sin(camera.azimuth) + pt.z * Math.cos(camera.azimuth);

    // then around the x axis to pi/2 minus the inclination angle
    const a = Math.PI / 2 - camera.inclination;
    const zp3 = zp2 * Math.cos(a) - pt.y * Math.sin(a);
    const yp3 = zp2 * Math.sin(a) + pt.y * Math.cos(a);

    return { x: xp2, y: yp3, z: zp3 };
  }

  function project_orthographic(surfaces, camera) { // eslint-disable-line
    surfaces.forEach(points => {
      points.forEach(point => {
        const pointT = translatePT(point, camera.center);
        const pointR = rotatePT(pointT, camera);

        point.px = pointR.x;
        point.py = pointR.y;
        point.pz = pointR.z;
      });
    });
    return surfaces;
  }

  function draw(surfaces) { // eslint-disable-line
    const polygons = container.selectAll('.polygon')
      .data(surfaces);

    polygons.enter().append('path')
      .attr('class', 'polygon');

    polygons
      .attr('d', datum => {
        const d = datum.map(point => [xScale(point.px), yScale(point.py)]);
        return `M${d.join('L')}Z`;
      })
      .attr('opacity', datum => {
        const d = datum.map(point => point.pz);
        return zScale(d3.max(d));
      });
  }

  function update(surfaces, camera) { // eslint-disable-line
    surfaces = project_orthographic(surfaces, camera);
    draw(surfaces);
  }

  let mousePanX = 0;
  let mousePanY = 0;
  let mouseRotX = 0;
  let mouseRotY = 0;
  let prevMouseX = 0;
  let prevMouseY = 0;
  let prevScale = 1;

  function zoomed() {
    const deltaX = d3.event.translate[0] - prevMouseX;
    const deltaY = d3.event.translate[1] - prevMouseY;

    prevMouseX = d3.event.translate[0];
    prevMouseY = d3.event.translate[1];

    if (d3.event.scale === prevScale) {
      if (panMode) {
        mousePanX += deltaX;
        mousePanY += deltaY;
        container.attr('transform', `'translate(${mousePanX}, ${mousePanY})`);
      } else {
        mouseRotX += deltaX;
        mouseRotY += deltaY;
        camera.inclination = Math.PI / 2 + mouseRotY / 500;
        camera.azimuth = -1 * mouseRotX / 500;
      }
    } else {
      xScale
        .range([width / 2 - d3.event.scale * squareLength / 2,
                width / 2 + d3.event.scale * squareLength / 2]);

      yScale
        .range([height / 2 + d3.event.scale * squareLength / 2,
                height / 2 - d3.event.scale * squareLength / 2]);

      prevScale = d3.event.scale;
    }
    // console.log('camera', camera);
    // console.log('xScale.range()', xScale.range());
    // console.log('yScale.range()', yScale.range());
    update(surfaces, camera);
  }

  function checkKeyDown(e) {
    const event = window.event ? window.event : e;
    if (event.keyCode === 32) { panMode = true; }
  }

  function checkKeyUp(e) {
    const event = window.event ? window.event : e;
    if (event.keyCode === 32) { panMode = false; }
  }

  document.onkeydown = checkKeyDown;
  document.onkeyup = checkKeyUp;

  // let surfaces;
  // let obj;
  d3.text('cessna.obj', (error, objFileText) => {
    if (error) throw error;

    obj = parse_obj_text(objFileText); // eslint-disable-line
    surfaces = obj.surfaces;
    const extreme = d3.max([Math.abs(obj.extents[0]), Math.abs(obj.extents[1])]);

    // set a custom starting position
    // by setting the properties of the camera object
    camera.center = {
      x: 1.0351724999999998,
      y: 0,
      z: 0
    };
    camera.azimuth = -1.046;
    camera.inclination = 1.9627963267948965;

    xScale.domain([-extreme, extreme]);
    yScale.domain([-extreme, extreme]);
    zScale.domain([-extreme, extreme]);

    // manually set the range of the
    // xScale and yScale as well
    xScale.range([25.05443972783297, 934.9455602721671]);
    yScale.range([704.9455602721671, -204.94556027216703]);

    update(surfaces, camera);
  });
</script>