Drawing a torus with three.js

Posted on June 26, 2018 by Stéphane Laurent

In previous posts, I have shown how to draw a torus whose equator passes by three given points with Haskell, R and POV-Ray. In this gist I provide the code for Asymptote. Here I’m going to provide the code for three.js.

Firstly, below is the Javascript function which returns the transformation matrix and the radius of the torus. The function takes as inputs the three points, each given as a Vector3, and returns an object containing the matrix as a Matrix4 and a number, the radius.

function TorusTransfo(p1, p2, p3) {
  var p12 = new THREE.Vector3();
  p12.addVectors(p1, p2).divideScalar(2);
  var p23 = new THREE.Vector3();
  p23.addVectors(p2, p3).divideScalar(2);
  var xcoef = (p1.y - p2.y) * (p2.z - p3.z) - (p1.z - p2.z) * (p2.y - p3.y);
  var ycoef = (p1.z - p2.z) * (p2.x - p3.x) - (p1.x - p2.x) * (p2.z - p3.z);
  var zcoef = (p1.x - p2.x) * (p2.y - p3.y) - (p1.y - p2.y) * (p2.x - p3.x);
  var offset1 = p1.x * xcoef + p1.y * ycoef + p1.z * zcoef;
  var v12 = p2.clone(); v12.sub(p1);
  var v23 = p3.clone(); v23.sub(p2);
  var offset21 = p12.dot(v12); var offset22 = p23.dot(v23);
  var M = new THREE.Matrix3();
  M.set(xcoef, v12.x, v23.x, ycoef, v12.y, v23.y, zcoef, v12.z, v23.z);
  invM = new THREE.Matrix3();
  invM.getInverse(M);
  // center = invM * (offset1, offset21, offset22)
  var A = invM.toArray();
  var center = new THREE.Vector3(
    A[0] * offset1 + A[1] * offset21 + A[2] * offset22,
    A[3] * offset1 + A[4] * offset21 + A[5] * offset22,
    A[6] * offset1 + A[7] * offset21 + A[8] * offset22
  );
  var v = p1.clone(); v.sub(center);
  var radius = v.length();
  var T = new THREE.Matrix4();
  if (xcoef == 0 && ycoef == 0) {
    T.identity();
    T.setPosition(center);
    return { matrix: T, radius: radius };
  }
  var n = new THREE.Vector3(xcoef, ycoef, zcoef);
  n.normalize();
  var s = Math.sqrt(n.x * n.x + n.y * n.y);
  var a = n.x / s; var b = n.y / s;
  var u = new THREE.Vector3(b, -a, 0);
  var v = new THREE.Vector3(); v.crossVectors(n, u);
  T.set(
    u.x, v.x, n.x, center.x,
    u.y, v.y, n.y, center.y,
    u.z, v.z, n.z, center.z,
    0, 0, 0, 1
  );
  return { matrix: T, radius: radius };
}

The addTorus function below conveniently adds the torus to a three.js object. The last argument r is the desired minor radius of the torus.

Then we apply the addTorus function as follows. First, define three points and the desired minor radius:

Then add the torus to an object in this way:

As an illustration, below are the linked cyclides plotted with three.js. Click on the object and drag the mouse to play with it. You can get the full code by looking at the source, or by going to this gist.