给定长短轴及长轴上两点构造椭圆的Python实现方法
实现方案
你最早选的「原点生成标准椭圆→旋转变换对齐长轴→平移到目标位置」思路是可行的,之前运行失败是代码存在未定义变量、变换参数计算错误、逻辑顺序混乱的问题,按以下步骤实现即可:
核心步骤
- 先根据长轴上的两个点计算椭圆基础参数:
- 若两点为长轴端点,椭圆中心为两点连线的中点,长半轴长度为两点间距的1/2
- 长轴的旋转角为两点连线与x轴正方向的夹角,用
math.atan2(y差, x差)直接计算
- 在坐标原点生成轴对齐的标准椭圆点集(长轴沿x轴,中心在原点),支持生成轮廓点或椭圆内部均匀随机点
- 对所有标准椭圆的点,先按计算出的旋转角绕原点旋转对齐长轴方向,再平移到实际椭圆中心位置,即可得到最终椭圆点列表
原有代码问题
random_point函数返回值使用了未定义的third、ward变量,属于低级语法错误- 旋转角计算逻辑错误,没有基于两个长轴点的连线方向推导角度
- 没有正确计算椭圆实际中心,平移变换逻辑缺失
- 长短半轴判断逻辑和长轴方向解绑,导致旋转后轴方向错位
可运行代码
import numpy as np import matplotlib.pyplot as plt import math from math import sin, cos def rotate(origin, point, angle): """绕指定原点逆时针旋转点,角度单位为弧度""" ox, oy = origin px, py = point qx = ox + cos(angle) * (px - ox) - sin(angle) * (py - oy) qy = oy + sin(angle) * (px - ox) + cos(angle) * (py - oy) return (qx, qy) def build_ellipse(p1, p2, b_short, mode="contour", point_num=200): """ 沿两点确定的长轴构造椭圆 :param p1: 长轴第一个端点,(x,y)格式元组 :param p2: 长轴第二个端点,(x,y)格式元组 :param b_short: 短半轴长度 :param mode: contour返回椭圆轮廓点,random返回椭圆内均匀分布随机点 :param point_num: 返回点的总数量 :return: 椭圆点列表,元素为(x,y)格式元组 """ p1_arr = np.array(p1) p2_arr = np.array(p2) # 计算椭圆基础参数 center = (p1_arr + p2_arr) / 2 a_long = np.linalg.norm(p2_arr - p1_arr) / 2 rot_angle = math.atan2(p2_arr[1] - p1_arr[1], p2_arr[0] - p1_arr[0]) points = [] if mode == "contour": # 生成椭圆轮廓点 t_list = np.linspace(0, 2 * np.pi, point_num) for t in t_list: x_local = a_long * cos(t) y_local = b_short * sin(t) # 坐标变换:旋转+平移 x_g, y_g = rotate((0, 0), (x_local, y_local), rot_angle) points.append((x_g + center[0], y_g + center[1])) elif mode == "random": # 生成椭圆内均匀随机点 for _ in range(point_num): # 均匀采样极角 u = np.random.random() / 4.0 theta = np.arctan(b_short / a_long * np.tan(2 * np.pi * u)) v = np.random.random() if v < 0.25: t = theta elif v < 0.5: t = np.pi - theta elif v < 0.75: t = np.pi + theta else: t = -theta # 按面积均匀分布采样半径 max_r = a_long * b_short / np.sqrt((b_short * cos(t))**2 + (a_long * sin(t))**2) r = max_r * np.sqrt(np.random.random()) x_local = r * cos(t) y_local = r * sin(t) # 坐标变换 x_g, y_g = rotate((0, 0), (x_local, y_local), rot_angle) points.append((x_g + center[0], y_g + center[1])) return points # 测试用例 if __name__ == "__main__": # 长轴端点设为(0,0)和(200,150),短半轴长度25 p_a = (0, 0) p_b = (200, 150) short_b = 25 contour_pts = np.array(build_ellipse(p_a, p_b, short_b, mode="contour", point_num=1000)) rand_pts = np.array(build_ellipse(p_a, p_b, short_b, mode="random", point_num=200)) # 可视化结果 plt.scatter(rand_pts[:,0], rand_pts[:,1], s=10, c='#1f77b4', label='椭圆内随机点') plt.plot(contour_pts[:,0], contour_pts[:,1], c='#ff4b00', linewidth=2, label='椭圆轮廓') plt.scatter([p_a[0], p_b[0]], [p_a[1], p_b[1]], c='black', s=60, label='长轴端点') plt.axis('equal') plt.legend() plt.show()
自定义调整说明
如果你的使用场景中,给定的两个点不是长轴端点、只是长轴上的任意点,只需要修改build_ellipse函数里中心center和长半轴a_long的计算逻辑即可,旋转角计算、点坐标变换的部分不需要改动。
内容的提问来源于stack exchange,提问作者Charles Moore
相关产品推荐
相关产品推荐

