internal BoundingBoxData(Vector3 startDir, Vector3 yDir, Edge rotatorEdge, Vector3 rotatorVector, TVGLConvexHull convexHull) { Direction = startDir; PosYDir = yDir; RotatorEdge = rotatorEdge; RotatorVector = rotatorVector; OrthGaussSphereArcs = new List <GaussSphereArc>(); // make arrays of the Dots with start and end directions (x-values) to help subsequent // foreach loop which will look up faces multiple times. var startingDots = new double[convexHull.Faces.Length]; for (var i = 0; i < convexHull.Faces.Length; i++) { var face = convexHull.Faces[i]; face.IndexInList = i; startingDots[i] = face.Normal.Dot(startDir); } foreach (var edge in convexHull.Edges) { var ownedX = startingDots[edge.OwnedFace.IndexInList]; var otherX = startingDots[edge.OtherFace.IndexInList]; if (otherX * ownedX <= 0) { var ownedY = edge.OwnedFace.Normal.Dot(yDir); var otherY = edge.OtherFace.Normal.Dot(yDir); //if ((ownedX < 0 && ownedY > 0) || (ownedX > 0 && ownedY < 0)) // OrthGaussSphereArcs.Add(new GaussSphereArc(edge, edge.OwnedFace)); //else if ((otherX < 0 && otherY > 0) || (otherX > 0 && otherY < 0)) // OrthGaussSphereArcs.Add(new GaussSphereArc(edge, edge.OtherFace)); if ((ownedX <= 0 && ownedY > 0) || (ownedX >= 0 && ownedY < 0)) { OrthGaussSphereArcs.Add(new GaussSphereArc(edge, edge.OwnedFace)); } else if ((otherX <= 0 && otherY > 0) || (otherX >= 0 && otherY < 0)) { OrthGaussSphereArcs.Add(new GaussSphereArc(edge, edge.OtherFace)); } } } OrthVertices = OrthGaussSphereArcs.SelectMany(arc => new[] { arc.Edge.From, arc.Edge.To }).Distinct().ToList(); var maxDistance = double.NegativeInfinity; foreach (var v in convexHull.Vertices) { var distance = rotatorEdge.From.Coordinates.Subtract(v.Coordinates).Dot(startDir); if (distance > maxDistance) { maxDistance = distance; BackVertex = v; } } }
/// <summary> /// The MC_ApproachOne rotates around each edge of the convex hull between the owned and /// other faces. In this way, it guarantees a much more optimal solution than the flat /// with face algorithm, but is, therefore, slower. /// </summary> /// <timeDomain> /// Since the computation cost for each Bounding Box is linear O(n), /// and the approximate worse case number of normals considered is n*PI/maxDeltaAngle, /// Lower Bound O(n^2). Upper Bound O(n^(2)*PI/maxDeltaAngle). [ex. upper bound is O(36*n^2) when MaxDeltaAngle = 5 /// degrees.] /// </timeDomain> /// <accuracy> /// Garantees the optimial orientation is within maxDeltaAngle error. /// </accuracy> private static BoundingBox OrientedBoundingBox(TVGLConvexHull convexHull) { var minBox = new BoundingBox( new Vector3(double.PositiveInfinity, double.PositiveInfinity, double.PositiveInfinity), new[] { Vector3.UnitX, Vector3.UnitY, Vector3.UnitZ }, new Vector3(double.NegativeInfinity, double.NegativeInfinity, double.NegativeInfinity)); foreach (var rotateEdge in convexHull.Edges) { #region Initialize variables //Initialize variables //rotatorVector is basically the edge in question - the vector that is being rotated about var rotatorVector = rotateEdge.Vector.Normalize(); // startDir is the starting Direction - based on the OtherFace var startDir = rotateEdge.OtherFace.Normal; // endDir is the OwnedFace final Direction - we go from Other to Owned since in order to be about // the positive Direction of the rotatorVector var endDir = rotateEdge.OwnedFace.Normal; // posYDir is the vector for the positive y-Direction. Well, this is a simplification of the //gauss sphere to a 2D circle. The Direction (such as startDir) represents the x-axis and this //, which is the orthogonal is the y Direction var origPosYDir = rotatorVector.Cross(startDir).Normalize(); var totalAngle = Math.PI - rotateEdge.InternalAngle; var thisBoxData = new BoundingBoxData(startDir, origPosYDir, rotateEdge, rotatorVector, convexHull); #endregion FindOBBAlongDirection(thisBoxData); if (thisBoxData.Box.Volume < minBox.Volume) { minBox = thisBoxData.Box; } var angle = 0.0; var deltaAngleToBackChange = 0.0; var deltaAngleOrthSet = 0.0; BoundingBoxData backChangeBox = null; BoundingBoxData sideChangeBox = null; do { if (deltaAngleToBackChange <= 0) { backChangeBox = thisBoxData.Copy(); deltaAngleToBackChange = UpdateBackAngle(backChangeBox); } if (deltaAngleOrthSet <= 0) { sideChangeBox = thisBoxData.Copy(); deltaAngleOrthSet = UpdateOrthAngle(sideChangeBox); } BoundingBoxData nextBoxData; if (deltaAngleOrthSet < deltaAngleToBackChange) { deltaAngleToBackChange -= deltaAngleOrthSet; angle += deltaAngleOrthSet; deltaAngleOrthSet = 0; nextBoxData = sideChangeBox; } else if (deltaAngleToBackChange < deltaAngleOrthSet) { deltaAngleOrthSet -= deltaAngleToBackChange; angle += deltaAngleToBackChange; deltaAngleToBackChange = 0; nextBoxData = backChangeBox; } else // if they are equal to each other { angle += deltaAngleToBackChange; deltaAngleOrthSet = deltaAngleToBackChange = 0; nextBoxData = backChangeBox; } if (angle > totalAngle) { // nextBoxData = new BoundingBoxData(endDir, rotatorVector.Cross(endDir).Normalize(), rotateEdge, rotatorVector, convexHull); nextBoxData.Angle = totalAngle; nextBoxData.Direction = endDir; } else { nextBoxData.Angle = angle; nextBoxData.Direction = UpdateDirection(startDir, rotatorVector, origPosYDir, angle); } nextBoxData.PosYDir = nextBoxData.RotatorVector.Cross(nextBoxData.Direction).Normalize(); /****************/ FindOBBAlongDirection(nextBoxData); /****************/ if (DifferentMembershipInExtrema(thisBoxData, nextBoxData)) { var lowerBox = thisBoxData; var upperBox = nextBoxData; var midBox = thisBoxData.Copy(); while (!lowerBox.Angle.IsPracticallySame(upperBox.Angle, Constants.OBBTolerance)) { midBox.Direction = (lowerBox.Direction + upperBox.Direction).Divide(2).Normalize(); midBox.Angle = (lowerBox.Angle + upperBox.Angle) / 2.0; FindOBBAlongDirection(midBox); if (midBox.Box.Volume > lowerBox.Box.Volume && midBox.Box.Volume > upperBox.Box.Volume) { break; } if (!DifferentMembershipInExtrema(lowerBox, midBox)) { lowerBox = midBox; } else if (!DifferentMembershipInExtrema(upperBox, midBox)) { upperBox = midBox; } else { throw new Exception("new midbox is different from BOTH neighbors!"); } } if (thisBoxData.Box.Volume < minBox.Volume) { minBox = midBox.Box; } } thisBoxData = nextBoxData; if (thisBoxData.Box.Volume < minBox.Volume) { minBox = thisBoxData.Box; } } while (angle < totalAngle); } return(minBox); }