【技术碎片】【Java】计算椭圆的外接矩形坐标

在这里插入图片描述

前言

遇到一个需要计算一般椭圆(斜椭圆)的外接矩形坐标的问题,在此记录一下

已知椭圆的中心点坐标centerX centerY,椭圆的长轴,短轴majorRadius minorRadius,和旋转角度 angle。

按理说java有原生的计算外接矩形的函数,先看看 java.awt.geom怎么实现的。

注:java.awt.geom 是 Java 2D 图形 API 中的一个包,它提供了用于处理几何图形和形状的类。这个包中的类可以用来创建、操作和分析各种复杂的二维图形,包括点、线段、矩形、椭圆、多边形以及自定义形状。

原生实现(错误方法)

java.awt.geom提供了 Ellipse2D对象,我们通过Ellipse2D对象的 setFrameFromCenter 方法可以直接创建相应尺寸的椭圆:

           
            // 一般椭圆的入参
            double majorRadius = 108;
            double minorRadius = 207;
            double centerX = 836;
            double centerY = 473;
            double angle = 45.5;
            
            // 创建椭圆 ellipse
            Ellipse2D ellipse = new Ellipse2D.Double();
            ellipse.setFrameFromCenter(centerX, centerY, centerX + majorRadius, centerY + minorRadius);
            

我们再创建AffineTransform对象,将ellipse进行旋转变换,就能得到最终的椭圆,再通过Shape对象的getBounds2D()方法,可以直接得到外接矩形。


            // 旋转椭圆得到 transformedEllipse
            AffineTransform transform = new AffineTransform();
            transform.rotate(Math.toRadians(45.5), centerX, centerY);
            Shape transformedEllipse = transform.createTransformedShape(ellipse);
            Rectangle2D bounds2D = transformedEllipse.getBounds2D();

为了更直观展示,我们通过 Graphics2D 把图像画出来。

完整代码如下:


import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class BoundingBoxUtil2 {


    /**
     * 绘图
     */
    static class DrawFrame extends JFrame {
        public DrawFrame() {
            add(new DrawComponent());
            pack();
        }
    }

    static class DrawComponent extends JComponent {

        // 绘图窗口的尺寸
        private static final int DEFAULT_WIDTH = 2000;
        private static final int DEFAULT_HEIGHT = 1000;

        // 绘图内容
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;

            // 一般椭圆的入参
            double majorRadius = 108;
            double minorRadius = 207;
            double centerX = 836;
            double centerY = 473;
            double angle = 45.5;
            
            // 创建椭圆 ellipse
            Ellipse2D ellipse = new Ellipse2D.Double();
            ellipse.setFrameFromCenter(centerX, centerY, centerX + 108, centerY + 207);
            g2.draw(ellipse);

            // 旋转椭圆得到 transformedEllipse
            AffineTransform transform = new AffineTransform();
            transform.rotate(Math.toRadians(45.5), centerX, centerY);
            Shape transformedEllipse = transform.createTransformedShape(ellipse);
            // 绘制旋转后的椭圆
            g2.draw(transformedEllipse);
            // 绘制旋转后的椭圆的外接矩形
            g2.draw(transformedEllipse.getBounds2D());
        }

        public Dimension getPreferredSize() {
            return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        }
    }


    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new DrawFrame();
            frame.setTitle("DrawTest");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }

}



运行结果如下:

在这里插入图片描述

可以看到这种方法是不行的。

