如何绘制起止点精确的弯曲箭头?优化R语言自定义函数
优化版弯曲箭头函数实现(支持精确起止点与曲率参数)
直接上可以精准控制起止点的arrow_curved()函数,参数逻辑和基础的arrows()完全对齐,不用再手动调椭圆参数:
arrow_curved <- function(x0, y0, x1, y1, curve = 0.2, length = 0.25, angle = 30, col = par("fg"), lty = par("lty"), lwd = par("lwd")) { # 计算起止点中点、距离和垂直方向向量 mid_x <- (x0 + x1) / 2 mid_y <- (y0 + y1) / 2 dx <- x1 - x0 dy <- y1 - y0 dist <- sqrt(dx^2 + dy^2) perp_x <- -dy / dist perp_y <- dx / dist # 基于曲率参数计算椭圆半轴长,确保起止点为椭圆焦点 a <- (dist + 2 * abs(curve) * dist) / 2 b <- sqrt(a^2 - (dist/2)^2) # 根据曲率正负确定椭圆中心偏移方向 offset_x <- mid_x + curve * perp_x * dist offset_y <- mid_y + curve * perp_y * dist # 计算椭圆旋转角度,匹配箭头方向 theta <- atan2(dy, dx) # 生成椭圆上的弧段点集 t_start <- atan2(y0 - offset_y, x0 - offset_x) - theta t_end <- atan2(y1 - offset_y, x1 - offset_x) - theta # 调整角度范围,确保取到正确的弯曲弧段 if (curve > 0) { if (t_end < t_start) t_end <- t_end + 2*pi } else { if (t_end > t_start) t_end <- t_end - 2*pi } t_seq <- seq(t_start, t_end, length.out = 100) x_curve <- offset_x + a * cos(t_seq) * cos(theta) - b * sin(t_seq) * sin(theta) y_curve <- offset_y + a * cos(t_seq) * sin(theta) + b * sin(t_seq) * cos(theta) # 绘制弯曲箭杆 lines(x_curve, y_curve, col = col, lty = lty, lwd = lwd) # 绘制箭头头部,方向匹配曲线切线 arrow_angle <- theta + ifelse(curve > 0, pi/2, -pi/2) x_arrow1 <- x1 + length * cos(arrow_angle + angle * pi/180) y_arrow1 <- y1 + length * sin(arrow_angle + angle * pi/180) x_arrow2 <- x1 + length * cos(arrow_angle - angle * pi/180) y_arrow2 <- y1 + length * sin(arrow_angle - angle * pi/180) lines(c(x1, x_arrow1), c(y1, y_arrow1), col = col, lty = lty, lwd = lwd) lines(c(x1, x_arrow2), c(y1, y_arrow2), col = col, lty = lty, lwd = lwd) }
参数说明
x0, y0/x1, y1: 箭头的起点/终点坐标,和arrows()完全一致curve: 曲率控制,正数向右弯,负数向左弯,绝对值越大弯度越夸张(推荐范围-0.5~0.5)length/angle: 箭头头部的长度和张开角度,和arrows()参数逻辑相同col/lty/lwd: 箭杆和箭头的颜色、线型、线宽,兼容基础绘图参数
用法示例
# 绘制不同方向、曲率的弯曲箭头 plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", main = "弯曲箭头演示") arrow_curved(-1, -1, 1, 1, curve = 0.3, col = "red") arrow_curved(-1, 1, 1, -1, curve = -0.3, col = "blue") arrow_curved(0, -1.5, 0, 1.5, curve = 0.2, col = "green") arrow_curved(-1.5, 0, 1.5, 0, curve = -0.2, col = "purple") # 叠加直线箭头做对比 arrows(-1, -1, 1, 1, col = "gray", lty = 2)
这个函数的核心是把起止点作为椭圆的两个焦点,通过curve参数动态调整椭圆形状,确保曲线严格经过指定的起点和终点,不会出现之前的近似偏移问题,用起来和arrows()一样顺手。
内容的提问来源于stack exchange,提问作者jay.sf
相关产品推荐
相关产品推荐

