判断顶点凹凸性、判断多边形的凹凸性、填充凹坑将凹多边形处理为凸多边形【java实现+原理讲解】

简介: 判断顶点凹凸性、判断多边形的凹凸性、填充凹坑将凹多边形处理为凸多边形【java实现+原理讲解】

问题

  1. 如何识别多边形中一个顶点的凹凸性
  2. 如何判断一个多边形的凹凸性(若多边形有一个点为凹的,则多边形为凹的)
  3. 如何通过填充凹坑的方式将凹多边形转化为凸多边形


方法

将多边形点集顺序修改为逆时针顺序

判断多边形点击顺序的方法可以参考判断多边形点集顺序,如果判断出多边形点击顺序为顺时针的话,直接将集合反转一下即可


判断一个点是否为凹点


注意:使用这方法判断的前提是多边形点击顺序是逆时针的才行

最终实现


/**
* 将凹多边形转化为凸多边形
*/
public class ConcaveToConvexApi {
   /**
    * 将凹多边形转化为凸多边形
    *
    * @param pointList
    * @return
    */
   public List<Point> concaveToConvex(List<Point> pointList) {
       List<Point> pointListClone = (List<Point>) deepClone(pointList);
       将点集方向转化为逆时针方向
       this.correctPolygonDirection(pointListClone);
       删除掉凹点
       int j = 0;
       while (j < pointListClone.size()) {
           int i = (j - 1 + pointListClone.size()) % pointListClone.size();
           int k = (j + 1) % pointListClone.size();
           double x1 = pointListClone.get(j).getX() - pointListClone.get(i).getX();
           double y1 = pointListClone.get(j).getY() - pointListClone.get(i).getY();
           double x2 = pointListClone.get(k).getX() - pointListClone.get(i).getX();
           double y2 = pointListClone.get(k).getY() - pointListClone.get(i).getY();
           //叉积
           double crossProduct = x1 * y2 - x2 * y1;
           if (crossProduct > 0) {
               //凸顶点
               j++;
           } else {
               //凹顶点或者共线
               pointListClone.remove(j);
               if (j == pointListClone.size()) {
                   /*
                   一开始是 A B C D E F G 开始时j=0,GAB不触发删除
                   但是FGA触发删除之后,序列变成A B C D E F,可能FAB也会触发删除,因此需要将j重置于0
                    */
                   j = 0;
               } else {
                   if (j > 0) {
                      //这里为什么j--,可以看程序下面的图
                       j--;
                   }
               }
           }
       }
       return pointListClone;
   }
   /**
    * 修正零件的点位方向
    * 先找出最高那个点的索引i,然后在对{i.x-(i-1).x,i.y-(i-1).y}和{(i+1).x-(i-1).x,(i+1).y-(i-1).y}来求叉积,看正负就知道了
    *
    * @param pointList
    */
   private void correctPolygonDirection(List<Point> pointList) {
       if (pointList.size() == 0) {
           return;
       }
       找到零件的最高点
       int highestPointIndex = -1;
       double highestY = -Double.MAX_VALUE;
       for (int i = 0; i < pointList.size(); i++) {
           if (pointList.get(i).getY() > highestY) {
               highestY = pointList.get(i).getY();
               highestPointIndex = i;
           }
       }
       纠正坐标
       ///判断随着索引的进位,夹角是否为正数
       //存储方向,1:顺时针;-1:逆时针
       int direction = 0;
       Point lastPoint = pointList.get((highestPointIndex - 1 + pointList.size()) % pointList.size());
       Point curPoint = pointList.get(highestPointIndex);
       Point nextPoint = pointList.get((highestPointIndex + 1) % pointList.size());
       double x1 = curPoint.getX() - lastPoint.getX();
       double y1 = curPoint.getY() - lastPoint.getY();
       double x2 = nextPoint.getX() - lastPoint.getX();
       double y2 = nextPoint.getY() - lastPoint.getY();
       //计算向量的叉积
       double crossProduct = x1 * y2 - x2 * y1;
       if (crossProduct > 0) {
           direction = -1;
       } else if (crossProduct < 0) {
           direction = 1;
       }
       //如果是顺时针的话,需要改为逆时针
       if (direction == 1) {
           //将集合的元素反转
           Collections.reverse(pointList);
       }
   }
}

ac54ee3a5202475d8e9001ed12b95efe.png


测试


目录
相关文章
|
21天前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。
|
1月前
|
存储 Java C语言
Java代码解释Flash原理
Java代码解释Flash原理
32 0
|
1月前
|
开发框架 Java API
java反射机制的原理与简单使用
java反射机制的原理与简单使用
17 1
|
28天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
79 0
|
1月前
|
Java
软件工程设计原理里氏替换原则 ,具体实现及JAVA代码举例
里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计的基本原则之一,由Barbara Liskov提出。这个原则指出,如果类 S 是类 T 的子类型,则程序中使用 T 的对象的地方都可以不经修改地使用 S 的对象。换句话说,子类的对象应该能够替换掉它们的父类对象,而不影响程序的正确性。这个原则强调了继承关系中的行为兼容性,保证了基类和派生类之间的正确抽象和继承关系。
24 3
|
16天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
16 0
|
24天前
|
Java 开发者
软件工程设计原理接口隔离原则 ,具体实现及JAVA代码举例
【4月更文挑战第7天】接口隔离原则(Interface Segregation Principle, ISP)是面向对象设计原则之一,旨在减少不必要的依赖关系,通过拆分庞大且臃肿的接口为更小、更具体的接口来实现。这个原则强调“客户端不应该被迫依赖于它不使用的接口”,意味着一个类不应该被迫实现它不使用的方法。
16 1
|
24天前
|
Java
软件工程设计原理依赖倒置原则 ,具体实现及JAVA代码举例
【4月更文挑战第5天】在软件工程中,依赖倒置原则(Dependency Inversion Principle, DIP)是一项重要的设计原则,它是SOLID原则中的一个组成部分。这个原则主张高层模块不应该依赖于低层模块,而是应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。这种设计方法有助于降低代码间的耦合度,增强系统的灵活性和可维护性
20 0
|
24天前
|
Java 关系型数据库
软件工程设计原理开放封闭原则 ,具体实现及JAVA代码举例
【4月更文挑战第4天】开放封闭原则(Open/Closed Principle, OCP)是面向对象设计的核心原则之一,它指出软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改已有代码的前提下,可以通过扩展来增加新的功能,从而提高软件系统的灵活性和可维护性
19 1
|
27天前
|
存储 安全 Java
Java线程池ThreadPoolExcutor源码解读详解04-阻塞队列之PriorityBlockingQueue原理及扩容机制详解
1. **继承实现图关系**: - `PriorityBlockingQueue`实现了`BlockingQueue`接口,提供了线程安全的队列操作。 - 内部基于优先级堆(小顶堆或大顶堆)的数据结构实现,可以保证元素按照优先级顺序出队。 2. **底层数据存储结构**: - 默认容量是11,存储数据的数组会在需要时动态扩容。 - 数组长度总是2的幂,以满足堆的性质。 3. **构造器**: - 无参构造器创建一个默认容量的队列,元素需要实现`Comparable`接口。 - 指定容量构造器允许设置初始容量,但不指定排序规则。 - 可指定容量和比较
42 2