/// <summary>
        /// Override to compute rotations unconstrained as a rotation about the gravity vector, Y-axis.
        /// </summary>
        /// <returns>True on success.</returns>
        /// <remarks>
        /// It takes at least 3 non-collinear points to imply a rotation.
        /// If there are fewer than that, this reverts back to the behavior of
        /// computing a rotation which pitches to align points but doesn't introduce roll.
        /// </remarks>
        protected override bool ComputeRotations()
        {
            bool haveNonZero = false;

            for (int i = 0; i < actives.Count; ++i)
            {
                for (int j = i + 1; j < actives.Count; ++j)
                {
                    for (int k = j + 1; k < actives.Count; ++k)
                    {
                        WeightedRotation wrotNew = ComputeRotation(actives[i].orientable, actives[j].orientable, actives[k].orientable);
                        if (wrotNew.weight > 0)
                        {
                            haveNonZero = true;
                            WeightedRotation wrot = actives[i];
                            wrot       = AverageRotation(wrot, wrotNew);
                            actives[i] = wrot;
                            wrot       = actives[j];
                            wrot       = AverageRotation(wrot, wrotNew);
                            actives[j] = wrot;
                            wrot       = actives[k];
                            wrot       = AverageRotation(wrot, wrotNew);
                            actives[k] = wrot;
                        }
                    }
                }
            }
            if (!haveNonZero)
            {
                // This can happen if there aren't enough points, or they are all collinear.
                return(base.ComputeRotations());
            }
            return(true);
        }
        /// <summary>
        /// Compute a new weighted rotation representing the two input weighted rotations.
        /// </summary>
        /// <param name="accum">The accumulator rotation.</param>
        /// <param name="add">The rotation to add in.</param>
        /// <returns>A new aggregate weighted rotation.</returns>
        private WeightedRotation AverageRotation(WeightedRotation accum, WeightedRotation add)
        {
            float interp = add.weight / (accum.weight + add.weight);

            Quaternion combinedRot = Quaternion.Slerp(accum.rotation, add.rotation, interp);

            combinedRot.Normalize();

            float combinedWeight = accum.weight + add.weight;

            return(new WeightedRotation()
            {
                orientable = accum.orientable,
                rotation = combinedRot,
                weight = combinedWeight
            });
        }
 /// <summary>
 /// Compute rotations by pairs, weighting by distance and averaging for each orientable.
 /// </summary>
 /// <returns></returns>
 private bool ComputeRotations()
 {
     for (int i = 0; i < actives.Count; ++i)
     {
         for (int j = i + 1; j < actives.Count; ++j)
         {
             WeightedRotation wrotNew = ComputeRotation(actives[i].orientable, actives[j].orientable);
             WeightedRotation wrot    = actives[i];
             wrot       = AverageRotation(wrot, wrotNew);
             actives[i] = wrot;
             wrot       = actives[j];
             wrot       = AverageRotation(wrot, wrotNew);
             actives[j] = wrot;
         }
     }
     return(true);
 }