前言
在进行canvas绘图的过程中我遇到了一个问题:我想将圆心与圆的边线上的某个点进行连线,可是我并不知道圆边点的坐标,因此我需要基于圆心坐标、圆半径和连线的角度去计算,这实际就是测量学中的坐标正算。这里面涉及到一些数学和测量学的知识,我曾经学习过,但可惜如今已经都还给老师了。现在我只能利用我脑中剩余的一点知识来完成这个计算。
1.已知条件
目前我已知的条件如下:
- 圆心坐标(
[900,500]
) - 圆的半径(300)
- 线段OA的角度(240°)
2. 通过坐标增量求A点的坐标
很快我就想到用O点的坐标加减∆x、∆y这两个值就可以算出A点的坐标,看下图:
从上图我们就可以看出,A点的坐标可以通过如下方式计算:
XA = XO - ∆x
YA = YO - ∆y
由于在上图中A点位于O点的左上方,而在canvas坐标系中越往左x坐标越小,越往上y坐标越小,所以这里要用O点的坐标减坐标增量。
但是很快我就注意到并非所有的情况下公式都是这样,比如说如果是下图这种情况,公式就是:
XA = XO + ∆x
YA = YO - ∆y
针对这种情况实际上就有两种理解:
(1)坐标增量绝对值说
这种理解的核心观点是:"坐标增量∆x、∆y代表两条线段的长度,所以它们都是正数"。但是与此同时在根据线段的坐标方位角不同,未知点的计算方式也不同,可能是已知点坐标减坐标增量,也可能是已知点坐标加坐标增量。所以计算公式如:
XA = XO ± ∆x
YA = YO ± ∆y
方位角与计算方式的关系如下:
坐标方位角及其所在的象限 | 计算方式 |
---|---|
0° ~ 90° (第一象限) | XA = XO + ∆xYA = YO + ∆y |
90° ~ 180° (第二象限) | XA = XO - ∆xYA = YO + ∆y |
180° ~ 270° (第三象限) | XA = XO - ∆xYA = YO - ∆y |
270° ~ 360° (第四象限) | XA = XO + ∆xYA = YO - ∆y |
因此之前的两种情况的公式就分别如下:
上图中的方位角为 240°,所以此时的计算公式为:
XA = XO - ∆x
YA = YO - ∆y
上图中的方位角为 330°,所以此时的计算公式为:
XA = XO + ∆x
YA = YO - ∆y
(2)坐标增量有符号说
这种解释的核心是"坐标增量是线段两点的坐标差值,因此坐标增量有正有负"。所以计算公式为
XA = XO + ∆x
YA = YO + ∆y
而坐标增量的符号与坐标方位角有关,它们的关系如下:
坐标方位角及其所在的象限 | ∆x的符号 | ∆y的符号 |
---|---|---|
0° ~ 90° (第一象限) | + | + |
90° ~ 180° (第二象限) | - | + |
180° ~ 270° (第三象限) | - | - |
270° ~ 360° (第四象限) | + | - |
因此之前的两种情况的公式就分别如下:
上图中的方位角为 240°,所以此时的计算公式为:
XA = XO - |∆x|
YA = YO - |∆y|
上图中的方位角为 330°,所以此时的计算公式为:
XA = XO + |∆x|
YA = YO - |∆y|
当然无论基于上面的那种解释,最后实际上都是殊途同归,上面的公式可以转换为如下的代码:
JavaScript
// 坐标增量
let xAdd = Math.abs(∆x),
yAdd = Math.abs(∆y),
if (angle >= 0 && angle < 90) {
XA = XO + xAdd
YA = YO + xAdd
} else if (angle >= 90 && angle < 180) {
XA = XO - xAdd
YA = YO + xAdd
} else if (angle >= 180 && angle < 270) {
XA = XO - xAdd
YA = YO - xAdd
} else {
XA = XO + xAdd
YA = YO - xAdd
}
3.通过三角函数求坐标增量
从上一节中我们已经知道通过坐标增量可以求得未知点A的坐标,那么下一步就是要求出坐标增量的值。
而坐标增量∆x和∆y可以通过三角函数计算出来(救命,这么多年三角函数终于派上用场了),公式如下:
∆x = cos(θ) * 300
∆y = sin(θ) * 300
以上的公式是这样推导的,下图中的三角形OBA是一个直角三角形所以可以使用三角函数。
又因为sin() = 对边/斜边
cos() = 临边/斜边
,
所以sin(θ) = ∆y / 300
cos(θ) = ∆x / 300
,
最后就可以推导出 ∆x = cos(θ) * 300
∆y = sin(θ) * 300
上面的公式可以用如下的代码表示:
JavaScript
// l是线段的长度
∆x = Math.cos(θ * 180 / Math.PI) * l
∆y = Math.sin(θ * 180 / Math.PI) * l
但是此时又有了一个问题: θ这个角度是多少?
4.坐标方位角与象限角
想要计算出θ的角度就必须要了解测量学中的两个概念: 坐标方位角和象限角。
坐标方位角是平面直角坐标系中某一直线与坐标主轴(X轴正北向)之间的夹角,从主轴起算,顺时针方向旋转。
以基本方向的北端或南段算起,顺时针或逆时针方向量至直线的水平角,称为象限角。
但是注意canvas坐标系与测量学的平面直角坐标系是有区别的,
- canvas坐标系中向右为x轴正方向,向下为y轴正方向
- 测量平面直角坐标系中向上为x轴正方向,向右为y轴正方向
因此在canvas当中坐标方位角 为从x轴正方向开始顺时针旋转到某一直线的角度;象限角为某一直线沿顺时针或逆时针到x轴的角度。
所以之前提到的θ角实际就是象限角它计算方式如下:
坐标方位角及其所在的象限 | 象限角的计算方式(angle为方位角) |
---|---|
0° ~ 90° (第一象限) | θ = angle |
90° ~ 180° (第二象限) | θ = 180° - angle |
180° ~ 270° (第三象限) | θ = angle - 180° |
270° ~ 360° (第四象限) | θ = 360° - angle |
转换成代码就是:
JavaScript
if (angle >= 0 && angle < 90) {
θ = angle
} else if (angle >= 90 && angle < 180) {
θ = 180 - angle
} else if (angle >= 180 && angle < 270) {
θ = angle - 180
} else {
θ = 360 - angle
}
5.封装坐标正算的方法
至此我们就可以开始封装坐标正算的方法了,在封装之前我们再梳理一下:
已知条件:起始点坐标(圆心坐标)、线段长度(圆的半径)、线段方位角
坐标正算过程:
第一步,根据方位角计算出象限角
第二步,使用三角函数计算出坐标增量
第三步,用起始点坐标加减坐标增量得到未知点坐标
JavaScript
/**
*
* @param {Array} startPoint 起始点坐标
* @param {number} length 线段长度
* @param {number} angle 坐标方位角
*/
function calcCoordinate(startPoint, length, angle) {
// 第一步:计算象限角
let qAngle // 象限角
if (angle >= 0 && angle < 90) {
qAngle = angle
} else if (angle >= 90 && angle < 180) {
qAngle = 180 - angle
} else if (angle >= 180 && angle < 270) {
qAngle = angle - 180
} else {
qAngle = 360 - angle
}
// 第二步:计算坐标增量
const _x = Math.cos((qAngle * Math.PI) / 180) * length
const _y = Math.sin((qAngle * Math.PI) / 180) * length
// 第三步:求未知点坐标
let result
const [x, y] = startPoint
if (angle >= 0 && angle < 90) {
result = [x + _x, y + _y]
} else if (angle >= 90 && angle < 180) {
result = [x - _x, y + _y]
} else if (angle >= 180 && angle < 270) {
result = [x - _x, y - _y]
} else {
result = [x + _x, y - _y]
}
return result
}