• CAD数据文件格式DXF部分实体(圆弧、椭圆、凸度)解析[原理讲解+公式推导+java实现]


    CAD图像读取与显示说明

      如果想要开发一个可以读取dxf图像的软件,为了方便图像在软件中的绘制,往往会将图形进行离散称为一系列点,然后将一系列点按照顺序相连即可绘制出图形。

    在这里插入图片描述

    CAD界面

    在这里插入图片描述

    软件系统界面

      软件系统界面的图形正是通过离散之后的点集绘制而成的,通过观察上面的两个图,可以发现用肉眼几乎看不出差异,说明通过连接离散点的方式来绘制图形在日常应用中绰绰有余。本文主要讲解一下一些复杂图形实体的离散。

    官方实体说明

    实体中文文档说明

    实体

    圆弧

    dxf记录信息

    在这里插入图片描述
    看到上面的信息,你是不是已经笑出声来,“这不是小学二年级的知识吗,简单,咔咔两下解决”
    在这里插入图片描述

    代码实现

        /**
         * 离散圆弧
         * 注意:起始角度、终止角度需要传入弧度制
         *
         * @param centerX
         * @param centerY
         * @param radius
         * @param startRadian 起始弧度
         * @param endRadian 终止弧度
         * @param discreteRadian 离散弧度,每隔多少绘制一个点
         * @param xList 记录离散点集的x坐标
         * @param yList 记录离散点集的y坐标
         */
        private void discreteArc(double centerX, double centerY, double radius, double startRadian, double endRadian, double discreteRadian,
                                 List<Float> xList, List<Float> yList) {
            if (endRadian < startRadian) {
                endRadian += 2 * Math.PI;
            }
            double radian = startRadian;
            while (radian < endRadian) {
                float tempX = (float) (centerX + radius * Math.cos(radian));
                float tempY = (float) (centerY + radius * Math.sin(radian));
                xList.add(tempX);
                yList.add(tempY);
                radian += discreteRadian;
            }
            //添加最终的点
            float tempX = (float) (centerX + radius * Math.cos(endRadian));
            float tempY = (float) (centerY + radius * Math.sin(endRadian));
            xList.add(tempX);
            yList.add(tempY);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    测试

    在这里插入图片描述
    在这里插入图片描述

    优化多段线的凸度

    dxf记录信息

    在这里插入图片描述
      为了用户可以在绘制图形的时候可以一气呵成,CAD为用户提供了优化多段线,优化多段线中也可以绘制圆弧,但是这里可没有告诉我们圆弧的圆心,周长,圆弧起始角度和终止角度这些,只给了一个凸度信息。
    在这里插入图片描述

    脑瓜疼
      如何根据凸度推导出圆弧的圆心和半径,可以参考大佬的文章[已知圆弧的起点端点和凸度计算圆心](https://blog.csdn.net/jiangyb999/article/details/89366912)

    在这里插入图片描述

    根据所引用文章可知最终结论

      那至于要怎么离散呢,如果求出了圆弧的信息了,那离散的方式直接使用上面所提到的圆弧离散方法即可
    在这里插入图片描述

    代码实现

    /**
         * 获取圆弧的圆心、起点角度、终点角度
         *
         * @param x1
         * @param y1
         * @param x2
         * @param y2
         * @param convexity 凸度:圆弧段四分之一夹角的正切值;凸度为0表示直线段;凸度为1表示半圆;凸度大于0,向里面凹;凸度小于0,向外面凸
         * @return
         */
        private double[] getArcMessage(double x1, double y1, double x2, double y2, double convexity) {
            double b = (1.0 / 2) * (1.0 / convexity - convexity);
    
            计算圆弧圆心
            //圆心坐标
            double centerX = 0.5 * ((x1 + x2) - b * (y2 - y1));
            double centerY = 0.5 * ((y1 + y2) + b * (x2 - x1));
    
            计算起点和终点所对应的角度
            double startRadian = MathUtil.getRadianByPoint(x1 - centerX, y1 - centerY);
            double endRadian = MathUtil.getRadianByPoint(x2 - centerX, y2 - centerY);
            if (convexity < 0) {
                double temp = startRadian;
                startRadian = endRadian;
                endRadian = temp;
            }
    
            计算圆弧半径
            double radius = MathUtil.getDistanceOfTwoPoint(x1, y1, centerX, centerY);
    
            存储圆弧信息
            double[] message = new double[5];
            message[0] = centerX;
            message[1] = centerY;
            message[2] = radius;
            message[3] = startRadian;
            message[4] = endRadian;
    
      /*      System.out.println("根据两点及其之间的凸度获取圆弧信息-------------------------------------------------------------------------------------");
            System.out.println("x1:" + x1 + ";y1:" + y1 + ";x2:" + x2 + ";y2:" + y2 + ";convexity:" + tempConvexity);
            System.out.println("centerX:" + centerX + ";centerY:" + centerY + ";radius:" + radius + ";startDegree:" + Math.toDegrees(startRadian) + ";endDegree:" + Math.toDegrees(endRadian));
            System.out.println("凸度解析完成---------------------------------------------------------------------------------------------------------");
            System.out.println();*/
    
            return message;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

       MathUtil.getRadianByPoint:获取一个向量和(1,0)向量的夹角(弧度制)
    在这里插入图片描述

       /**
         * 已知 x、y,求角度 0,2PI
         * 获取(x,y)与x轴正方向(1,0)的夹角
         *
         * @param x
         * @param y
         * @return
         */
        public static double getRadianByPoint(double x, double y) {
            double acos = Math.acos(x / Math.sqrt(x * x + y * y));
            double radian = y > 0 ? acos : (2 * Math.PI - acos);
            if (radian < 0 || radian > 2 * Math.PI) {
                System.out.println("radian:" + radian);
                System.out.println(1 / 0);
            }
            return radian;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      MathUtil.getDistanceOfTwoPoint:获取两点之间的直线距离

     /**
         * 获取两个点之间的距离
         *
         * @return
         */
        public static double getDistanceOfTwoPoint(double x1, double y1, double x2, double y2) {
            return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试

    在这里插入图片描述
    在这里插入图片描述

    椭圆

    dxf记录信息

    在这里插入图片描述
      看完上面的信息,你可能会一脸疑惑,心想:“啥呀这是,长轴端点是啥子哦,高中也mei学啊”

    在这里插入图片描述
      高中的椭圆都是老老实实趴在坐标原点,但是CAD的椭圆因为要满足更多的奇形怪状的图形,于是身法需要飘忽不定,不讲武德,莫急,待老夫降伏它。

    在这里插入图片描述
      相较于中心的长轴端点其实就是(x,y)=(x2-x1,y2-y1)(别问我怎么知道的,我是在CAD画图然后反推出来的,宝宝心里苦),这下是不是豁然开朗了,至于位置椭圆不是水平的,圆形也不在坐标原点。只需要先将椭圆的位置掰正,就可以调用椭圆公式了,最后再将得到的离散点坐标通过旋转和平移即可回到原位,不说了 ,直接操作一手代码。

    代码实现

      /**
         * 离散椭圆
         * 注意:需要传入弧度
         *
         * @param centerX        椭圆圆心
         * @param centerY        椭圆圆心
         * @param axisDirectionX 相较于中心的长轴端点x坐标
         * @param axisDirectionY 相较于中心的长轴端点y坐标
         * @param aspectRatio    短轴/长轴
         * @param startRadian    起始弧度 (对于闭合椭圆,该值为 0.0)
         * @param endRadian      终止弧度 (对于闭合椭圆,该值为 2pi)
         * @param discreteRadian 离散弧度
         * @param xListList
         * @param yListList
         */
        private void discreteEllipse(float centerX, float centerY, float axisDirectionX, float axisDirectionY, float aspectRatio, float startRadian, float endRadian, double discreteRadian,
                                     List<List<Float>> xListList, List<List<Float>> yListList) {
            List<Float> xList = new ArrayList<>();
            List<Float> yList = new ArrayList<>();
    
            //长轴长度
            double a = Math.sqrt(Math.pow(axisDirectionX, 2) + Math.pow(axisDirectionY, 2));
            //短轴长度
            double b = aspectRatio * a;
            //计算椭圆端点偏转角度(0,360)
            double degree = Math.toDegrees(MathUtil.getRadianByPoint(axisDirectionX, axisDirectionY));
            //重新处理startRadian和endRadian
            startRadian = (float) Math.toRadians(Math.toDegrees(startRadian));
            endRadian = (float) Math.toRadians(Math.toDegrees(endRadian));
    
            double radian = startRadian;
            while (radian < endRadian) {
                double x = centerX + a * Math.cos(radian);
                double y = centerY + b * Math.sin(radian);
                //将坐标旋转 degree
                double[] rotate = MathUtil.rotate(x, y, centerX, centerY, degree);
                xList.add((float) rotate[0]);
                yList.add((float) rotate[1]);
                radian += discreteRadian;
            }
            //添加最终的点
            double x = centerX + a * Math.cos(endRadian);
            double y = centerY + b * Math.sin(endRadian);
            //将坐标旋转 degree
            double[] rotate = MathUtil.rotate(x, y, centerX, centerY, degree);
            xList.add((float) rotate[0]);
            yList.add((float) rotate[1]);
            xListList.add(xList);
            yListList.add(yList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

      旋转方法 MathUtil.rotate

     /**
         * 将(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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试

    在这里插入图片描述
    在这里插入图片描述

    引用

    图片引用

    [脑瓜疼表情包]脑瓜疼
    [一脸疑惑表情包]一脸疑惑

    文章引用

    已知圆弧的起点端点和凸度计算圆心:https://blog.csdn.net/jiangyb999/article/details/89366912

  • 相关阅读:
    Ninja: Towards Transparent Tracing and Debugging on ARM【TEE的应用】
    TCP协议之《预分配缓存额度sk_forward_alloc--TCP接收》
    第10章 无持久存储的文件系统 (1)
    阿里面试失败后,一气之下我图解了Java中18把锁
    【Linux进阶】-- 1.python脚本实现守护进程daemon调度,启停等
    网球天地杂志网球天地杂志社网球天地编辑部2022年第7期目录
    对DataFrame各行列累乘:prod()函数
    云计算与大数据第16章 分布式内存计算平台Spark习题
    《DREEAM Guiding Attention with Evidence for Improving Document-Level Relation Extraction》阅读笔记
    eventfd
  • 原文地址:https://blog.csdn.net/laodanqiu/article/details/127958725