公司门户网站建设公司,长沙广告招牌制作公司,wordpress免签约接口,深圳微信网站运营图形开发学院#xff5c;GraphAnyWhere 课程名称#xff1a;图形系统开发实战课程#xff1a;进阶篇(上)课程章节#xff1a;“图形样式”原文地址#xff1a;https://www.graphanywhere.com/graph/advanced/2-9.html 第九章 空间算法#xff08;一#xff09; \quad 在… 图形开发学院GraphAnyWhere 课程名称图形系统开发实战课程进阶篇(上)课程章节“图形样式”原文地址https://www.graphanywhere.com/graph/advanced/2-9.html 第九章 空间算法一 \quad 在图形开发过程中经常会使用到数学运算、坐标转换、空间算法等方面的功能本章就来讲解一下anyGraph 所提供的一些工具类。
1. 数学工具类 \quad anyGraph 提供了 MathUtil 工具类提供了一些常用的数学函数。
1.1 弧度和角度 \quad 角度是角的度量单位它是指两条射线在圆周上形成的夹角的度数通常使用度来表示。一个完整的圆有360度。弧度是一种度量角度的方法是将圆周分为360等分每一份的角度为1弧度。 \quad 弧度数/π角度值/180°其中π是一个常数等于圆周率约为3.14159265…
因此
角度 弧度 * (180 / π)弧度 角度 / (180 / π) \quad MathUtil 类提供了 toDegrees(angleInRadians) 和 toRadians(angleInDegrees) 方法实现弧度和角度相互转换其源代码如下
/*** 将弧度转换为度* param {number} angleInRadians 以弧度为单位的角度* return {number} 角度以度为单位*/
function toDegrees(angleInRadians) {return (angleInRadians * 180) / Math.PI;
}/*** 将度数转换为弧度* param {number} angleInDegrees 以度为单位的角度* return {number} 角度弧度*/
function toRadians(angleInDegrees) {return (angleInDegrees * Math.PI) / 180;
}1.2 生成随机数 \quad JavaScript 内置的 Math 对象提供了一个生成随机数的函数 random()该函数返回一个浮点数伪随机数在范围从0 到小于1。 \quad 实际在应用过程中经常需要生成的是某个范围内的随机整数因此 MathUtil 类提供了 getRandomNum(min, max) 方法可生成大于等于 min 小于等于 max 的随机整数其源代码如下
/*** 获取min和Max之间的随机整数*/
function getRandomNum(min, max) {let range max - min;let rand Math.random();return Math.floor(min Math.round(rand * range));
}1.3 返回指定小数位数的值 \quad 在 JavaScript 中浮点运算的小数位是由计算机的浮点数表示方式决定的。JavaScript 使用 IEEE 754 标准来表示浮点数其中包括单精度浮点数32位和双精度浮点数64位。单精度浮点数32位可以表示大约7位小数而双精度浮点数64位可以表示大约15位小数。 \quad 实际在应用过程中可能并不需要保留这么长的小数位数因此 MathUtil 类提供了 toFixed(n, decimals) 方法可返回指定小数位数的浮点数如果不指定小数位数则返回两位数的浮点数。其源代码如下
/*** 返回一个小数位数有限的数字(四舍五入到指定的小数位数)* 例如toFixed(10.465,2)的返回值为10.47, toFixed(10.995, 2)的返回值为11* param {number} n The input number.* param {number} decimals The maximum number of decimal digits.* return {number} The input number with a limited number of decimal digits.*/
function toFixed(n, decimals 0) {const factor Math.pow(10, decimals);return Math.round(n * factor) / factor;
}1.4 线性差值 \quad 线性插值法是指使用连接两个已知量的直线来确定在这两个已知量之间的一个未知量的值的方法。 \quad 下面这张图是线性差值的一个示例蓝色的两个点为已知量中间橙色的点为根据系数计算出的未知量系数的取值范围是 0 ~ 1 。 \quad MathUtil 类提供了 lerp(a, b, x) 方法可返回指定系数的差值。
/*** 计算a和b之间的x的线性插值。* param {number} a 开始值* param {number} b 结束值* param {number} x 插值系数.* return {number} 插值.*/
function lerp(a, b, x) {return a x * (b - a);
}该方法经常配合缓动函数使用。 1.5 返回指定范围内的数字 \quad MathUtil 类提供了 clamp(value, min, max) 方法可返回指定范围内的数字。
/*** 获取指定范围内的数字* param {number} value 数值.* param {number} min 范围最小值.* param {number} max 范围最大值.* return {number} 指定范围内的数字或与范围最接近的数字*/
function clamp(value, min, max) {return Math.min(Math.max(value, min), max);
}2. 测量工具类 \quad anyGraph 提供了 Measure 工具类提供了一些常用的测量函数。
2.1 点与点之间的距离 \quad 点与点之间的距离可使用勾股定理 a 2 b 2 c 2 a^2 b^2 c^2 a2b2c2 进行计算其图示分解过程如下图所示 其源代码如下
/*** 返回点p1(x1,y1)和p2(x2,y2)之间距离的平方。* param {Array} p1[x1, y1]* param {Array} p2[x2, y2]* return {number} Squared distance.*/
static dist(p1, p2) {const dx p2[0] - p1[0];const dy p2[1] - p1[1];return Math.sqrt(dx * dx dy * dy);
}Math.sqrt() 方法为 javaScript内置的求一个数的平方根。 2.2 折线长度 \quad 点与点之间的距离其实就是线段的长度折线由多个线段组成因此通过循环计算每一个线段的长度即可计算出折线的长度。
/*** 计算折线长度* param {Array} coords 折线点坐标数组*/
static getLength(coords) {let length 0;for (let i 0; i coords.length - 1; i) {length this.dist(coords[i], coords[i 1]);}return length;
}2.3 点与线之间的距离 \quad 计算点与线之间的距离可先计算点到线段的垂足垂足是指一个点它与线段上的两个端点形成的两条线段互相垂直。当垂足在线段上时点与线之间的距离为坐标到线段上垂足的距离当垂足在线段外部时点与线之间的距离为点与离线段最近的线段坐标之间的距离。
/*** 返回点p[x,y]和线段(p1[x1,y1], p2[x2,y2])之间最接近距离。* param {Array} p[x, y]* param {Array} p1[x1, y1]* param {Array} p2[x2, y2]* return {number} distance.*/
static distToSegment(p, p1, p2) {// 计算线段两端点的差值const dx p2[0] - p1[0];const dy p2[1] - p1[1];// 如果dx和dy都不等于0, p1p2是一个线段否则p1p2为同一个点if (dx ! 0 || dy ! 0) {// 计算p到线段p1p2的垂足tconst t ((p[0] - p1[0]) * dx (p[1] - p1[1]) * dy) / (dx * dx dy * dy);if (t 1) {// 如果t大于1说明垂足在线段p1p2外此时将p1设置为p2p1[0] p2[0];p1[1] p2[1];} else if (t 0) {// 如果t大于0且小于等于1说明垂足在线段p1p2上此时将p1设置为垂足的坐标p1[0] dx * t;p1[1] dy * t;}}// 计算并返回两点之间的距离return this.dist(p, p1);
}2.4 多边形面积 \quad 对于任意一个多边形如果已知其各个顶点的坐标 A 1 ( x 1 , y 1 ) , A 2 ( x 2 , y 2 ) , ⋅ ⋅ ⋅ , A n ( x n , y n ) A_1(x_1,y_1), A_2(x_2,y_2), ···, A_n(x_n,y_n) A1(x1,y1),A2(x2,y2),⋅⋅⋅,An(xn,yn)那么这个多边形的面积为 S 1 2 ∑ i 1 n ( x i y i 1 − x i 1 y i ) S \frac{1}{2} \sum_{i1}^n (x_iy_{i1} - x_{i1}y_i) S21∑i1n(xiyi1−xi1yi) \quad 其中 x n 1 x 1 , y n 1 y 1 x_{n1} x_1, y_{n1} y_1 xn1x1,yn1y1 \quad 算法原理参见利用鞋带定理Shoelace formula求2D多边形面积
/*** 多边形计算面积* param {Array} coords 多边形顶点坐标数组*/
static getArea(coords) {// 多边形面积换算公式// S Math.abs(0.5 * (x1 * y2 - y1 * x2 x2 * y3 - y2 * x3 …. xn * y1 - yn * x1)));let area 0;for (let i 2; i coords.length; i) {let ax coords[i - 1][0] - coords[0][0];let bx coords[i - 1][1] - coords[0][1];let cx coords[i][0] - coords[0][0];let dx coords[i][1] - coords[0][1];// 三角形面积公式// S 0.5 * (ax * dx - cx * bx);area 0.5 * (ax * dx - cx * bx);};//顺时针为正逆时针为负return Math.abs(area);
}2.5 两点与X轴夹角的角度 \quad 两点连线与x轴的夹角取旋转角对应的tanθ三角函数值就是斜率所以求出斜率再用反三角函数计算即可。 \quad JavaScript中有一个函数Math.atan2()返回从原点(0,0)到(x,y)点的线段与x轴正方向之间的平面角度(弧度值)也就是Math.atan2(y,x)该函数可通过斜率计算出角度。 \quad 计算公式如下 t a n θ ( y 2 − y 1 ) / ( x 2 − x 1 ) \qquad tanθ(y2 - y1)/(x2 - x1) tanθ(y2−y1)/(x2−x1) θ a t a n 2 ( ( y 2 − y 1 ) / ( x 2 − x 1 ) ) \qquad θ atan2((y2-y1)/(x2-x1)) θatan2((y2−y1)/(x2−x1)) \quad 源代码如下
/*** 计算两点与X轴夹角的角度* param {*} p1* param {*} p2* returns*/
static calcAngle(p1, p2) {return MathUtil.toFixed(MathUtil.toDegrees(Math.atan2(p2[1] - p1[1], p2[0] - p1[0])), 2);
}3 坐标工具类 \quad 在第一章 基础知识 基础数学知识 中分别讲述了使用三角函数和矩阵变换两种方式实现坐标的平移、缩放、旋转等变换的数学知识本节讲述 anyGraph 中使用三角函数实现坐标变换的具体实现。 \quad 这些方法封装到了 anyGraph 的工具类 Coordinate 中。
3.1 坐标平移 \quad 平移就是将一个向量或者点的 x 和 y 各自移动一段距离。将平移前的坐标(x,y)换算到平移后的新坐标( x ′ x^\prime x′, y ′ y^\prime y′)的等式如下 x ′ x d x \qquad x^\prime x dx x′xdx y ′ y d y \qquad y^\prime y dy y′ydy
/*** 坐标平移* param {ArrayCoord} coords 坐标.* param {number} deltaX x方向上的平移距离* param {number} deltaY y方向上的平移距离* return {ArrayCoord} 转换后的坐标.*/
static translate(coords, deltaX, deltaY) {let dest [];for (let j 0; j coords.length; j 1) {dest[j] [];dest[j][0] coords[j][0] deltaX;dest[j][1] coords[j][1] deltaY;}return dest;
}3.2 基于原点坐标旋转 \quad 旋转是一种线性变换是指将一个向量或者点的 x 和 y 绕着一个定点旋转一定的角度。将旋转前的坐标(x, y)换算到旋转后的新坐标( x ′ x^\prime x′, y ′ y^\prime y′)的等式需要用到以下几个三角函数 三角函数 c o s α x / r x r × c o s α cos \alpha x / r x r \times cos \alpha cosαx/rxr×cosα s i n α y / r y r × s i n α sin \alpha y / r y r \times sin \alpha sinαy/ryr×sinα 三角恒等式中的两角和差公式 s i n ( α θ ) s i n α × c o s θ c o s α × s i n θ sin(\alpha \theta) sin\alpha \times cos\theta cos\alpha \times sin\theta sin(αθ)sinα×cosθcosα×sinθ c o s ( α θ ) c o s α × c o s θ − s i n α × s i n θ cos(\alpha \theta) cos\alpha \times cos\theta - sin\alpha \times sin\theta cos(αθ)cosα×cosθ−sinα×sinθ
根据三角函数可知在旋转之前 x r × c o s α x r \times cos \alpha xr×cosα y r × s i n α y r \times sin \alpha yr×sinα
在旋转之后 r r r 是不变的假设旋转角度为 θ \theta θ 根据三角函数和三角恒等式可得出 x ′ r × c o s ( α θ ) r × c o s α × c o s θ − r × s i n α × s i n θ x × c o s θ − y × s i n θ x^\prime r \times cos(\alpha \theta) r \times cos \alpha \times cos \theta - r \times sin \alpha \times sin \theta x \times cos\theta - y \times sin\theta x′r×cos(αθ)r×cosα×cosθ−r×sinα×sinθx×cosθ−y×sinθ y ′ r × c o s ( α θ ) r × c o s α × s i n θ r × s i n α × c o s θ y × c o s θ x × s i n θ y^\prime r \times cos(\alpha \theta) r \times cos \alpha \times sin \theta r \times sin \alpha \times cos \theta y \times cos\theta x \times sin\theta y′r×cos(αθ)r×cosα×sinθr×sinα×cosθy×cosθx×sinθ
即 x ′ x × c o s θ − y × s i n θ x^\prime x \times cos\theta - y \times sin\theta x′x×cosθ−y×sinθ y ′ y × c o s θ x × s i n θ y^\prime y \times cos\theta x \times sin\theta y′y×cosθx×sinθ
因此旋转的源代码如下
/*** 基于原点坐标旋转* param {ArrayCoord} coords 坐标* param {number} angle 角度* return {ArrayCoord} 转换后的坐标*/
static rotate(coords, angle) {let cos Math.cos(angle);let sin Math.sin(angle);let dest [];for (let j 0; j coords.length; j) {let x coords[j][0] * cos - coords[j][1] * sin;let y coords[j][1] * cos coords[j][0] * sin;dest.push([x, y]);}return dest;
}3.3 基于锚点进行坐标旋转 \quad 基于锚点进行坐标旋转是一种组合变换它包括了3个过程 \quad 1. 将锚点坐标平移至原点 \quad 2. 基于原点进行坐标旋转 \quad 3. 将原点平移至锚点 \quad 因此可在将基于原点坐标旋转的代码基础之上增加坐标平移的功能即可其源代码如下
/*** 基于锚点进行坐标旋转* param {ArrayCoord} coords 坐标* param {number} angle 角度* param {Coord} anchor 锚点坐标* return {ArrayCoord} 转换后的坐标*/
static rotateByAnchor(coords, angle, anchor) {let cos Math.cos(angle);let sin Math.sin(angle);let anchorX anchor[0];let anchorY anchor[1];let dest [];for (let j 0; j coords.length; j) {let deltaX coords[j][0] - anchorX;let deltaY coords[j][1] - anchorY;dest.push([anchorX deltaX * cos - deltaY * sin, anchorY deltaX * sin deltaY * cos]);}return dest;
}3.4 基于原点进行坐标缩放 \quad 缩放是指将一个向量或者点的 x 和 y 各自进行指定比例的缩放。将缩放前的坐标(x, y)换算到缩放后的新坐标( x ′ x^\prime x′, y ′ y^\prime y′)的等式如下 x ′ x × s x \qquad x^\prime x \times sx x′x×sx y ′ y × s y \qquad y^\prime y \times sy y′y×sy \quad 在这个等式中坐标轴的横向缩放倍数记为sx将原有x坐标乘以它则可以得出新的横坐标同时坐标轴的纵向缩放倍数记为sy将原有y坐标乘以它则可以得出新的纵坐标。
/*** 基于原点进行坐标缩放* param {ArrayCoord} coords 坐标.* param {number} sx x方向上的缩放比例* param {number} sy y方向上的缩放比例* return {ArrayCoord} 转换后的坐标.*/
static scale(coords, sx, sy sx) {let dest [];for (let j 0; j coords.length; j) {dest.push([coords[j][0] * sx, coords[j][1] * sy]);}return dest;
}3.5 基于锚点进行坐标缩放 \quad 基于锚点进行坐标缩放是一种组合变换它包括了3个过程 \quad 1. 将锚点坐标平移至原点 \quad 2. 基于原点进行坐标缩放 \quad 3. 将原点平移至锚点
/*** 基于锚点进行坐标缩放* param {ArrayCoord} coords 坐标.* param {number} sx x方向上的缩放比例* param {number} sy y方向上的缩放比例* param {ArrayCoord} anchor Scale anchor point.* return {ArrayCoord} 转换后的坐标.*/
static scaleByAnchor(coords, sx, sy, anchor) {let dest [];let anchorX anchor[0];let anchorY anchor[1];for (let j 0; j coords.length; j 1) {let deltaX coords[j][0] - anchorX;let deltaY coords[j][1] - anchorY;dest[j] [];dest[j][0] anchorX sx * deltaX;dest[j][1] anchorY sy * deltaY;}return dest;
}3.6 坐标反向 \quad 坐标反向是对已有坐标值的数组或列表进行逆序操作从而得到新的坐标数组。
/*** 坐标反转* param {ArrayCoord} coords * returns {ArrayCoord} coords*/
static reverse(coords) {let dest [];for (let j coords.length - 1; j 0; j - 1) {dest.push(coords[j]);}return dest;
}\quad “图形系统实战开发-进阶篇 第九章 空间算法(一)” 的内容讲解到这里就结束了如果觉得对你有帮助有收获可以关注我们的官方账号持续关注更多精彩内容。
相关资料
▶ 系列教程及代码资料https://GraphAnyWhere.com ▶ 图形系统开发实战课程进阶篇上——前言 ▶ 图形系统开发实战课程进阶篇上——1.基础知识 ▶ 图形系统开发实战课程进阶篇上——2.图形管理类Graph ▶ 图形系统开发实战课程进阶篇上——3.图层类Layer ▶ 图形系统开发实战课程进阶篇上——4.图形基本形状 ▶ 图形系统开发实战课程进阶篇上——5.图形交互操作:平移和缩放 ▶ 图形系统开发实战课程进阶篇上——6.图形交互操作:拾取 ▶ 图形系统开发实战课程进阶篇上——7.图形交互操作: 视点控制与动画 ▶ 图形系统开发实战课程进阶篇上——8.图形样式 作者信息 作者 图形开发学院 CSDN https://blog.csdn.net/2301_81340430?typeblog 官网https://graphanywhere.com