| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- using System;
- using System.Collections.Generic;
- using o0.Geometry2D.Float;
- using Unity.VisualScripting;
- namespace ZIM
- {
- // 屏幕识别使用,固定的顶点顺序: 左下,右下,左上,右上,严格遵守
- public class OrdinalQuadrilateral : Geometry<OrdinalQuadrilateral>
- {
- 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<Vector> 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));
- /// <summary>四边形的质心(顶点平均值)</summary>
- 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<Vector> 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);
- }
- }
- }
|