// Returns a new axis aligned bounding box that contains the old // bounding box post the given transformation. internal static Rect3D ComputeTransformedAxisAlignedBoundingBox( /* IN */ ref Rect3D originalBox, /* IN */ ref Matrix3D matrix) { if (originalBox.IsEmpty) { return originalBox; } if (matrix.IsAffine) { return ComputeTransformedAxisAlignedBoundingBoxAffine(ref originalBox, ref matrix); } else { return ComputeTransformedAxisAlignedBoundingBoxNonAffine(ref originalBox, ref matrix); } }
// Helper method for compiting the bounds of a set of points. The given point // is added to the bounds of the given Rect3D. The point/bounds are both passed // by reference for perf. Only the bounds may be modified. private static void AddPointToBounds(ref Point3D point, ref Rect3D bounds) { Debug.Assert(!bounds.IsEmpty, "Caller should construct the Rect3D from the first point before calling this method."); if (point.X < bounds.X) { bounds.SizeX += (bounds.X - point.X); bounds.X = point.X; } else if (point.X > (bounds.X + bounds.SizeX)) { bounds.SizeX = point.X - bounds.X; } if (point.Y < bounds.Y) { bounds.SizeY += (bounds.Y - point.Y); bounds.Y = point.Y; } else if (point.Y > (bounds.Y + bounds.SizeY)) { bounds.SizeY = point.Y - bounds.Y; } if (point.Z < bounds.Z) { bounds.SizeZ += (bounds.Z - point.Z); bounds.Z = point.Z; } else if (point.Z > (bounds.Z + bounds.SizeZ)) { bounds.SizeZ = point.Z - bounds.Z; } #if NEVER // Because we do not store rectangles as TLRB (+ another dimension in 3D) // we need to compute SizeX/Y/Z which involves subtraction and introduces // cancelation so this assert isn't accurate. Debug.Assert(bounds.Contains(point), "Error detect - bounds did not contain point on exit."); #endif }
private static Rect3D CreateInfiniteRect3D() { // NTRAID#Longhorn-1044943-2005/11/15-[....] - Robustness with infinities // // Once the issue with Rect robustness with infinities is addressed we // should change the values below to make this rectangle truely extend // from -Infinite to +Infinity. // // Until then we use a Rect from -float.MaxValue to +float.MaxValue. // Because this rect is used only as a conservative bounding box for // ScreenSpaceLines3D this span should be sufficient for the following // reasons: // // 1. Our meshes and transforms are reprensented in single precision // at render time. If it's not in this range it will not be // rendered. // // 2. SSLines3Ds are constructed as simple quads at render time. // We will hit the guard band on the GPU at a limit far less than // +/- float.MaxValue. // // 3. We do our managed math in double precision so this still // leaves us ample space to account for transforms, etc. // Rect3D infinite = new Rect3D(); infinite._x = -float.MaxValue; infinite._y = -float.MaxValue; infinite._z = -float.MaxValue; infinite._sizeX = float.MaxValue * 2.0; infinite._sizeY = float.MaxValue * 2.0; infinite._sizeZ = float.MaxValue * 2.0; return infinite; }
private static Rect3D CreateEmptyRect3D() { Rect3D empty = new Rect3D(); empty._x = Double.PositiveInfinity; empty._y = Double.PositiveInfinity; empty._z = Double.PositiveInfinity; // Can't use setters because they throw on negative values empty._sizeX = Double.NegativeInfinity; empty._sizeY = Double.NegativeInfinity; empty._sizeZ = Double.NegativeInfinity; return empty; }
/// <summary> /// Offset - return the result of offsetting rect by the offset provided /// If this is Empty, this method is illegal. /// </summary> /// <param name="rect"></param> /// <param name="offsetX"></param> /// <param name="offsetY"></param> /// <param name="offsetZ"></param> /// <returns></returns> public static Rect3D Offset(Rect3D rect, double offsetX, double offsetY, double offsetZ) { rect.Offset(offsetX, offsetY, offsetZ); return rect; }
// CTAABB for non-affine transformations internal static Rect3D ComputeTransformedAxisAlignedBoundingBoxNonAffine(/* IN */ ref Rect3D originalBox, /* IN */ ref Matrix3D matrix) { Debug.Assert(!matrix.IsAffine); double x1 = originalBox.X; double y1 = originalBox.Y; double z1 = originalBox.Z; double x2 = originalBox.X + originalBox.SizeX; double y2 = originalBox.Y + originalBox.SizeY; double z2 = originalBox.Z + originalBox.SizeZ; Point3D[] points = new Point3D[] { new Point3D(x1, y1, z1), new Point3D(x1, y1, z2), new Point3D(x1, y2, z1), new Point3D(x1, y2, z2), new Point3D(x2, y1, z1), new Point3D(x2, y1, z2), new Point3D(x2, y2, z1), new Point3D(x2, y2, z2), }; matrix.Transform(points); Point3D p = points[0]; Rect3D newBounds = new Rect3D(p.X, p.Y, p.Z, 0, 0, 0); // Traverse the entire mesh and compute bounding box. for (int i = 1; i < points.Length; i++) { p = points[i]; AddPointToBounds(ref p, ref newBounds); } return newBounds; }
/// <summary> /// Offset - return the result of offsetting rect by the offset provided /// If this is Empty, this method is illegal. /// </summary> /// <param name="rect"></param> /// <param name="offsetX"></param> /// <param name="offsetY"></param> /// <param name="offsetZ"></param> /// <returns></returns> public static Rect3D Offset(Rect3D rect, double offsetX, double offsetY, double offsetZ) { rect.Offset(offsetX, offsetY, offsetZ); return(rect); }
/// <summary> /// Return the result of the union of rect and point. /// </summary> /// <param name="rect">Rectangle.</param> /// <param name="point">Point.</param> /// <returns>The result of the union of rect and point.</returns> public static Rect3D Union(Rect3D rect, Point3D point) { rect.Union(new Rect3D(point, point)); return(rect); }
/// <summary> /// Return the result of the intersection of rect1 and rect2. /// If either this or rect are Empty, the result is Empty as well. /// </summary> /// <param name="rect1">First rectangle.</param> /// <param name="rect2">Second rectangle.</param> /// <returns>The result of the intersection of rect1 and rect2.</returns> public static Rect3D Intersect(Rect3D rect1, Rect3D rect2) { rect1.Intersect(rect2); return(rect1); }
/// <summary> /// Return the result of the intersection of rect1 and rect2. /// If either this or rect are Empty, the result is Empty as well. /// </summary> /// <param name="rect1">First rectangle.</param> /// <param name="rect2">Second rectangle.</param> /// <returns>The result of the intersection of rect1 and rect2.</returns> public static Rect3D Intersect(Rect3D rect1, Rect3D rect2) { rect1.Intersect(rect2); return rect1; }
/// <summary> /// Intersect - Update this rectangle to be the intersection of this and rect /// If either this or rect are Empty, the result is Empty as well. /// </summary> /// <param name="rect"> The rect to intersect with this </param> public void Intersect(Rect3D rect) { if (IsEmpty || rect.IsEmpty || !this.IntersectsWith(rect)) { this = Empty; } else { double x = Math.Max(_x, rect._x); double y = Math.Max(_y, rect._y); double z = Math.Max(_z, rect._z); _sizeX = Math.Min(_x + _sizeX, rect._x + rect._sizeX) - x; _sizeY = Math.Min(_y + _sizeY, rect._y + rect._sizeY) - y; _sizeZ = Math.Min(_z + _sizeZ, rect._z + rect._sizeZ) - z; _x = x; _y = y; _z = z; } }
/// <summary> /// Returns true if the rectangle intersects with this rectangle. /// Returns false otherwise. Note that if one edge is coincident, this is considered /// an intersection. /// </summary> /// <param name="rect">Rectangle being tested.</param> /// <returns>True if the rectangle intersects with this rectangle. /// False otherwise.</returns> public bool IntersectsWith(Rect3D rect) { if (IsEmpty || rect.IsEmpty) { return false; } return (rect._x <= (_x + _sizeX)) && ((rect._x + rect._sizeX) >= _x) && (rect._y <= (_y + _sizeY)) && ((rect._y + rect._sizeY) >= _y) && (rect._z <= (_z + _sizeZ)) && ((rect._z + rect._sizeZ) >= _z); }
/// <summary> /// Returns true if the rectangle is non-Empty and is entirely contained within the /// rectangle, inclusive of the edges. Returns false otherwise. /// </summary> /// <param name="rect">Rectangle being tested.</param> /// <returns>Returns true if the rectangle is non-Empty and is entirely contained within the /// rectangle, inclusive of the edges. Returns false otherwise.</returns> public bool Contains(Rect3D rect) { if (IsEmpty || rect.IsEmpty) { return false; } return (_x <= rect._x && _y <= rect._y && _z <= rect._z && _x + _sizeX >= rect._x + rect._sizeX && _y + _sizeY >= rect._y + rect._sizeY && _z + _sizeZ >= rect._z + rect._sizeZ); }
// CTAABB for an affine transforms internal static Rect3D ComputeTransformedAxisAlignedBoundingBoxAffine(/* IN */ ref Rect3D originalBox, /* IN */ ref Matrix3D matrix) { Debug.Assert(matrix.IsAffine); // Based on Arvo's paper "Transforming Axis-Aligned Bounding Boxes" // from the original Graphics Gems book. Specifically, this code // is based on Figure 1 which is for a box stored as min and // max points. Our bounding boxes are stored as a min point and // a diagonal so we'll convert when needed. Also, we have row // vectors. // // Mapping Arvo's variables to ours: // A - the untransformed box (originalBox) // B - the transformed box (what we return at the end) // M - the rotation + scale (matrix.Mji) // T - the translation (matrix.Offset?) // // for i = 1 ... 3 // Bmin_i = Bmax_i = T_i // for j = 1 ... 3 // a = M_ij * Amin_j // b = M_ij * Amax_j // Bmin_i += min(a, b) // Bmax_i += max(a, b) // // Matrix3D doesn't have indexers because they're too slow so we'll // have to unroll the loops. A complete unroll of both loops was // found to be the fastest. double oldMaxX = originalBox.X + originalBox.SizeX; double oldMaxY = originalBox.Y + originalBox.SizeY; double oldMaxZ = originalBox.Z + originalBox.SizeZ; // i = 1 (X) double newMinX = matrix.OffsetX; double newMaxX = matrix.OffsetX; { // i = 1 (X), j = 1 (X) double a = matrix.M11 * originalBox.X; double b = matrix.M11 * oldMaxX; if (b > a) { newMinX += a; newMaxX += b; } else { newMinX += b; newMaxX += a; } // i = 1 (X), j = 2 (Y) a = matrix.M21 * originalBox.Y; b = matrix.M21 * oldMaxY; if (b > a) { newMinX += a; newMaxX += b; } else { newMinX += b; newMaxX += a; } // i = 1 (X), j = 3 (Z) a = matrix.M31 * originalBox.Z; b = matrix.M31 * oldMaxZ; if (b > a) { newMinX += a; newMaxX += b; } else { newMinX += b; newMaxX += a; } } // i = 2 (Y) double newMinY = matrix.OffsetY; double newMaxY = matrix.OffsetY; { // i = 2 (Y), j = 1 (X) double a = matrix.M12 * originalBox.X; double b = matrix.M12 * oldMaxX; if (b > a) { newMinY += a; newMaxY += b; } else { newMinY += b; newMaxY += a; } // i = 2 (Y), j = 2 (Y) a = matrix.M22 * originalBox.Y; b = matrix.M22 * oldMaxY; if (b > a) { newMinY += a; newMaxY += b; } else { newMinY += b; newMaxY += a; } // i = 2 (Y), j = 3 (Z) a = matrix.M32 * originalBox.Z; b = matrix.M32 * oldMaxZ; if (b > a) { newMinY += a; newMaxY += b; } else { newMinY += b; newMaxY += a; } } // i = 3 (Z) double newMinZ = matrix.OffsetZ; double newMaxZ = matrix.OffsetZ; { // i = 3 (Z), j = 1 (X) double a = matrix.M13 * originalBox.X; double b = matrix.M13 * oldMaxX; if (b > a) { newMinZ += a; newMaxZ += b; } else { newMinZ += b; newMaxZ += a; } // i = 3 (Z), j = 2 (Y) a = matrix.M23 * originalBox.Y; b = matrix.M23 * oldMaxY; if (b > a) { newMinZ += a; newMaxZ += b; } else { newMinZ += b; newMaxZ += a; } // i = 3 (Z), j = 3 (Z) a = matrix.M33 * originalBox.Z; b = matrix.M33 * oldMaxZ; if (b > a) { newMinZ += a; newMaxZ += b; } else { newMinZ += b; newMaxZ += a; } } return new Rect3D(newMinX, newMinY, newMinZ, newMaxX - newMinX, newMaxY - newMinY, newMaxZ - newMinZ); }
/// <summary> /// Compares two Rect3D instances for object equality. In this equality /// Double.NaN is equal to itself, unlike in numeric equality. /// Note that double values can acquire error when operated upon, such that /// an exact comparison between two values which /// are logically equal may fail. /// </summary> /// <returns> /// bool - true if the two Rect3D instances are exactly equal, false otherwise /// </returns> /// <param name='rect1'>The first Rect3D to compare</param> /// <param name='rect2'>The second Rect3D to compare</param> public static bool Equals(Rect3D rect1, Rect3D rect2) { if (rect1.IsEmpty) { return rect2.IsEmpty; } else { return rect1.X.Equals(rect2.X) && rect1.Y.Equals(rect2.Y) && rect1.Z.Equals(rect2.Z) && rect1.SizeX.Equals(rect2.SizeX) && rect1.SizeY.Equals(rect2.SizeY) && rect1.SizeZ.Equals(rect2.SizeZ); } }
/// <summary> /// Equals - compares this Rect3D with the passed in object. In this equality /// Double.NaN is equal to itself, unlike in numeric equality. /// Note that double values can acquire error when operated upon, such that /// an exact comparison between two values which /// are logically equal may fail. /// </summary> /// <returns> /// bool - true if "value" is equal to "this". /// </returns> /// <param name='value'>The Rect3D to compare to "this"</param> public bool Equals(Rect3D value) { return Rect3D.Equals(this, value); }
/// <summary> /// Update this rectangle to be the union of this and rect. /// </summary> /// <param name="rect">Rectangle.</param> public void Union(Rect3D rect) { if (IsEmpty) { this = rect; } else if (!rect.IsEmpty) { double x = Math.Min(_x, rect._x); double y = Math.Min(_y, rect._y); double z = Math.Min(_z, rect._z); _sizeX = Math.Max(_x + _sizeX, rect._x + rect._sizeX) - x; _sizeY = Math.Max(_y + _sizeY, rect._y + rect._sizeY) - y; _sizeZ = Math.Max(_z + _sizeZ, rect._z + rect._sizeZ) - z; _x = x; _y = y; _z = z; } }
/// <summary> /// Return the result of the union of rect1 and rect2. /// </summary> /// <param name="rect1">First rectangle.</param> /// <param name="rect2">Second rectangle.</param> /// <returns>The result of the union of the two rectangles.</returns> public static Rect3D Union(Rect3D rect1, Rect3D rect2) { rect1.Union(rect2); return(rect1); }
/// <summary> /// Return the result of the union of rect1 and rect2. /// </summary> /// <param name="rect1">First rectangle.</param> /// <param name="rect2">Second rectangle.</param> /// <returns>The result of the union of the two rectangles.</returns> public static Rect3D Union(Rect3D rect1, Rect3D rect2) { rect1.Union(rect2); return rect1; }
/// <summary> /// Offset - return the result of offsetting rect by the offset provided /// If this is Empty, this method is illegal. /// </summary> /// <param name="rect"></param> /// <param name="offsetVector"></param> /// <returns></returns> public static Rect3D Offset(Rect3D rect, Vector3D offsetVector) { rect.Offset(offsetVector._x, offsetVector._y, offsetVector._z); return(rect); }
/// <summary> /// Return the result of the union of rect and point. /// </summary> /// <param name="rect">Rectangle.</param> /// <param name="point">Point.</param> /// <returns>The result of the union of rect and point.</returns> public static Rect3D Union(Rect3D rect, Point3D point) { rect.Union(new Rect3D(point, point)); return rect; }
/// <summary> /// Equals - compares this Rect3D with the passed in object. In this equality /// Double.NaN is equal to itself, unlike in numeric equality. /// Note that double values can acquire error when operated upon, such that /// an exact comparison between two values which /// are logically equal may fail. /// </summary> /// <returns> /// bool - true if "value" is equal to "this". /// </returns> /// <param name='value'>The Rect3D to compare to "this"</param> public bool Equals(Rect3D value) { return(Rect3D.Equals(this, value)); }
/// <summary> /// Offset - return the result of offsetting rect by the offset provided /// If this is Empty, this method is illegal. /// </summary> /// <param name="rect"></param> /// <param name="offsetVector"></param> /// <returns></returns> public static Rect3D Offset(Rect3D rect, Vector3D offsetVector) { rect.Offset(offsetVector._x, offsetVector._y, offsetVector._z); return rect; }