画图中的弧度与角度转换(Math.atan与Math.atan2)

本文介绍了一种在JavaScript中计算两点间直线倾斜角的方法。通过使用Math.atan2()函数代替Math.atan(),解决了角度计算的周期性和特殊角度问题,适用于游戏开发等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做一个连线游戏时,用到了计算两点之间的角度,如何计算任意两点间直线的倾斜角呢?只需要将两点x,y坐标分别相减得到一个新的x,y轴距离(x2-x1,y2-y1).然后利用它求出角度就可以了。

1、js中Math.atan()
Math.atan()接受一个参数:用法如下:
angel=Math.atan(slope) 
angel为一个角度的弧度值,slope为直线的斜率,是一个数字,这个数字可以是负的无穷大到正无穷大之间的任何一个值即y/x.
不过,利用他进行计算比较复杂.因为它的周期性,一个数字的反正切值不止一个.例如atan(-1)的值可能是45度,也可能是225度.这样就是他的周期性,对于正切函数来说,他的周期是180度,所以两个相差180度的角具有相同的正切和斜率:
tanθ=tan(θ+180)
然而,Math.atan()只能返回一个角度值,因此确定他的角度非常的复杂,而且,90度和270度的正切是无穷大,因为除数为零,我们也是比较难以处理的~!因此我放弃使用该函数.

2、Math.atan2()
Math.atan2()接受两个参数x和y,方法如下:
angel=Math.atan2(y,x)
x 指定点的 x 坐标的数字。
y 指定点的 y 坐标的数字。
计算出来的结果angel是一个弧度值,也可以表示相对直角三角形对角的角,其中 x 是临边边长,而 y 是对边边长。 

例如:
x=Math.atan(1)//计算正切值为1的数字对应的弧度值0.785398163397448
x=180*x/Math.PI//转换为角度值45deg

回归到游戏中,我计算出终点与起始点的y轴间距与x轴间距,代入到公式中得到一个弧度,然后*180/Math.PI得到角度,至此大功告成!!