如果真的这么简单就好了,可以看到getBounds2D()得到的外接矩形并不是精确的,。我们看看源码描述:


    /**
     * Returns a high precision and more accurate bounding box of
     * the {@code Shape} than the {@code getBounds} method.
     * Note that there is no guarantee that the returned
     * {@link Rectangle2D} is the smallest bounding box that encloses
     * the {@code Shape}, only that the {@code Shape} lies
     * entirely within the indicated {@code Rectangle2D}.  The
     * bounding box returned by this method is usually tighter than that
     * returned by the {@code getBounds} method and never fails due
     * to overflow problems since the return value can be an instance of
     * the {@code Rectangle2D} that uses double precision values to
     * store the dimensions.
     *
     * <p>
     * Note that the
     * <a href="{@docRoot}/java.desktop/java/awt/Shape.html#def_insideness">
     * definition of insideness</a> can lead to situations where points
     * on the defining outline of the {@code shape} may not be considered
     * contained in the returned {@code bounds} object, but only in cases
     * where those points are also not considered contained in the original
     * {@code shape}.
     * </p>
     * <p>
     * If a {@code point} is inside the {@code shape} according to the
     * {@link #contains(Point2D p) contains(point)} method, then it must
     * be inside the returned {@code Rectangle2D} bounds object according
     * to the {@link #contains(Point2D p) contains(point)} method of the
     * {@code bounds}. Specifically:
     * </p>
     * <p>
     *  {@code shape.contains(p)} requires {@code bounds.contains(p)}
     * </p>
     * <p>
     * If a {@code point} is not inside the {@code shape}, then it might
     * still be contained in the {@code bounds} object:
     * </p>
     * <p>
     *  {@code bounds.contains(p)} does not imply {@code shape.contains(p)}
     * </p>
     * @return an instance of {@code Rectangle2D} that is a
     *                 high-precision bounding box of the {@code Shape}.
     * @see #getBounds
     * @since 1.2
     */
    public Rectangle2D getBounds2D();
    

大意为:

返回Shape的高精度且比getBounds方法更精确的边界框。请注意,不能保证返回的Rectangle2D是包围该形状的最小边界框,只能保证该形状完全位于指示的Rectangle 2D内。此方法返回的边界框通常比getBounds方法返回的更紧,并且从不因溢出问题而失败,因为返回值可以是使用双精度值来存储尺寸的Rectangle2D的实例。

事实上,如果直接生成不旋转的椭圆,通过getBounds2D()方法是可以找到准确的外接矩形的。

在这里插入图片描述

但是java.awt.geom没有考虑到一般椭圆(斜椭圆)的情况。

精确实现(数学解)

其实椭圆的外接矩形有数学解,我们通过还原椭圆一般式的参数,从而可以直接求外接矩形坐标。

中心点位于原点时,椭圆一般方程为:Ax^2 + Bxy + Cy^2 + F=0

因此可以通过已知短轴,长轴,旋转角,确定一般方程的参数:


    /**
     * 计算一般椭圆(斜椭圆)的参数A,B,C,F
     * 中心点位于原点的椭圆一般方程为:Ax^2+Bxy+Cy^2+F=0
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角
     * @return
     */
    public static double[] getEllipseParam(double majorRadius, double minorRadius, double angle) {
        double a = majorRadius;
        double b = minorRadius;
        double sinTheta = Math.sin(-angle);
        double cosTheta = Math.cos(-angle);
        double A = Math.pow(a, 2) * Math.pow(sinTheta, 2) + Math.pow(b, 2) * Math.pow(cosTheta, 2);
        double B = 2 * (Math.pow(a, 2) - Math.pow(b, 2)) * sinTheta * cosTheta;
        double C = Math.pow(a, 2) * Math.pow(cosTheta, 2) + Math.pow(b, 2) * Math.pow(sinTheta, 2);
        double F = -1 * Math.pow(a, 2) * Math.pow(b, 2);
        return new double[]{A, B, C, F};
    }

因此可以计算中心点位于原点时,外接矩形的坐标:


    /**
     * 计算中心点位于原点的一般椭圆的外接矩形坐标
     * @param A
     * @param B
     * @param C
     * @param F
     * @return
     */
    public static Point2D[] calculateRectangle(double A, double B, double C, double F) {
        double y = Math.sqrt(4 * A * F / (Math.pow(B, 2) - 4 * A * C));
        double y1 = -1 * Math.abs(y);
        double y2 = Math.abs(y);

        double x = Math.sqrt(4 * C * F / (Math.pow(B, 2) - 4 * C * A));
        double x1 = -1 * Math.abs(x);
        double x2 = Math.abs(x);

        Point2D p1 = new Point2D.Double(x1, y1);
        Point2D p2 = new Point2D.Double(x2, y2);
        return new Point2D[]{p1, p2};
    }

