简介:Gift Wrapping算法顾名思义,是将点集看作礼物,像用礼物带对礼物进行包装一样,一点点缠好就求得了这个点集的一个凸包。更形象的比喻是,将点集中的点比作一根根钉在木板上的钉子,再用一根绳子从外围将这些钉子包围起来。
Gift Wrapping的具体实现步骤是这样的:
-
在点集中随意选择一个必然在凸包上的点,可以根据该点的横纵坐标选出一个横坐标最小(大),纵坐标最小(大)的点,共四种组合;
-
以该点为出发点进行凸包的构建,一点点扯长我们手里这根“绳子”,扯长这根绳子的办法就是计算夹角(当然也可以计算叉乘),用Java中带有的Math.atan方法计算这根绳子需要转过的最小角度就可以了,那个构成最小角度的点即为可以加入凸包的点;
-
对上一步进行循环直到加入的点为最初加入的点就可以了。
步骤很清晰,但是实现过程中有三点需要注意:
- 对于个数小于3的顶点集需要分类:
int num = points.size();
if (num <= 2) {
convexHullPoints = points;// 0、1、2个点时,凸包即为其本身
}
-
计算角度时要注意分类:arctan计算的角度大小范围为(- , ),因此以源点src为直角坐标系坐标原点,目标点end在一、三象限时大小为正数( 、 ),在二、四象限时为负数( 、 );
进行分类,在一、四象限为一类,二、三象限为另一类,这当中的sumAngle为之前所有加点入凸包转过的角度minAngle之和。double tmpAngle = 90 - sumAngle - Math .toDegrees(Math.atan((array[i].y() - array[end].y()) / (array[i].x() - array[end].x()))); if (array[i].x() < array[end].x()) tmpAngle += 180; if (tmpAngle < 0) tmpAngle += 360;//算当前点与end点的角度大小
若sumAngle为0,即图中y轴,根据这种方法对上图进行测试:
- 一象限的点:转动
- 二象限的点:转动
- 三象限的点:转动
- 四象限的点:转动
-
最后一点要注意所求的凸包是最大还是最小,注意在凸包集形成的凸多边形边上的点,如果所求为最小凸包,需要避免加入这些点,反之则必须要加入这些点。在这里引入变量distance,在角度相同的情况下,根据要求选择合适的点加入凸包。
if (tmpAngle < minAngle) { minAngle = tmpAngle; current = i; } else if (tmpAngle == minAngle) {//求最小凸包,凸包边上的点不要包括 if (Math.pow(array[i].x() - array[end].x(), 2) + Math.pow(array[i].y() - array[end].y(), 2) > Math.pow(array[current].x() - array[end].x(), 2) + Math.pow(array[current].y() - array[end].y(), 2)) current = i; }
以上就是我在Gift Wrapping求凸包算法中发现需要注意的三个问题。