canvas绘图学习:坐标正算

前言

在进行canvas绘图的过程中我遇到了一个问题:我想将圆心与圆的边线上的某个点进行连线,可是我并不知道圆边点的坐标,因此我需要基于圆心坐标、圆半径和连线的角度去计算,这实际就是测量学中的坐标正算。这里面涉及到一些数学和测量学的知识,我曾经学习过,但可惜如今已经都还给老师了。现在我只能利用我脑中剩余的一点知识来完成这个计算。

1.已知条件

目前我已知的条件如下:

  1. 圆心坐标([900,500]
  2. 圆的半径(300)
  3. 线段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
}

参考资料

  1. 测量员系列二:坐标正反算
  2. 方位角与象限角的关系---博客园
相关推荐
小池先生10 分钟前
记录让cursor帮我给ruoyi-vue后台管理项目整合mybatis-plus
前端·vue.js·mybatis
Gipsyz12 分钟前
批量修改图片资源的属性。
前端·unity
我头发乱了伢14 分钟前
jQuery小游戏
前端·javascript·jquery
呦呦鹿鸣Rzh1 小时前
Web前端开发
前端
惊鸿一博1 小时前
正则表示式_匹配一个含有范围类型的数值字符串
javascript
jcsx1 小时前
证券量化交易选择合适的编程语言
javascript·servlet·numpy·pandas·pyqt
会说法语的猪2 小时前
uniapp使用uni.navigateBack返回页面时携带参数到上个页面
前端·uni-app
古蓬莱掌管玉米的神10 小时前
vue3语法watch与watchEffect
前端·javascript
林涧泣11 小时前
【Uniapp-Vue3】uni-icons的安装和使用
前端·vue.js·uni-app
雾恋11 小时前
AI导航工具我开源了利用node爬取了几百条数据
前端·开源·github