public static List<IntPoint> FindQuadrilateralCorners( IEnumerable<IntPoint> cloud )
{
// quadrilateral's corners
List<IntPoint> corners = new List<IntPoint>( );
// get bounding rectangle of the points list
IntPoint minXY, maxXY;
PointsCloud.GetBoundingRectangle( cloud, out minXY, out maxXY );
// get cloud's size
IntPoint cloudSize = maxXY - minXY;
// calculate center point
IntPoint center = minXY + cloudSize / 2;
// acceptable deviation limit
double distortionLimit = quadrilateralRelativeDistortionLimit * ( cloudSize.X + cloudSize.Y ) / 2;
// get the furthest point from (0,0)
IntPoint point1 = PointsCloud.GetFurthestPoint( cloud, center );
// get the furthest point from the first point
IntPoint point2 = PointsCloud.GetFurthestPoint( cloud, point1 );
corners.Add( point1 );
corners.Add( point2 );
// get two furthest points from line
IntPoint point3, point4;
double distance3, distance4;
PointsCloud.GetFurthestPointsFromLine( cloud, point1, point2,
out point3, out distance3, out point4, out distance4 );
// ideally points 1 and 2 form a diagonal of the
// quadrilateral area, and points 3 and 4 form another diagonal
// but if one of the points (3 or 4) is very close to the line
// connecting points 1 and 2, then it is one the same line ...
// which means corner was not found.
// in this case we deal with a trapezoid or triangle, where
// (1-2) line is one of it sides.
// another interesting case is when both points (3) and (4) are
// very close the (1-2) line. in this case we may have just a flat
// quadrilateral.
if (
( ( distance3 >= distortionLimit ) && ( distance4 >= distortionLimit ) ) ||
( ( distance3 < distortionLimit ) && ( distance3 != 0 ) &&
( distance4 < distortionLimit ) && ( distance4 != 0 ) ) )
{
corners.Add( point3 );
corners.Add( point4 );
}
else
{
// it seems that we deal with kind of trapezoid,
// where point 1 and 2 are on the same edge
IntPoint tempPoint = ( distance3 > distance4 ) ? point3 : point4;
// try to find 3rd point
PointsCloud.GetFurthestPointsFromLine( cloud, point1, tempPoint,
out point3, out distance3, out point4, out distance4 );
bool thirdPointIsFound = false;
if ( ( distance3 >= distortionLimit ) && ( distance4 >= distortionLimit ) )
{
if ( point4.DistanceTo( point2 ) > point3.DistanceTo( point2 ) )
point3 = point4;
thirdPointIsFound = true;
}
else
{
PointsCloud.GetFurthestPointsFromLine( cloud, point2, tempPoint,
out point3, out distance3, out point4, out distance4 );
if ( ( distance3 >= distortionLimit ) && ( distance4 >= distortionLimit ) )
{
if ( point4.DistanceTo( point1 ) > point3.DistanceTo( point1 ) )
point3 = point4;
thirdPointIsFound = true;
}
}
if ( !thirdPointIsFound )
{
// failed to find 3rd edge point, which is away enough from the temp point.
// this means that the clound looks more like triangle
corners.Add( tempPoint );
}
else
{
corners.Add( point3 );
// try to find 4th point
double tempDistance;
PointsCloud.GetFurthestPointsFromLine( cloud, point1, point3,
out tempPoint, out tempDistance, out point4, out distance4 );
if ( ( distance4 >= distortionLimit ) && ( tempDistance >= distortionLimit ) )
{
if ( tempPoint.DistanceTo( point2 ) > point4.DistanceTo( point2 ) )
point4 = tempPoint;
}
else
{
PointsCloud.GetFurthestPointsFromLine( cloud, point2, point3,
out tempPoint, out tempDistance, out point4, out distance4 );
if ( ( tempPoint.DistanceTo( point1 ) > point4.DistanceTo( point1 ) ) &&
( tempPoint != point2 ) && ( tempPoint != point3 ) )
{
point4 = tempPoint;
}
}
if ( ( point4 != point1 ) && ( point4 != point2 ) && ( point4 != point3 ) )
corners.Add( point4 );
}
}
// put the point with lowest X as the first
for ( int i = 1, n = corners.Count; i < n; i++ )
{
if ( ( corners[i].X < corners[0].X ) ||
( ( corners[i].X == corners[0].X ) && ( corners[i].Y < corners[0].Y ) ) )
{
IntPoint temp = corners[i];
corners[i] = corners[0];
corners[0] = temp;
}
}
// sort other points in counter clockwise order
double k1 = ( corners[1].X != corners[0].X ) ?
( (double) ( corners[1].Y - corners[0].Y ) / ( corners[1].X - corners[0].X ) ) :
( ( corners[1].Y > corners[0].Y ) ? double.PositiveInfinity : double.NegativeInfinity );
double k2 = ( corners[2].X != corners[0].X ) ?
( (double) ( corners[2].Y - corners[0].Y ) / ( corners[2].X - corners[0].X ) ) :
( ( corners[2].Y > corners[0].Y ) ? double.PositiveInfinity : double.NegativeInfinity );
if ( k2 < k1 )
{
IntPoint temp = corners[1];
corners[1] = corners[2];
corners[2] = temp;
double tk = k1;
k1 = k2;
k2 = tk;
}
if ( corners.Count == 4 )
{
double k3 = ( corners[3].X != corners[0].X ) ?
( (double) ( corners[3].Y - corners[0].Y ) / ( corners[3].X - corners[0].X ) ) :
( ( corners[3].Y > corners[0].Y ) ? double.PositiveInfinity : double.NegativeInfinity );
if ( k3 < k1 )
{
IntPoint temp = corners[1];
corners[1] = corners[3];
corners[3] = temp;
double tk = k1;
k1 = k3;
k3 = tk;
}
if ( k3 < k2 )
{
IntPoint temp = corners[2];
corners[2] = corners[3];
corners[3] = temp;
double tk = k2;
k2 = k3;
k3 = tk;
}
}
return corners;
}