中心点位于原点的椭圆外接矩形能算了,原来的椭圆的外接矩形其实就是按照中心点平移罢了:


    /**
     * 计算一般椭圆的外接矩形实际坐标
     * 根据一般椭圆的实际中心点坐标,短轴,长轴,旋转角参数,计算一般椭圆的外接矩形实际坐标
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角
     * @param centerX 中心点横坐标
     * @param centerY 中心点纵坐标
     * @return
     */
    public static Point2D[] getCircumscribedRectangle(double majorRadius, double minorRadius, double angle, double centerX, double centerY) {
        double[] param = getEllipseParam(majorRadius, minorRadius, angle);
        Point2D[] points = calculateRectangle(param[0], param[1], param[2], param[3]);
        Point2D p1 = new Point2D.Double(centerX + points[0].getX(), centerY + points[0].getY());
        Point2D p2 = new Point2D.Double(centerX + points[1].getX(), centerY + points[1].getY());
        return new Point2D[] { p1, p2 };
    }


这样就能求得一般椭圆的外接矩形坐标了。

为了方便展示做一下绘图,完整代码如下:



import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class BoundingBoxUtil2 {


    /**
     * 绘图
     */
    static class DrawFrame extends JFrame {
        public DrawFrame() {
            add(new DrawComponent());
            pack();
        }
    }

    static class DrawComponent extends JComponent {

        // 绘图窗口的尺寸
        private static final int DEFAULT_WIDTH = 2000;
        private static final int DEFAULT_HEIGHT = 1000;

        // 绘图内容
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;

            // 一般椭圆的入参
            double majorRadius = 108;
            double minorRadius = 207;
            double centerX = 836;
            double centerY = 473;
            double angle = 45.5;

            Point2D[] rectangle = getCircumscribedRectangle(majorRadius, minorRadius, Math.toRadians(angle), centerX, centerY);
            double x1 = rectangle[0].getX();
            double y1 = rectangle[0].getY();
            double x2 = rectangle[1].getX();
            double y2 = rectangle[1].getY();
            double width = x2 - x1;
            double height = y2 - y1;
            Rectangle2D circumscribedRectangle = new Rectangle2D.Double();
            circumscribedRectangle.setRect(x1, y1, width, height);


            // 创建椭圆 ellipse
            Ellipse2D ellipse = new Ellipse2D.Double();
            ellipse.setFrameFromCenter(centerX, centerY, centerX + majorRadius, centerY + minorRadius);
            g2.draw(ellipse);

            // 旋转椭圆得到 transformedEllipse
            AffineTransform transform = new AffineTransform();
            transform.rotate(Math.toRadians(angle), centerX, centerY);
            Shape transformedEllipse = transform.createTransformedShape(ellipse);
            // 绘制旋转后的椭圆
            g2.draw(transformedEllipse);
            // 绘制旋转后的椭圆的外接矩形
//            g2.draw(transformedEllipse.getBounds2D());

            // 绘制真正的外接矩形
            g2.draw(circumscribedRectangle);
        }

        public Dimension getPreferredSize() {
            return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        }
    }

    /**
     * 计算一般椭圆(斜椭圆)的参数A,B,C,F
     * 中心点位于原点的椭圆一般方程为:Ax^2+Bxy+Cy^2+F=0
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角
     * @return
     */
    public static double[] getEllipseParam(double majorRadius, double minorRadius, double angle) {
        double a = majorRadius;
        double b = minorRadius;
        double sinTheta = Math.sin(-angle);
        double cosTheta = Math.cos(-angle);
        double A = Math.pow(a, 2) * Math.pow(sinTheta, 2) + Math.pow(b, 2) * Math.pow(cosTheta, 2);
        double B = 2 * (Math.pow(a, 2) - Math.pow(b, 2)) * sinTheta * cosTheta;
        double C = Math.pow(a, 2) * Math.pow(cosTheta, 2) + Math.pow(b, 2) * Math.pow(sinTheta, 2);
        double F = -1 * Math.pow(a, 2) * Math.pow(b, 2);
        return new double[]{A, B, C, F};
    }

    /**
     * 计算中心点位于原点的一般椭圆的外接矩形坐标
     * @param A
     * @param B
     * @param C
     * @param F
     * @return
     */
    public static Point2D[] calculateRectangle(double A, double B, double C, double F) {
        double y = Math.sqrt(4 * A * F / (Math.pow(B, 2) - 4 * A * C));
        double y1 = -1 * Math.abs(y);
        double y2 = Math.abs(y);

        double x = Math.sqrt(4 * C * F / (Math.pow(B, 2) - 4 * C * A));
        double x1 = -1 * Math.abs(x);
        double x2 = Math.abs(x);

        Point2D p1 = new Point2D.Double(x1, y1);
        Point2D p2 = new Point2D.Double(x2, y2);
        return new Point2D[]{p1, p2};
    }

    /**
     * 计算一般椭圆的外接矩形实际坐标
     * 根据一般椭圆的实际中心点坐标,短轴,长轴,旋转角参数,计算一般椭圆的外接矩形实际坐标
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角
     * @param centerX 中心点横坐标
     * @param centerY 中心点纵坐标
     * @return
     */
    public static Point2D[] getCircumscribedRectangle(double majorRadius, double minorRadius, double angle, double centerX, double centerY) {
        double[] param = getEllipseParam(majorRadius, minorRadius, angle);
        Point2D[] points = calculateRectangle(param[0], param[1], param[2], param[3]);
        Point2D p1 = new Point2D.Double(centerX + points[0].getX(), centerY + points[0].getY());
        Point2D p2 = new Point2D.Double(centerX + points[1].getX(), centerY + points[1].getY());
        return new Point2D[] { p1, p2 };
    }


    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new DrawFrame();
            frame.setTitle("DrawTest");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }

}


