求向量的角度

简单记录一个计算机图形学方面的算法。即计算 2D 平面中,从点 A 到点 B 形成的向量的夹角。

这算是游戏里面比较底层的算法,一般游戏引擎都会包装好这个方法,如 GMS2 的 point_direction 方法,常用于实现旋转一个物体,比如拨动时钟的指针、人物的武器使用鼠标瞄准敌人。

本文代码使用 JavaScript 实现。

问题描述

图片

如图,求这里朝上的白色虚线到红线的角度。

我们规定角度区间为 (-180, 180]。当向量朝上的时候,角度为0,向量正朝下时,角度为 180。当然你也可以规定为 [0, 360),只需要在代码得到的角度 angle 的基础上,计算 (angle + 360) % 360 即可。

另外要注意这里的坐标系的 y 轴是朝下的(一般计算机屏幕的坐标系都是y轴朝下的),x 轴朝向为右。

计算

已知中心点 cx、cy,光标的坐标 x,y,求角度。

说到底,求的是向量 a (x - cx, y - cy) 和向量 b (0, -1) 的夹角。这里我们需要用到 点积公式

图片

我们要求的是角度,于是得到下面公式:

图片

假设向量 a 为 (x, y), 向量 b 为 (x2, y2),则 a · bx * x2 + y * y2

|a| 指的是向量 a 的模,即向量 a 的 x 和 y 平方和并开方,即 Math.sqrt(x * x + y * y)

通过公式,我们得到了余弦值 cos,然后反余弦 Math.acos(),得到弧度,再进行单位换算得到角度。

function calCos(a, b) {
    // 点积
    let dotProduct = a[0] * b[0] + a[1] * b[1];
    let d = Math.sqrt(a[0] * a[0] + a[1] * a[1]) * Math.sqrt(b[0] * b[0] + b[1] * b[1]);
    return dotProduct/d;
}

let angle = Math.acos(radian) * 180 / Math.PI;


我们先看看 余弦函数 和 反余弦函数的示意图:

![图片](https://mmbiz.qpic.cn/mmbiz_png/Axu3yFGzpWibLO9dMv7IN91xX4giaib2PS5FwjjvztsR5D9nEugUIdVvyGsvDz0yTUyvLLCsj9y50CndWQL4jqQHw/640?wx_fmt=gif)

由图中的反余弦函数可知,求得的角度范围为 \[0, 180\]。那 \[-180, 0) 的范围如何获取呢?我们还有个办法,就是通过 **判断光标的 x 坐标大于还是小于 中心点的 x 坐标** 来设置正负,如果大于,角度为正;反之为负。

let angle = Math.acos(radian) * 180 / Math.PI;
if (x < cx) angle = -angle;


完整代码
----

function calAngle(cx, cy, x, y) {
    const radian = getCosBy2pt(x, y, cx, cy);
    let angle = Math.acos(radian) * 180 / Math.PI;

if (x < cx) angle = -angle;
    return angle;

// 计算 点1指点2形成 的向量 
    function getCosBy2pt(x, y, cx, cy) {
        let a = [x - cx, y - cy];
        let b = [0, -1];
        return calCos(a, b);
    }
    function calCos(a, b) {
        // 点积
        let dotProduct = a[0] * b[0] + a[1] * b[1];
        let d = Math.sqrt(a[0] * a[0] + a[1] * a[1]) * Math.sqrt(b[0] * b[0] + b[1] * b[1]);
        return dotProduct/d;
    }
}


演示
--

本人使用 svg 做了一个演示 demo 出来,使用了 svgjs 库。

https://f-star.github.io/my-test/calAngle/

这里你会发现我这里有个 蓝色的弧线 表示了角度经过的路径,是不是很好玩,其实这个弧的绘制也是通过 **点积公式** 计算出中心点到光标的连线上的一个点的坐标来实现的。有兴趣你可以自己研究一下。

  

**相关文章:**

[图形编辑器——矩形选区是如何实现选中多个图形的?](http://mp.weixin.qq.com/s?__biz=MzI0NTc2NTEyNA==&mid=2247483989&idx=1&sn=ebd008733a7a799369e4af45560c41fd&chksm=e948c73ede3f4e28d3e2e44b36640da4c9589db569840e58eeea738d21088114d1c7781a8ea0&scene=21#wechat_redirect)

[在容器内显示图片的五种方案:contain、cover、fill、none、scale-down](http://mp.weixin.qq.com/s?__biz=MzI0NTc2NTEyNA==&mid=2247483971&idx=1&sn=e67ee2754c2cd19c5538bd43d0067bfc&chksm=e948c728de3f4e3ee9e0f749ad4edb49ff080142c181b184d13b182908fbdb1aa5263329f69b&scene=21#wechat_redirect)  

![](https://img-blog.csdnimg.cn/img_convert/6ad96d2dda185bee5e1b80b452790dde.png)

**前端西瓜哥**

坚持日更原创,分享前端知识。

112篇原创内容

公众号
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,我们可以使用类方法来计算两个向量之间的夹角。下面是一个实现的示例代码: ```python import math class Vector: def __init__(self, x, y): self.x = x self.y = y @classmethod def angle(cls, vec1, vec2): dot_product = vec1.x * vec2.x + vec1.y * vec2.y magnitude1 = math.sqrt(vec1.x ** 2 + vec1.y ** 2) magnitude2 = math.sqrt(vec2.x ** 2 + vec2.y ** 2) if magnitude1 == 0 or magnitude2 == 0: raise ValueError("Vectors cannot have zero magnitude.") angle_in_radians = math.acos(dot_product / (magnitude1 * magnitude2)) angle_in_degrees = math.degrees(angle_in_radians) return angle_in_degrees # 创建两个向量对象 vec1 = Vector(3, 4) vec2 = Vector(5, 2) # 调用类方法计算向量夹角 angle = Vector.angle(vec1, vec2) print(f"向量夹角为:{angle}度") ``` 在代码中,我们首先定义了一个`Vector`类,它包含了两个属性`x`和`y`表示向量的坐标。然后,我们使用一个`@classmethod`装饰器定义了一个类方法`angle`,接收两个向量作为参数。 在类方法中,我们使用点积的公式计算两个向量的点积,并使用欧几里得范数公式计算向量的模。然后,我们使用`math.acos`函数计算出两个向量的夹角的弧度值,并使用`math.degrees`将其转换为角度值。最后,我们返回夹角的角度值。 在主程序中,我们创建了两个向量对象`vec1`和`vec2`,并调用`Vector`类的类方法`angle`来计算向量夹角。最后,打印出向量夹角的结果。 以上就是使用Python类方法计算向量夹角的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值