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);
        }