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

2023-05-16

在这里插入图片描述

目录

  • 前言
  • 原生实现(错误方法)
  • 精确实现(数学解)
  • 最小外接矩形
  • 参考

前言

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

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

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

原生实现(错误方法)

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • @TableGenerator 的初始值属性在 Hibernate 中显示问题,但在 JPA 中则不然

    package com sb firstjpaexample pojo import javax persistence Column import javax persistence Entity import javax persist
  • SPNEGO 密码身份验证问题

    我已将我的应用程序配置为通过 SPNEGO 与 Websphere 使用 Kerberos 身份验证 这是详细信息 krb5 conf libdefaults default realm ABC MYCOMPANY COM default
  • java.lang.VerifyError:JVMVRFY012堆栈形状不一致;

    在 WAS 8 5 5 中部署 Maven 项目时出现以下错误 我在WAS中安装了JDK 1 6和1 7 错误500 org springframework web util NestedServletException 处理程序处理失败
  • 策略模式还是命令模式?

    假设我有一个金融交易列表 我需要针对这些交易执行一系列验证规则 一个例子是我有一笔购买产品的交易 但是首先我需要验证交易中的帐户是否有足够的可用资金 产品没有售完等 由于这些规则 交易将是标记为拒绝 并应指定错误代码 当然 我正在考虑用一个
  • 对象数组的数组(二维数组)JNI

    我正在努力创建自定义对象类型 ShareStruct 的二维数组 jobjectArray ret jobjectArray ins jobjectArray outs jclass myClass env gt FindClass env
  • 使用 Apache POI Excel 写入特定单元格位置

    如果我有一个未排序的参数 x y z 列表 是否有一种简单的方法将它们写入使用 POI 创建的 Excel 文档中的特定单元格 就好像前两个参数是 X 和Y 坐标 例如 我有如下行 10 4 100 是否可以在第 10 行第 4 列的单元格
  • 重写 getPreferredSize() 会破坏 LSP

    我总是在这个压倒一切的网站上看到建议getPreferredSize 而不是使用setPreferredSize 例如 如前面的线程所示 对于固定大小的组件 使用重写 getPreferredSize 而不是使用 setPreferredS
  • 运行 java -jar 时出现 java.lang.ClassNotFoundException

    我正在使用 ant 来构建我的build xml文件 它编译正常 但随后得到运行时java lang NoClassDefFoundError通过 运行生成的 jar 时java jar my jar jar 似乎这个问题出现了很多 但没有
  • Spring中的ProxyFactoryBean

    有人可以解释一下吗代理工厂Bean http static springsource org spring docs current javadoc api org springframework aop framework ProxyFa
  • 无法访问“不安全”java方法的java表达式语言

    我正在开发一个项目 让用户向服务器提交小 脚本 然后我将执行这些脚本 有很多脚本语言可以嵌入到Java程序中 例如mvel ognl uel clojure rhino javascript等 但是 据我所知 它们都允许脚本编写者调用Jav
  • 我需要在 JFileChooser(打开模式)中显示不带扩展名的文件名。如何?

    我在打开模式下使用 JFileChooser 我需要显示不带扩展名的 文件名 字段 如何 我知道文件视图 它删除文件系统文件中的扩展名 但将所选文件中的扩展名保留在 文件名 字段中解释 http saveimg ru show image
  • 这个等待通知线程语义的真正目的是什么?

    我刚刚遇到一些代码 它使用等待通知构造通过其其他成员方法与类中定义的线程进行通信 有趣的是 获取锁后 同步范围内的所有线程都会在同一锁上进行定时等待 请参见下面的代码片段 随后 在非同步作用域中 线程执行其关键函数 即 做一些有用的事情1
  • 在java中将字符串日期转换为美国格式

    我有下面的代码 其中日期为字符串类型 我必须将其设置为美国格式 所以下面我已经展示了它 private static final SimpleDateFormat usOutputDate new SimpleDateFormat MM d
  • 如何在 HandlerInterceptorAdapter 中添加 HttpServletRequest 标头?

    我正在尝试将授权标头添加到我的请求中 作为我们切换环境时的临时解决方法 我试图在扩展 HandlerInterceptorAdapter 的拦截器中处理它 我使用 MutableHttpServletRequest 类制作here http
  • Struts 1 到 Spring 迁移 - 策略

    我有一个legacy银行应用程序编码为Struts 1 JSP现在的要求是迁移后端 目前为 MVC to Springboot MVC 后续UI JSP 将迁移到angular Caveats 1 后端不是无状态的 2 会话对象中存储了大量
  • 纱线上的火花,连接到资源管理器 /0.0.0.0:8032

    我正在我的开发机器 Mac 上编写 Spark 程序 hadoop的版本是2 6 spark的版本是1 6 2 hadoop集群有3个节点 当然都在linux机器上 我在idea IDE中以spark独立模式运行spark程序 它运行成功
  • Java 中 JButton 的击键/热键

    最初我使用 JMenu 并建立热键以使用加速器工作 它运行得很好 现在我想在 JButton 中实现相同的行为 但我陷入困境 这是我编写的代码 请分享您的想法 以便我可以走上正确的道路 import javax swing import j
  • java中使用多线程调用同一类的不同方法

    我有一个类 如下所示 具有三种方法 public class MyRunnable implements Runnable Override public void run what code need to write here to c
  • Collections.sort(list) 和 list.sort(Comparator) 之间的区别

    有什么理由让我应该选择Collections sort list 方法而不是简单地调用list sort 内部Collections sort只是调用sort的方法List无论如何 上课 令人惊讶的是几乎每个人都告诉我使用Collectio
  • 受信任的 1.5 小程序可以执行系统命令吗?

    如果是的话 这个能力有什么限制吗 具体来说 我需要以 Mac OSX 为目标 我以前用过这个在 Windows 系统上启动东西 但从未在 Mac 上尝试过 public void launchScript String args Strin

随机推荐