运行一下:

在这里插入图片描述

可以看到,数学解是成功的。

最小外接矩形

最小外接矩形其实很简单

假设椭圆没有旋转时,外接矩形四个点的坐标其实就是椭圆中心点,按照分别像左上,右上,右下,左下四个方向平移的结果,其中x平移短轴的距离,y平移长轴的距离。

如果椭圆旋转angle角度了,那么相应的四个点也跟着旋转四个角度就行

核心代码如下:


    /**
     * 计算获取椭圆的最小外接矩形的实际坐标
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角(弧度制)
     * @param centerX 中心点横坐标
     * @param centerY 中心点纵坐标
     * @return
     */
    public static Point2D.Double[] getMinimumBoundingRectangle(double majorRadius, double minorRadius, double angle, double centerX, double centerY) {
        // 矩形的宽长
        double width = majorRadius * 2;
        double height = minorRadius * 2;

        // 计算椭圆旋转前的最小外接矩形的四个顶点坐标
        double x1 = centerX - (width / 2);
        double y1 = centerY - (height / 2);
        double x2 = centerX + (width / 2);
        double y2 = centerY - (height / 2);
        double x3 = centerX + (width / 2);
        double y3 = centerY + (height / 2);
        double x4 = centerX - (width / 2);
        double y4 = centerY + (height / 2);

        // 计算椭圆旋转后的最小外接矩形的四个顶点坐标
        double rotatedX1 = centerX + (x1 - centerX) * Math.cos(angle) - (y1 - centerY) * Math.sin(angle);
        double rotatedY1 = centerY + (x1 - centerX) * Math.sin(angle) + (y1 - centerY) * Math.cos(angle);
        double rotatedX2 = centerX + (x2 - centerX) * Math.cos(angle) - (y2 - centerY) * Math.sin(angle);
        double rotatedY2 = centerY + (x2 - centerX) * Math.sin(angle) + (y2 - centerY) * Math.cos(angle);
        double rotatedX3 = centerX + (x3 - centerX) * Math.cos(angle) - (y3 - centerY) * Math.sin(angle);
        double rotatedY3 = centerY + (x3 - centerX) * Math.sin(angle) + (y3 - centerY) * Math.cos(angle);
        double rotatedX4 = centerX + (x4 - centerX) * Math.cos(angle) - (y4 - centerY) * Math.sin(angle);
        double rotatedY4 = centerY + (x4 - centerX) * Math.sin(angle) + (y4 - centerY) * Math.cos(angle);

        Point2D.Double point1 = new Point2D.Double(rotatedX1, rotatedY1);
        Point2D.Double point2 = new Point2D.Double(rotatedX2, rotatedY2);
        Point2D.Double point3 = new Point2D.Double(rotatedX3, rotatedY3);
        Point2D.Double point4 = new Point2D.Double(rotatedX4, rotatedY4);

        return new Point2D.Double[] { point1, point2, point3, point4 };
    }


