Python实现与两条线段相切的圆弧绘制问题求助
问题:2D图形倒角圆弧绘制错误修正
我尝试在两条线段的交点处绘制一段含n个步长的圆弧做倒角,实现思路如下:
- 从目标顶点(红色点)沿远离线段的方向移动半径距离
- 获取两条线段的法线
- 求法线交点确定圆心
- 从圆心出发绘制两点间的圆弧
当前绘制的圆并未与线段相切,猜测错误在于生成法线的点的移动方式。
错误原因分析
你错误地从顶点沿线段延伸方向移动了半径距离,而正确逻辑是:圆心到两条线段的距离必须等于半径,因此应该将两条线段向角的内侧平移半径长度,平移后的线段交点才是圆心。你的做法相当于移动了顶点而非平移线段,导致法线位置错误,最终圆心偏离正确位置。
正确实现步骤
- 计算两条线段的单位方向向量,进而得到指向角内侧的单位法线向量
- 将两条线段分别沿各自的内侧法线方向平移半径长度
- 计算平移后两条线段的交点,即为圆弧圆心
- 计算圆心到原两条线段的切点(圆弧的两个端点)
- 根据两个切点对应的角度,生成圆弧的点集
修正后的代码
import matplotlib.pyplot as plt import numpy as np def plot_line(line, color="go--", label=""): plt.plot([p[0] for p in line], [p[1] for p in line], color, label=label) def line_intersection(line1, line2): xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0]) ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) def det(a, b): return a[0] * b[1] - a[1] * b[0] div = det(xdiff, ydiff) if div == 0: raise Exception('lines do not intersect') d = (det(*line1), det(*line2)) x = det(d, xdiff) / div y = det(d, ydiff) / div return x, y def get_unit_normal(line_segment, is_inner): # 线段方向向量 dx = line_segment[1][0] - line_segment[0][0] dy = line_segment[1][1] - line_segment[0][1] # 单位方向向量 mag = np.sqrt(dx**2 + dy**2) ux, uy = dx/mag, dy/mag # 根据线段组合选择内侧法线方向 if is_inner: if line_segment == line_segment1: return uy, -ux else: return -uy, ux else: return -uy, ux def translate_line(line_segment, dx, dy): # 平移线段 return [[p[0]+dx, p[1]+dy] for p in line_segment] def get_tangent_point(center, line_segment, radius): # 计算圆心到线段的切点 x0, y0 = center x1, y1 = line_segment[0] x2, y2 = line_segment[1] # 线段方向向量 dx = x2 - x1 dy = y2 - y1 # 单位法线向量 nx = (y1 - y2)/np.sqrt(dx**2 + dy**2) ny = (x2 - x1)/np.sqrt(dx**2 + dy**2) # 切点为圆心沿法线方向移动半径长度到线段上的点 tx = x0 - nx*radius ty = y0 - ny*radius return tx, ty # 原始线段 line_segment1 = [[1,1],[4,8]] line_segment2 = [[4,8],[8,8]] line = line_segment1 + line_segment2 plot_line(line, 'k-', label="原始线段") radius = 2 # 获取两条线段的内侧单位法线向量 n1 = get_unit_normal(line_segment1, is_inner=True) n2 = get_unit_normal(line_segment2, is_inner=True) # 平移线段:沿法线方向移动半径长度 translated_line1 = translate_line(line_segment1, n1[0]*radius, n1[1]*radius) translated_line2 = translate_line(line_segment2, n2[0]*radius, n2[1]*radius) plot_line(translated_line1, 'm--', label="平移线段1") plot_line(translated_line2, 'g--', label="平移线段2") # 求平移后线段的交点,即圆心 center = line_intersection(translated_line1, translated_line2) plt.plot(center[0], center[1], 'bo', label="圆心") # 计算两个切点 p_tangent1 = get_tangent_point(center, line_segment1, radius) p_tangent2 = get_tangent_point(center, line_segment2, radius) plt.plot(p_tangent1[0], p_tangent1[1], 'yo', label="切点1") plt.plot(p_tangent2[0], p_tangent2[1], 'co', label="切点2") # 计算圆弧的起始和结束角度 theta1 = np.arctan2(p_tangent1[1] - center[1], p_tangent1[0] - center[0]) theta2 = np.arctan2(p_tangent2[1] - center[1], p_tangent2[0] - center[0]) # 确保角度顺序正确,生成圆弧点 if theta2 < theta1: theta2 += 2*np.pi theta = np.linspace(theta1, theta2, 50) arc_x = center[0] + radius * np.cos(theta) arc_y = center[1] + radius * np.sin(theta) plt.plot(arc_x, arc_y, 'b-', label="倒角圆弧") # 绘制顶点 plt.plot(line_segment1[1][0], line_segment1[1][1], 'ro', label="顶点") plt.legend() plt.axis('square') plt.show()
代码说明
get_unit_normal:根据线段和角度内侧方向返回单位法线向量,确保线段向角内侧平移translate_line:将线段沿指定方向平移指定距离get_tangent_point:计算圆心到原线段的切点,作为圆弧的两个端点- 生成圆弧时,根据两个切点的角度范围生成点集,只绘制倒角所需的圆弧部分而非整圆
内容的提问来源于stack exchange,提问作者Dr. Pontchartrain
相关产品推荐
相关产品推荐

