You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python实现与两条线段相切的圆弧绘制问题求助

问题:2D图形倒角圆弧绘制错误修正

我尝试在两条线段的交点处绘制一段含n个步长的圆弧做倒角,实现思路如下:

  • 从目标顶点(红色点)沿远离线段的方向移动半径距离
  • 获取两条线段的法线
  • 求法线交点确定圆心
  • 从圆心出发绘制两点间的圆弧

当前绘制的圆并未与线段相切,猜测错误在于生成法线的点的移动方式。


错误原因分析

你错误地从顶点沿线段延伸方向移动了半径距离,而正确逻辑是:圆心到两条线段的距离必须等于半径,因此应该将两条线段向角的内侧平移半径长度,平移后的线段交点才是圆心。你的做法相当于移动了顶点而非平移线段,导致法线位置错误,最终圆心偏离正确位置。

正确实现步骤

  1. 计算两条线段的单位方向向量,进而得到指向角内侧的单位法线向量
  2. 将两条线段分别沿各自的内侧法线方向平移半径长度
  3. 计算平移后两条线段的交点,即为圆弧圆心
  4. 计算圆心到原两条线段的切点(圆弧的两个端点)
  5. 根据两个切点对应的角度,生成圆弧的点集

修正后的代码

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()

代码说明

  1. get_unit_normal:根据线段和角度内侧方向返回单位法线向量,确保线段向角内侧平移
  2. translate_line:将线段沿指定方向平移指定距离
  3. get_tangent_point:计算圆心到原线段的切点,作为圆弧的两个端点
  4. 生成圆弧时,根据两个切点的角度范围生成点集,只绘制倒角所需的圆弧部分而非整圆

内容的提问来源于stack exchange,提问作者Dr. Pontchartrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:40:25