Performance tricks§

nalgebra defines a few types that may save you valuable computation times. Those types have strong restrictions in their use and have a quite narrow semantic. However, they can sometimes save you a few square roots, corner-cases checking, or even avoid costly matrix multiplications.

The Unit wrapper§

Many geometrical algorithms require some of its inputs to have a unit norm. For example the normal of a triangle or a quaternion that represents a 3D rotation should have a magnitude equal to 1. That’s why the Unit wrapper type is here to ensure that the underlying value has a unit norm. For example Unit<Vector3<f32>> is a normalized 3D vector, i.e., it lies on the unit 3-dimensional sphere . Also note that the UnitQuaternion<T> representing a 3D rotation is actually a type alias for Unit<Quaternion<T>>. In general, the Unit wrapper should be used whenever you write an algorithm that expects a normalized direction as an input. Doing so, you avoid the need to normalize the input vector yourself and don’t have to deal with special cases where the given direction is zero. Here is a simple example that computes the length of one vector along a given direction:

fn length_on_direction(v: &Vector3<f32>, dir: &Unit<Vector3<f32>>) -> f32 {
  // No need to normalize `dir`: we know that it is non-zero and normalized.
  na::dot(v, dir.as_ref())
}
fn length_on_direction(v: &Vector3<f32>, dir: &Vector3<f32>) -> f32 {
  // Obligatory normalization of the direction vector (and test, for robustness).
  if let Some(unit_dir) = dir.try_normalize(1.0e-6) {
    na::dot(v, &unit_dir)
  }
  else {
    // Normalization failed because the norm was too small.
    panic!("Invalid input direction.")
  }
}

The Id type§

The Id type (re-exported from the alga crate) represents the identity element of any operator for any algebraic entity. In practice, all its operations are no-ops. While using this where all types are explicitly known might seem useless, it can save performances in generic code. Indeed, Id implements all transformation traits from the alga crate: Translation, Rotation, Transformation etc. Thus, a generic algorithm that performs lots of multiplication by a transformation given in input will benefit from the fact that Id does all of them for free. In other words, passing Id instead of, say, Rotation3::<f32>::identity(), to a generic function will avoid useless matrix multiplications.

/*
 * Applies `n` times the transformation `t` to the vector `v` and sum each
 * intermediate value.
 */
fn complicated_algorithm<T>(v: &Vector3<f32>, t: &T, n: usize) -> Vector3<f32>
  where T: Transformation<Point3<f32>> {

  let mut result = *v;

  // Do lots of operations involving t.
  for _ in 0 .. n {
    result = v + t.transform_vector(&result);
  }

  result
}


/*
 * The two following calls are equivalent in term of result.
 */
fn main() {
  let v = Vector3::new(1.0, 2.0, 3.0);

  // The specialization generated by the compiler will do vector additions only.
  let result1 = complicated_algorithm(&v, &Id::new(), 100000);

  // The specialization generated by the compiler will also include matrix multiplications.
  let iso     = Isometry3::identity();
  let result2 = complicated_algorithm(&v, &iso, 100000);

  // They both return the same result.
  assert!(result1 == Vector3::new(100001.0, 200002.0, 300003.0));
  assert!(result2 == Vector3::new(100001.0, 200002.0, 300003.0));
}

Computer graphics recipes Generic programming