我们完整代码如下:

package org.example.project_fragments.cv;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.List;

public class BoundingBoxUtil2 {

    /**
     * 绘图
     */
    static class DrawFrame extends JFrame {
        public DrawFrame() {
            add(new DrawComponent());
            pack();
        }
    }

    static class DrawComponent extends JComponent {

        // 绘图窗口的尺寸
        private static final int DEFAULT_WIDTH = 2000;
        private static final int DEFAULT_HEIGHT = 1000;

        // 绘图内容
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;

            // 模拟图像尺寸
            Rectangle2D image = new Rectangle2D.Double();
            image.setRect(1, 1, 1440, 1000);
            g2.draw(image);

            // 一般椭圆的入参
            int majorRadius = 108;
            int minorRadius = 207;
            int centerX = 836;
            int centerY = 473;
            double angle = 45.5;

            // 外接矩形
            Point2D.Double[] rectangle = getCircumscribedRectangle(majorRadius, minorRadius, Math.toRadians(angle), centerX, centerY);
            double x1 = rectangle[0].getX();
            double y1 = rectangle[0].getY();
            double x2 = rectangle[1].getX();
            double y2 = rectangle[1].getY();
            double width = x2 - x1;
            double height = y2 - y1;
            Rectangle2D circumscribedRectangle = new Rectangle2D.Double();
            circumscribedRectangle.setRect(x1, y1, width, height);
            // 绘制真正的外接矩形
            g2.draw(circumscribedRectangle);

            // 2023/5/11 最小外接矩形
            Point2D.Double[] minimumBoundingRectangle = getMinimumBoundingRectangle(majorRadius, minorRadius, Math.toRadians(angle), centerX, centerY);
            Polygon polygon = new Polygon();
            for (Point2D.Double point : minimumBoundingRectangle) {
                polygon.addPoint((int) point.getX(), (int) point.getY());
            }
            // 2023/5/11 最小外接矩形
            g2.drawPolygon(polygon);

            // 创建椭圆 ellipse
            Ellipse2D ellipse = new Ellipse2D.Double();
            ellipse.setFrameFromCenter(centerX, centerY, centerX + majorRadius, centerY + minorRadius);
//            g2.draw(ellipse);

            // 旋转椭圆得到 transformedEllipse
            AffineTransform transform = new AffineTransform();
            transform.rotate(Math.toRadians(angle), centerX, centerY);
            Shape transformedEllipse = transform.createTransformedShape(ellipse);
            // 绘制旋转后的椭圆
            g2.draw(transformedEllipse);
            // 绘制旋转后的椭圆的伪外接矩形
//            g2.draw(transformedEllipse.getBounds2D());

        }

