/// <summary> /// @note Exp should really only be used after Log. /// Assumes a quaternion with W=0 and V=theta*v (where |v| = 1). /// Exp(q) = (sin(theta)*v, cos(theta)) /// </summary> public FQuat Exp() { float angle = FMath.Sqrt(X * X + Y * Y + Z * Z); float sinAngle = FMath.Sin(angle); FQuat result; result.W = FMath.Cos(angle); if (FMath.Abs(sinAngle) >= FMath.SmallNumber) { float scale = sinAngle / angle; result.X = scale * X; result.Y = scale * Y; result.Z = scale * Z; } else { result.X = X; result.Y = Y; result.Z = Z; } return(result); }
/// <summary> /// Returns a random unit vector, uniformly distributed, within the specified cone. /// </summary> /// <param name="dir">The center direction of the cone</param> /// <param name="horizontalConeHalfAngleRad">Horizontal half-angle of cone, in radians.</param> /// <param name="verticalConeHalfAngleRad">Vertical half-angle of cone, in radians.</param> /// <returns>Normalized vector within the specified cone.</returns> public FVector VRandCone(FVector dir, float horizontalConeHalfAngleRad, float verticalConeHalfAngleRad) { if ((verticalConeHalfAngleRad > 0.0f) && (horizontalConeHalfAngleRad > 0.0f)) { float randU = FRand(); float randV = FRand(); // Get spherical coords that have an even distribution over the unit sphere // Method described at http://mathworld.wolfram.com/SpherePointPicking.html float theta = 2.0f * FMath.PI * randU; float phi = FMath.Acos((2.0f * randV) - 1.0f); // restrict phi to [0, ConeHalfAngleRad] // where ConeHalfAngleRad is now a function of Theta // (specifically, radius of an ellipse as a function of angle) // function is ellipse function (x/a)^2 + (y/b)^2 = 1, converted to polar coords float coneHalfAngleRad = FMath.Square(FMath.Cos(theta) / verticalConeHalfAngleRad) + FMath.Square(FMath.Sin(theta) / horizontalConeHalfAngleRad); coneHalfAngleRad = FMath.Sqrt(1.0f / coneHalfAngleRad); // clamp to make a cone instead of a sphere phi = FMath.Fmod(phi, coneHalfAngleRad); // get axes we need to rotate around FMatrix dirMat = FMatrix.CreateRotation(dir.Rotation()); // note the axis translation, since we want the variation to be around X FVector dirZ = dirMat.GetUnitAxis(EAxis.X); FVector dirY = dirMat.GetUnitAxis(EAxis.Y); FVector result = dir.RotateAngleAxis(phi * 180.0f / FMath.PI, dirY); result = result.RotateAngleAxis(theta * 180.0f / FMath.PI, dirZ); // ensure it's a unit vector (might not have been passed in that way) result = result.GetSafeNormal(); return(result); } else { return(dir.GetSafeNormal()); } }
/// <summary> /// Converts spherical coordinates on the unit sphere into a Cartesian unit length vector. /// </summary> public FVector SphericalToUnitCartesian() { float SinTheta = FMath.Sin(X); return(new FVector(FMath.Cos(Y) * SinTheta, FMath.Sin(Y) * SinTheta, FMath.Cos(X))); }