获取点的凸包之后,可以实现以下获取凸包的最小外接矩形,获取思路:
1、以其中两点作为矩形的一条边
2、以该边作为x轴基坐标,并做y轴基坐标
3、将所有点以该基坐标进行旋转,找到以该边为基准的所有点的x坐标的最小和最大值,既y轴的最大值
4、获取该范围的面积值,并获取边界数据
5、重复每条边,并每次判断保存最小面积及参数
6、最终输出最小外接矩形的参数
参考:https://blog.csdn.net/wang_heng199/article/details/74477738
https://blog.csdn.net/chuchur/article/details/84966164
#include <iostream>
#include <cmath>
using namespace std;
/* min value */
#define FLT_MIN 1.175494351e-38F
/* max value */
#define FLT_MAX 3.402823466e+38F
struct Point
{
float x, y;
};
struct OBB {
Point u[2]; //x, y轴
Point c; //中心点
float e[2]; //半长,半宽
};
float Dot(Point v, Point p)
{
return v.x * p.x + v.y * p.y;
}
float Length(Point p)
{
return sqrt(p.x*p.x + p.y*p.y);
}
float Cos(Point v, Point p1) {
float dot = Dot(v,p1);
float cos = dot/(Length(v)*Length(p1));
return cos;
}
// 坐标转换基坐标
void Setu0u1(Point e, Point &u0, Point &u1) {
//以e方向为x轴方向,设定xy轴
u0 = e / Length(e);
u1 = Point( 0 - u0.y, u0.x);
}
int GetMinAngleIndex(int imin1, int imax0, int imax1, int imin0,
Point* e, Point u0, Point u1) {
//返回旋转角度最小(cos值最大)的点的下标
int imin_angle_index = 0;
float cos = 0, maxCos = FLT_MIN;
cos = Cos(e[imin1], u0);
if(cos > maxCos){maxCos = cos; imin_angle_index = imin1;}
cos = Cos(e[imax0], u1);
if(cos > maxCos){maxCos = cos; imin_angle_index = imax0;}
cos = Cos(e[imax1], Point(0-u0.x,0-u0.y));
if(cos > maxCos){maxCos = cos; imin_angle_index = imax1;}
cos = Cos(e[imin0], Point(0-u1.x,0-u1.y));
if(cos > maxCos){maxCos = cos; imin_angle_index = imin0;}
return imin_angle_index;
}
//搜索以该基坐标的所有点中最大最小范围
void SetMinMax(Point *pts, int i, int iu, Point u0, Point u1,
float &max0, float &min0, float &max1,
int & new_imax0, int &new_imin0, int &new_imax1)
{
//找到x轴投影最大最小,y轴投影最大的长度(y轴最小则是重合边上点,长度为0)
//以及极值点在pts中的下标
Point d = pts[i] - pts[iu];
float dist0 = Dot( d, u0);
if(dist0 > max0){ max0 = dist0; new_imax0 = i;}
if(dist0 < min0){ min0 = dist0; new_imin0 = i;}
float dist1 = Dot( d, u1);
if(dist1 > max1){ max1 = dist1; new_imax1 = i;}
}
vector<Point> My_MinareaRec(vector<Point> myPoint, vector<Point> boundRect)
{
int ptsNum = myPoint.size();
OBB obb;
//必须是凸包
float minArea = FLT_MAX;
Point *pts = new Point[ ptsNum ];
for(int i = 0; i < ptsNum; i++)
{
pts[i].x = myPoint[i].x;
pts[i].y = myPoint[i].y;
}
//初始化边e
Point *e = new Point[ ptsNum ];
for(int i = 0; i < ptsNum; i++)
{
e[i] = pts[(i+1)%ptsNum] - pts[i];
}
int iu = 0;//以e[0]为重合边
//初始化u0 u1
Point u0,u1;
Setu0u1(e[iu], u0, u1);
int imax0 = 0, imax1 = 0, imin0 = 0, imin1 = 0;
float min0 = FLT_MAX, max0 = FLT_MIN,
max1 = FLT_MIN, min1 = 0; //min1其实可以不需要设定的,始终为0
//只是为了理解方便加上
//要去掉则需要把下方用到的min1都改为0
//求三个极值坐标
for( int i = 0; i < ptsNum; i++) {
SetMinMax(pts, i, iu, u0, u1,
max0, min0, max1,
imax0, imin0, imax1) ;
}
for(int i = 0; i < ptsNum ; i++) {
int iminangle = 0;
iminangle = GetMinAngleIndex((iu+1)%ptsNum, imax0, imax1, imin0, e, u0, u1);
if(iminangle == 0)break;//旋转回了初始点 没必要继续
if(iminangle == imax0){imax0 = (iu + 1)%ptsNum;iu = iminangle;}
else if(iminangle == imax1){imax1 = (iu + 1)%ptsNum;iu = iminangle;}
else if(iminangle == imin0){imin0 = (iu + 1)%ptsNum;iu = iminangle;}
else if(iminangle == (iu+1)%ptsNum){iu = (iu+1)%ptsNum;}
Setu0u1(e[iu], u0, u1);//重设u0u1
//维护三个极值点
int new_imax0 = imax0, new_imax1 = imax1, new_imin0 = imin0;
min0 =FLT_MAX, max0 = FLT_MIN, max1 = FLT_MIN;
//确定原先imax0在新坐标系中是什么极值
SetMinMax(pts, imax0, iu, u0, u1,
max0, min0, max1,
new_imax0, new_imin0, new_imax1) ;
//确定原先imax1在新坐标系中是什么极值
SetMinMax(pts, imax1, iu, u0, u1,
max0, min0, max1,
new_imax0, new_imin0, new_imax1) ;
//确定原先imin0在新坐标系中是什么极值
SetMinMax(pts, imin0, iu, u0, u1,
max0, min0, max1,
new_imax0, new_imin0, new_imax1) ;
imax0 = new_imax0;
imax1 = new_imax1;
imin0 = new_imin0;
//维护完毕
//求面积 设置obb
float area = (max0 - min0)*(max1 - min1);
if(area < minArea)
{
minArea = area;
obb.e[0] = (max0 - min0)*0.5f;
obb.e[1] = (max1 - min1)*0.5f;
obb.u[0] = u0;
obb.u[1] = u1;
obb.c = pts[iu] + ( u0 * (max0 + min0) + u1 * (max1 + min1) )*0.5f;
}
}
boundRect.push_back(Point(obb.e[0] * 2, obb.e[1] * 2));
boundRect.push_back(obb.c + ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] ));
boundRect.push_back(obb.c + ( obb.u[0] * obb.e[0] - obb.u[1] * obb.e[1] ));
boundRect.push_back(obb.c - ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] ));
boundRect.push_back(obb.c + ( obb.u[1] * obb.e[1] - obb.u[0] * obb.e[0] ));
cout << boundRect.size() << endl;
delete[] pts;
delete[] e;
return boundRect;
}