``` import numpy as np from math import cos, sin, atan2, sqrt, pi import math # 定义D-H参数示例(需根据实际参数修改) dh_params = [ {'a': 0, 'd': 0.1, 'alpha': pi / 2}, # 关节1 {'a': 0.5, 'd': 0, 'alpha': 0}, # 关节2 {'a': 0.5, 'd': 0, 'alpha': 0}, # 关节3 {'a': 0.3, 'd': 0, 'alpha': pi / 2}, # 关节4 {'a': 0, 'd': 0, 'alpha': 0} # 关节5 ] cam_offset = np.array([0, -0.05, 0]) # 摄像头偏移量 R_cam_in_j5 = np.eye(3) # 假设摄像头J5坐标系对齐 def camera_to_j5(target_cam): """将摄像头坐标系下的目标转换到关节5坐标系""" target_j5 = R_cam_in_j5.T @ (target_cam - cam_offset) return target_j5 def inverse_kinematics(target_pos, target_orient): # 坐标转换 target_j5 = camera_to_j5(target_pos) # 前三个关节求解(位置解) x, y, z = target_j5 # 关节1角度计算 theta1 = atan2(y, x) # 关节2-3几何解 L = sqrt(x ** 2 + y ** 2) - dh_params[0]['d'] D = (L ** 2 + z ** 2 - dh_params[1]['a'] ** 2 - dh_params[2]['a'] ** 2) / ( 2 * dh_params[1]['a'] * dh_params[2]['a']) theta3 = atan2(-sqrt(1 - D ** 2), D) # 关节2角度计算 theta2 = atan2(z, L) - atan2(dh_params[2]['a'] * sin(theta3), dh_params[1]['a'] + dh_params[2]['a'] * cos(theta3)) # 后两个关节求解(姿态解) # 计算工具坐标系到基坐标系的齐次变换矩阵 T = forward_kinematics([theta1, theta2, theta3, 0, 0]) # 需要正运动学实现 R_base_tool = T[:3, :3] # 计算目标姿态当前姿态的旋转差 R_error = target_orient @ R_base_tool.T theta4, theta5, _ = extract_euler_angles(R_error) # 需要欧拉角提取实现 return [theta1, theta2, theta3, theta4, theta5] def forward_kinematics(theta): """正运动学计算(需完整实现)""" T = np.eye(4) for i in range(5): a = dh_params[i]['a'] d = dh_params[i]['d'] alpha = dh_params[i]['alpha'] T_i = np.array([ [cos(theta[i]), -sin(theta[i]) * cos(alpha), sin(theta[i]) * sin(alpha), a * cos(theta[i])], [sin(theta[i]), cos(theta[i]) * cos(alpha), -cos(theta[i]) * sin(alpha), a * sin(theta[i])], [0, sin(alpha), cos(alpha), d], [0, 0, 0, 1] ]) T = T @ T_i return T def extract_euler_angles(rotation_matrix): """ 从3x3旋转矩阵提取ZYX顺序的欧拉角(单位:度) 参数格式:列主序矩阵(例如OpenGL) """ m = rotation_matrix theta_y = math.asin(-m[2][0]) # 计算绕Y轴旋转角度 # 避免万向锁 if abs(math.cos(theta_y)) > 1e-6: theta_z = math.atan2(m[1][0], m[0][0]) # 绕Z轴角度 theta_x = math.atan2(m[2][1], m[2][2]) # 绕X轴角度 else: theta_z = 0.0 theta_x = math.atan2(-m[0][1], m[1][1]) # 转换角度并返回 return ( math.degrees(theta_x), math.degrees(theta_y), math.degrees(theta_z) ) # 示例用法 matrix = [ [0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0] ] print(extract_euler_angles(matrix)) # 输出应接近 (0.0, 0.0, 90.0) # 示例使用 target_cam = np.array([0.7, 0.2, 0.4]) # 摄像头坐标系下的目标位置 target_orient = np.eye(3) # 目标姿态(需根据实际情况设置) solutions = inverse_kinematics(target_cam, target_orient) print("关节角度解:", solutions)```解释代码,给出数学原理相关尺寸单位
03-11
import math import matplotlib.pyplot as plt # 颜色枚举 class Color: c = 0 d = 1 f = 2 # 透镜表面 class Face: def __init__(self, radius, distance, n, v): self.radius = radius # 曲率半径 self.distance = distance # 到下一个表面的距离 self.n = n # d光折射率 self.v = v # 阿贝数 # 光线 class Ray: def __init__(self, y, angle_deg, color): self.y = y # 光轴交点y self.angle = angle_deg # 倾角(度) self.color = color # c/d/f光 # 折射率色散修正 def get_dispersion_n(nd, vd, color): if color == Color.d: return nd elif color == Color.c: return nd + (nd - 1) / vd * 0.0081 # c光 elif color == Color.f: return nd - (nd - 1) / vd * 0.0049 # f光 else: return nd # 单面追迹,返回新Ray实际传播路径终点(x, y) def trace_surface(ray, face, next_face, x0): n1 = get_dispersion_n(face.n, face.v, ray.color) n2 = get_dispersion_n(next_face.n, next_face.v, ray.color) # 入射角(相对于法线) if face.radius == 0: normal_angle = 0 i1 = math.radians(ray.angle) center_sign = 1 else: # 法线方向:球心在正方向为正,负方向为负 center_sign = 1 if face.radius > 0 else -1 # 球心到交点的连线光轴的夹角 normal_angle = math.asin(ray.y / abs(face.radius)) i1 = math.radians(ray.angle) - center_sign * normal_angle # 折射角 sin_i2 = n1 / n2 * math.sin(i1) if abs(sin_i2) > 1: raise ValueError("全反射发生,无法追迹") i2 = math.asin(sin_i2) # 新光线倾角 new_angle = math.degrees(i2 + center_sign * normal_angle) # 传播到下一个表面 # 以当前角度,计算实际(x, y)终点 theta = math.radians(new_angle) dx = face.distance / math.cos(theta) x1 = x0 + face.distance y1 = ray.y + face.distance * math.tan(theta) return Ray(y1, new_angle, ray.color), x1, y1 # 主追迹函数,记录真实路径 def trace_with_path(ray, faces): x_list = [0] y_list = [ray.y] x = 0 for i in range(len(faces) - 1): face = faces[i] ray, x, y = trace_surface(ray, face, faces[i+1], x) x_list.append(x) y_list.append(y) return ray, x_list, y_list # 示例输入 in_ray = Ray(0, 5, Color.d) faces = [ Face(0, 0, 1.0, 0), # 物面(平面,空气) Face(50, 10, 1.5, 60), # 第1面 Face(-50, 0, 1.0, 0) # 第2面(像面,空气) ] # 追迹并记录路径 out_ray, x_list, y_list = trace_with_path(in_ray, faces) # 输出 print(f"出射光线光轴交点y: {out_ray.y:.4f}") print(f"出射光线倾角: {out_ray.angle:.4f} 度") # 绘图 plt.figure(figsize=(8, 4)) plt.plot(x_list, y_list, marker='o', label='光线路径') for x in x_list: plt.axvline(x, color='gray', linestyle='--', linewidth=0.5) plt.xlabel('光轴方向 (mm)') plt.ylabel('y (mm)') plt.title('光线追迹路径') plt.legend() plt.grid(True) plt.show()这段代码无法正确实现光路的追迹光路绘制,请修改
最新发布
06-25
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值