求多边形的最小包络矩形【java实现+原理讲解】

简介: 求多边形的最小包络矩形【java实现+原理讲解】

问题

给定一个多边形的点集,如何找出一个矩形,该矩形可以将整个多边形包住,且矩形的面积最小。


方法

整个问题的求解分为两个过程,如果多边形为凹多边形,需要先将凹多边形转化为凸多边形,紧接着开始寻找凸多边形的最小包络矩形。


将凹多边形转化为凸多边形

如果不了解凹多边形和凸边形如何定义,以及凹多边形转化为凸多边形的具体操作,可以参考文章凹多边形处理成凸多边形


寻找最小包络矩形

尝试将凸多边形的每条边都旋转到水平方向,然后求旋转之后的矩形的水平最小包络矩形,在遍历过程中不断更新最小包络矩形。

/**
 * 获取多边形的 最小包络矩形
 */
public class GetMinimumEnvelopeRectangleApi {
    /**
     * 获取多边形的最小包络矩形
     *
     * @param pointList 多边形点集
     * @return
     */
    public static Rectangle getMinimumEnvelopeRectangle(List<Point> pointList) {
        将凹多边形转化为凸多边形
        pointList = new ConcaveToConvexApi().concaveToConvex(pointList);
        寻找最小包络矩形
        double minRectangleArea = Double.MAX_VALUE;
        double bestLen = 0;
        double bestWid = 0;
        Point[] bestPointArr = new Point[4];
        for (int m = 0; m < pointList.size(); m++) {
            int n = (m + 1) % pointList.size();
            float mX = pointList.get(m).getX();
            float mY = pointList.get(m).getY();
            //求当前边和向量(1,0)的角度
            double angle = -MathUtil.calculateAngleOfVector(
                    pointList.get(n).getX() - mX, pointList.get(n).getY() - mY
            );
            ///将除了 点m 之外的每个点绕着 点m 旋转, 然后构造矩形
            double minX = mX;
            double minY = mY;
            double maxX = mX;
            double maxY = mY;
            for (int i = 0; i < pointList.size(); i++) {
                if (i == m) {
                    continue;
                }
                Point pointI = pointList.get(i);
                //旋转
                double[] rotate = MathUtil.rotate(pointI.getX(), pointI.getY(), mX, mY, angle);
                minX = Math.min(minX, rotate[0]);
                maxX = Math.max(maxX, rotate[0]);
                minY = Math.min(minY, rotate[1]);
                maxY = Math.max(maxY, rotate[1]);
            }
            ///如果找到更小的矩形,更新最小矩形
            if ((maxX - minX) * (maxY - minY) < minRectangleArea) {
                minRectangleArea = (maxX - minX) * (maxY - minY);
                bestLen = maxX - minX;
                bestWid = maxY - minY;
                if (bestLen < bestWid) {
                    double temp = bestLen;
                    bestLen = bestWid;
                    bestWid = temp;
                }
                //存储矩形的左下角
                bestPointArr[0] = new Point((float) minX, (float) minY);
                //存储矩形的右下角
                bestPointArr[1] = new Point((float) maxX, (float) minY);
                //存储矩形的右上角
                bestPointArr[2] = new Point((float) maxX, (float) maxY);
                //存储矩形的左上角
                bestPointArr[3] = new Point((float) minX, (float) maxY);
                //将矩形的点位旋转回来
                for (Point point : bestPointArr) {
                    double[] rotateArr = MathUtil.rotate(point.getX(), point.getY(), mX, mY, -angle);
                    point.setX((float) rotateArr[0]);
                    point.setY((float) rotateArr[1]);
                }
            }
        }
        List<Float> pointXList = new ArrayList<>();
        List<Float> pointYList = new ArrayList<>();
        for (Point point : pointList) {
            pointXList.add(point.getX());
            pointYList.add(point.getY());
        }
        pointXList.add(pointList.get(0).getX());
        pointYList.add(pointList.get(0).getY());
        return new Rectangle((float) bestLen, (float) bestWid, bestPointArr, pointXList, pointYList);
    }
}


package com.dam;
/**
 * 数学工具
 */
public class MathUtil {
    /**
     * 计算向量的模
     *
     * @param x
     * @param y
     * @return
     */
    public static double calculateModulusOfVector(double x, double y) {
        return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    }
    /**
     * 求(x,y)的角度(0,360),从x坐标轴正方向开始计算
     *
     * @param x2
     * @param y2
     * @return
     */
    public static double calculateAngleOfVector(double x2, double y2) {
        double x1 = 1;
        double y1 = 0;
        double radian = Math.acos((x1 * x2 + y1 * y2) / (MathUtil.calculateModulusOfVector(x1, y1) * MathUtil.calculateModulusOfVector(x2, y2)));
        double angle = Math.toDegrees(radian);
        return y2 > 0 ? angle : 360 - angle;
    }
    /**
     * 将(x1,y1)绕着(x2,y2)逆时针旋转rotateDegree
     *
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param rotateDegree
     * @return
     */
    public static double[] rotate(double x1, double y1, double x2, double y2, double rotateDegree) {
        double[] arr = new double[2];
        //根据角度求弧度
        double radian = (rotateDegree * 1.0 / 180) * Math.PI;
        //旋转
        arr[0] = (x1 - x2) * Math.cos(radian) - (y1 - y2) * Math.sin(radian) + x2;
        arr[1] = (y1 - y2) * Math.cos(radian) + (x1 - x2) * Math.sin(radian) + y2;
        return arr;
    }
}

测试


参考文献

[1]曹新明,蒋瑞斌.不规则零件最小包络矩形的求解研究[J].科技通报,2007(01):102-105.

目录
相关文章
|
9月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
256 0
|
9月前
|
存储 缓存 安全
深入讲解 Java 并发编程核心原理与应用案例
本教程全面讲解Java并发编程,涵盖并发基础、线程安全、同步机制、并发工具类、线程池及实际应用案例,助你掌握多线程开发核心技术,提升程序性能与响应能力。
336 0
|
9月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
405 24
|
10月前
|
存储 缓存 Java
我们来详细讲一讲 Java NIO 底层原理
我是小假 期待与你的下一次相遇 ~
322 2
|
10月前
|
XML JSON Java
Java 反射:从原理到实战的全面解析与应用指南
本文深度解析Java反射机制,从原理到实战应用全覆盖。首先讲解反射的概念与核心原理,包括类加载过程和`Class`对象的作用;接着详细分析反射的核心API用法,如`Class`、`Constructor`、`Method`和`Field`的操作方法;最后通过动态代理和注解驱动配置解析等实战场景,帮助读者掌握反射技术的实际应用。内容翔实,适合希望深入理解Java反射机制的开发者。
810 13
|
10月前
|
算法 Java 索引
说一说 Java 并发队列原理剖析
我是小假 期待与你的下一次相遇 ~
104 1
|
10月前
|
安全 Java 编译器
JD-GUI,java反编译工具及原理: JavaDecompiler一个Java反编译器
Java Decompiler (JD-GUI) 是一款由 Pavel Kouznetsov 开发的图形化 Java 反编译工具,支持 Windows、Linux 和 Mac Os。它能将 `.class` 文件反编译为 Java 源代码,支持多文件标签浏览、高亮显示,并兼容 Java 5 及以上版本。JD-GUI 支持对整个 Jar 文件进行反编译,可跳转源码,适用于多种 JDK 和编译器。其原理基于将字节码转换为抽象语法树 (AST),再通过反编译生成代码。尽管程序可能带来安全风险,但可通过代码混淆降低可读性。最新版修复了多项识别错误并优化了内存管理。
8190 1
|
10月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
652 58