using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DfdIO;
using NetTopologySuite.Geometries;
namespace Construction.BatchCreateMap
{
///
/// 凸包算法
///
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]点,并加入下一点,继续判断
* */
///
/// 凸包算法-braham扫描
///
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;
}
///
/// 两点距离
///
///
///
///
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));
}
///
/// 获得中心点
///
///
///
private static DfPoint getCenterPoint(List 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;
}
///
/// 计算垂足
///
///
///
///
///
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;
}
///
/// 扫描并插入接近边界的点
///
///
///
///
private static List insertScan(List set, double distanceStep)
{
List lstReturn = new List();
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;
}
///
/// 判断垂足是否在线段内部
///
///
///
///
///
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、b,a×b=axby-bxay,其值大于0则夹角为正
//P*Q > 0,P在Q的顺时针方向... ...
///
/// 叉积运算
///
///
///
///
///
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);
}
///
/// 快速排序 以set[0]为基点,对set[1]..set[length-1]排序
///
///
///
///
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]= 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);
}
}
}
}