        public Dimension getPreferredSize() {
            return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        }
    }

    /**
     * 计算一般椭圆(斜椭圆)的参数A,B,C,F
     * 中心点位于原点的椭圆一般方程为:Ax^2+Bxy+Cy^2+F=0
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角
     * @return
     */
    public static double[] getEllipseParam(double majorRadius, double minorRadius, double angle) {
        double a = majorRadius;
        double b = minorRadius;
        double sinTheta = Math.sin(-angle);
        double cosTheta = Math.cos(-angle);
        double A = Math.pow(a, 2) * Math.pow(sinTheta, 2) + Math.pow(b, 2) * Math.pow(cosTheta, 2);
        double B = 2 * (Math.pow(a, 2) - Math.pow(b, 2)) * sinTheta * cosTheta;
        double C = Math.pow(a, 2) * Math.pow(cosTheta, 2) + Math.pow(b, 2) * Math.pow(sinTheta, 2);
        double F = -1 * Math.pow(a, 2) * Math.pow(b, 2);
        return new double[]{A, B, C, F};
    }

    /**
     * 计算中心点位于原点的一般椭圆的外接矩形坐标
     * @param A
     * @param B
     * @param C
     * @param F
     * @return
     */
    public static Point2D.Double[] calculateRectangle(double A, double B, double C, double F) {
        double y = Math.sqrt(4 * A * F / (Math.pow(B, 2) - 4 * A * C));
        double y1 = -1 * Math.abs(y);
        double y2 = Math.abs(y);

        double x = Math.sqrt(4 * C * F / (Math.pow(B, 2) - 4 * C * A));
        double x1 = -1 * Math.abs(x);
        double x2 = Math.abs(x);

        Point2D.Double p1 = new Point2D.Double(x1, y1);
        Point2D.Double p2 = new Point2D.Double(x2, y2);
        return new Point2D.Double[]{p1, p2};
    }

    /**
     * 计算一般椭圆的外接矩形实际坐标
     * 根据一般椭圆的实际中心点坐标,短轴,长轴,旋转角参数,计算一般椭圆的外接矩形实际坐标
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角(弧度制)
     * @param centerX 中心点横坐标
     * @param centerY 中心点纵坐标
     * @return
     */
    public static Point2D.Double[] getCircumscribedRectangle(double majorRadius, double minorRadius, double angle, double centerX, double centerY) {
        double[] param = getEllipseParam(majorRadius, minorRadius, angle);
        Point2D.Double[] points = calculateRectangle(param[0], param[1], param[2], param[3]);
        Point2D.Double p1 = new Point2D.Double(centerX + points[0].getX(), centerY + points[0].getY());
        Point2D.Double p2 = new Point2D.Double(centerX + points[1].getX(), centerY + points[1].getY());
        return new Point2D.Double[] { p1, p2 };
    }


    /**
     * 计算获取椭圆的最小外接矩形的实际坐标
     * @param majorRadius 长轴
     * @param minorRadius 短轴
     * @param angle 旋转角(弧度制)
     * @param centerX 中心点横坐标
     * @param centerY 中心点纵坐标
     * @return
     */
    public static Point2D.Double[] getMinimumBoundingRectangle(double majorRadius, double minorRadius, double angle, double centerX, double centerY) {
        // 矩形的宽长
        double width = majorRadius * 2;
        double height = minorRadius * 2;

        // 计算椭圆旋转前的最小外接矩形的四个顶点坐标
        double x1 = centerX - (width / 2);
        double y1 = centerY - (height / 2);
        double x2 = centerX + (width / 2);
        double y2 = centerY - (height / 2);
        double x3 = centerX + (width / 2);
        double y3 = centerY + (height / 2);
        double x4 = centerX - (width / 2);
        double y4 = centerY + (height / 2);

        // 计算椭圆旋转后的最小外接矩形的四个顶点坐标
        double rotatedX1 = centerX + (x1 - centerX) * Math.cos(angle) - (y1 - centerY) * Math.sin(angle);
        double rotatedY1 = centerY + (x1 - centerX) * Math.sin(angle) + (y1 - centerY) * Math.cos(angle);
        double rotatedX2 = centerX + (x2 - centerX) * Math.cos(angle) - (y2 - centerY) * Math.sin(angle);
        double rotatedY2 = centerY + (x2 - centerX) * Math.sin(angle) + (y2 - centerY) * Math.cos(angle);
        double rotatedX3 = centerX + (x3 - centerX) * Math.cos(angle) - (y3 - centerY) * Math.sin(angle);
        double rotatedY3 = centerY + (x3 - centerX) * Math.sin(angle) + (y3 - centerY) * Math.cos(angle);
        double rotatedX4 = centerX + (x4 - centerX) * Math.cos(angle) - (y4 - centerY) * Math.sin(angle);
        double rotatedY4 = centerY + (x4 - centerX) * Math.sin(angle) + (y4 - centerY) * Math.cos(angle);

        Point2D.Double point1 = new Point2D.Double(rotatedX1, rotatedY1);
        Point2D.Double point2 = new Point2D.Double(rotatedX2, rotatedY2);
        Point2D.Double point3 = new Point2D.Double(rotatedX3, rotatedY3);
        Point2D.Double point4 = new Point2D.Double(rotatedX4, rotatedY4);

        return new Point2D.Double[] { point1, point2, point3, point4 };
    }


    /**
     * 可以自己测试椭圆在图像的情况
     */
    public static void main(String[] args) {
        // 图像展示
        EventQueue.invokeLater(() -> {
            JFrame frame = new DrawFrame();
            frame.setTitle("DrawTest");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });

    }
}


运行:

在这里插入图片描述
可以看到我们得到了最小外接矩形。

参考

https://zhuanlan.zhihu.com/p/82184417

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

锥栗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值