lens optics

playing around with the thin lens model

by Stephan Mantler

HTML

<script src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.js@3.0/dist/svg.min.js"></script>
<html>
<body>
<div id="svg" />
<div><label class="label" for="focal">Focal Length</label><span id="value-focal">...</span><span class="bounds bounds-min">14mm</span><input type="range" value="50" min="14" max="200" id="focal" /><span class="bounds bounds-max">200mm</span></div>
<div><label class="label" for="aperture">Aperture</label><span id="value-aperture">...</span><span class="bounds bounds-min">f/1.4</span><input type="range" value="2.8" min="1.4" max="16" id="aperture" /><span class="bounds bounds-max">f/16</span></div>
<div><label class="label" for="focus">Focus Distance</label><span id="value-focus">...</span><span class="bounds bounds-min">10mm</span><input type="range" value="40" id="focus" min="1" max="500" /><span class="bounds bounds-max">500mm</span></div>
<div><label class="label" for="object">Object Distance</label><span id="value-object">...</span><span class="bounds bounds-min">10mm</span><input type="range" value="40" id="object" min="1" max="500" /><span class="bounds bounds-max">500mm</span></div>
<div><label class="label" for="object">Lens Tilt</label><span id="value-tilt">...</span><span class="bounds bounds-min">10mm</span><input type="range" value="0" id="tilt" min="-20" max="20" /><span class="bounds bounds-max">500mm</span></div>



</body>
</html>

CSS

.label {
  display: inline-block;
  width: 10em;
}

input {
  width: 30em;
}

span.bounds {
  display: inline-block;
  width: 4em;
  font-size: 0.8em;
  vertical-align: center;
}

.bounds-min {
  text-align: right;
}

JavaScript

/**
 * Super simple sketch of basic lens optics
 *
 * Stephan Mantler
 * step@stepman.is | instagram.com/stepman 
 */

/*
 * A simple class for 2D points / vectors
 * This could probably be folded into SVG.Point but here it's a
 * separate class for clarity.
 */
class Vector {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  
  // return a copy that is rotated 90 degrees counterclockwise
  get ccw90() {
    return new Vector(-this.y, this.x);
  }
  
  // return a copy of this vector with unit length
  get normalized() {
    return this.scaledBy(1/this.length);
  }
  
  // return a copy that is scaled by the specified factor
  scaledBy(scalar) {
    return new Vector(this.x * scalar, this.y * scalar);
  }
  
  // returns a copy that is the sum of this and another vector
  add(vector) {
    return new Vector(this.x + vector.x, this.y + vector.y);
  }
  
  // returns a copy that is the difference of this and another vector
  subtract(vector) {
    return new Vector(this.x - vector.x, this.y - vector.y);
  }
  
	// return the dot product of this and another vector
  dot(vector) {
    return this.x * vector.x + this.y * vector.y;
  }
  
  // return the length of this vector
  get length() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }
  
}

/*
 * Implements a simple optical lens
 *
 * We assume the optical assembly is along the X axis, but this lens
 * can be tilted in any way (effeectively rotating it off axis from
 * the focus plane, which we assume is always vertical)
 */
class Lens {
	constructor(focalLength, aperture = 4) {
    this.focalLength = focalLength;
    this.aperture = aperture;
    this.position = new Vector(0, 0);
    // optical axis of the lens
    this.orientation = new Vector(1, 0).normalized;
  }
  
  // returns the effective diameter (from focal length & aperture)
  get diameter() { return this.focalLength / this.aperture; }
  // returns the effective radius
  get radius() { return this.diameter / 2; }
  
  // return a...