/// <summary> /// Принадлежность точки выпуклому многоугольнику /// </summary> /// <param name="pnt">Проверяямая точка</param> /// <param name="sideIsJoint">Стороны принадлежат многоугольнику</param> /// <returns></returns> public bool JointPointConvex(PointF pnt, bool sideIsJoint) { var s = 0f; var dirs = new int[3]; var v = new Vector(); for (var i = 0; i < pnts.Count; i++) { v.Begin = pnts[i]; v.End = pnts[(i + 1) % pnts.Count]; dirs[v.LocatePointDir(pnt) + 1]++; // суммируются направления } // кол-во направлений == кол-во сторон return Math.Max(dirs[0], dirs[2]) // макс кол-во слева или справа + (sideIsJoint ? dirs[1] : 0) == pnts.Count; }
/// <summary> /// Определение самопересечения многоугольника /// </summary> /// <returns></returns> public bool IsSelfIntersection() { if (pnts.Count < 3) throw new Exception("Точек в полигоне должно быть минимум 3"); if (pnts.Count == 3) return false; Vector v1 = new Vector(), v2 = new Vector(); for (var i = 0; i < pnts.Count - 2; i++) { v1.Begin = pnts[i]; v2.End = pnts[i + 1]; for (var j = i + 2; j < pnts.Count; j++) { if (i == 0 && j == pnts.Count - 1) continue;// исключение пересечения первого вектора с последним v2.Begin = pnts[j]; v2.End = pnts[(j + 1) % pnts.Count]; if (v1.IsCross(v2)) return true; } } return false; }
/// <summary> /// Угол между векторами в радианах /// </summary> /// <param name="v2">Второй вектор</param> /// <returns></returns> public float GetAngle(Vector v2) { var a = ToOrigin(); var b = v2.ToOrigin(); return (float)Math.Atan2(a.X * b.Y - b.X * a.Y, a.X * b.X + a.Y * b.Y); }
/// <summary> /// Параллельность (коллинеарность) векторов /// </summary> /// <param name="a">Правый вектор</param> /// <param name="mathError">Погрешность</param> public bool IsVectorCollinear(Vector a, float mathError = float.Epsilon) { return Math.Abs(X * a.Y - a.X * Y) <= mathError; }
/// <summary> /// Нахождение точки пересечения двух отрезков (если есть) /// </summary> /// <param name="b"> </param> /// <param name="crs">Точка пересечения</param> /// <returns>Пересекаются или нет</returns> public bool JointVectors(Vector b, out PointF crs) { crs = new PointF(float.NaN, float.NaN); var w = (End.Y - Begin.Y) * (b.End.X - b.Begin.X) - (b.End.Y - b.Begin.Y) * (End.X - Begin.X); if (Math.Abs(w) > EPSILON) { var w1 = (b.Begin.Y - Begin.Y) * (b.End.X - b.Begin.X) * (End.X - Begin.X) + Begin.X * (End.Y - Begin.Y) * (b.End.X - b.Begin.X) - b.Begin.X * (b.End.Y - b.Begin.Y) * (End.X - Begin.X); var w2 = (b.Begin.X - Begin.X) * (b.End.Y - b.Begin.Y) * (End.Y - Begin.Y) + Begin.Y * (End.X - Begin.X) * (b.End.Y - b.Begin.Y) - b.Begin.Y * (b.End.X - b.Begin.X) * (End.Y - Begin.Y); crs.X = w1 / w; crs.Y = -w2 / w; return IsOwnPoint(crs) && b.IsOwnPoint(crs); } else return false; }
/// <summary> /// Определение факта пересечения двух векторов /// </summary> /// <param name="other"></param> /// <returns></returns> public bool IsCross(Vector other) { var pp = new[]{ LocatePointDir(other.Begin), LocatePointDir(other.End), other.LocatePointDir(Begin), other.LocatePointDir(End) }; return (Math.Abs(pp[0] + pp[1]) != 2 && Math.Abs(pp[2] + pp[3]) != 2 && pp.Any(s => s != 0)) // на случай канонического пересечения || pp[0] == 0 && IsOwnPoint(other.Begin) // начало другого вектора лежит на этом отрезке || pp[1] == 0 && IsOwnPoint(other.End) // конец другого вектора лежит на этом отрезке || pp[2] == 0 && other.IsOwnPoint(Begin) // начало этого вектора лежит на другом отрезке || pp[3] == 0 && other.IsOwnPoint(End); // конец этого вектора лежит на другом отрезке }
/// <summary> /// Расстояние от точки до полигона /// </summary> /// <param name="pnt">Точка</param> /// <returns></returns> public float DistancePoint(PointF pnt) { if (pnts.Count < 3) throw new Exception("Точек в полигоне должно быть минимум 3"); var v1 = new Vector(); float minDistPow2 = float.MaxValue, distPow2; PointF curr; for (var i = 0; i < pnts.Count; i++) { v1.Begin = pnts[i]; v1.End = pnts[(i + 1) % pnts.Count]; curr = v1.GetNormal(pnt); distPow2 = pnt.DistancePow2(curr); if (distPow2 < minDistPow2) minDistPow2 = distPow2; } minDistPow2 = (float)Math.Sqrt(minDistPow2); return JointPoint(pnt) ? -minDistPow2 : minDistPow2; }
/// <summary> /// Точка нормали лежащая на границе полигона /// </summary> /// <param name="pnt">Внешняя точка</param> /// <returns></returns> public PointF GetNormalPoint(PointF pnt) { if (pnts.Count < 3) throw new Exception("Точек в полигоне должно быть минимум 3"); var v1 = new Vector(); float minDistPow2 = float.MaxValue, distPow2; PointF curr, res = new PointF(); for (var i = 0; i < pnts.Count; i++) { v1.Begin = pnts[i]; v1.End = pnts[(i + 1) % pnts.Count]; curr = v1.GetNormal(pnt); distPow2 = pnt.DistancePow2(curr); if (distPow2 < minDistPow2) { res = curr; minDistPow2 = distPow2; } } return res; }