#include<iostream>
#include<fstream>//ifstream/ostream的头文件,文件流
#include<sstream>//string流,istringstream,ostringstream
#include "doublychain.h"
#include "assert.h"//assert的头文件
using namespace std;
//凸包的形成
void GeConvexHull(doublychain<pointInFlat>& S)
{
//任取两点画直线,检查其他点是否都在该直线上,若是则共线,求出两端点,反之则求出凸包。
const float PI = 3.1415926;
chainNode<pointInFlat>*f, *s, *p;
f = S.firstNode;
s = f->next;
float x1 = (f->element).x;
float y1 = (f->element).y;
float x2 = (s->element).x;
float y2 = (s->element).y;
float a = x2 - x1;
float b = y2 - y1;
float x = x1;
float y = y1;
p = s->next;
while (a*(y - y1) - b*(x - x1) == 0 && p !=S.firstNode)
{
x = (p->element).x;
y = (p->element).y;
p = p->next;
}
if (p == S.firstNode)//共线,有两种情况,垂直和不垂直,垂直时两端点的y值不一样,不垂直时只要求出横坐标的极值就得出两端点
{
pointInFlat min, max;
chainNode<pointInFlat>*pt = S.firstNode;
if (a == 0)//共线直线垂直于x轴
{
min.y = (pt->element).y; //纵坐标最小的那个点
max.y = (pt->element).y; //纵坐标最大的那个点
do //while (pt != NULL)
{
if (min.y >= (pt->element).y)
min.y = (pt->element).y;
if (max.y <= (pt->element).y)
max.y = (pt->element).y;
pt = pt->next;
} while (pt != S.firstNode);
}
else //共线直线没有垂直于x轴
{
min.x = (pt->element).x; //纵坐标最小的那个点
max.x = (pt->element).x; //纵坐标最大的那个点
do //while (pt != NULL)
{
if (min.x >= (pt->element).x)
min.x = (pt->element).x;
if (max.x <= (pt->element).x)
max.x = (pt->element).x;
pt = pt->next;
} while (pt != S.firstNode);
}
}
else { //S内的点不共线,形成凸包
pointInFlat X; //确定S内部的一个点X
X.x = (x1 + x2 + x) / 3;
X.y = (y1 + y2 + y) / 3;
for (int k =0; k <2; ++k)
{
//采用冒泡排序对S中的点进行排序
chainNode<pointInFlat>*pr, *pd, *pb, *ps,*p;
pr = S.lastNode;
pd =S.firstNode; //pd = NULL;
bool sorted = false;
do{
sorted = true;
ps = pr->next; //由于X在计算机中的计算结果和实际有偏差,故实际相等的点会改变原来点的顺序,不过对结果没影响
while (ps->next != pd) //比较后面的元素
{
pb = ps->next;
float xs = ps->element.x;
float ys = ps->element.y;
float xb = pb->element.x;
float yb = pb->element.y;
float lA_s= 0;
float lA_b= 0;
if (k == 0)
{
lA_s = sqrt(pow(xs - X.x, 2) + pow(ys - X.y, 2));
lA_b = sqrt(pow(xb - X.x, 2) + pow(yb - X.y, 2));//计算开始的两个点到X的距离,进行比较
}
else
{
float txs = X.y - ys;
float tys = xs - X.x;
lA_s = asin(tys / sqrt(txs*txs + tys*tys));
lA_s= lA_s*180/PI;
if (lA_s < 0 && txs>0) //角在第四象限,需要转化
lA_s += 360;
else if (txs<0) //角在第二三象限,需要转化
lA_s=180 - lA_s;
float txb = X.y - yb;
float tyb = xb - X.x;
lA_b = asin(tyb / sqrt(txb*txb + tyb*tyb));
lA_b = lA_b*180/PI;
if (lA_b<0 && txb>0)
lA_b+=360;
else if (txb<0)
lA_b= 180 - lA_b;
}
if (lA_s>lA_b) //交换指针域,速度要比交换数据域快
{
pr->next = pb;
pb->pre = pr;
ps->next = pb->next;
pb->next->pre = ps;
pb->next = ps;
ps->pre = pb;
sorted = false;
if (pr == S.lastNode) //在交换时要特别注意首和尾节点,首节点和尾节点参与交换后,要说明尾节点和头节点
{
S.firstNode = pb;
pb->pre = S.lastNode;
}
if (pb == S.lastNode)
{
S.lastNode = ps;
ps->next = S.firstNode;
}
}
pr = pr->next;
ps = pr->next;
pd = S.firstNode;
}
pd = ps;
pr = S.lastNode;
} while (pr->next != pd && !sorted);
}
//删除非极点的点
//首先找到y最小的点
pointInFlat ymin;
chainNode<pointInFlat>*p_ym,*deleteNode;//记录y最小的指针
chainNode<pointInFlat>*px,*prx,*prrx;//px为当前点的指针,prx为逆时针第二个指针,prrx为第三个指针
chainNode<pointInFlat>*pi = S.firstNode;
ymin.y = (pi->element).y;
p_ym = S.firstNode;
do
{
if (ymin.y>(pi->element).y)
p_ym= pi;
pi = pi->next;
} while (pi->next != S.firstNode);
for (px = p_ym,prx=px->next; prx!=p_ym;) //删除非极点的点,for循环的第一部分是起始值,第二部分是判断
{
prrx = prx->next;
//如果x,rx,rrx的逆时针夹角小于或等于180度,则删除rx. 向量叉乘积结果可以判断是顺时针还是逆时针。这里要把向量做三维处理才可以
auto ax = (prx->element).x - (px->element).x;
auto ay = (prx->element).y - (px->element).y; //建立向量a
auto bx= (prrx->element).x - (prx->element).x;
auto by = (prrx->element).y - (prx->element).y; //建立向量b
if (ax*by -bx*ay <=0) //条件进行转化
{
deleteNode = prx;
prx = px;
px = prx->pre; //开始的两节点都后退
delete deleteNode;
prx->next = prrx;
prrx->pre = prx;
//delete deleteNode;
}
px = prx; //如果节点方向是左转的,则继续前进。
prx = prrx;
}
for (chainNode<pointInFlat>*p = S.firstNode; p != S.lastNode; p = p->next)
cout << (p->element).x << " " << (p->element).y << " ";
cout << (S.lastNode->element).x << " " << (S.lastNode->element).y << " ";
}
}
int main()
{
doublychain<pointInFlat>S;
int i = 0;
ifstream infile("coordinate.txt"); //把txt文件转换为输入流
assert(infile.is_open()); //检查文件是否打开
string str; //逐行读入的中间变量
while (getline(infile, str)) //逐行读入
{
pointInFlat po;
istringstream istr(str); //将string分割为单个单词,相当于从string中读取数据
istr>>po.x>>po.y;
S.insert(i, po);
++i;
}
GeConvexHull(S);
return 0;
}
测试结果:
txt输入坐标:
0 0
0 1
1 1
2 1
2 0
1 -1
1 0
1 3
输出坐标:
1 -1 2 0 2 1 1 3 0 1 0 0