#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using Microsoft.Xna.Framework;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Geometry.Areas {
/// Test for the three-dimensional plane implementation
[TestFixture]
public class Plane3Test {
#region class BrokenRandom
/// A broken random number generator
private class BrokenRandom : IRandom {
///
/// Returns a nonnegative random number less than the specified maximum
///
///
/// The exclusive upper bound of the random number to be generated. maxValue must
/// be greater than or equal to zero
///
///
/// A 32-bit signed integer greater than or equal to zero, and less than maxValue;
/// that is, the range of return values ordinarily includes zero but not maxValue.
/// However, if maxValue equals zero, maxValue is returned
///
///
/// maximumValue is less than zero
///
public int Next(int maximumValue) {
return -maximumValue; // Intentional misbehavior
}
/// Returns a random number between 0.0 and 1.0
///
/// A double-precision floating point number greater than or equal to 0.0,
/// and less than 1.0
///
public double NextDouble() { return 0.0; }
}
#endregion // class BrokenRandom
/// Verifies that the constructor accepting an offset is working
[Test]
public void TestOffsetConstructor() {
Vector3 offset = new Vector3(1.0f, 2.0f, 3.0f);
Plane3 plane = new Plane3(offset, Vector3.Up);
Assert.AreEqual(offset, plane.Offset);
}
/// Verifies that the surface area of a plane is infinite
[Test]
public void TestDistanceConstructor() {
Plane3 plane = new Plane3(123.0f, Vector3.Up);
Assert.AreEqual(123.0f, plane.Offset.Length());
}
/// Verifies that the surface area of a plane is infinite
[Test]
public void TestAreaIsInfinite() {
Plane3 plane = new Plane3(Vector3.Zero, Vector3.Up);
Assert.AreEqual(float.PositiveInfinity, plane.Area);
}
/// Verifies that the circumference of a plane is infinite
[Test]
public void TestCircumferenceIsInfinite() {
Plane3 plane = new Plane3(Vector3.Zero, Vector3.Up);
Assert.AreEqual(float.PositiveInfinity, plane.CircumferenceLength);
}
/// Validates that a plane's center of mass can be calculated
[Test]
public void TestCenterOfMass() {
Plane3 zeroPlane = new Plane3(Vector3.Zero, Vector3.Up);
Assert.AreEqual(Vector3.Zero, zeroPlane.CenterOfMass);
Plane3 xPlane = new Plane3(Vector3.UnitX, Vector3.Right);
Assert.AreEqual(Vector3.Right, xPlane.CenterOfMass);
Plane3 yPlane = new Plane3(Vector3.UnitY, Vector3.Up);
Assert.AreEqual(Vector3.Up, yPlane.CenterOfMass);
Plane3 zPlane = new Plane3(Vector3.UnitZ, Vector3.Backward);
Assert.AreEqual(Vector3.Backward, zPlane.CenterOfMass);
}
///
/// Validates that a plane's side determination helper is working as expected
///
[Test]
public void TestSideDetermination() {
Assert.AreEqual(
Side.Positive, Plane3.GetSide(Vector3.Zero, Vector3.Up, Vector3.UnitY)
);
Assert.AreEqual(
Side.Negative, Plane3.GetSide(Vector3.Zero, Vector3.Up, -Vector3.UnitY)
);
Assert.AreEqual(
Side.Negative, Plane3.GetSide(Vector3.UnitY * 2.0f, Vector3.Up, Vector3.UnitY)
);
}
///
/// Verifies that the bounding box calculation of the plane class is working
/// for planes perfectly aligned to the Y/Z axes
///
[Test]
public void TestBoundingBoxCalculationForXPlane() {
Plane3 xPlane = new Plane3(Vector3.UnitX * 2.0f, Vector3.Right);
Assert.AreEqual(
new Volumes.AxisAlignedBox3(
new Vector3(2.0f, float.NegativeInfinity, float.NegativeInfinity),
new Vector3(2.0f, float.PositiveInfinity, float.PositiveInfinity)
),
xPlane.BoundingBox
);
}
///
/// Verifies that the bounding box calculation of the plane class is working
/// for planes perfectly aligned to the X/Z axes
///
[Test]
public void TestBoundingBoxCalculationForYPlane() {
Plane3 yPlane = new Plane3(Vector3.UnitY * 2.0f, Vector3.Up);
Assert.AreEqual(
new Volumes.AxisAlignedBox3(
new Vector3(float.NegativeInfinity, 2.0f, float.NegativeInfinity),
new Vector3(float.PositiveInfinity, 2.0f, float.PositiveInfinity)
),
yPlane.BoundingBox
);
}
///
/// Verifies that the bounding box calculation of the plane class is working
/// for planes perfectly aligned to the X/Y axes
///
[Test]
public void TestBoundingBoxCalculationForZPlane() {
Plane3 zPlane = new Plane3(Vector3.UnitZ * 2.0f, Vector3.Backward);
Assert.AreEqual(
new Volumes.AxisAlignedBox3(
new Vector3(float.NegativeInfinity, float.NegativeInfinity, 2.0f),
new Vector3(float.PositiveInfinity, float.PositiveInfinity, 2.0f)
),
zPlane.BoundingBox
);
}
///
/// Verifies that the bounding box calculation of the plane class is working
/// for planes
///
[Test]
public void TestBoundingBoxCalculation() {
Vector3 diagonal = Vector3.Normalize(Vector3.One);
Plane3 plane = new Plane3(Vector3.Zero, diagonal);
Assert.AreEqual(
new Volumes.AxisAlignedBox3(
new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity),
new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity)
),
plane.BoundingBox
);
}
///
/// Tests whether the plane class is able to locate the closest point on the plane
/// to an arbitrary point
///
[Test]
public void TestClosestPointToPlane() {
Plane3 xPlane = new Plane3(Vector3.UnitX * 2.0f, Vector3.Right);
Assert.AreEqual(
new Vector3(2.0f, 20.0f, 30.0f),
xPlane.ClosestPointTo(new Vector3(10.0f, 20.0f, 30.0f))
);
Plane3 yPlane = new Plane3(Vector3.UnitY * 2.0f, Vector3.Up);
Assert.AreEqual(
new Vector3(10.0f, 2.0f, 30.0f),
yPlane.ClosestPointTo(new Vector3(10.0f, 20.0f, 30.0f))
);
Plane3 zPlane = new Plane3(Vector3.UnitZ * 2.0f, Vector3.Backward);
Assert.AreEqual(
new Vector3(10.0f, 20.0f, 2.0f),
zPlane.ClosestPointTo(new Vector3(10.0f, 20.0f, 30.0f))
);
}
///
/// Tests whether the plane class can find a random point on the plane's perimeter
/// for a plane perfectly aligned to the Y/Z axes
///
[Test]
public void TestRandomPointOnPerimeterForXPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 xPlane = new Plane3(Vector3.UnitX * 2.0f, Vector3.Right);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = xPlane.RandomPointOnPerimeter(randomNumberGenerator);
Assert.AreEqual(2.0f, randomPoint.X);
Assert.IsTrue(float.IsInfinity(randomPoint.Y));
Assert.IsTrue(float.IsInfinity(randomPoint.Z));
}
}
///
/// Tests whether the plane class can find a random point on the plane's perimeter
/// for a plane perfectly aligned to the X/Z axes
///
[Test]
public void TestRandomPointOnPerimeterForYPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 yPlane = new Plane3(Vector3.UnitY * 2.0f, Vector3.Up);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = yPlane.RandomPointOnPerimeter(randomNumberGenerator);
Assert.IsTrue(float.IsInfinity(randomPoint.X));
Assert.AreEqual(2.0f, randomPoint.Y);
Assert.IsTrue(float.IsInfinity(randomPoint.Z));
}
}
///
/// Tests whether the plane class can find a random point on the plane's perimeter
/// for a plane perfectly aligned to the X/Y axes
///
[Test]
public void TestRandomPointOnPerimeterForZPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 zPlane = new Plane3(Vector3.UnitZ * 2.0f, Vector3.Backward);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = zPlane.RandomPointOnPerimeter(randomNumberGenerator);
Assert.IsTrue(float.IsInfinity(randomPoint.X));
Assert.IsTrue(float.IsInfinity(randomPoint.Y));
Assert.AreEqual(2.0f, randomPoint.Z);
}
}
///
/// Tests whether the plane class can find a random point on the plane's perimeter
///
[Test]
public void TestRandomPointOnPerimeter() {
IRandom randomNumberGenerator = new DefaultRandom();
// A random point has to involve infinity (since the chance that of hitting the
// meager numeric range of a float, or any other finite number system, is about
// zero for a infinitely large plane). But given the orientation of the plane,
// only these combinations of positive and negative infinity can be possible.
Vector3[] possiblePoints = new Vector3[] {
new Vector3(float.NegativeInfinity, float.PositiveInfinity, float.PositiveInfinity),
new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.PositiveInfinity),
new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity),
new Vector3(float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity),
};
Plane3 plane = new Plane3(Vector3.Zero, Vector3.Normalize(Vector3.One));
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = plane.RandomPointOnPerimeter(randomNumberGenerator);
CollectionAssert.Contains(possiblePoints, randomPoint);
}
}
///
/// Tests whether the plane class can find a random point within the plane
/// for a plane perfectly aligned to the Y/Z axes
///
[Test]
public void TestRandomPointWithinForXPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 xPlane = new Plane3(Vector3.UnitX * 2.0f, Vector3.Right);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = xPlane.RandomPointWithin(randomNumberGenerator);
Assert.AreEqual(2.0f, randomPoint.X);
Assert.IsTrue(float.IsInfinity(randomPoint.Y));
Assert.IsTrue(float.IsInfinity(randomPoint.Z));
}
}
///
/// Tests whether the plane class can find a random point within the plane
/// for a plane perfectly aligned to the X/Z axes
///
[Test]
public void TestRandomPointWithinForYPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 yPlane = new Plane3(Vector3.UnitY * 2.0f, Vector3.Up);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = yPlane.RandomPointWithin(randomNumberGenerator);
Assert.IsTrue(float.IsInfinity(randomPoint.X));
Assert.AreEqual(2.0f, randomPoint.Y);
Assert.IsTrue(float.IsInfinity(randomPoint.Z));
}
}
///
/// Tests whether the plane class can find a random point within the plane
/// for a plane perfectly aligned to the X/Y axes
///
[Test]
public void TestRandomPointWithinForZPlane() {
IRandom randomNumberGenerator = new DefaultRandom();
Plane3 zPlane = new Plane3(Vector3.UnitZ * 2.0f, Vector3.Backward);
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = zPlane.RandomPointWithin(randomNumberGenerator);
Assert.IsTrue(float.IsInfinity(randomPoint.X));
Assert.IsTrue(float.IsInfinity(randomPoint.Y));
Assert.AreEqual(2.0f, randomPoint.Z);
}
}
///
/// Tests whether the plane class can find a random point within the plane
///
[Test]
public void TestRandomPointWithin() {
IRandom randomNumberGenerator = new DefaultRandom();
// A random point has to involve infinity (since the chance that of hitting the
// meager numeric range of a float, or any other finite number system, is about
// zero for a infinitely large plane). But given the orientation of the plane,
// only these combinations of positive and negative infinity can be possible.
Vector3[] possiblePoints = new Vector3[] {
new Vector3(float.NegativeInfinity, float.PositiveInfinity, float.PositiveInfinity),
new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.PositiveInfinity),
new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.NegativeInfinity),
new Vector3(float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity),
};
Plane3 plane = new Plane3(Vector3.Zero, Vector3.Normalize(Vector3.One));
for(int index = 0; index < Specifications.ProbabilisticFunctionSamples; ++index) {
Vector3 randomPoint = plane.RandomPointWithin(randomNumberGenerator);
//Assert.That(randomPoint, Is.OneOf(possiblePoints));
CollectionAssert.Contains(possiblePoints, randomPoint);
}
}
///
/// Tests whether the plane class throws an exception if the random number generator
/// is malfunctioning (talk about edge cases...)
///
[Test]
public void TestThrowOnRandomPointOnPerimeterWithBrokenRandom() {
IRandom randomNumberGenerator = new BrokenRandom();
Plane3 plane = new Plane3(Vector3.Zero, Vector3.Normalize(Vector3.One));
Assert.Throws(
delegate() { plane.RandomPointOnPerimeter(randomNumberGenerator); }
);
}
}
} // namespace Nuclex.Geometry.Areas
#endif // UNITTEST