CAD图像读取与显示说明
如果想要开发一个可以读取dxf图像的软件,为了方便图像在软件中的绘制,往往会将图形进行离散称为一系列点,然后将一系列点按照顺序相连即可绘制出图形。
软件系统界面 软件系统界面的图形正是通过离散之后的点集绘制而成的,通过观察上面的两个图,可以发现用肉眼几乎看不出差异,说明通过连接离散点的方式来绘制图形在日常应用中绰绰有余。本文主要讲解一下一些复杂图形实体的离散。
官方实体说明
实体
圆弧
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); }
测试
优化多段线的凸度
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)); }
测试
椭圆
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; }
测试
引用
图片引用
[脑瓜疼表情包]:脑瓜疼
[一脸疑惑表情包]:一脸疑惑