MinBox2<Real>::MinBox2 ( int numPoints, const Vector2<Real>* points, Real epsilon, Query::Type queryType, bool isConvexPolygon ) { // Get the convex hull of the points. Vector2<Real>* hullPoints = 0; if ( isConvexPolygon ) { hullPoints = ( Vector2<Real>* )points; } else { ConvexHull2<Real> hull( numPoints, ( Vector2<Real>* )points, epsilon, false, queryType ); int hullDim = hull.GetDimension(); int hullNumSimplices = hull.GetNumSimplices(); const int* hullIndices = hull.GetIndices(); if ( hullDim == 0 ) { mMinBox.Center = points[0]; mMinBox.Axis[0] = Vector2<Real>::UNIT_X; mMinBox.Axis[1] = Vector2<Real>::UNIT_Y; mMinBox.Extent[0] = ( Real )0; mMinBox.Extent[1] = ( Real )0; return; } if ( hullDim == 1 ) { ConvexHull1<Real>* hull1 = hull.GetConvexHull1(); hullIndices = hull1->GetIndices(); mMinBox.Center = ( ( Real )0.5 ) * ( points[hullIndices[0]] + points[hullIndices[1]] ); Vector2<Real> diff = points[hullIndices[1]] - points[hullIndices[0]]; mMinBox.Extent[0] = ( ( Real )0.5 ) * diff.Normalize(); mMinBox.Extent[1] = ( Real )0.0; mMinBox.Axis[0] = diff; mMinBox.Axis[1] = -mMinBox.Axis[0].Perp(); delete0( hull1 ); return; } numPoints = hullNumSimplices; hullPoints = new1<Vector2<Real> >( numPoints ); for ( int i = 0; i < numPoints; ++i ) { hullPoints[i] = points[hullIndices[i]]; } } // The input points are V[0] through V[N-1] and are assumed to be the // vertices of a convex polygon that are counterclockwise ordered. The // input points must not contain three consecutive collinear points. // Unit-length edge directions of convex polygon. These could be // precomputed and passed to this routine if the application requires it. int numPointsM1 = numPoints - 1; Vector2<Real>* edges = new1<Vector2<Real> >( numPoints ); bool* visited = new1<bool>( numPoints ); int i; for ( i = 0; i < numPointsM1; ++i ) { edges[i] = hullPoints[i + 1] - hullPoints[i]; edges[i].Normalize(); visited[i] = false; } edges[numPointsM1] = hullPoints[0] - hullPoints[numPointsM1]; edges[numPointsM1].Normalize(); visited[numPointsM1] = false; // Find the smallest axis-aligned box containing the points. Keep track // of the extremum indices, L (left), R (right), B (bottom), and T (top) // so that the following constraints are met: // V[L].X() <= V[i].X() for all i and V[(L+1)%N].X() > V[L].X() // V[R].X() >= V[i].X() for all i and V[(R+1)%N].X() < V[R].X() // V[B].Y() <= V[i].Y() for all i and V[(B+1)%N].Y() > V[B].Y() // V[T].Y() >= V[i].Y() for all i and V[(T+1)%N].Y() < V[T].Y() Real xmin = hullPoints[0].X(), xmax = xmin; Real ymin = hullPoints[0].Y(), ymax = ymin; int LIndex = 0, RIndex = 0, BIndex = 0, TIndex = 0; for ( i = 1; i < numPoints; ++i ) { if ( hullPoints[i].X() <= xmin ) { xmin = hullPoints[i].X(); LIndex = i; } if ( hullPoints[i].X() >= xmax ) { xmax = hullPoints[i].X(); RIndex = i; } if ( hullPoints[i].Y() <= ymin ) { ymin = hullPoints[i].Y(); BIndex = i; } if ( hullPoints[i].Y() >= ymax ) { ymax = hullPoints[i].Y(); TIndex = i; } } // Apply wrap-around tests to ensure the constraints mentioned above are // satisfied. if ( LIndex == numPointsM1 ) { if ( hullPoints[0].X() <= xmin ) { xmin = hullPoints[0].X(); LIndex = 0; } } if ( RIndex == numPointsM1 ) { if ( hullPoints[0].X() >= xmax ) { xmax = hullPoints[0].X(); RIndex = 0; } } if ( BIndex == numPointsM1 ) { if ( hullPoints[0].Y() <= ymin ) { ymin = hullPoints[0].Y(); BIndex = 0; } } if ( TIndex == numPointsM1 ) { if ( hullPoints[0].Y() >= ymax ) { ymax = hullPoints[0].Y(); TIndex = 0; } } // The dimensions of the axis-aligned box. The extents store width and // height for now. mMinBox.Center.X() = ( ( Real )0.5 ) * ( xmin + xmax ); mMinBox.Center.Y() = ( ( Real )0.5 ) * ( ymin + ymax ); mMinBox.Axis[0] = Vector2<Real>::UNIT_X; mMinBox.Axis[1] = Vector2<Real>::UNIT_Y; mMinBox.Extent[0] = ( ( Real )0.5 ) * ( xmax - xmin ); mMinBox.Extent[1] = ( ( Real )0.5 ) * ( ymax - ymin ); Real minAreaDiv4 = mMinBox.Extent[0] * mMinBox.Extent[1]; // The rotating calipers algorithm. Vector2<Real> U = Vector2<Real>::UNIT_X; Vector2<Real> V = Vector2<Real>::UNIT_Y; bool done = false; while ( !done ) { // Determine the edge that forms the smallest angle with the current // box edges. int flag = F_NONE; Real maxDot = ( Real )0; Real dot = U.Dot( edges[BIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_BOTTOM; } dot = V.Dot( edges[RIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_RIGHT; } dot = -U.Dot( edges[TIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_TOP; } dot = -V.Dot( edges[LIndex] ); if ( dot > maxDot ) { maxDot = dot; flag = F_LEFT; } switch ( flag ) { case F_BOTTOM: if ( visited[BIndex] ) { done = true; } else { // Compute box axes with E[B] as an edge. U = edges[BIndex]; V = -U.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[BIndex] = true; if ( ++BIndex == numPoints ) { BIndex = 0; } } break; case F_RIGHT: if ( visited[RIndex] ) { done = true; } else { // Compute box axes with E[R] as an edge. V = edges[RIndex]; U = V.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[RIndex] = true; if ( ++RIndex == numPoints ) { RIndex = 0; } } break; case F_TOP: if ( visited[TIndex] ) { done = true; } else { // Compute box axes with E[T] as an edge. U = -edges[TIndex]; V = -U.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[TIndex] = true; if ( ++TIndex == numPoints ) { TIndex = 0; } } break; case F_LEFT: if ( visited[LIndex] ) { done = true; } else { // Compute box axes with E[L] as an edge. V = -edges[LIndex]; U = V.Perp(); UpdateBox( hullPoints[LIndex], hullPoints[RIndex], hullPoints[BIndex], hullPoints[TIndex], U, V, minAreaDiv4 ); // Mark edge visited and rotate the calipers. visited[LIndex] = true; if ( ++LIndex == numPoints ) { LIndex = 0; } } break; case F_NONE: // The polygon is a rectangle. done = true; break; } } delete1( visited ); delete1( edges ); if ( !isConvexPolygon ) { delete1( hullPoints ); } }