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#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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:
* 判断set中的点是否全部都在start->end 构成的向量的逆时针方向
* */
/**
*
* 以set[0]为基点对set[1]..set[length-1]排序
* 如果multi(set[0],set[i],set[j])<0说明set[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);
}
}
}
}