You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
11 KiB
C#

1 month ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DfdIO;
using NetTopologySuite.Geometries;
namespace Construction.BatchCreateMap
{
/// <summary>
/// 凸包算法
/// </summary>
public class PointsHull
{
private static double ToleranceFactor = 1e-30;
/**
* braham
* 1[0]
* 2[0]
* 3[0][1][2][top-1][top]
* 3.1
* 3.2 [top]
* */
/// <summary>
/// 凸包算法braham扫描
/// </summary>
public static CoordinateList Brahamscan(Coordinate[] set, double distanceStep)
{
//记录下来满足起始点要求的元素的下标
int nCount = set.Length;
int unum = 0;
for (int i = 1; i < nCount; i++)
{
if (set[unum].Y > set[i].Y ||
(set[unum].Y == set[i].Y && set[unum].X > set[i].X))
{
unum = i;
}
}
//将满足要求的元素放在第一位
if (unum != 0)
{
Coordinate tmp = set[0];
set[0] = set[unum];
set[unum] = tmp;
}
//对set进行排序快速排序
QSort(set, 1, nCount - 1);
CoordinateList lstReturn = new CoordinateList();
lstReturn.Add(set[0]);
for (int i = 1; i < nCount; i++)
{
if (isok(set, lstReturn[lstReturn.Count - 1], set[i]))
{
lstReturn.Add(set[i]);
}
}
lstReturn.Add(lstReturn[0]);
// 重新以中心点进行排序
//DfPoint dfpCenter = getCenterPoint(set);
//set.Insert(0, dfpCenter);
//QSort(set, 1, set.Count - 1);
//DfPoint dpStart, dpEnd;
//for (int i = 0; i < lstReturn.Count - 1; i++)
//{
// dpStart = lstReturn[i];
// dpEnd = lstReturn[i + 1];
// for (int j = 0; j < set.Count; j++)
// {
// if ((set[j].X == dpStart.X && set[j].Y == dpStart.Y)
// || (set[j].X == dpEnd.X && set[j].Y == dpEnd.Y))
// {
// continue;
// }
// double h = Math.Abs(Multi(dpStart, set[j], dpEnd)) / Dist(dpStart, dpEnd);
// // 距离小于步长,并且垂足在线段内部
// if (h < distanceStep && isPedalIn(dpStart, dpEnd, set[j]))
// {
// lstReturn.Insert(i + 1, set[j]);
// set.RemoveAt(j);
// j--;
// i++;
// }
// }
//}
return lstReturn;
}
/// <summary>
/// 两点距离
/// </summary>
/// <param name="arg1"> </param>
/// <param name="arg2"> </param>
/// <returns> </returns>
static double Dist(DfPoint arg1, DfPoint arg2)
{
return Math.Sqrt((arg1.X - arg2.X) * (arg1.X - arg2.X)
+ (arg1.Y - arg2.Y) * (arg1.Y - arg2.Y));
}
/// <summary>
/// 获得中心点
/// </summary>
/// <param name="trackPoints"> </param>
/// <returns> </returns>
private static DfPoint getCenterPoint(List<DfPoint> trackPoints)
{
DfPoint dfpReturn = new DfPoint();
int nLength = trackPoints.Count;
for (int i = 0; i < nLength; i++)
{
dfpReturn.X += trackPoints[i].X / nLength;
dfpReturn.Y += trackPoints[i].Y / nLength;
}
return dfpReturn;
}
/// <summary>
/// 计算垂足
/// </summary>
/// <param name="startPoint"> </param>
/// <param name="endPoint"> </param>
/// <param name="outPoint"> </param>
/// <returns> </returns>
private static DfPoint getPedal(DfPoint startPoint, DfPoint endPoint, DfPoint outPoint)
{
DfPoint dfpReturn = new DfPoint();
double dA, dB, dC;// 直线方程系数
dA = (endPoint.Y - startPoint.Y);
dB = startPoint.X - endPoint.X;
dC = dA * startPoint.X + dB * startPoint.Y;
double dC2 = outPoint.Y * dA - outPoint.X * dB;
dfpReturn.Y = (dB * dC + dC2 * dA) / (dA * dA + dB * dB);
dfpReturn.X = (dA * dC - dB * dC2) / (dA * dA + dB * dB);
return dfpReturn;
}
/// <summary>
/// 扫描并插入接近边界的点
/// </summary>
/// <param name="set"> </param>
/// <param name="distanceStep"> </param>
/// <returns> </returns>
private static List<DfPoint> insertScan(List<DfPoint> set, double distanceStep)
{
List<DfPoint> lstReturn = new List<DfPoint>();
return lstReturn;
}
private static bool isok(Coordinate[] set, Coordinate start, Coordinate end)
{
bool flag = true;
int nCount = set.Length;
for (int i = 0; i < nCount; i++)
{
//if((end.Y> start.Y && set[i].Y < end.Y)
// || (end.Y < start.Y && set[i].Y > end.Y))
//{
// continue;
//}
//<0 : 说明set[i] 在start->end向量的顺时针方向,退出
if (Multi(start, end, set[i]) < -ToleranceFactor)
{
flag = false;
break;
}
}
return flag;
}
/// <summary>
/// 判断垂足是否在线段内部
/// </summary>
/// <param name="startPoint"> </param>
/// <param name="endPoint"> </param>
/// <param name="outPoint"> </param>
/// <returns> </returns>
private static bool isPedalIn(DfPoint startPoint, DfPoint endPoint, DfPoint outPoint)
{
bool isIn = false;
double dA, dB, dC;// 直线方程系数
dA = (endPoint.Y - startPoint.Y);
dB = startPoint.X - endPoint.X;
dC = dA * startPoint.X + dB * startPoint.Y;
double dC2 = outPoint.Y * dA - outPoint.X * dB;
double dCrossX, dCrossY;
dCrossY = (dB * dC + dC2 * dA) / (dA * dA + dB * dB);
dCrossX = (dA * dC - dB * dC2) / (dA * dA + dB * dB);
if ((dCrossY - startPoint.Y) * (dCrossY - endPoint.Y) < 0
&& (dCrossX - startPoint.X) * (dCrossX - endPoint.X) < 0)
{
isIn = true;
}
return isIn;
}
/**
* isok:
* setstart->end
* */
/**
*
* set[0]set[1]..set[length-1]
* multi(set[0],set[i],set[j])<0set[j]set[0]->set[i]
* set[0]->set[j]set[0]->set[i]set[j]set[i]
* */
/**
*
* */
//通过矢量叉积求极角关系(p0p1)(p0p2)
// 叉积可以用来判断平面向量夹角的正负。对于向量a、ba×b=axby-bxay其值大于0则夹角为正
//P*Q > 0,P在Q的顺时针方向... ...
/// <summary>
/// 叉积运算
/// </summary>
/// <param name="p0"> </param>
/// <param name="p1"> </param>
/// <param name="p2"> </param>
/// <returns> </returns>
static double Multi(Coordinate p0, Coordinate p1, Coordinate p2)
{
return (p1.X - p0.X) * (p2.Y - p0.Y) - (p2.X - p0.X) * (p1.Y - p0.Y);
}
/// <summary>
/// 快速排序 以set[0]为基点对set[1]..set[length-1]排序
/// </summary>
/// <param name="set"> </param>
/// <param name="start"> </param>
/// <param name="end"> </param>
private static void QSort(Coordinate[] set, int start, int end)
{
if (start < end)
{
int x = start;
int j = end;
int i = start + 1;
//当i=j时所有大于set[x]的元素都在set[x]的右面所有小于set[x]的元素都在set[x]的左面
while (i <= j)
{
//找到一个set[j]<set[x],并交换
while (j >= i)
{
//<0 : set[j]点在set[0]->set[x]向量的顺时针方向
//set[j]与set[x]交换
//==0 三点共线,判断距离
//距离近的小OrientationIndex
int flag = (int)Multi(set[0], set[x], set[j]);
if (flag < 0 || (flag == 0 && (set[j].Distance(set[0]) < set[x].Distance(set[0]))))
//if (flag < 0 || (flag == 0 && (Dist(set[j], set[0]) < Dist(set[x], set[0]))))
{
Coordinate tmp = set[x];
set[x] = set[j];
set[j] = tmp;
x = j;
j--;
break;
}
j--;
}
//找到set[i]>set[x],并交换
while (i <= j)
{
//>0 : set[i]点在set[0]->set[x]向量的逆时针方向
//set[i]与set[x]交换
//==0 三点共线,判断距离
//距离大的大
double flag = Multi(set[0], set[x], set[i]);
//if (flag > 0.0 || (flag == 0.0 && (Dist(set[i], set[0]) > Dist(set[x], set[0]))))
if (flag > 0.0 || (flag == 0.0 && (set[i].Distance(set[0]) > set[x].Distance(set[0]))))
{
Coordinate tmp = set[x];
set[x] = set[i];
set[i] = tmp;
x = i;
i++;
break;
}
i++;
}
}
QSort(set, start, x - 1);
QSort(set, x + 1, end);
}
}
}
}