算法原理CSDN或者知乎上都有,不难。
代码主要参考C语言实现凸包Graham_scan算法
但是一只小蒟蒻的代码没有经过测试,细节上有一些小问题。我将代码修改测试后,放下下面,防走失。
将vector结构去除,就可以改为C语言代码。
#include<math.h>
#include<vector>
#define PI 3.1415926
typedef struct Point {
int x, y;
double angle;
}Point;
int less(Point a, Point b) { //定义点a小于点b法则(排序用)
if (a.angle != b.angle)
return a.angle < b.angle;
else return
a.x < b.x;
}
int ifAnticlockwise(Point a, Point b, Point c) { //是否为逆时针
int crossProduct = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); //求向量积
return crossProduct > 0; //向量积为正表示逆时针
}
std::vector<Point> Graham_scan(std::vector<Point> point) {
double sx = HUGE_VAL, sy = HUGE_VAL;
for (int i = 0; i < point.size(); i++) { //寻找起始点
if (point[i].y < sy)
sx = point[i].x, sy = point[i].y;
else if (point[i].y == sy && point[i].x < sx)
sx = point[i].x;
}
for (int i = 0; i < point.size(); i++) { //求夹角
if (point[i].x == sx && point[i].y == sy)
point[i].angle = 0;
else if (point[i].x == sx)
point[i].angle = PI / 2;
else {
point[i].angle = atan((point[i].y - sy) * 1.0 / (point[i].x - sx));
if (point[i].angle < 0)
point[i].angle = PI + point[i].angle;
//转换为与x轴正方向夹角
}
}
for (int i = 0; i < point.size() - 1; i++) {
for (int j = i + 1; j < point.size(); j++) { //冒泡排序
if (less(point[j], point[i])) {
Point temp = point[i];
point[i] = point[j];
point[j] = temp;
}
}
}
int cnt = 3; //栈顶指针
for (int i = 3; i < point.size(); ) {
if (ifAnticlockwise(point[cnt - 2], point[cnt - 1], point[i]))
point[cnt++] = point[i++];
else
point[--cnt] = point[i];
}
std::vector<Point> convex_hull;
for (int i = cnt - 1; i >= 0; i--) { //逆序输出
convex_hull.push_back(point[i]);
}
return convex_hull;
}
测试用main,将点与凸包均绘制在图上,结果正确与否比较直观。
#include<stdio.h>
#include"opencv2/opencv.hpp"
int main() {
srand((unsigned)time(NULL));
int n = 19, minx = 1, maxx = 98, miny = 1, maxy = 98;
std::vector<Point> point;
for (int i = 0; i < n; i++) { //生成随机坐标
Point pt;
pt.x = rand() % (maxx - minx + 1) + minx;
pt.y = rand() % (maxy - miny + 1) + miny;
point.push_back(pt);
}
//point[0].x = 6; point[0].y = 80;
//point[1].x = 72; point[1].y = 18;
//point[2].x = 62; point[2].y = 65;
//point[3].x = 62; point[3].y = 12;
//point[4].x = 6; point[4].y = 71;
//point[5].x = 79; point[5].y = 48;
//point[6].x = 91; point[6].y = 44;
//point[7].x = 4; point[7].y = 65;
//point[8].x = 87; point[8].y = 2;
//point[9].x = 31; point[9].y = 56;
//point[10].x = 79; point[10].y = 48;
//point[11].x = 67; point[11].y = 78;
//point[12].x = 19; point[12].y = 49;
//point[13].x = 48; point[13].y = 38;
//point[14].x = 25; point[14].y = 26;
//point[15].x = 39; point[15].y = 50;
//point[16].x = 65; point[16].y = 98;
//point[17].x = 24; point[17].y = 94;
//point[18].x = 78; point[18].y = 62;
for (int i = 0; i <n; i++) {
printf("point[%d].x = %d;point[%d].y = %d;\n", i,point[i].x, i, point[i].y);
}
cv::Mat showMat(100, 100, CV_8UC1, cv::Scalar(0));
for (int i = 0; i < n; i++) {
cv::circle(showMat, cv::Point(point[i].x, point[i].y), 2, cv::Scalar(255));
}
std::vector<Point> convex_hull = Graham_scan(point);
for (int i = 1; i < convex_hull.size(); i++) { //逆序输出
cv::line(showMat, cv::Point(convex_hull[i].x, convex_hull[i].y), cv::Point(convex_hull[i - 1].x, convex_hull[i - 1].y), cv::Scalar(125), 1, cv::LINE_4);
}
cv::line(showMat, cv::Point(convex_hull[0].x, convex_hull[0].y), cv::Point(convex_hull.back().x, convex_hull.back().y), cv::Scalar(200), 1, cv::LINE_4);
return 0;
}