如何用ggplot的geom_bezier2实现带渐变且末端仅单个箭头的曲线?
问题描述
我想用ggplot()绘制一条带颜色渐变且末端只有单个箭头的曲线:
- 使用
geom_curve能得到合适的曲线和美观的单个箭头,但无法添加颜色渐变; - 使用
geom_bezier2能实现颜色渐变和曲线效果,但箭头看起来像是多个叠加在一起。
请问有没有办法让geom_bezier2生成的曲线拥有geom_curve那样的单个箭头效果?
测试代码
# 箭头正确但无颜色渐变的曲线 ggplot() + geom_curve( aes(x = -75, y = 42.5, xend = -85, yend = 30.5, size=3), curvature = -0.2, lineend = "round", arrow = arrow(length = unit(0.3, "cm"))) # 贝塞尔曲线数据框 bezArrow <- data.frame( x = c(-75, -77, -85), y = c(42.5, 35, 30.5), color = c(3, 2, 1)) # 有颜色渐变但箭头效果不佳的曲线 ggplot() + geom_bezier2(data = bezArrow, aes(x = x, y = y, color = color, linewidth = 3), lineend = 'round', arrow = arrow(length = unit(0.3, "cm"), ends = 'last')) + scale_color_gradient(high = 'grey', low = 'black')
效果示意图


解决方案
问题根源是geom_bezier2的箭头逻辑:它会给贝塞尔曲线的每个分段都添加箭头,因此出现叠加效果。解决思路是拆分绘制步骤:让geom_bezier2负责渐变曲线,用其他图层单独添加单个箭头。
方法1:用geom_curve快速叠加箭头
直接在渐变贝塞尔曲线上,叠加一条同路径的"隐形"曲线,只保留箭头:
library(ggplot2) library(ggforce) bezArrow <- data.frame( x = c(-75, -77, -85), y = c(42.5, 35, 30.5), color = c(3, 2, 1)) ggplot() + # 绘制带渐变的贝塞尔曲线,不添加箭头 geom_bezier2(data = bezArrow, aes(x = x, y = y, color = color, linewidth = 3), lineend = 'round') + scale_color_gradient(high = 'grey', low = 'black') + # 叠加同路径的曲线,隐藏线条只显示箭头 geom_curve( aes(x = -75, y = 42.5, xend = -85, yend = 30.5), curvature = -0.2, linewidth = 0, # 隐藏曲线本体 arrow = arrow(length = unit(0.3, "cm"), type = "closed") # 单个箭头 )
方法2:精准计算切线后用geom_segment加箭头
如果需要箭头完全贴合曲线末端的方向,可先计算贝塞尔曲线的末端切线,再用geom_segment添加箭头:
library(ggplot2) library(ggforce) library(bezier) bezArrow <- data.frame( x = c(-75, -77, -85), y = c(42.5, 35, 30.5), color = c(3, 2, 1)) # 生成贝塞尔曲线上的密集点,用于计算末端切线 bez_points <- bezier::bezier(t = seq(0, 1, length.out = 100), p = as.matrix(bezArrow[,1:2])) # 取末端点和前一个点,计算箭头起始位置(避免和曲线重叠) end_point <- tail(bez_points, 1) prev_point <- tail(bez_points, 2)[1,] arrow_start <- end_point - 0.1*(end_point - prev_point) ggplot() + geom_bezier2(data = bezArrow, aes(x = x, y = y, color = color, linewidth = 3), lineend = 'round') + scale_color_gradient(high = 'grey', low = 'black') + # 添加匹配曲线宽度的单个箭头 geom_segment( aes(x = arrow_start[1], y = arrow_start[2], xend = end_point[1], yend = end_point[2]), linewidth = 3, arrow = arrow(length = unit(0.3, "cm"), type = "closed"), color = "black" # 和曲线末端颜色保持一致 )
内容的提问来源于stack exchange,提问作者Vanessa Niemczyk
相关产品推荐
相关产品推荐

