computeFrenetFrames method

FrenetFrames computeFrenetFrames(
  1. int segments,
  2. bool closed
)

Generates the Frenet Frames. Requires a curve definition in 3D space. Used in geometries like TubeGeometry or ExtrudeGeometry.

Implementation

FrenetFrames computeFrenetFrames(int segments, bool closed) {
  // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf

  final normal = Vector3();

  final List<Vector3> tangents = [];
  final List<Vector3> normals = [];
  final List<Vector3> binormals = [];

  final vec = Vector3();
  final mat = Matrix4();

  // compute the tangent vectors for each segment on the curve

  for (int i = 0; i <= segments; i++) {
    final u = i / segments;

    tangents.add(
      getTangentAt(u, Vector3()) as Vector3
    );
    tangents[i].normalize();
  }

  // select an initial normal vector perpendicular to the first tangent vector,
  // and in the direction of the minimum tangent xyz component

  normals.add(Vector3());
  binormals.add(Vector3());
  double min = double.maxFinite;
  final tx = tangents[0].x.abs();
  final ty = tangents[0].y.abs();
  final tz = tangents[0].z.abs();

  if (tx <= min) {
    min = tx;
    normal.setValues(1, 0, 0);
  }

  if (ty <= min) {
    min = ty;
    normal.setValues(0, 1, 0);
  }

  if (tz <= min) {
    normal.setValues(0, 0, 1);
  }

  vec.cross2(tangents[0], normal).normalize();

  normals[0].cross2(tangents[0], vec);
  binormals[0].cross2(tangents[0], normals[0]);

  // compute the slowly-varying normal and binormal vectors for each segment on the curve

  for (int i = 1; i <= segments; i++) {
    normals.add(normals[i - 1].clone());

    binormals.add(binormals[i - 1].clone());

    vec.cross2(tangents[i - 1], tangents[i]);

    if (vec.length > MathUtils.epsilon) {
      vec.normalize();

      final theta = math.acos(MathUtils.clamp(tangents[i - 1].dot(tangents[i]),
          -1, 1)); // clamp for floating pt errors

      normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
    }

    binormals[i].cross2(tangents[i], normals[i]);
  }

  // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same

  if (closed) {
    double theta = math.acos(MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1));
    theta /= segments;

    if (tangents[0].dot(vec.cross2(normals[0], normals[segments])) >
        0) {
      theta = -theta;
    }

    for (int i = 1; i <= segments; i++) {
      // twist a little...
      normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i));
      binormals[i].cross2(tangents[i], normals[i]);
    }
  }

  return FrenetFrames(tangents: tangents, normals: normals, binormals: binormals);
}