凸包计算学习笔记

海龟绘图

凸包计算 convexHull()

什么是凸包

  • 凸包(Convex Hull)是一个计算几何(图形学)中的概念。在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。

如何计算凸包

1.判断点集内元素的个数
  • 若点集内元素的个数少于三个,那么凸包就是该点集。

  • 若点集内元素的个数多于三个,那么就有可能有的点不在凸包的点集内。

2.当多于三个点,寻找起始点
  • 起始点一般为最为左下角或右下角或左上角或右上角的点(其中一个)(后文都以左下角的点)

		for(Point point : points) {//blPoint为左下角的点
                if(blPoint == null){
                    blPoint = point;
                    continue;
                }
                if(blPoint.x() > point.x())
                    blPoint = point;
                else if(blPoint.x() == point.x())
                {
                    if(point.y() < blPoint.y())
                        blPoint = point;
                }
            }
			turnPoint.add(blPoint); //最左下角点一定为凸包点集内的点,加入集合
            nowPoint = blPoint;
            angle = 0.0; //起始角度
  1. 点集遍历,按顺时针找到凸包点集的点

while(true)
            {
                nextangle = 360;
                nextLength = Double.MAX_VALUE;
                for(Point point : points)
                {
                    if(point.equals(nowPoint))
                        continue;
                    nowangle = calculateBearingToPoint(angle,(int)nowPoint.x(),(int)nowPoint.y(),(int)point.x(),(int)point.y());
                    if(nowangle<0){//**nowangle为负数是要转换成正数**要不然会出现死循环
                        nowangle=360.0+nowangle;
                    }
                    if(nextangle == nowangle){//角度相等时 比较长度 选取较长的
                        if(nextLength < ((point.x()-nowPoint.x())*(point.x()-nowPoint.x())+(point.y()-nowPoint.y())*(point.y()-nowPoint.y()))
                        {
                            nextLength = (point.x()-nowPoint.x())*(point.x()-nowPoint.x())+(point.y()-nowPoint.y())*(point.y()-nowPoint.y());
                            nextPoint = point;
                        }
                    }
                    else if(nextangle > nowangle) {//角度不同时 选取角度较小的
                        nextLength = (point.x()-nowPoint.x())*(point.x()-nowPoint.x())+(point.y()-nowPoint.y())*(point.y()-nowPoint.y());
                        nextangle = nowangle;
                        nextPoint = point;
                    }
                }
                if(blPoint.equals(nextPoint))//当循环回到左下角的点时说明结束了
                {
                    break;
                }
                nowPoint = nextPoint;
                angle += nextangle;
                angle =angle%360;//防止大于360度,但好像没用
                turnPoint.add(nextPoint);
            }
4.calculateBearingToPoint函数
  • 这个函数是为了求出两点间角度偏向

写的较为繁琐...

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY,
                                                int targetX, int targetY) {//计算两点旋转角度角度
       if(currentX==targetX&&currentY==targetY){
           System.out.println("是同一个点");
           return 0.0;
       }
       double sum=0.0;
       if(currentX<targetX&currentY<targetY){
           double angle;
           double b=targetY-currentY;
           double c=targetX-currentX;
           double a=Math.sqrt(c*c+b*b);
           angle = Math.toDegrees(Math.acos((a * a + b * b - c * c) / (2 * a * b)));
           sum = angle - currentBearing;
       } else if (currentX>targetX&currentY<targetY) {
           double angle;
           double b=targetY-currentY;
           double c=currentX-targetX;
           double a=Math.sqrt(c*c+b*b);
           angle = Math.toDegrees(Math.acos((a * a + b * b - c * c) / (2 * a * b)));
           sum = 360.0 - currentBearing - angle;
       } else if (currentX<targetX&currentY>targetY) {
           double angle;
           double b=currentY-targetY;
           double c=targetX-currentX;
           double a=Math.sqrt(c*c+b*b);
               angle = Math.toDegrees(Math.acos((a * a + b * b - c * c) / (2 * a * b)));
               sum = 180.0 - angle - currentBearing;
       } else if (currentX>targetX & currentY>targetY) {
           double angle;
           double b=currentY-targetY;
           double c=currentX-targetX;
           double a=Math.sqrt(c*c+b*b);
               angle = Math.toDegrees(Math.acos((a * a + b * b - c * c) / (2 * a * b)));
               sum = 180 - currentBearing + angle;
       }else if (targetX==currentX&targetY>currentY) {
           sum=360.0-currentBearing;
       } else if (targetX==currentX&targetY<currentY) {
           sum=180-currentBearing;
       } else if (targetX<currentX&targetY==currentY) {
           sum=270-currentBearing;
       } else if (targetX>currentX&targetY==currentY) {
           sum=90-currentBearing;
   	}
       if(sum%360==0.0){
           sum=0.0;
       }
       return  sum;
       //throw new RuntimeException("implement me!");
   }
返回数据
 return turnlPoint;
测试类
@Test
	public void convexHullTest() {
		Set<Point> points = new HashSet<Point>();
		Set<Point> convexHull = new HashSet<Point>();

		assertEquals(convexHull, TurtleSoup.convexHull(points));

		Point p11 = new Point(1, 1);
		Point p1010 = new Point(10, 10);
		Point p110 = new Point(1, 10);
		Point p12 = new Point(1, 2);
		Point p23 = new Point(2, 3);
		Point p32 = new Point(3, 2);

		points.add(p11);
		convexHull.add(p11);
		assertEquals(convexHull, TurtleSoup.convexHull(points));

		points.add(p1010);
		convexHull.add(p1010);
		assertEquals(convexHull, TurtleSoup.convexHull(points));

		points.add(p110);
		convexHull.add(p110);
		assertEquals(convexHull, TurtleSoup.convexHull(points));

		points.add(p12);
		assertEquals(convexHull, TurtleSoup.convexHull(points));

		points.add(p23);
		assertEquals(convexHull, TurtleSoup.convexHull(points));

		points.add(p32);
		convexHull.add(p32);
		assertEquals(convexHull, TurtleSoup.convexHull(points));
	}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

收起小桌板

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值