Files
ParkingRobot/CommonUsage-MultiVehicleSync/commonusage/Mathematics/CommonMath.cs
T

775 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace CommonUsage.Mathematics
{
using T3 = Tuple<float, float, float>;
using D3 = Tuple<double, double, double>;
public class CommonMath
{
public class PrimeEnumerator<T>
{
public PrimeEnumerator(List<T> items, Func<T, bool> process)
{
_n = items.Count;
_items = items;
_process = process;
foreach (var pNum in _primes)
{
if (_n % pNum != 0)
{
_a = pNum;
_b = 11;
break;
}
}
}
public void Enumerate()
{
using (var enumerator = Get().GetEnumerator())
{
while (enumerator.MoveNext()) { }
}
}
private readonly int _n, _a, _b;
private readonly int[] _primes = new[] { 29, 23, 19, 17, 13 };
private List<T> _items;
private readonly Func<T, bool> _process;
private IEnumerable<bool> Get()
{
for (var i = 0; i < _n; ++i)
{
var id = (i * _a + _b) % _n;
yield return _process(_items[id]);
}
}
}
private static IEnumerable<IEnumerable<T>> GetPermutationsInternal<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutationsInternal(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
/// <summary>
/// 得到一组数据的所有排列。
/// </summary>
/// <typeparam name="T">元素数据类型</typeparam>
/// <param name="list">所有待选元素</param>
/// <param name="selectNum">所选出的元素数量</param>
/// <returns></returns>
public static List<List<T>> GetPermutations<T>(List<T> list, int selectNum)
{
return GetPermutationsInternal(list, selectNum).Select(ll => ll.ToList()).ToList();
}
public static (float bias, Vector2 hPnt, float d) Project2DLine(Vector2 pnt, Vector2 segSt,
Vector2 segEnd)
{
var dir = Vector2.Normalize(segEnd - segSt);
var fd = Vector2.Dot(pnt - segSt, dir);
var hPnt = segSt + fd * dir;
var bias = dir.X * (pnt.Y-segSt.Y) - (pnt.X-segSt.X) * dir.Y;
return (bias, hPnt, fd);
}
public static (float bias, Vector2 hPnt, float fd) Project2DLine(Vector2 pnt, Vector2 segSt, float tangent)
{
var dir = new Vector2((float)System.Math.Cos(tangent / 180 * System.Math.PI), (float)System.Math.Sin(tangent / 180 * System.Math.PI));
var fd = Vector2.Dot(pnt - segSt, dir);
var hPnt = segSt + fd * dir;
var bias = dir.X * (pnt.Y - segSt.Y) - (pnt.X - segSt.X) * dir.Y;
return (bias, hPnt, fd);
}
public class LineEqu
{
public double A, B, C, ln, dAB;
public double px1, px2, py1, py2;
public float midX;
public float midY;
}
// Fit line with PCA.
public LineEqu CalcLine(IEnumerable<Vector2> tls)
{
var lidarPoint2Ds = tls as Vector2[] ?? tls.ToArray();
float fx = lidarPoint2Ds.Average(f => f.X);
float fy = lidarPoint2Ds.Average(f => f.Y);
float fxx = lidarPoint2Ds.Average(f => f.X * f.X);
float fxy = lidarPoint2Ds.Average(f => f.X * f.Y);
float fyy = lidarPoint2Ds.Average(f => f.Y * f.Y);
float a = fxx - fx * fx, b = fxy - fx * fy, c = fyy - fy * fy;
double sqt = System.Math.Sqrt((a - c) * (a - c) + 4 * b * b);
double l1 = a + c + sqt;
double l2 = a + c - sqt;
double dx, dy;
if (System.Math.Abs(a - l1 / 2) > System.Math.Abs(c - l1 / 2))
{
dy = l1 / 2 - a; dx = b;
}
else
{
dx = l1 / 2 - c; dy = b;
}
double norm = System.Math.Sqrt(dx * dx + dy * dy);
dx /= norm; dy /= norm;
double A = dy, B = -dx, C = dx * fy - dy * fx;
double dAB = System.Math.Sqrt(A * A + B * B);
return new CommonMath.LineEqu
{
A = A,
B = B,
C = C,
ln = lidarPoint2Ds.Average(p => System.Math.Abs(p.X * A + p.Y * B + C) / dAB),
midX = fx,
midY = fy
};
}
public static double QuadInterp3(double[] confsF)
{
if (confsF[0] > confsF[1] && confsF[0] > confsF[2])
{
//printf("left overflow...\n");
return -1;
}
if (confsF[1] > confsF[0] && confsF[1] > confsF[2])
{
return (-(confsF[2] - confsF[0]) / 2.0f / (confsF[0] + confsF[2] - 2.0f * confsF[1] + 0.0001f));
}
if (confsF[2] > confsF[0] && confsF[2] > confsF[1])
{
//printf("right overflow...\n");
return 1;
}
return 0;
}
public static double cross(PointF O, PointF A, PointF B)
{
return (A.X - O.X) * (B.Y - O.Y) - (A.Y - O.Y) * (B.X - O.X);
}
public static List<PointF> GetConvexHull(List<PointF> points)
{
if (points == null)
return null;
if (points.Count() <= 1)
return points;
int n = points.Count(), k = 0;
List<PointF> H = new List<PointF>(new PointF[2 * n]);
points.Sort((a, b) =>
a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X));
// Build lower hull
for (int i = 0; i < n; ++i)
{
while (k >= 2 && cross(H[k - 2], H[k - 1], points[i]) <= 0)
k--;
H[k++] = points[i];
}
// Build upper hull
for (int i = n - 2, t = k + 1; i >= 0; i--)
{
while (k >= t && cross(H[k - 2], H[k - 1], points[i]) <= 0)
k--;
H[k++] = points[i];
}
return H.Take(k - 1).ToList();
}
public static bool IsPointInPolygon4(PointF[] polygon, PointF testPoint)
{
// ray casting odd even test.
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++)
{
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y ||
polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
{
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) *
(polygon[j].X - polygon[i].X) < testPoint.X)
{
result = !result;
}
}
j = i;
}
return result;
}
public static double Exp(double val)
{
if (val < -20) return 0.0000001;
if (val > 20) return 99999999999999;
long tmp = (long)(1512775 * val + 1072632447);
return BitConverter.Int64BitsToDouble(tmp << 32);
}
public static double gaussmf(double x, double sig, double c)
{
return Exp(-(x - c) * (x - c) / (2 * sig * sig));
}
public static float Exp(float x)
{
if (x < -10) return 0;
if (x > 10) return 99999999999999;
x = 1.0f + x / 64f;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
return x;
}
public static float gaussmf(float x, float sig, float c)
{
return Exp(-(x - c) * (x - c) / (2 * sig * sig));
}
public static D3 Transform2D(D3 src, D3 t)
{
var rth = src.Item3 / 180.0 * System.Math.PI;
var p1dtx = (src.Item1 + System.Math.Cos(rth) * t.Item1 -
System.Math.Sin(rth) * t.Item2);
var p1dty = (src.Item2 + System.Math.Sin(rth) * t.Item1 +
System.Math.Cos(rth) * t.Item2);
var p1dtth = src.Item3 + t.Item3;
return Tuple.Create(p1dtx, p1dty, p1dtth);
}
public struct LngLatToXY
{
public double scale;
public double rad;
public double biasX, biasY;
}
public static LngLatToXY GetTransformLngLatToXY(Vector2 lnglat1, Vector2 xy1, Vector2 lnglat2, Vector2 xy2)
{
var scale = (xy1 - xy2).Length() / (lnglat1 - lnglat2).Length();
var dxy = (xy1 - xy2);
var dlnglat = lnglat1 - lnglat2;
var rad = System.Math.Atan2(dxy.X, dxy.Y) - System.Math.Atan2(dlnglat.X, dlnglat.Y);
var intm = lnglat1 * scale;
var biasX = xy1.X - (intm.X * System.Math.Cos(rad) - intm.Y * System.Math.Sin(rad));
var biasY = xy1.Y - (intm.X * System.Math.Sin(rad) + intm.Y * System.Math.Cos(rad));
return new LngLatToXY {rad = rad, biasX = biasX, biasY = biasY, scale = scale};
}
public Vector2 TransformLngLatToXY(Vector2 lnglat, LngLatToXY t)
{
var intm = lnglat * (float) t.scale;
return new Vector2((float) (intm.X * System.Math.Cos(t.rad) - intm.Y * System.Math.Sin(t.rad) + t.biasX),
(float) (intm.X * System.Math.Sin(t.rad) + intm.Y * System.Math.Cos(t.rad) + t.biasY));
}
public static D3 ReverseTransform(D3 dest, D3 t)
{
var rth = (dest.Item3 - t.Item3) / 180.0 * System.Math.PI;
var nxT = (dest.Item1 - System.Math.Cos(rth) * t.Item1 +
System.Math.Sin(rth) * t.Item2);
var nyT = (dest.Item2 - System.Math.Sin(rth) * t.Item1 -
System.Math.Cos(rth) * t.Item2);
var pth = dest.Item3 - t.Item3;
return Tuple.Create(nxT, nyT, pth);
}
public static D3 SolveTransform2D(D3 src, D3 dest)
{
var th = dest.Item3 - src.Item3;
th = (th - System.Math.Round((th) / 360.0f) * 360);
var rth = src.Item3 / 180.0 * System.Math.PI;
var x = ((dest.Item1 - src.Item1) * System.Math.Cos(rth) +
(dest.Item2 - src.Item2) * System.Math.Sin(rth));
var y = (-(dest.Item1 - src.Item1) * System.Math.Sin(rth) +
(dest.Item2 - src.Item2) * System.Math.Cos(rth));
return Tuple.Create(x, y, th);
}
public static T3 Transform2D(T3 src, T3 t)
{
var rth = src.Item3 / 180.0 * System.Math.PI;
var p1dtx = (float)(src.Item1 + System.Math.Cos(rth) * t.Item1 -
System.Math.Sin(rth) * t.Item2);
var p1dty = (float)(src.Item2 + System.Math.Sin(rth) * t.Item1 +
System.Math.Cos(rth) * t.Item2);
var p1dtth = src.Item3 + t.Item3;
return Tuple.Create(p1dtx, p1dty, p1dtth);
}
public static Vector2 Transform2D(Vector3 src, Vector3 t)
{
var tup = Transform2D(Tuple.Create(src.X, src.Y, src.Z), Tuple.Create(t.X, t.Y, t.Z));
return new Vector2(tup.Item1, tup.Item2);
}
public static Vector2 Transform2D(Vector2 srcPos, float srcTh, Vector2 dt, float dth = 0)
{
var tup = Transform2D(Tuple.Create(srcPos.X, srcPos.Y, srcTh), Tuple.Create(dt.X, dt.Y, dth));
return new Vector2(tup.Item1, tup.Item2);
}
public static T3 ReverseTransform(T3 dest, T3 t)
{
var rth = (dest.Item3 - t.Item3) / 180.0 * System.Math.PI;
var nxT = (float)(dest.Item1 - System.Math.Cos(rth) * t.Item1 +
System.Math.Sin(rth) * t.Item2);
var nyT = (float)(dest.Item2 - System.Math.Sin(rth) * t.Item1 -
System.Math.Cos(rth) * t.Item2);
var pth = dest.Item3 - t.Item3;
return Tuple.Create(nxT, nyT, pth);
}
public static T3 SolveTransform2D(T3 src, T3 dest)
{
var th = dest.Item3 - src.Item3;
th = (float)(th - System.Math.Round((th) / 360.0f) * 360);
var rth = src.Item3 / 180.0 * System.Math.PI;
var x = (float)((dest.Item1 - src.Item1) * System.Math.Cos(rth) +
(dest.Item2 - src.Item2) * System.Math.Sin(rth));
var y = (float)(-(dest.Item1 - src.Item1) * System.Math.Sin(rth) +
(dest.Item2 - src.Item2) * System.Math.Cos(rth));
return Tuple.Create(x, y, th);
}
public static Vector2 SolveTransform2D(Vector2 srcPos, float srcTh, Vector2 dt, float dth = 0)
{
var tup = SolveTransform2D(Tuple.Create(srcPos.X, srcPos.Y, srcTh), Tuple.Create(dt.X, dt.Y, dth));
return new Vector2(tup.Item1, tup.Item2);
}
public static double dist(double x1, double y1, double x2, double y2)
{
return System.Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
[StructLayout(LayoutKind.Explicit)]
private struct FloatIntUnion
{
[FieldOffset(0)] public float f;
[FieldOffset(0)] public int tmp;
}
public static float Sqrt(float z)
{
FloatIntUnion u;
u.tmp = 0;
u.f = z;
u.tmp -= 1 << 23; /* Subtract 2^m. */
u.tmp >>= 1; /* Divide by 2. */
u.tmp += 1 << 29; /* Add ((b + 1) / 2) * 2^m. */
return u.f;
}
public static float dist2(float x1, float y1, float x2, float y2)
{
return ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
public static float d2(float x1, float y1, float x2, float y2)
{
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}
public static float ThAverage(List<float> angles)
{
var anchor = angles[0];
var diff = 0f;
foreach (var angle in angles)
diff += ThDiff(angle, anchor);
return RoundTh(anchor + diff / angles.Count);
}
public static float ThDiff(float th1, float th2)
{
return (float)(th1 - th2 -
System.Math.Round((th1 - th2) / 360.0f) * 360);
}
public static double ThDiff(double th1, double th2)
{
return th1 - th2 -
System.Math.Round((th1 - th2) / 360.0f) * 360;
}
public static double refine(double x)
{
if (x < 1 && x > -1) return x;
if (x > 1)
return (2 / (1 + System.Math.Exp(-((x - 1) * 2))));
return (2 / (1 + System.Math.Exp(-((x + 1) * 2)))) - 2;
}
/// <summary>
/// 求点p到两点式直线p1p2的距离
/// </summary>
/// <param name="x">点p的x坐标</param>
/// <param name="y">点p的y坐标</param>
/// <param name="x1">直线点p1的x坐标</param>
/// <param name="y1">直线点p1的y坐标</param>
/// <param name="x2">直线点p2的x坐标</param>
/// <param name="y2">直线点p2的y坐标</param>
/// <returns></returns>
public static double Point2LineDist(double x, double y, double x1, double y1, double x2, double y2)
{
double a1 = -(y1 - y2) / 10;
double b1 = (x1 - x2) / 10;
double c1 = (x1 * (y1 - y2) - y1 * (x1 - x2)) / 10;
return System.Math.Abs(a1 * x + b1 * y + c1) / System.Math.Sqrt(a1 * a1 + b1 * b1);
}
public static double Point2LineDist(Vector2 p, LineSegment ll)
{
double a1 = -(ll.Src.Y - ll.Dst.Y) / 10;
double b1 = (ll.Src.X - ll.Dst.X) / 10;
double c1 = (ll.Src.X * (ll.Src.Y - ll.Dst.Y) - ll.Src.Y * (ll.Src.X - ll.Dst.X)) / 10;
return System.Math.Abs(a1 * p.X + b1 * p.Y + c1) / System.Math.Sqrt(a1 * a1 + b1 * b1);
}
/// <summary>
/// 两条两点式直线间的夹角
/// </summary>
/// <param name="x1"></param>
/// <param name="y1"></param>
/// <param name="x2"></param>
/// <param name="y2"></param>
/// <param name="x3"></param>
/// <param name="y3"></param>
/// <param name="x4"></param>
/// <param name="y4"></param>
/// <returns>角度制</returns>
public static double AngleBetweenLines(double x1, double y1, double x2, double y2, double x3, double y3,
double x4, double y4)
{
var vec1 = new Vector2((float)(x2 - x1), (float)(y2 - y1));
var vec2 = new Vector2((float)(x4 - x3), (float)(y4 - y3));
return System.Math.Acos(System.Math.Abs(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length())) / System.Math.PI * 180;
}
public static double AngleBetweenLines(LineSegment ls1, LineSegment ls2)
{
return AngleBetweenLines(ls1.Src.X, ls1.Src.Y, ls1.Dst.X, ls1.Dst.Y, ls2.Src.X, ls2.Src.Y, ls2.Dst.X,
ls2.Dst.Y);
}
/// <summary>
/// 两向量间夹角
/// </summary>
/// <param name="x1"></param>
/// <param name="y1"></param>
/// <param name="x2"></param>
/// <param name="y2"></param>
/// <param name="x3"></param>
/// <param name="y3"></param>
/// <param name="x4"></param>
/// <param name="y4"></param>
/// <returns>角度制</returns>
public static double AngleBetweenVectors(double x1, double y1, double x2, double y2, double x3, double y3,
double x4, double y4)
{
var vec1 = new Vector2((float)(x2 - x1), (float)(y2 - y1));
var vec2 = new Vector2((float)(x4 - x3), (float)(y4 - y3));
return System.Math.Acos(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length()) / System.Math.PI * 180;
}
/// <summary>
/// 两向量间夹角.
/// </summary>
/// <param name="vec1"></param>
/// <param name="vec2"></param>
/// <returns>角度制</returns>
public static double AngleBetweenVectors(Vector2 vec1, Vector2 vec2)
{
return System.Math.Acos(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length()) / System.Math.PI * 180;
}
public static double AngleBetweenVectors(Vector3 vector1, Vector3 vector2)
{
float dotProduct = Vector3.Dot(vector1, vector2);
float magnitude1 = vector1.Length();
float magnitude2 = vector2.Length();
float cosine = dotProduct / (magnitude1 * magnitude2);
return System.Math.Acos(cosine) / System.Math.PI * 180;
}
/// <summary>
/// 求点到直线的垂足
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="x1"></param>
/// <param name="y1"></param>
/// <param name="x2"></param>
/// <param name="y2"></param>
/// <returns></returns>
public static (double, double) PerpendicularPoint(double x, double y, double x1, double y1, double x2, double y2)
{
double lx = x2 - x1, ly = y2 - y1, dAB = lx * lx + ly * ly;
var u = ((x - x1) * lx + (y - y1) * ly) / dAB;
return new(x1 + u * lx, y1 + u * ly);
}
public static Vector2 PerpendicularPoint(Vector2 p, LineSegment ls)
{
double lx = ls.Dst.X - ls.Src.X, ly = ls.Dst.Y - ls.Src.Y, dAB = lx * lx + ly * ly;
var u = ((p.X - ls.Src.X) * lx + (p.Y - ls.Src.Y) * ly) / dAB;
return new Vector2((float)(ls.Src.X + u * lx), (float)(ls.Src.Y + u * ly));
}
public static double PerpendicularPosition(double x, double y, double x1, double y1, double x2, double y2)
{
double lx = x2 - x1, ly = y2 - y1;
var dAB = CommonMath.Sqrt((float)(lx * lx + ly * ly));
lx /= dAB;
ly /= dAB;
return (x - x1) * lx + (y - y1) * ly;
}
/// <summary>
/// 最小二乘法拟合直线,得到两点式。
/// </summary>
/// <param name="pts">待拟合的点集,应至少有2个点。</param>
/// <param name="maxDist2Line">检查是否所有点距直线的距离均小于maxDist2Line,若为-1则不检查。</param>
/// <returns>返回两点式的两个端点坐标。若坐标为全0,则拟合失败。</returns>
public static (bool, Vector2, Vector2) FitLineSegment(List<Vector2> pts, double maxDist2Line = -1)
{
if (pts.Count < 2)
{
Console.WriteLine($"Points too Few! {pts.Count}! Cannot perform line fitting!",
MethodBase.GetCurrentMethod()?.Name ?? "FitLine");
return (false, Vector2.Zero, Vector2.Zero);
};
// y = kx + b
double A = 0, B = 0, C = 0, D = 0;
foreach (var p in pts)
{
A += p.X * p.X;
B += p.X;
C += p.X * p.Y;
D += p.Y;
}
var tmp = A * pts.Count - B * B;
var k = (C * pts.Count - B * D) / tmp;
var b = (A * D - C * B) / tmp;
double x1 = 0,
y1 = k * x1 + b,
x2 = 1000,
y2 = k * x2 + b;
double CalcDist(ref bool fail, ref Vector2 endP, ref Vector2 endQ)
{
double distSum = 0;
double lx = x2 - x1, ly = y2 - y1, dAB = lx * lx + ly * ly;
double minU = double.MaxValue, maxU = double.MinValue;
foreach (var p in pts)
{
var u = ((p.X - x1) * lx + (p.Y - y1) * ly) / dAB;
var perp = new Vector2((float)(x1 + u * lx), (float)(y1 + u * ly));
if (u < minU)
{
endP = perp;
minU = u;
}
if (u > maxU)
{
endQ = perp;
maxU = u;
}
var curDist = dist(perp.X, perp.Y, p.X, p.Y);
if (maxDist2Line > -1 && curDist > maxDist2Line) fail = true;
distSum += curDist;
}
return distSum;
}
var kbFail = false;
Vector2 endP1 = new Vector2(), endQ1 = new Vector2();
double kbDist = CalcDist(ref kbFail, ref endP1, ref endQ1);
// x = my + n
A = 0;
B = 0;
C = 0;
D = 0;
foreach (var p in pts)
{
A += p.X * p.Y;
B += p.Y * p.Y;
C += p.Y;
D += p.X;
}
tmp = C * C - B * pts.Count;
var m = (C * D - A * pts.Count) / tmp;
var n = (A * C - B * D) / tmp;
y1 = 0;
x1 = m * y1 + n;
y2 = 1000;
x2 = m * y2 + n;
var mnFail = false;
Vector2 endP2 = new Vector2(), endQ2 = new Vector2();
double mnDist = CalcDist(ref mnFail, ref endP2, ref endQ2);
Vector2 endP = endP1, endQ = endQ1;
if (mnDist < kbDist)
{
if (mnFail) return (false, new Vector2(), new Vector2());
endP = endP2;
endQ = endQ2;
}
else if (kbFail) return (false, new Vector2(), new Vector2());
return (true, endP, endQ);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int toId(int x, int y, int z)
{
return (x * 1140671485 + 12820163) ^ (y * 134775813 + 1) ^ (z * 1103515245 + 12345);
}
public class Clustering<T>
{
public int numIteration = 3;
public int itemNumThreshold = 10;
public Func<T, T, bool> inRange;
public Func<List<T>, T> average;
private readonly List<T> _inputData;
private Dictionary<T, List<T>> _clusters = new Dictionary<T, List<T>>();
public Clustering(List<T> data, Func<T, T, bool> inRange, Func<List<T>, T> average)
{
_inputData = data;
this.inRange = inRange;
this.average = average;
}
public Dictionary<T, List<T>> GetClusters()
{
var tmp = new List<(T center, List<T> items)>();
for (var iter = 0; iter < numIteration; iter++)
{
tmp = tmp.Where(cluster => cluster.items.Count > itemNumThreshold)
.Select(cluster => (average(cluster.items), new List<T>())).ToList();
foreach (var data in _inputData)
{
var added = false;
foreach (var cluster in tmp)
{
if (inRange(cluster.center, data))
{
cluster.items.Add(data);
added = true;
break;
}
}
if (!added)
tmp.Add((data, new List<T>() { data }));
}
}
_clusters = tmp.Where(cluster => cluster.items.Count > itemNumThreshold)
.ToDictionary(cluster => cluster.center, cluster => cluster.items);
return _clusters;
}
}
public static (bool, Vector2) TwoLinesIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D)
{
// Line AB represented as a1x + b1y = c1
double a1 = B.Y - A.Y;
double b1 = A.X - B.X;
double c1 = a1 * (A.X) + b1 * (A.Y);
// Line CD represented as a2x + b2y = c2
double a2 = D.Y - C.Y;
double b2 = C.X - D.X;
double c2 = a2 * (C.X) + b2 * (C.Y);
double determinant = a1 * b2 - a2 * b1;
if (determinant == 0)
{
// The lines are parallel. This is simplified
// by returning a pair of FLT_MAX
return new(false, new Vector2());
}
else
{
double x = (b2 * c1 - b1 * c2) / determinant;
double y = (a1 * c2 - a2 * c1) / determinant;
return (true, new Vector2((float)x, (float)y));
}
}
public static bool IsAtLeft(Vector2 anchor, Vector2 dest, Vector2 p)
{
var v1 = new Vector3(anchor - p, 0);
var v2 = new Vector3(dest - p, 0);
return Vector3.Cross(v1, v2).Z > 0;
}
/// <summary>
/// 将角度转化至-180到180度的范围内。
/// </summary>
/// <param name="th"></param>
/// <returns></returns>
public static double RoundTh(double th)
{
return th - System.Math.Round(th / 360) * 360;
}
/// <summary>
/// 将角度转化至-180到180度的范围内。
/// </summary>
/// <param name="th"></param>
/// <returns></returns>
public static float RoundTh(float th)
{
return th - (float)System.Math.Round(th / 360f) * 360f;
}
}
}