如何实现Python参数化代码以自动完成几何图形对齐?
几何图形对齐功能优化:修复除零错误并实现自动判断
需求说明
- 实现几何图形对齐功能:当图形存在旋转时,将其主坐标轴与参考系XY轴平行(旋转角度归零);无旋转则不执行操作。
- 现有代码可处理旋转后的图形,但在处理无旋转的坐标集时出现
division by zero错误。
测试坐标集
旋转坐标集
X2_coordinate = [0.0, 0.87, 1.37, 1.87, 2.73, 3.6, 4.46, 4.96, 5.46, 4.6, 3.73, 2.87, 2.0, 1.5, 1.0, 0.5, 2.37, 3.23, 4.1] Y2_coordinate = [0.0, 0.5, -0.37, -1.23, -0.73, -0.23, 0.27, -0.6, -1.46, -1.96, -2.46, -2.96, -3.46, -2.6, -1.73, -0.87, -2.1, -1.6, -1.1]
无旋转坐标集
X2_coordinate = [0, 0, 1, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 3, 2, 1, 3, 3, 3] Y2_coordinate = [0, 1, 1, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 1, 2, 3]
现有代码核心问题
- 除零错误:计算线段角度时使用
arctan((p1.x - p2.x)/(p1.y - p2.y)),当两点Y坐标相等时,分母为0触发异常。 - 无自动判断:无论图形是否旋转,都会执行旋转操作,无意义且可能引入误差。
- 耦合度高:代码未封装为可复用的函数,无法灵活调用。
优化后的代码
from sbNative.debugtools import log, cleanRepr import math import matplotlib.pyplot as plt def rotate(origin, point, angle): """将点绕原点逆时针旋转指定弧度角度""" qx = origin.x + math.cos(angle) * (point.x - origin.x) - math.sin(angle) * (point.y - origin.y) qy = origin.y + math.sin(angle) * (point.x - origin.x) + math.cos(angle) * (point.y - origin.y) return qx, qy @cleanRepr() class Point: def __init__(self, *pts): pt_names = "xyzw" for n, v in zip(pt_names[:len(pts)], pts): self.__setattr__(n, v) def align_geometry(X_coords, Y_coords, angle_tolerance=0.1): """ 对齐几何图形:自动判断是否需要旋转,将主坐标轴与XY轴平行 参数: X_coords: X坐标列表 Y_coords: Y坐标列表 angle_tolerance: 角度容差(弧度),判断是否接近坐标轴方向 返回: 对齐后的X、Y坐标列表 """ coordinates = list(zip(X_coords, Y_coords)) points = [Point(*coors) for coors in coordinates] point_pairs = [(points[i], points[i+1]) for i in range(len(points)-1)] + [(points[-1], points[0])] # 使用arctan2避免除零错误,计算每条线段的绝对角度(归一化到[0, π/2]) angles = [] for p1, p2 in point_pairs: dx = p2.x - p1.x dy = p2.y - p1.y angle = abs(math.atan2(dy, dx)) # 将角度归一到0到π/2区间,统一处理水平/垂直方向的等价性 if angle > math.pi/2: angle = math.pi - angle angles.append(angle) # 按容差分组统计角度频率,避免浮点精度干扰 angle_counts = {} for angle in angles: rounded_angle = round(angle / angle_tolerance) * angle_tolerance angle_counts[rounded_angle] = angle_counts.get(rounded_angle, 0) + 1 if not angle_counts: return X_coords, Y_coords # 获取出现频率最高的主方向角度 main_angle = max(angle_counts, key=angle_counts.get) # 判断是否需要旋转:主角度是否远离坐标轴方向 need_rotation = not ( abs(main_angle) < angle_tolerance or abs(main_angle - math.pi/2) < angle_tolerance ) if not need_rotation: log("图形已对齐,无需旋转") return X_coords, Y_coords # 计算最优旋转角度:选择最小幅度将主方向对齐到坐标轴 rotation_angle = -main_angle if main_angle < math.pi/4 else math.pi/2 - main_angle # 执行旋转操作 origin = Point(0, 0) new_points = [rotate(origin, p, rotation_angle) for p in points] X_aligned, Y_aligned = zip(*new_points) # 保留一位小数,统一输出精度 X_aligned = [round(num, 1) for num in X_aligned] Y_aligned = [round(num, 1) for num in Y_aligned] log(f"完成图形对齐,旋转角度:{math.degrees(rotation_angle):.1f}度") return X_aligned, Y_aligned # 示例调用 if __name__ == "__main__": # 测试旋转坐标集 X_rot = [0.0, 0.87, 1.37, 1.87, 2.73, 3.6, 4.46, 4.96, 5.46, 4.6, 3.73, 2.87, 2.0, 1.5, 1.0, 0.5, 2.37, 3.23, 4.1] Y_rot = [0.0, 0.5, -0.37, -1.23, -0.73, -0.23, 0.27, -0.6, -1.46, -1.96, -2.46, -2.96, -3.46, -2.6, -1.73, -0.87, -2.1, -1.6, -1.1] X_rot_aligned, Y_rot_aligned = align_geometry(X_rot, Y_rot) plt.scatter(X_rot_aligned, Y_rot_aligned) plt.title("旋转图形对齐结果") plt.show() # 测试无旋转坐标集 X_no_rot = [0, 0, 1, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 3, 2, 1, 3, 3, 3] Y_no_rot = [0, 1, 1, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 1, 2, 3] X_no_rot_aligned, Y_no_rot_aligned = align_geometry(X_no_rot, Y_no_rot) plt.scatter(X_no_rot_aligned, Y_no_rot_aligned) plt.title("无旋转图形结果") plt.show()
关键优化细节
- 修复除零错误:用
math.atan2(dy, dx)替代除法式的角度计算,直接支持水平/垂直线段的角度求解。 - 自动旋转判断:通过对比主方向角度与坐标轴的偏差,自动跳过无需旋转的场景。
- 参数化封装:将对齐逻辑封装为独立函数,支持自定义角度容差,提升复用性。
- 浮点精度处理:按容差对角度分组统计,避免因浮点误差导致角度统计偏差。
- 最优旋转策略:选择最小旋转幅度将主方向对齐到坐标轴,减少不必要的计算误差。
内容的提问来源于stack exchange,提问作者Urvesh
相关产品推荐
相关产品推荐

