FlyCube/FlyBase/FlyBase.cs

1662 lines
71 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.SqlServer.Server;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Shapes;
using static System.Net.WebRequestMethods;
namespace FlightRouteV2
{
//矩阵类
public struct Matrix
{
public double M11, M12, M13;
public double M21, M22, M23;
public double M31, M32, M33;
public double M41, M42, M43;
/// <summary>
/// 构造函数,接受四个 Vector3 对象
/// </summary>
/// <param name="row1">向量1</param>
/// <param name="row2">向量2</param>
/// <param name="row3">向量3</param>
/// <param name="row4">向量4</param>
public Matrix(Vector3 row1, Vector3 row2, Vector3 row3, Vector3 row4)
{
M11 = row1.X; M12 = row1.Y; M13 = row1.Z;
M21 = row2.X; M22 = row2.Y; M23 = row2.Z;
M31 = row3.X; M32 = row3.Y; M33 = row3.Z;
M41 = row4.X; M42 = row4.Y; M43 = row4.Z;
}
/// <summary>
/// 通过方法实现通过索引的访问
/// </summary>
/// <exception cref="IndexOutOfRangeException"></exception>
public Vector3 this[int index]
{
get
{
switch (index)
{
case 0: return new Vector3(M11, M12, M13);
case 1: return new Vector3(M21, M22, M23);
case 2: return new Vector3(M31, M32, M33);
case 3: return new Vector3(M41, M42, M43);
default:
throw new IndexOutOfRangeException("Index out of range for Matrix");
}
}
}
/// <summary>
/// // 重写 ToString 方法,以便能够直接打印矩阵
/// </summary>
public override string ToString()
{
return $"v1:{M11}, {M12}, {M13}\n" +
$"v2:{M21}, {M22}, {M23}\n" +
$"v3:{M31}, {M32}, {M33}\n" +
$"off:{M41}, {M42}, {M43}";
}
}
//向量类
public struct Vector3
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
/// <summary>
/// [数组下标]方式 访问XYZ属性
/// </summary>
/// <param name="index"></param>
/// <returns>[0]X [1]Y [2]Z</returns>
/// <exception cref="IndexOutOfRangeException">访问下标0-2</exception>
public double this[int index]
{
get
{
switch (index)
{
case 0:
return X;
case 1:
return Y;
case 2:
return Z;
default:
throw new IndexOutOfRangeException($"Index {index} is out of range for Vector3");
}
}
set
{
switch (index)
{
case 0:
X = value;
break;
case 1:
Y = value;
break;
case 2:
Z = value;
break;
default:
throw new IndexOutOfRangeException($"Index {index} is out of range for Vector3");
}
}
}
// 静态属性表示单位向量
public static Vector3 UnitX { get { return new Vector3(1.0, 0.0, 0.0); } }
public static Vector3 UnitY { get { return new Vector3(0.0, 1.0, 0.0); } }
public static Vector3 UnitZ { get { return new Vector3(0.0, 0.0, 1.0); } }
/// <summary>
/// 构造 初始化
/// </summary>
/// <param name="x">x坐标</param>
/// <param name="y">y坐标</param>
/// <param name="z">z坐标</param>
public Vector3(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
/// <summary>
/// 重载二元坐标加法+ 向量+向量
/// </summary>
/// <param name="v1">向量加数</param>
/// <param name="v2">向量加数</param>
/// <returns></returns>
public static Vector3 operator +(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
/// <summary>
/// 重载一元坐标加法+ 向量+小数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">小数</param>
/// <returns></returns>
public static Vector3 operator +(Vector3 v1, double i)
{
return new Vector3(v1.X + i, v1.Y + i, v1.Z + i);
}
/// <summary>
/// 重载一元坐标加法+ 向量+整数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">整数</param>
/// <returns></returns>
public static Vector3 operator +(Vector3 v1, int i)
{
return new Vector3(v1.X + (double)i, v1.Y + (double)i, v1.Z + (double)i);
}
/// <summary>
/// 重载二元坐标减法- 向量-向量
/// </summary>
/// <param name="v1">向量被减数</param>
/// <param name="v2">向量减数</param>
/// <returns></returns>
public static Vector3 operator -(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
/// <summary>
/// 重载一元坐标加法- 向量-小数
/// </summary>
/// <param name="v1">向量被减数</param>
/// <param name="i">小数减数</param>
/// <returns></returns>
public static Vector3 operator -(Vector3 v1, double i)
{
return new Vector3(v1.X - i, v1.Y - i, v1.Z - i);
}
/// <summary>
/// 重载一元坐标加法- 向量-整数
/// </summary>
/// <param name="v1">向量被减数</param>
/// <param name="i">整数减数</param>
/// <returns></returns>
public static Vector3 operator -(Vector3 v1, int i)
{
return new Vector3(v1.X - (double)i, v1.Y - (double)i, v1.Z - (double)i);
}
/// <summary>
/// 重载一元坐标乘法* 向量*小数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">小数乘数</param>
/// <returns>得到向量的倍数向量</returns>
public static Vector3 operator *(Vector3 v1, double i)
{
return new Vector3(v1.X * i, v1.Y * i, v1.Z * i);
}
/// <summary>
/// 重载一元坐标乘法* 向量*整数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">整数乘数</param>
/// <returns>得到向量的倍数向量</returns>
public static Vector3 operator *(Vector3 v1, int i)
{
return new Vector3(v1.X * (double)i, v1.Y * (double)i, v1.Z * (double)i);
}
/// <summary>
/// 重载一元坐标除法/ 向量/小数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">小数除数</param>
/// <returns>得到向量的商向量</returns>
public static Vector3 operator /(Vector3 v1, double i)
{
return new Vector3(v1.X / i, v1.Y / i, v1.Z / i);
}
/// <summary>
/// 重载一元坐标除法/ 向量/整数
/// </summary>
/// <param name="v1">向量</param>
/// <param name="i">整数除数</param>
/// <returns>得到向量的商向量</returns>
public static Vector3 operator /(Vector3 v1, int i)
{
return new Vector3(v1.X / (double)i, v1.Y / (double)i, v1.Z / (double)i);
}
/// <summary>
/// 重载== 向量==向量
/// </summary>
/// <param name="v1">向量1</param>
/// <param name="v2">向量2</param>
/// <returns>布尔值 判断向量是否相等</returns>
public static bool operator ==(Vector3 v1, Vector3 v2)
{
if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z) return true;
return false;
}
/// <summary>
/// 重载!= 向量!=向量
/// </summary>
/// <param name="v1">向量1</param>
/// <param name="v2">向量2</param>
/// <returns>布尔值 判断向量是否不相等</returns>
public static bool operator !=(Vector3 v1, Vector3 v2)
{
if (v1.X != v2.X || v1.Y != v2.Y || v1.Z != v2.Z) return true;
return false;
}
/// <summary>
/// 确定指定的对象是否等于当前向量。
/// </summary>
/// <param name="obj">要与当前向量比较的对象。</param>
/// <returns>如果指定的对象等于当前向量,则为 <c>true</c>;否则为 <c>false</c>。</returns>
public override bool Equals(object obj)
{
// 检查对象是否与当前向量具有相同的类型
if (obj is Vector3)
{
Vector3 vector = (Vector3)obj;
// 比较向量的每个分量
return X == vector.X &&
Y == vector.Y &&
Z == vector.Z;
}
// 如果对象不是 Vector3 类型,则它们不相等
return false;
}
/// <summary>
/// 向量按矩阵旋转和偏移
/// </summary>
/// <param name="mat">矩阵</param>
/// <returns>返回一个新的向量</returns>
public Vector3 Multiply(Matrix mat)
{
Vector3 re = new Vector3();
//矩阵相乘 ps:旋转角度
re.X = this.X * mat[1].X + this.Y * mat[2].X + this.Z * mat[0].X;
re.Y = this.X * mat[1].Y + this.Y * mat[2].Y + this.Z * mat[0].Y;
re.Z = this.X * mat[1].Z + this.Y * mat[2].Z + this.Z * mat[0].Z;
//off偏移
re = re + mat[3];
return re;
}
/// <summary>
/// 求模长
/// </summary>
/// <returns>向量到原点的模长</returns>
public double GetMag()
{
return Math.Sqrt(Math.Pow(this.X, 2) + Math.Pow(this.Y, 2) + Math.Pow(this.Z, 2));
}
/// <summary>
/// 求模长 平方
/// </summary>
/// <returns>向量到原点的模长的平方值</returns>
public double GetMagSquared()
{
return Math.Pow(this.X, 2) + Math.Pow(this.Y, 2) + Math.Pow(this.Z, 2);
}
/// <summary>
/// 标准化坐标 无返回值 直接改变愿坐标
/// </summary>
/// <param name="multiple">标准化单位</param>
public void Normalize(double multiple = 1.0)
{
double magSq = Math.Pow(this.X, 2) + Math.Pow(this.Y, 2) + Math.Pow(this.Z, 2);
if (magSq > 0)
{
double oneOverMag = multiple / Math.Sqrt(magSq);
this.X *= oneOverMag;
this.Y *= oneOverMag;
this.Z *= oneOverMag;
}
}
/// <summary>
/// 标准化 返回一个标准化之后的值 不改变自身
/// </summary>
/// <param name="multiple">标准化单位</param>
/// <returns>标准化之后的值</returns>
public Vector3 NormalizEd(double multiple = 1.0)
{
Vector3 re = new Vector3();
double magSq = Math.Pow(this.X, 2) + Math.Pow(this.Y, 2) + Math.Pow(this.Z, 2);
if (magSq > 0)
{
double oneOverMag = multiple / Math.Sqrt(magSq);
re.X = this.X * oneOverMag;
re.Y = this.Y * oneOverMag;
re.Z = this.Z * oneOverMag;
}
return re;
}
/// <summary>
/// 归零 改变自身数值 一般配合归位使用
/// </summary>
/// <param name="v2"></param>
public void SetZero(Vector3 v2)
{
this.X -= v2.X;
this.Y -= v2.Y;
this.Z -= v2.Z;
}
/// <summary>
/// 归零 返回一个归零值 不改变自身
/// </summary>
/// <param name="v2"></param>
/// <returns></returns>
public Vector3 SetZeroEd(Vector3 v2)
{
Vector3 re = new Vector3(this.X - v2.X, this.Y - v2.Y, this.Z - v2.Z);
return re;
}
/// <summary>
/// 归位
/// </summary>
/// <param name="v2"></param>
public void SetFormerly(Vector3 v2)
{
this.X += v2.X;
this.Y += v2.Y;
this.Z += v2.Z;
}
/// <summary>
/// 重写ToString 打印坐标
/// </summary>
/// <returns>坐标字符串</returns>
public override string ToString()
{
string x = Convert.ToString(this.X);
string y = Convert.ToString(this.Y);
string z = Convert.ToString(this.Z);
return string.Format($"X轴:{x} Y轴:{y} Z轴:{z}");
}
/// <summary>
/// 哈希码是一个整数值,用于对对象进行快速比较和索引
/// </summary>
/// <returns>哈希码</returns>
public override int GetHashCode()
{
int hashCode = -307843816;
hashCode = hashCode * -1521134295 + X.GetHashCode();
hashCode = hashCode * -1521134295 + Y.GetHashCode();
hashCode = hashCode * -1521134295 + Z.GetHashCode();
return hashCode;
}
}
//坐标操作与验证
public static class FlyVecFun
{
/// <summary>
/// 输出日志回调函数
/// </summary>
/// <param name="str">日志内容</param>
public delegate void SomeCalculateWay(string str);
/// <summary>
/// Arraylist 转 Vector3[] 坐标集
/// </summary>
/// <param name="arr">Arraylist 坐标集</param>
/// <returns>Vector3[] 坐标集</returns>
public static Vector3[] ArrToVec(ArrayList arr)
{
int cou = arr.Count;
Vector3[] re = new Vector3[cou];
int key = 0;
foreach (Vector3 item in arr)
{
re[key] = item;
key++;
}
return re;
}
/// <summary>
/// 取数组最大 或者最小值
/// </summary>
/// <param name="arr">数组</param>
/// <param name="isMax">true返回最大值 false返回最小值</param>
/// <returns>根据参数返回 最大或者最小值</returns>
private static double GetMaxOrMin(double[] arr, bool isMax = true)
{
Array.Sort(arr);//给数组arr排序
if (isMax)
return arr[arr.Length - 1];
else
return arr[0];
}
/// <summary>
/// 取数组最大 或者最小值 的数组下标
/// </summary>
/// <param name="arr">数组</param>
/// <param name="isMax">true返回最大值 false返回最小值</param>
/// <returns>数组下标</returns>
private static int GetIndexOfMaxOrMin(double[] arr, bool isMax = true)
{
int[] indexes = new int[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
indexes[i] = i; // 保存每个元素的原始索引
}
Array.Sort(arr, indexes); // 按值排序同时更新索引数组
// 根据 isMax 参数返回相应的索引
return isMax ? indexes[arr.Length - 1] : indexes[0];
}
/// <summary>
/// 获取列表中最小值的随机下标
/// </summary>
/// <param name="list">输入的列表</param>
/// <returns>最小值的随机下标</returns>
private static int GetRandomMinIndex(List<int> list)
{
// 检查输入的列表是否为 null 或为空
if (list == null || list.Count == 0)
{
throw new ArgumentException("列表不能为 null 或为空");
}
int minValue = int.MaxValue; // 初始化为 int 类型的最大值
List<int> minIndices = new List<int>(); // 存储最小值的下标列表
for (int i = 0; i < list.Count; i++)
{
// 如果当前元素比最小值小,更新最小值和清空之前的下标列表
if (list[i] < minValue)
{
minValue = list[i];
minIndices.Clear(); // 清空之前的下标列表
minIndices.Add(i); // 添加当前下标
}
// 如果当前元素等于最小值,添加当前下标到列表
else if (list[i] == minValue)
{
minIndices.Add(i);
}
}
// 如果最小值下标列表为空,表示没有找到最小值
if (minIndices.Count == 0)
{
throw new InvalidOperationException("列表中没有找到最小值");
}
// 生成一个随机数,用于从最小值下标列表中随机选择一个下标
Random random = new Random();
return minIndices[random.Next(minIndices.Count)];
}
/// <summary>
/// 二维数组转一维数组 并去重
/// </summary>
/// <param name="twoArr">二维数组</param>
/// <returns>去重一维数组</returns>
private static ArrayList TwoArrToArr(ArrayList twoArr)
{
ArrayList arr = new ArrayList();
foreach (int[] item in twoArr)
{
int i = arr.IndexOf(item[0]);
if (i < 1) arr.Add(item[0]);
int x = arr.IndexOf(item[1]);
if (x < 1) arr.Add(item[1]);
}
return arr;
}
/// <summary>
/// 二维数组转一维数组 并去重
/// </summary>
/// <param name="twoArr">二维数组</param>
/// <returns>去重一维数组</returns>
private static List<int> TwoArrToArr(List<int[]> twoArr)
{
List<int> arr = new List<int>();
foreach (int[] item in twoArr)
{
int i = arr.IndexOf(item[0]);
if (i < 1) arr.Add(item[0]);
int x = arr.IndexOf(item[1]);
if (x < 1) arr.Add(item[1]);
}
return arr;
}
/// <summary>
/// 处理二维数组 把有关联的子数组合并 例如:[[0,2][0,3][3,4][5,6]] 结果[[0,2,3,4][5,6]]
/// </summary>
/// <param name="arr">需要处理的二维数组 </param>
/// <returns>处理完成的数组</returns>
private static List<List<int>> FindConnected(List<int[]> arr)
{
Dictionary<int, List<int>> graph = new Dictionary<int, List<int>>();
Dictionary<int, bool> visited = new Dictionary<int, bool>();
List<List<int>> result = new List<List<int>>();
// 构建图
foreach (var edge in arr)
{
foreach (var node in edge)
{
if (!graph.ContainsKey(node))
{
graph[node] = new List<int>();
visited[node] = false;
}
}
}
foreach (var edge in arr)
{
graph[edge[0]].Add(edge[1]);
graph[edge[1]].Add(edge[0]);
}
foreach (var node in graph.Keys)
{
if (!visited[node])
{
List<int> connected = new List<int>();
DFS(node, connected, graph, visited);
result.Add(connected);
}
}
return result;
}
private static void DFS(int node, List<int> connected, Dictionary<int, List<int>> graph, Dictionary<int, bool> visited)
{
visited[node] = true;
connected.Add(node);
foreach (var neighbor in graph[node])
{
if (!visited[neighbor])
{
DFS(neighbor, connected, graph, visited);
}
}
}
/// <summary>
/// 获取一组序列的所有排列方式 ps:[0,1,2] 结果[[0, 1, 2],[0, 2, 1],[1, 0, 2],[1, 2, 0],[2, 0, 1],[2, 1, 0]]
/// </summary>
/// <param name="array">一组序列</param>
/// <returns>所有序列的排列方式</returns>
private static List<List<int>> Permutations(List<int> array)
{
List<List<int>> result = new List<List<int>>();
GeneratePermutations(array, 0, array.Count - 1, result);
return result;
}
private static void GeneratePermutations(List<int> array, int start, int end, List<List<int>> result)
{
if (start == end)
{
result.Add(new List<int>(array));
}
else
{
for (int i = start; i <= end; i++)
{
int temp = array[start];
array[start] = array[i];
array[i] = temp;
GeneratePermutations(array, start + 1, end, result);
temp = array[start];
array[start] = array[i];
array[i] = temp;
}
}
}
/// <summary>
/// 按照对应关系 生成新的b坐标集合
/// </summary>
/// <param name="aVecs">a坐标集合</param>
/// <param name="bVecs">b坐标集合</param>
/// <param name="match">a b集合的对应关系</param>
/// <returns>坐标集合</returns>
private static Vector3[] CreateNewBVecs(Vector3[] bVecs, List<int[]> match)
{
Vector3[] new_bVecs = new Vector3[bVecs.Length];
foreach (int[] m in match)
{
new_bVecs[m[0]] = bVecs[m[1]];
}
return new_bVecs;
}
/// <summary>
/// 从一组向量集合中 不重复的随机选择4个向量为一组 最多100组 组合成二维数组 如:[[1,2,3,4][5,6,7,8]...]
/// </summary>
/// <param name="vecs">坐标集和</param>
/// <returns>re空数组则代表不够4个坐标 </returns>
private static List<Vector3[]> RandomSel4Vec(Vector3[] vecs)
{
List<Vector3[]> result = new List<Vector3[]>();
int len = vecs.Length;
// 如果坐标数组少于4个或者为空则返回空列表
if (len < 4)
{
return result;
}
// 确定最大可选择的组数最多为100组
int numGroups = Math.Min(len / 4, 100);
List<Vector3> flattenedList = vecs.ToList();
Random random = new Random();
// 遍历每一组
for (int i = 0; i < numGroups; i++)
{
List<Vector3> selectedGroup = new List<Vector3>();
// 随机选择4个不重复的坐标
for (int j = 0; j < 4; j++)
{
int index = random.Next(flattenedList.Count);
selectedGroup.Add(flattenedList[index]);
flattenedList.RemoveAt(index);
}
// 将选择的坐标组成的 List 转换为数组,并添加到结果中
result.Add(selectedGroup.ToArray());
}
return result;
}
/// <summary>
/// 设置中间航点
/// </summary>
/// <param name="aVec">起点</param>
/// <param name="bVec">目标点</param>
/// <param name="middlePos">比例</param>
/// <returns></returns>
private static Vector3 SetMiddleVec(Vector3 aVec, Vector3 bVec, double middlePos = 0.5)
{
return (bVec - aVec) * middlePos + aVec;
}
/// <summary>
/// 两点距离
/// </summary>
/// <param name="v1">坐标1</param>
/// <param name="v2">坐标2</param>
/// <returns>两点之间距离</returns>
private static double GageLength(Vector3 v1, Vector3 v2)
{
return Math.Sqrt(GageLengthSquare(v1, v2));
}
/// <summary>
/// 两点距离的平方
/// </summary>
/// <param name="v1">坐标1</param>
/// <param name="v2">坐标2</param>
/// <returns>两点距离的平方</returns>
private static double GageLengthSquare(Vector3 v1, Vector3 v2)
{
return Math.Pow(v1.X - v2.X, 2) + Math.Pow(v1.Y - v2.Y, 2) + Math.Pow(v1.Z - v2.Z, 2);
}
/// <summary>
/// 点积
/// </summary>
/// <param name="v1">向量1</param>
/// <param name="v2">向量2</param>
/// <returns>点积</returns>
private static double DotPro(Vector3 v1, Vector3 v2)
{
return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
}
/// <summary>
/// 叉积
/// </summary>
/// <param name="v1">向量1</param>
/// <param name="v2">向量2</param>
/// <returns>两个向量叉积</returns>
private static Vector3 CrossPro(Vector3 v1, Vector3 v2)
{
double x = v1.Y * v2.Z - v1.Z * v2.Y;
double y = v1.Z * v2.X - v1.X * v2.Z;
double z = v1.X * v2.Y - v1.Y * v2.X;
return new Vector3(x, y, z);
}
/// <summary>
/// 检查4个点是否在一个平面上
/// </summary>
/// <param name="vector1">点1</param>
/// <param name="vector2">点2</param>
/// <param name="vector3">点3</param>
/// <param name="vector4">点4</param>
/// <returns>true在一个平面 false不在一个平面</returns>
public static bool IsVecsOnPlane(Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4)
{
//计算三个向量
Vector3 v1v2 = vec2 - vec1;
Vector3 v1v3 = vec3 - vec1;
Vector3 v1v4 = vec4 - vec1;
//计算法线向量
Vector3 normal_vector = CrossPro(v1v2,v1v3);
//计算点到平面的距离
double distance = DotPro(normal_vector, v1v4) / normal_vector.GetMag();
//设置一个阈值,判断是否共面
double epsilon = 0.1;
return Math.Abs(distance) < epsilon;
}
/// <summary>
/// 判断4个点是否在同一条直线上
/// </summary>
/// <param name="vector1">点1</param>
/// <param name="vector2">点2</param>
/// <param name="vector3">点3</param>
/// <param name="vector4">点4</param>
/// <returns>true在一条直线上 false不在一条直线上</returns>
private static bool IsVecsOnLine(Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4)
{
// 计算相邻点之间的方向向量
Vector3 dir1 = new Vector3(vec2.X - vec1.X, vec2.Y - vec1.Y, vec2.Z - vec1.Z);
Vector3 dir2 = new Vector3(vec3.X - vec2.X, vec3.Y - vec2.Y, vec3.Z - vec2.Z);
Vector3 dir3 = new Vector3(vec4.X - vec3.X, vec4.Y - vec3.Y, vec4.Z - vec3.Z);
// 检查任意两个相邻方向向量的叉积是否为零
// 这是在三维空间中判断共线的条件
return CrossPro(dir1, dir2).GetMag() <0.1 && CrossPro(dir2, dir3).GetMag() <0.1;
}
/// <summary>
/// 辅助方法,用于检查向量是否为零向量
/// </summary>
/// <param name="vector">向量</param>
/// <returns></returns>
private static bool IsZero(this Vector3 vector)
{
return vector.X == 0 && vector.Y == 0 && vector.Z == 0;
}
/// <summary>
/// 获取两条线段 的最近位置的距离和占比
/// </summary>
/// <param name="a1">线段1起始点</param>
/// <param name="a2">线段1起终点</param>
/// <param name="b1">线段2起始点</param>
/// <param name="b2">线段2起终点</param>
/// <returns>[在线段1占比在线段2占比最近距离]</returns>
private static double RecentlySquareOfLine(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2)
{
if (a1 == a2) a2 += 1;// 防止线段长度为0
if (b1 == b2) b2 += 1;
double ux = a2.X - a1.X;
double uy = a2.Y - a1.Y;
double uz = a2.Z - a1.Z;
double vx = b2.X - b1.X;
double vy = b2.Y - b1.Y;
double vz = b2.Z - b1.Z;
double wx = a1.X - b1.X;
double wy = a1.Y - b1.Y;
double wz = a1.Z - b1.Z;
double a = (ux * ux + uy * uy + uz * uz);
double b = (ux * vx + uy * vy + uz * vz);
double c = (vx * vx + vy * vy + vz * vz);
double d = (ux * wx + uy * wy + uz * wz);
double e = (vx * wx + vy * wy + vz * wz);
double dt = a * c - b * b;
double sd = dt;
double td = dt;
double sn = 0.0;
double tn = 0.0;
if (IsEqual(dt, 0.0))
{
sn = 0.0;
sd = 1.00;
tn = e;
td = c;
}
else
{
sn = (b * e - c * d);
tn = (a * e - b * d);
if (sn < 0.0)
{
sn = 0.0;
tn = e;
td = c;
}
else if (sn > sd)
{
sn = sd;
tn = e + b;
td = c;
}
}
if (tn < 0.0)
{
tn = 0.0;
if (-d < 0.0) sn = 0.0;
else if (-d > a) sn = sd;
else
{
sn = -d;
sd = a;
}
}
else if (tn > td)
{
tn = td;
if ((-d + b) < 0.0) sn = 0.0;
else if ((-d + b) > a) sn = sd;
else
{
sn = (-d + b);
sd = a;
}
}
double sc = 0.0;
double tc = 0.0;
if (IsEqual(sn, 0.0)) sc = 0.0;
else sc = sn / sd;//最近点在第一条线占比
if (IsEqual(tn, 0.0)) tc = 0.0;
else tc = tn / td;//最近点在第二条线占比
double dx = wx + (sc * ux) - (tc * vx);
double dy = wy + (sc * uy) - (tc * vy);
double dz = wz + (sc * uz) - (tc * vz);
return dx * dx + dy * dy + dz * dz;//两线最近距离的平方
}
private static bool IsEqual(double x, double y)
{
if (Math.Abs(x - y) < 1e-7)
{
return true;
}
return false;
}
/// <summary>
/// 按比例在两条线段上截取对应点间的最小距离 平方
/// </summary>
/// <param name="a1">线段1起始点</param>
/// <param name="a2">线段1起终点</param>
/// <param name="b1">线段2起始点</param>
/// <param name="b2">线段2起终点</param>
/// <returns>最小距离的平方值</returns>
private static double MinDistanceSquareOfLine(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2)
{
//相对位置和相对速度
Vector3 d0 = b1 - a1;
Vector3 v_rel = (b2 - b1) - (a2 - a1);
//计算最小距离位置 占比
double vd = DotPro(v_rel, v_rel);
double proportion;//最近距离占比
if (vd != 0)
{
proportion = Math.Max(0, Math.Min(1, -DotPro(d0, v_rel) / vd));//max min函数把比例限制在0-1 保证比例在线段上
}
else
{
proportion = 0;
}
//计算最小距离平方
Vector3 d_min = d0 + v_rel * proportion;
return d_min.GetMagSquared();
}
/// <summary>
/// 获取坐标集合的重心或中心
/// </summary>
/// <param name="pos">坐标集合</param>
/// <param name="isCentroid">默认返回为true重心 false则为中心</param>
/// <returns>重心或中心坐标</returns>
public static Vector3 GetPosCenter(Vector3[] pos, bool isCentroid = true)
{
int cou = pos.Length;
if (isCentroid)//重心
{
double x = 0;
double y = 0;
double z = 0;
foreach (var item in pos)
{
x += item.X;
y += item.Y;
z += item.Z;
}
x = x / cou;
y = y / cou;
z = z / cou;
return new Vector3(x, y, z);
}
else
{
double[] x = new double[cou];
double[] y = new double[cou];
double[] z = new double[cou];
int key = 0;
foreach (var item in pos)
{
x[key] = item.X;
y[key] = item.Y;
z[key] = item.Z;
key++;
}
double xc = (GetMaxOrMin(x) + GetMaxOrMin(x, false)) * .5;
double yc = (GetMaxOrMin(y) + GetMaxOrMin(y, false)) * .5;
double zc = (GetMaxOrMin(z) + GetMaxOrMin(z, false)) * .5;
return new Vector3(xc, yc, zc);
}
}
/// <summary>
/// 获取坐标集合的重心或中心
/// </summary>
/// <param name="pos">坐标集合</param>
/// <param name="isCentroid">默认返回为true重心 false则为中心</param>
/// <returns>重心或中心坐标</returns>
private static Vector3 GetPosCenter(List<Vector3> pos, bool isCentroid = true)
{
int cou = pos.Count;
if (isCentroid)//重心
{
double x = 0;
double y = 0;
double z = 0;
foreach (var item in pos)
{
x += item.X;
y += item.Y;
z += item.Z;
}
x = x / cou;
y = y / cou;
z = z / cou;
return new Vector3(x, y, z);
}
else
{
double[] x = new double[cou];
double[] y = new double[cou];
double[] z = new double[cou];
int key = 0;
foreach (var item in pos)
{
x[key] = item.X;
y[key] = item.Y;
z[key] = item.Z;
key++;
}
double xc = (GetMaxOrMin(x) + GetMaxOrMin(x, false)) * .5;
double yc = (GetMaxOrMin(y) + GetMaxOrMin(y, false)) * .5;
double zc = (GetMaxOrMin(z) + GetMaxOrMin(z, false)) * .5;
return new Vector3(xc, yc, zc);
}
}
/// <summary>
/// 获取坐标集合 的总宽度 高度 长度
/// </summary>
/// <param name="pos">坐标集合</param>
/// <returns>返回数组[0]宽度[1]高度[2]长度</returns>
public static double[] GetVecsWithHighLength(Vector3[] pos)
{
List<double> w = new List<double>();
List<double> h = new List<double>();
List<double> l = new List<double>();
foreach (var item in pos)
{
w.Add(item.X);
h.Add(item.Y);
l.Add(item.Z);
}
double[] re = new double[3];
re[0] = w.Max() - w.Min();
re[1] = h.Max() - h.Min();
re[2] = l.Max() - l.Min();
return re;
}
/// <summary>
/// 碰撞检测
/// </summary>
/// <param name="aVecs">始点坐标集合</param>
/// <param name="bVecs">终点坐标集合</param>
/// <param name="lineDistanceSquare">航线最小间距平方值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距平方值</param>
/// <returns></returns>
private static List<int[]> AirImitation(Vector3[] aVecs, Vector3[] bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 650000)
{
List<int[]> planesCollision = new List<int[]>(); //所有碰撞的组
int planeCou = aVecs.Length; //获取飞机总数
for (int a = 0; a < planeCou; a++)
{
for (int b = 0; a + 1 + b < planeCou; b++)
{
//判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[a], bVecs[a], aVecs[a + 1 + b], bVecs[a + 1 + b]);
if (distanceSquare < lineDistanceSquare)
{
//判断飞机距离是否过近
double planeLenSquare = MinDistanceSquareOfLine(aVecs[a], bVecs[a], aVecs[a + 1 + b], bVecs[a + 1 + b]);
if (planeLenSquare < spaceBetweenSquare)
{
planesCollision.Add(new int[] { a, a + 1 + b });
}
}
}
}
return planesCollision;
}
/// <summary>
/// 碰撞检测
/// </summary>
/// <param name="aVecs">始点坐标集合</param>
/// <param name="bVecs">终点坐标集合</param>
/// <param name="lineDistanceSquare">航线最小间距平方值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距平方值</param>
/// <returns></returns>
private static List<int[]> AirImitation(List<Vector3> aVecs, List<Vector3> bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 650000)
{
List<int[]> planesCollision = new List<int[]>(); //所有碰撞的组
int planeCou = aVecs.Count; //获取飞机总数
for (int a = 0; a < planeCou; a++)
{
for (int b = 0; a + 1 + b < planeCou; b++)
{
//判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[a], bVecs[a], aVecs[a + 1 + b], bVecs[a + 1 + b]);
if (distanceSquare < lineDistanceSquare)
{
//判断飞机距离是否过近
double planeLenSquare = MinDistanceSquareOfLine(aVecs[a], bVecs[a], aVecs[a + 1 + b], bVecs[a + 1 + b]);
if (planeLenSquare < spaceBetweenSquare)
{
planesCollision.Add(new int[] { a, a + 1 + b });
}
}
}
}
return planesCollision;
}
/// <summary>
/// 单机碰撞检测
/// </summary>
/// <param name="onlyPlaneId">飞机的id PS:id从0开始</param>
/// <param name="aVecs">飞机起始坐标集合</param>
/// <param name="bVecs">飞机终点坐标集合</param>
/// <param name="lineDistanceSquare">航线小距离的平方对比值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距的平方对比值</param>
/// <returns>true:有碰撞 false:无碰撞</returns>
private static bool OnlyImitation(int onlyPlaneId, Vector3[] aVecs, Vector3[] bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 250000)
{
//选出与指定飞机 航线有交叉的飞机 用于模拟飞行碰撞检测
for (int contrastId = 0; contrastId < aVecs.Length; contrastId++)
{
if (onlyPlaneId == contrastId) continue;//不和自己比较
// 判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//航线最小距离
if (distanceSquare < lineDistanceSquare)
{
double minDistanceSquare = MinDistanceSquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//按比例飞行最小间距
if (minDistanceSquare < spaceBetweenSquare)
{
return true;//返回有碰撞
}
}
}
return false;//返回没有碰撞;
}
/// <summary>
/// 单机碰撞检测
/// </summary>
/// <param name="onlyPlaneId">飞机的id PS:id从0开始</param>
/// <param name="aVecs">飞机起始坐标集合</param>
/// <param name="bVecs">飞机终点坐标集合</param>
/// <param name="lineDistanceSquare">航线小距离的平方对比值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距的平方对比值</param>
/// <returns>true:有碰撞 false:无碰撞</returns>
private static bool OnlyImitation(int onlyPlaneId, List<Vector3> aVecs, List<Vector3> bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 250000)
{
//选出与指定飞机 航线有交叉的飞机 用于模拟飞行碰撞检测
for (int contrastId = 0; contrastId < aVecs.Count; contrastId++)
{
if (onlyPlaneId == contrastId) continue;//不和自己比较
// 判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//航线最小距离
if (distanceSquare < lineDistanceSquare)
{
double minDistanceSquare = MinDistanceSquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//按比例飞行最小间距
if (minDistanceSquare < spaceBetweenSquare)
{
return true;//返回有碰撞
}
}
}
return false;//返回没有碰撞;
}
/// <summary>
/// 单机碰撞检测
/// </summary>
/// <param name="onlyPlaneId">飞机的id PS:id从0开始</param>
/// <param name="aVecs">飞机起始坐标集合</param>
/// <param name="bVecs">飞机终点坐标集合</param>
/// <param name="lineDistanceSquare">航线小距离的平方对比值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距的平方对比值</param>
/// <returns>true:有碰撞 false:无碰撞</returns>
private static bool OnlyImitation(int onlyPlaneId, Vector3[] aVecs, List<Vector3> bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 250000)
{
//选出与指定飞机 航线有交叉的飞机 用于模拟飞行碰撞检测
for (int contrastId = 0; contrastId < aVecs.Length; contrastId++)
{
if (onlyPlaneId == contrastId) continue;//不和自己比较
// 判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//航线最小距离
if (distanceSquare < lineDistanceSquare)
{
double minDistanceSquare = MinDistanceSquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//按比例飞行最小间距
if (minDistanceSquare < spaceBetweenSquare)
{
return true;//返回有碰撞
}
}
}
return false;//返回没有碰撞;
}
/// <summary>
/// 单机碰撞检测
/// </summary>
/// <param name="onlyPlaneId">飞机的id PS:id从0开始</param>
/// <param name="aVecs">飞机起始坐标集合</param>
/// <param name="bVecs">飞机终点坐标集合</param>
/// <param name="lineDistanceSquare">航线小距离的平方对比值</param>
/// <param name="spaceBetweenSquare">飞行过程中最小间距的平方对比值</param>
/// <returns>true:有碰撞 false:无碰撞</returns>
private static bool OnlyImitation(int onlyPlaneId, List<Vector3> aVecs, Vector3[] bVecs, double lineDistanceSquare = 36100, double spaceBetweenSquare = 250000)
{
//选出与指定飞机 航线有交叉的飞机 用于模拟飞行碰撞检测
for (int contrastId = 0; contrastId < aVecs.Count; contrastId++)
{
if (onlyPlaneId == contrastId) continue;//不和自己比较
// 判断两条轨迹 之间的最小距离
double distanceSquare = RecentlySquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//航线最小距离
if (distanceSquare < lineDistanceSquare)
{
double minDistanceSquare = MinDistanceSquareOfLine(aVecs[onlyPlaneId], bVecs[onlyPlaneId], aVecs[contrastId], bVecs[contrastId]);//按比例飞行最小间距
if (minDistanceSquare < spaceBetweenSquare)
{
return true;//返回有碰撞
}
}
}
return false;//返回没有碰撞;
}
/// <summary>
/// 智能路径 规则找ab组共同最外圈的点 然后对应找a或b里面最近点 为一组匹配 最后进行碰撞交叉互换
/// </summary>
/// <param name="aVecs">起始点坐标组</param>
/// <param name="bVecs">目标点坐标组</param>
/// <param name="StrPrint">日志输出 回调函数</param>
/// <param name="swapCount">交换次数</param>
/// <param name="crossingLimit">交叉线路数量上限 ps:超过这个数量则不进行交换</param>
/// <returns>新的目标点</returns>
public static Vector3[] ContactABOut(Vector3[] aVecs, Vector3[] bVecs, SomeCalculateWay StrPrint, int swapCount = 5, int crossingLimit = 6)
{
int planeCou = aVecs.Length; // 飞机总数
List<int[]> match = new List<int[]>(); // ab对应关系
//记录a b集合索引
List<int> aIndex = new List<int>();
List<int> bIndex = new List<int>();
for (int k = 0; k < planeCou; k++)
{
aIndex.Add(k);
bIndex.Add(k);
}
// 遍历找出 AB集合对应关系
for (int k = 0; k < planeCou; k++)
{
// 遍历每次刷新 剩下为未匹配总数
int remainCou = aIndex.Count;
// 遍历每次刷新 重心
List<Vector3> allVecs = new List<Vector3>();
for (int i = 0; i < remainCou; i++)
{
allVecs.Add(aVecs[aIndex[i]]);
allVecs.Add(bVecs[bIndex[i]]);
}
Vector3 centerVec = GetPosCenter(allVecs);//重心点
// 遍历所有ab点距离重心点的距离
double[] aLens = new double[remainCou];
double[] bLens = new double[remainCou];
for (int i = 0; i < remainCou; i++)
{
aLens[i] = GageLengthSquare(aVecs[aIndex[i]], centerVec);
bLens[i] = GageLengthSquare(bVecs[bIndex[i]], centerVec);
}
// 找出ab集合最外层坐标的下标 即离重心点最远的点
int aMaxIndex = GetIndexOfMaxOrMin(aLens); // a集合到重心点最长的距离 数组的下标
int bMaxIndex = GetIndexOfMaxOrMin(bLens); // b集合到重心点最长的距离 数组的下标
if (aLens[aMaxIndex] > bLens[bMaxIndex])//找出 最外层如果为A集合点 对应B集合最近点 的下标
{
double[] outAtoBLen = new double[remainCou];//最外层A点到 B集合所有点的距离
for (int i = 0; i < remainCou; i++)
{
outAtoBLen[i] = GageLengthSquare(aVecs[aIndex[aMaxIndex]], bVecs[bIndex[i]]);
}
int bMinIndex = GetIndexOfMaxOrMin(outAtoBLen, false);// 最短距离
match.Add(new int[] { aIndex[aMaxIndex], bIndex[bMinIndex] });// 映射到配对
aIndex.RemoveAt(aMaxIndex); // 删除已经配对的a集合 ID
bIndex.RemoveAt(bMinIndex); // 删除已经配对的b集合 ID
}
else//找出 最外层如果为B集合点 对应A集合最近点 的下标
{
double[] outBtoALen = new double[remainCou];//最外层B点到 A集合所有点的距离
for (int i = 0; i < remainCou; i++)
{
outBtoALen[i] = GageLengthSquare(aVecs[aIndex[i]], bVecs[bIndex[bMaxIndex]]);
}
int aMinIndex = GetIndexOfMaxOrMin(outBtoALen, false);// 最短距离
match.Add(new int[] { aIndex[aMinIndex], bIndex[bMaxIndex] });// 映射到配对
aIndex.RemoveAt(aMinIndex); // 删除已经配对的a集合 ID
bIndex.RemoveAt(bMaxIndex); // 删除已经配对的b集合 ID
}
}
Vector3[] new_bVecs = CreateNewBVecs(bVecs, match);// 按照映射 获取a 对应的 新的b集合
//交叉 交换
for (int i = 0; i < swapCount; i++)
{
List<int[]> planesCollision = AirImitation(aVecs, new_bVecs);// 获取碰撞组
List<List<int>> formatCollision = FindConnected(planesCollision);// 格式化碰撞检测组 例如:[[0,2][0,3][3,4][5,6]] 结果[[0,2,3,4][5,6]]
// 遍历所有交叉组 分组做交换
foreach (List<int> swap_indices in formatCollision)
{
if (swap_indices.Count <= crossingLimit) // 只尝试5组交叉碰撞以下的航线互换
{
//交叉 生成所有排列
List<List<int>> all_permutations = Permutations(swap_indices);
//删掉第一个 既原始排列
all_permutations.RemoveAt(0);
// 按所有的排列 互换航线 并检测出最佳的对应目标点
List<int> tempLen = new List<int>(); //记录所有排列的 的碰撞次数
List<Vector3[]> tempNew_bVecsS = new List<Vector3[]>();//记录所有排列交换之后的目标坐标集
foreach (List<int> indices in all_permutations)
{
Vector3[] current_array = new_bVecs.ToArray();//复制一个new_bVecs 副本
for (int k = 0; k < indices.Count - 1; k++)
{
int index1 = indices[k];
int index2 = indices[k + 1];
// 交换元素
Vector3 temp = current_array[index1];
current_array[index1] = current_array[index2];
current_array[index2] = temp;
}
tempLen.Add(AirImitation(aVecs, current_array).Count);// 迭代 记录所有排列的碰撞次数
tempNew_bVecsS.Add(current_array); //迭代 记录所有排列交换之后的目标坐标集
}
new_bVecs = tempNew_bVecsS[GetRandomMinIndex(tempLen)].ToArray();
}
}
}
return new_bVecs;
}
/// <summary>
/// 智能挫层
/// </summary>
/// <param name="aVecs">起始坐标集合</param>
/// <param name="bVecs">终点做标集合</param>
/// <param name="StrPrint">日志输出 回调函数</param>
/// <param name="layHight">挫层层高</param>
/// <returns>返回一个二维向量坐标集合 middle[0]是第一个中间航点 middle[1]是第二个中间航点 返回空数组则代表两个图形不在一个平面上或者不够4个点</returns>
public static List<Vector3[]> CollisionLayer(Vector3[] aVecs, Vector3[] bVecs, SomeCalculateWay StrPrint, double layHight = 220)
{
List<Vector3[]> re = new List<Vector3[]>();
//获取飞机总数
int planeCou = aVecs.Length;
if (planeCou < 2)//至少不少于2架飞机 才开始挫层
{
StrPrint("图案至少要两个点阵!");
return re;
}
//检测两个图形是否在一个平面上
Vector3 vec0 = new Vector3(0, 0, 0);
Vector3 vec1 = new Vector3(0, 0, 0);
Vector3 vec2 = new Vector3(0, 0, 0);
Vector3 vec3 = new Vector3(0, 0, 0);
//遍历所有点 查看是不是都再一个平面内
for (int i = 0; i < planeCou; i++)
{
vec0 = aVecs[i]; vec1 = bVecs[planeCou - i - 1]; vec2 = aVecs[planeCou - i - 1]; vec3 = bVecs[i];
if (!(IsVecsOnPlane(vec0, vec1, vec2, vec3)))
{
StrPrint("图案不在一个平面!");
return re;//两个图形不在一个平面上 返回 空数组
}
else
{
break;
}
}
//遍历选出4个不共线的点 ps:方便后续叉积算法线标量
bool isVecsOnLine = false;
for (int i = 0; i < planeCou; i++) //遍历所有点 找出不在一条直线上的四个点
{
vec0 = aVecs[i]; vec1 = bVecs[planeCou - i - 1]; vec2 = aVecs[planeCou - i - 1]; vec3 = bVecs[i];
isVecsOnLine = IsVecsOnLine(vec0, vec1, vec2, vec3);
if (!isVecsOnLine)
{
break;
}
}
if (isVecsOnLine)
{
StrPrint("所有点都在一条直线上!");
return re;//两个图案的所有点都在一条直线上 返回 空数组
}
//检查完毕后
//计算法线向量
Vector3 side1 = vec0 - vec1;
Vector3 side2 = vec2 - vec3;
Vector3 normal = CrossPro(side1, side2);
Vector3 normalScalar = normal.NormalizEd();//法线标量
//开始挫层
for (int i = 0; i < planeCou; i++)
{
int shiftCou = 1; //记录循环次数 即层数
Vector3 aOrigin = aVecs[i]; //原点位置
Vector3 bOrigin = bVecs[i]; //原点位置
while (OnlyImitation(i, aVecs, bVecs))
{
Vector3 shiftVec = normalScalar * ((shiftCou + 1) / 2 * layHight);
if (shiftCou % 2 == 1)
{
aVecs[i] = aOrigin + shiftVec;
bVecs[i] = bOrigin + shiftVec;
}
else
{
aVecs[i] = aOrigin - shiftVec;
bVecs[i] = bOrigin - shiftVec;
}
shiftCou += 1;
}
}
StrPrint("挫层完成!");
re.Add(aVecs);
re.Add(bVecs);
return re;
}
/// <summary>
/// 路径绕行
/// </summary>
/// <param name="aVecs">起始坐标集合</param>
/// <param name="bVecs">终点做标集合</param>
/// <param name="strPrint">日志输出 回调函数</param>
/// <returns>返回一个二维数组 返回值长度0绕行失败 长度1为一个中间航点 长度为3为三个中间航点顺序(前中后)</returns>
public static List<List<Vector3>> ABypassB(Vector3[] aVecs, Vector3[] bVecs, SomeCalculateWay StrPrint)
{
List<List<Vector3>> re = new List<List<Vector3>>();
int planeCou = aVecs.Length;
//第一次绕行
//起始坐标组重心点 与 目标坐标组重心点
Vector3 aCenterPoint = GetPosCenter(aVecs);
Vector3 bCenterPoint = GetPosCenter(bVecs);
//记录所有航线中心距 ps:起点离起点组中心点距离 + 目标点离目标点组中心点距离
double[] centerDistanceS = new double[planeCou];
for (int i = 0; i < planeCou; i++)
{
centerDistanceS[i] = GageLengthSquare(aVecs[i], aCenterPoint) + GageLengthSquare(bVecs[i], bCenterPoint);
}
//记录从中心开始排序
int[] sortedIndices = Enumerable.Range(0, centerDistanceS.Length).ToArray();
Array.Sort(sortedIndices, (x, y) => centerDistanceS[x].CompareTo(centerDistanceS[y]));// 对索引数组按原始数组的值进行排序
//从中心航线 逐条绕行 ps:映射从中心内部到此到外部顺序
List<Vector3> tempAVecs = new List<Vector3>(); // 逐条起点坐标组
List<Vector3> tempBVecs = new List<Vector3>(); // 逐条目标点坐标组
List<Vector3> middleVecs = new List<Vector3>(); //中心航点坐标组
List<int> firstCollisionGroup = new List<int>(); //有碰撞飞机列表(第一次绕行)
for (int i = 0; i < planeCou; i++)
{
StrPrint($"第一次绕行进度:{(int)((double)i/planeCou*100)}%");
bool isPassMark = false; //初始标记
int index = sortedIndices[i];//从内到外排序的索引
tempAVecs.Add(aVecs[index]);
tempBVecs.Add(bVecs[index]);
if (i == 0) //如果是第一条航线 直接添加中间航点
{
middleVecs.Add(SetMiddleVec(aVecs[index], bVecs[index])); //添加中间航点
continue; //跳出 继续第二条航线计算
}
else //从第二条开始绕
{
middleVecs.Add(SetMiddleVec(aVecs[index], bVecs[index])); // 添加默认中间航点
// 首次默认中间航点检测
if (!(OnlyImitation(i, tempAVecs, middleVecs)) && !(OnlyImitation(i, middleVecs, tempBVecs))) //前半段碰 碰撞检测
{
continue; //不撞则跳出 进行下一航线的绕行
}
else
{
middleVecs.RemoveAt(i); //碰撞检测未通过删除临时中间航点
}
// 绕行列表遍历 检测
List<Vector3> ringVecs = GetRingVec(aVecs[index], bVecs[index], 0.5); //所有可绕行的中间航点集合
foreach (Vector3 v in ringVecs)
{
middleVecs.Add(v); //添加临时绕行航点
//检测
if (!(OnlyImitation(i, tempAVecs, middleVecs)) && !(OnlyImitation(i, middleVecs, tempBVecs))) //前半段碰 碰撞检测
{
isPassMark = true; //中间航点 碰撞检测通过标记
break; //不撞则跳出 进行下一航线的绕行
}
else
{
middleVecs.RemoveAt(i); //碰撞检测未通过删除临时中间航点
}
}
}
// 如果绕行失败
if (!isPassMark)
{
middleVecs.Add(SetMiddleVec(aVecs[index], bVecs[index])); //添加一个默认中间航点
firstCollisionGroup.Add(index);
}
}
//中间航点坐标集 按飞机id顺序重新映射 ps:恢复原id映射 排序
Vector3[] tempMiddleVecs = new Vector3[planeCou];
for (int i = 0; i < planeCou; i++)
{
tempMiddleVecs[sortedIndices[i]] = middleVecs[i];
}
middleVecs = new List<Vector3>(tempMiddleVecs);
//没有碰撞 返回一个中间航点 并返回
if (firstCollisionGroup.Count == 0)
{
StrPrint("第一次绕行通过!");
re.Add(middleVecs);
return re;
}
else
{
string str = "";
foreach (int v in firstCollisionGroup) {
str += v.ToString() + "号,";
}
str += "有碰撞,共" + firstCollisionGroup.Count.ToString() + "架!";
StrPrint(str);
StrPrint("第一次绕行未通过,准备开始第二次绕行!");
}
//第二次绕行 ps:有碰撞进行第二次 前后再各加一航点 嵌套迭代
List<Vector3> firstMiddleVecs = new List<Vector3>(); //前中间航点
List<Vector3> secondMiddleVecs = new List<Vector3>(); //后中间航点
List<int> secondCollisionGroup = new List<int>(); //最终有碰撞飞机列表
for (int i = 0; i < planeCou; i++)
{
firstMiddleVecs.Add(SetMiddleVec(aVecs[i], middleVecs[i], 0));
secondMiddleVecs.Add(SetMiddleVec(middleVecs[i], bVecs[i], 1));
}
int z = 0;//计算进度
foreach (int index in firstCollisionGroup)
{
z++;//计算进度
StrPrint($"第二次绕行进度:{(int)((double)z / firstCollisionGroup.Count * 100)}%");
bool isPassMark = false;
// 正中 绕行列表
List<Vector3> ringVecs = GetRingVec(aVecs[index], bVecs[index], 0.5); //所有可绕行的中间航点集合
foreach (Vector3 r in ringVecs)
{
middleVecs[index] = r; //中点逐点
List<Vector3> firstRingVecs = GetRingVec(aVecs[index], middleVecs[index], 0, 220, 6); //所有可绕行的 前中间航点集合
foreach (Vector3 fr in firstRingVecs)
{
firstMiddleVecs[index] = fr; //前中点逐点
List<Vector3> secondRingVecs = GetRingVec(middleVecs[index], bVecs[index], 1, 220, 6); //所有可绕行的 后中间航点集合
foreach (Vector3 sr in secondRingVecs)
{
secondMiddleVecs[index] = sr; //后中点逐点
// 检测 起点到 第一中间航点
if (!(OnlyImitation(index, aVecs, firstMiddleVecs)) && !(OnlyImitation(index, firstMiddleVecs, middleVecs)) && !(OnlyImitation(index, middleVecs, secondMiddleVecs)) && !(OnlyImitation(index, secondMiddleVecs, bVecs)))
{
isPassMark = true; //碰撞检测通过标记
break; //不撞则跳出 进行下一航线的绕行
}
}
if (isPassMark)
{
break; //不撞则跳出 进行下一航线的绕行
}
}
if (isPassMark)
{
break; //不撞则跳出 进行下一航线的绕行
}
}
if (!isPassMark) //如果绕行失败 添加一个默认中间航点
{
middleVecs[index] = SetMiddleVec(aVecs[index], bVecs[index]); //添加中间航点
firstMiddleVecs[index] = SetMiddleVec(aVecs[index], middleVecs[index], 0); // 添加中间航点
secondMiddleVecs[index] = SetMiddleVec(middleVecs[index], bVecs[index], 1); // 添加中间航点
secondCollisionGroup.Add(index);// 二次绕行仍不通过 添加到列表 (暂时无用)
}
}
if (secondCollisionGroup.Count == 0)//如果二次绕行通过 添加中间三航点
{
re.Add(firstMiddleVecs);
re.Add(middleVecs);
re.Add(secondMiddleVecs);
StrPrint("第二次绕行通过!");
}
else
{
string str = "";
foreach (int v in secondCollisionGroup)
{
str += v.ToString() + "号,";
}
str += "仍有碰撞,共" + secondCollisionGroup.Count.ToString() + "架!";
StrPrint(str);
StrPrint("第二次绕行仍未通过!");
}
return re;
}
/// <summary>
/// 在圆圈上 用固定弦长手拉手 分割点
/// </summary>
/// <param name="radius">大圆半径</param>
/// <param name="chordLength">固定弦长 ps:这个值决定绕行点一圈的密集成都值越小越密集</param>
/// <returns></returns>
private static List<Vector3> GetVecsOnCircle(double radius, double chordLength = 220)
{
List<Vector3> vecs = new List<Vector3>();
// 计算圆的周长
double circumference = (double)(2 * Math.PI * radius);
// 计算弧上的点数
int numberOfPoints = (int)(circumference / chordLength);
// 计算每个点的弧度
double angleIncrement = (double)(2 * Math.PI / numberOfPoints);
// 生成点的坐标
for (int i = 0; i < numberOfPoints; i++)
{
double angle = i * angleIncrement;
double x = radius * (double)Math.Cos(angle);
double y = radius * (double)Math.Sin(angle);
double z = 0.0f; // 如果是在平面上Z 可能是 0
vecs.Add(new Vector3(x, y, z));
}
return vecs;
}
/// <summary>
/// 获取a指向b的向量矩阵 ps:偏移次数 加1 会在终点前后往返偏移
/// </summary>
/// <param name="aVec">a向量</param>
/// <param name="bVec">b向量</param>
/// <param name="middleProportion">默认中点位置比例</param>
/// <param name="offCount">偏移次数 从0开始,每+1在中点前后往返偏移</param>
/// <param name="layHight">偏移层高</param>
/// <returns>矩阵</returns>
private static Matrix GetBasisMatrix(Vector3 aVec, Vector3 bVec, double middleProportion = 0.5, int offCount = 0, double layHight = 220)
{
// 计算前向向量 k帽
Vector3 k_hat = (bVec - aVec).NormalizEd();
// 计算右向量,使用 Vector3.UnitY 作为上向量 i帽
Vector3 i_hat = CrossPro(Vector3.UnitY, k_hat).NormalizEd();
// 计算上向量 j帽
Vector3 j_hat = CrossPro(k_hat, i_hat);
//计算 对应的偏移比例
double length = (bVec - aVec).GetMag();
double offShift;//偏移比例
if (offCount % 2 == 1) //奇数
offShift = middleProportion + (offCount + 2) / 2 * layHight / length;
else //偶数
offShift = middleProportion - (offCount + 1) / 2 * layHight / length;
// 计算偏向量
Vector3 off = aVec + (bVec - aVec) * offShift;
// 构建矩阵
Matrix matrix = new Matrix
{
M11 = k_hat.X,
M12 = k_hat.Y,
M13 = k_hat.Z,
M21 = i_hat.X,
M22 = i_hat.Y,
M23 = i_hat.Z,
M31 = j_hat.X,
M32 = j_hat.Y,
M33 = j_hat.Z,
M41 = off.X,
M42 = off.Y,
M43 = off.Z
};
return matrix;
}
/// <summary>
/// a b点中间的绕行航点列表
/// </summary>
/// <param name="aVec">起点</param>
/// <param name="bVec">目标点</param>
/// <param name="middleProportion">中间航点位置比例</param>
/// <param name="transfer">传递圈函数的弦长 向量矩阵函数的层高</param>
/// <param name="paunch">绕行航点范围 ps:绕行中间航点肚子(值越大肚子越小)</param>
/// <returns></returns>
private static List<Vector3> GetRingVec(Vector3 aVec, Vector3 bVec, double middleProportion, double transfer = 220, double paunch = 3)
{
List<Vector3> ringVec = new List<Vector3>(); //记录所有绕行中间航点坐标
// 根据a到b的长度 算出中间绕行几圈
int ringCou = (int)Math.Ceiling(GageLength(aVec, bVec) / paunch / transfer); //绝对中心绕行的圈数 即中间航点的'肚子'大小
if (ringCou < 2) ringCou = 2; //最少两圈 两层
if (ringCou > 15) ringCou = 15; //最多15圈 15层
for (int z = 0; z < ringCou; z++) //迭代层
{
Matrix mat = GetBasisMatrix(aVec, bVec, middleProportion, z, transfer); //根据圈数越多 偏移层数也越多 即每层矩阵off位置
for (int i = 0; i < ringCou; i++) //迭代圈
{
if (i != 0)
{
List<Vector3> tempCi = GetVecsOnCircle(i * transfer, transfer);
foreach (Vector3 vec in tempCi)
{
ringVec.Add(vec.Multiply(mat));//按照矩阵旋转之后 添加到中间航点列表
}
}
else
{
ringVec.Add(mat[3]); //第一次循环 并非圈 只在航线上 按层比例取点 即可
}
}
}
return ringVec;
}
}
}