/// <summary> /// Check if a shape specified by the set of points fits a convex polygon /// specified by the set of corners. /// </summary> /// /// <param name="edgePoints">Shape's points to check.</param> /// <param name="corners">Corners of convex polygon to check fitting into.</param> /// /// <returns>Returns <see langword="true"/> if the specified shape fits /// the specified convex polygon or <see langword="false"/> otherwise.</returns> /// /// <remarks><para>The method checks if the set of specified points form the same shape /// as the set of provided corners.</para></remarks> /// public bool CheckIfPointsFitShape(List <IntPoint> edgePoints, List <IntPoint> corners) { int cornersCount = corners.Count; // lines coefficients (for representation as y(x)=k*x+b) float[] k = new float[cornersCount]; float[] b = new float[cornersCount]; float[] div = new float[cornersCount]; // precalculated divisor bool[] isVert = new bool[cornersCount]; for (int i = 0; i < cornersCount; i++) { IntPoint currentPoint = corners[i]; IntPoint nextPoint = (i + 1 == cornersCount) ? corners[0] : corners[i + 1]; if (!(isVert[i] = nextPoint.X == currentPoint.X)) { k[i] = (float)(nextPoint.Y - currentPoint.Y) / (nextPoint.X - currentPoint.X); b[i] = currentPoint.Y - k[i] * currentPoint.X; div[i] = (float)Math.Sqrt(k[i] * k[i] + 1); } } // calculate distances between edge points and polygon sides float meanDistance = 0; for (int i = 0, n = edgePoints.Count; i < n; i++) { float minDistance = float.MaxValue; for (int j = 0; j < cornersCount; j++) { float distance = 0; if (!isVert[j]) { distance = (float)Math.Abs((k[j] * edgePoints[i].X + b[j] - edgePoints[i].Y) / div[j]); } else { distance = Math.Abs(edgePoints[i].X - corners[j].X); } if (distance < minDistance) { minDistance = distance; } } meanDistance += minDistance; } meanDistance /= edgePoints.Count; // get bounding rectangle of the corners list IntPoint minXY, maxXY; PointsCloud.GetBoundingRectangle(corners, out minXY, out maxXY); IntPoint rectSize = maxXY - minXY; float maxDitance = Math.Max(minAcceptableDistortion, ((float)rectSize.X + rectSize.Y) / 2 * relativeDistortionLimit); return(meanDistance <= maxDitance); }
// Get optimized quadrilateral area private List <IntPoint> GetShapeCorners(List <IntPoint> edgePoints) { return(shapeOptimizer.OptimizeShape(PointsCloud.FindQuadrilateralCorners(edgePoints))); }
/// <summary> /// Check sub type of a convex polygon. /// </summary> /// /// <param name="corners">Corners of the convex polygon to check.</param> /// /// <returns>Return detected sub type of the specified shape.</returns> /// /// <remarks><para>The method check corners of a convex polygon detecting /// its subtype. Polygon's corners are usually retrieved using <see cref="IsConvexPolygon"/> /// method, but can be any list of 3-4 points (only sub types of triangles and /// quadrilateral are checked).</para> /// /// <para>See <see cref="AngleError"/> and <see cref="LengthError"/> properties, /// which set acceptable errors for polygon sub type checking.</para> /// </remarks> /// public PolygonSubType CheckPolygonSubType(List <IntPoint> corners) { PolygonSubType subType = PolygonSubType.Unknown; // get bounding rectangle of the points list IntPoint minXY, maxXY; PointsCloud.GetBoundingRectangle(corners, out minXY, out maxXY); // get cloud's size IntPoint cloudSize = maxXY - minXY; float maxLengthDiff = lengthError * (cloudSize.X + cloudSize.Y) / 2; if (corners.Count == 3) { // get angles of the triangle float angle1 = GeometryTools.GetAngleBetweenVectors(corners[0], corners[1], corners[2]); float angle2 = GeometryTools.GetAngleBetweenVectors(corners[1], corners[2], corners[0]); float angle3 = GeometryTools.GetAngleBetweenVectors(corners[2], corners[0], corners[1]); // check for equilateral triangle if ((Math.Abs(angle1 - 60) <= angleError) && (Math.Abs(angle2 - 60) <= angleError) && (Math.Abs(angle3 - 60) <= angleError)) { subType = PolygonSubType.EquilateralTriangle; } else { // check for isosceles triangle if ((Math.Abs(angle1 - angle2) <= angleError) || (Math.Abs(angle2 - angle3) <= angleError) || (Math.Abs(angle3 - angle1) <= angleError)) { subType = PolygonSubType.IsoscelesTriangle; } // check for rectangled triangle if ((Math.Abs(angle1 - 90) <= angleError) || (Math.Abs(angle2 - 90) <= angleError) || (Math.Abs(angle3 - 90) <= angleError)) { subType = (subType == PolygonSubType.IsoscelesTriangle) ? PolygonSubType.RectangledIsoscelesTriangle : PolygonSubType.RectangledTriangle; } } } else if (corners.Count == 4) { // get angles between 2 pairs of opposite sides float angleBetween1stPair = GeometryTools.GetAngleBetweenLines(corners[0], corners[1], corners[2], corners[3]); float angleBetween2ndPair = GeometryTools.GetAngleBetweenLines(corners[1], corners[2], corners[3], corners[0]); // check 1st pair for parallelism if (angleBetween1stPair <= angleError) { subType = PolygonSubType.Trapezoid; // check 2nd pair for parallelism if (angleBetween2ndPair <= angleError) { subType = PolygonSubType.Parallelogram; // check angle between adjacent sides if (Math.Abs(GeometryTools.GetAngleBetweenVectors(corners[1], corners[0], corners[2]) - 90) <= angleError) { subType = PolygonSubType.Rectangle; } // get length of 2 adjacent sides float side1Length = (float)corners[0].DistanceTo(corners[1]); float side2Length = (float)corners[0].DistanceTo(corners[3]); if (Math.Abs(side1Length - side2Length) <= maxLengthDiff) { subType = (subType == PolygonSubType.Parallelogram) ? PolygonSubType.Rhombus : PolygonSubType.Square; } } } else { // check 2nd pair for parallelism - last chence to detect trapezoid if (angleBetween2ndPair <= angleError) { subType = PolygonSubType.Trapezoid; } } } return(subType); }