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

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

CAD图像读取与显示说明

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

8d05ad9c3d234e51bb6653339eb8b4ae.png



7c7fe7c1419e4e5eb53283fc60de8eab.png

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


官方实体说明

实体中文文档说明

实体

圆弧

dxf记录信息


4975d3bc3e0840218c7304a1e4339312.png

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



代码实现

    /**
     * 离散圆弧
     * 注意:起始角度、终止角度需要传入弧度制
     *
     * @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);
    }


测试



4acb6ba3174047ab9a50efead55e510e.png


优化多段线的凸度

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;
    }


  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;
    }


 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));
    }


测试



7ac8478355a14a7c94e00fb359a9bafe.png

椭圆

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);
    }

 旋转方法 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;
    }


测试


3ddb8aa80c014a9f8f88b15e6d527f6e.png


引用

图片引用

[脑瓜疼表情包]脑瓜疼

[一脸疑惑表情包]一脸疑惑

目录
相关文章
|
1月前
|
Java
Java的CAS机制深度解析
CAS(Compare-And-Swap)是并发编程中的原子操作,用于实现多线程环境下的无锁数据同步。它通过比较内存值与预期值,决定是否更新值,从而避免锁的使用。CAS广泛应用于Java的原子类和并发包中,如AtomicInteger和ConcurrentHashMap,提升了并发性能。尽管CAS具有高性能、无死锁等优点,但也存在ABA问题、循环开销大及仅支持单变量原子操作等缺点。合理使用CAS,结合实际场景选择同步机制,能有效提升程序性能。
|
20天前
|
机器学习/深度学习 JSON Java
Java调用Python的5种实用方案:从简单到进阶的全场景解析
在机器学习与大数据融合背景下,Java与Python协同开发成为企业常见需求。本文通过真实案例解析5种主流调用方案,涵盖脚本调用到微服务架构,助力开发者根据业务场景选择最优方案,提升开发效率与系统性能。
174 0
|
16天前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
305 100
|
2月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
2月前
|
缓存 安全 Java
Java并发性能优化|读写锁与互斥锁解析
本文深入解析Java中两种核心锁机制——互斥锁与读写锁,通过概念对比、代码示例及性能测试,揭示其适用场景。互斥锁适用于写多或强一致性场景,读写锁则在读多写少时显著提升并发性能。结合锁降级、公平模式等高级特性,助你编写高效稳定的并发程序。
142 0
|
11天前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
20天前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
96 1
|
2月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
199 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
2月前
|
存储 缓存 算法
Java数据类型与运算符深度解析
本文深入解析Java中容易混淆的基础知识,包括八大基本数据类型(如int、Integer)、自动装箱与拆箱机制,以及运算符(如&与&&)的使用区别。通过代码示例剖析内存布局、取值范围及常见陷阱,帮助开发者写出更高效、健壮的代码,并附有面试高频问题解析,夯实基础。
|
2月前
|
算法 Java 测试技术
零基础学 Java: 从语法入门到企业级项目实战的详细学习路线解析
本文为零基础学习者提供完整的Java学习路线,涵盖语法基础、面向对象编程、数据结构与算法、多线程、JVM原理、Spring框架、Spring Boot及项目实战,助你从入门到进阶,系统掌握Java编程技能,提升实战开发能力。
124 0

热门文章

最新文章

推荐镜像

更多
  • DNS