canvas绘图学习:坐标正算

174 阅读6分钟

前言

在进行canvas绘图的过程中我遇到了一个问题:我想将圆心与圆的边线上的某个点进行连线,我需要基于圆心坐标、圆半径和连线的角度去计算圆边点的坐标,这实际就是测量学中的坐标正算。

1.坐标正算介绍

坐标正算的概念如下:

根据直线起点的坐标、直线的水平距离及其坐标方位角来计算直线终点的坐标,称为坐标正算。

坐标正算的计算公式 如下:

2.坐标正算的原理

在上图中,已知A点的坐标,要求B点的坐标。可以看出用A点的坐标减去∆x、∆y这两个值就可以算出B点的坐标;而∆x、∆y这两个值都可以用三角函数sin(R)cos(R)算出 ;R是直线AB的象限角,可以通过方位角αAB计算出象限角R。

因此坐标正算的步骤如下:

第一步,根据方位角计算出象限角

第二步,使用三角函数计算出坐标增量

第三步,用起始点坐标加减坐标增量得到未知的结束点坐标

3.坐标方位角与象限角

我们先了解一下测量学中坐标方位角和象限角的概念:

坐标方位角是平面直角坐标系中某一直线与坐标主轴(X轴正北向)之间的夹角,从主轴起算,顺时针方向旋转。

以基本方向的北端或南段算起,顺时针或逆时针方向量至直线的水平角,称为象限角。

但是注意canvas坐标系与测量学的平面直角坐标系是有区别的,

  • canvas坐标系中向右为x轴正方向,向下为y轴正方向
  • 测量平面直角坐标系中向上为x轴正方向,向右为y轴正方向

因此在canvas当中坐标方位角为从x轴正方向开始顺时针旋转到某一直线的角度;象限角为某一直线沿顺时针或逆时针到x轴的角度。

象限角与坐标方位角的转换方式如下:

坐标方位角及其所在的象限方位角转象限角象限角转方位角
0° ~ 90° (第一象限)R = αα = R
90° ~ 180° (第二象限)R = 180° - αα = 180° - R
180° ~ 270° (第三象限)R = α - 180°α = R + 180°
270° ~ 360° (第四象限)R = 360° - αα = 360° - R

转换成代码就是:


  if ( α >= 0 && α < 90) {
    R = α
  } else if (α >= 90 && α < 180) {
    R = 180 - angle
  } else if (α >= 180 && α < 270) {
    R =  α - 180
  } else {
    R =  360 -  α
  } 

4.通过三角函数求坐标增量

坐标增量∆x和∆y可以通过三角函数计算出来,公式如下:

∆x = cos(θ) * L

∆y = sin(θ) * L

公式的推导过程如下:

下图中的三角形ABa是一个直角三角形所以可以使用三角函数。因为sin() = 对边/斜边``cos() = 临边/斜边,所以sin(R) = ∆y / L cos(R) = ∆x / L。最后就可以推导出 ∆x = cos(θ) * 300 ∆y = sin(θ) * 300

上面的公式可以用如下的代码表示:

// l是线段的长度
∆x = Math.cos(R * 180 / Math.PI) * L
∆y = Math.sin(R * 180 / Math.PI) * L

注意:JS的Math.cos()Math.sin()方法都只接收弧度制角度作为参数 ,所以普通角度要先乘以 π / 180° 转换为弧度制。

5. 起始点坐标加减坐标增量

在上一节中我们计算出了坐标增量,现在只需要用起始点A的坐标减去坐标增量就可以求出B点的坐标。

由于在上图中B点位于A点的左上方,而在canvas坐标系中越往左x坐标越小,越往上y坐标越小,所以这里要用B点的坐标减坐标增量。

XB = XA - ∆x

YB = YA - ∆y

但是难道所有情况下都是这样计算吗?答案显然是否定的。例如下面的例子中B点就在A点的右上方,因此根据canvas坐标系的规则此时的计算方式就应该是:

XB = XA + ∆x

YB = YA - ∆y

实际上对于坐标增量有如下的两种解释:

(1)坐标增量绝对值说

这种理解的核心观点是:“坐标增量∆x、∆y代表两条线段的长度,所以它们都是正数”。但是与此同时在根据线段的坐标方位角不同,未知点的计算方式也不同,可能是已知点坐标减坐标增量,也可能是已知点坐标加坐标增量。所以计算公式如:

XB = XA ± ∆x

YB = YA ± ∆y

方位角与计算方式的关系如下:

坐标方位角及其所在的象限未知点的计算方式
0° ~ 90° (第一象限)XB = XA + ∆xYB = YA + ∆y
90° ~ 180° (第二象限)XB = XA - ∆xYB = YA + ∆y
180° ~ 270° (第三象限)XB = XA - ∆xYB = YA - ∆y
270° ~ 360° (第四象限)XB = XA + ∆xYB = YA - ∆y

(2)坐标增量有符号说

这种解释的核心是“坐标增量是线段两点的坐标差值,因此坐标增量有正有负”。所以计算公式为:

XB = XA + ∆x

YB = YA + ∆y

而坐标增量的符号与坐标方位角有关,它们的关系如下:

坐标方位角及其所在的象限∆x的符号∆y的符号
0° ~ 90° (第一象限)++
90° ~ 180° (第二象限)-+
180° ~ 270° (第三象限)--
270° ~ 360° (第四象限)+-

当然无论基于上面的那种解释,最后实际上都是殊途同归,上面的公式可以转换为如下的代码:


  if (angle >= 0 && angle < 90) {
    XB  = XA + ∆x
    YB  = YA + ∆y
  } else if (angle >= 90 && angle < 180) {
    XB  = XA - ∆x
    YB  = YA + ∆y
  } else if (angle >= 180 && angle < 270) {
    XB  = XA - ∆x
    YB  = YA - ∆y
  } else {
    XB  = XA + ∆x
    YB  = YA - ∆y
  } 

5.封装坐标正算的方法

/**
 *
 * @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
}

参考资料

  1. 工程测量(第二版)第六章 直线方位角测量
  2. 测量员系列二:坐标正反算
  3. 方位角与象限角的关系—博客园