using System; using System.Collections.Generic; using o0.Geometry2D.Float; using Unity.VisualScripting; namespace ZIM { // 屏幕识别使用,固定的顶点顺序: 左下,右下,左上,右上,严格遵守 public class OrdinalQuadrilateral : Geometry { public Vector A; public Vector B; public Vector C; public Vector D; public override int Count => 4; public OrdinalQuadrilateral(Vector a, Vector b, Vector c, Vector d) { this.A = a; this.B = b; this.C = c; this.D = d; } public OrdinalQuadrilateral(IEnumerable enumable) { var e = enumable.GetEnumerator(); this.A = e.MoveNext() ? e.Current : default; this.B = e.MoveNext() ? e.Current : default; this.C = e.MoveNext() ? e.Current : default; this.D = e.MoveNext() ? e.Current : default; } public static OrdinalQuadrilateral Identity { get; } = new OrdinalQuadrilateral(new Vector(0, 0), new Vector(1, 0), new Vector(0, 1), new Vector(1, 1)); /// 四边形的质心(顶点平均值) public Vector Centroid => new Vector(A.x + B.x + C.x + D.x, A.y + B.y + C.y + D.y) / 4f; public override Vector this[int index] { get { switch (index % Count) { default: case 0: return A; case 1: return B; case 2: return C; case 3: return D; } } set { switch (index % Count) { default: case 0: A = value; break; case 1: B = value; break; case 2: C = value; break; case 3: D = value; break; } } } public override string ToString() { return $"[{A}, {B}, {C}, {D}]"; } // AABB包围盒,返回矩形左下角和右上角的坐标 public (Vector, Vector) AABBRect() { float minX = this[0].x; float maxX = this[0].x; float minY = this[0].y; float maxY = this[0].y; for (int i = 1; i < Count; i++) { minX = Math.Min(minX, this[i].x); maxX = Math.Max(maxX, this[i].x); minY = Math.Min(minY, this[i].y); maxY = Math.Max(maxY, this[i].y); } return (new Vector(minX, minY), new Vector(maxX, maxY)); } public Vector InterpolationFactors(Vector p) { float u, v; float _a = (C.x - D.x + B.x - A.x) * (A.y - C.y) - (C.y - D.y + B.y - A.y) * (A.x - C.x); float _b = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x) + (p.x - C.x) * (C.y - D.y + B.y - A.y) - (p.y - C.y) * (C.x - D.x + B.x - A.x); float _c = (p.x - C.x) * (D.y - C.y) - (p.y - C.y) * (D.x - C.x); if (_a < float.Epsilon) { v = -_c / _b; } else { float delta = _b * _b - 4 * _a * _c; if (delta < 0) { return default; throw new Exception("Delta is smaller than zero."); } float sqrtDelta = (float)Math.Sqrt(delta); v = (-_b + sqrtDelta) / (2 * _a); if (v < 0 || v > 1) v = (-_b - sqrtDelta) / (2 * _a); } u = ((p.x - C.x) - (A.x - C.x) * v) / ((D.x - C.x) + (C.x - D.x + B.x - A.x) * v); return new Vector(u, 1 - v); } public static OrdinalQuadrilateral Fit(IEnumerable pixels, Vector textureSize) { Vector[] vertex = new Vector[4] { new Vector(0, 0), new Vector(textureSize.x, 0), new Vector(textureSize.y, 0), textureSize }; Vector[] dir = new Vector[4] { new Vector(1, 1).Normalized, new Vector(-1, 1).Normalized, new Vector(1, -1).Normalized, new Vector(-1, -1).Normalized }; (float, Vector)[] min = new (float, Vector)[4] { (float.MaxValue, default), (float.MaxValue, default), (float.MaxValue, default), (float.MaxValue, default) }; foreach (var i in pixels) { for (int j = 0; j < 4; j++) { var len = (i - vertex[j]).Dot(dir[j]); if (len < min[j].Item1) min[j] = (len, i); } } return new OrdinalQuadrilateral(min[0].Item2, min[1].Item2, min[2].Item2, min[3].Item2); } } }