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

单向偏移折线问题续:消除拐角尖刺的实现方案

单向偏移折线(自相交场景优化需求)

需实现折线的单向偏移,满足以下严格要求:

  • 仅向单侧偏移,可通过调整距离符号切换方向;
  • 支持自相交折线偏移,且不合并交叉点;
  • 拐角需平滑无异常尖刺,最好可指定linejoin样式;
  • 保留点顺序,偏移线首尾坐标需与原折线对应。

尝试过的方法及问题

1. sf::st_buffer

该方法会产生异常尖刺且合并交叉点:

# 创建带尖锐拐角的自相交图形
t <- seq(0, 2 * pi, length.out = 360)

x <- c(0.25, sin(t) + seq(0, 2, length.out = 360), 1.75)
y <- c(2, cos(t), 2)

plot(x, y, type = 'l')

# 生成偏移线,但存在异常尖刺且交叉点被合并
plot(sf::st_buffer(
  sf::st_linestring(cbind(x, y)),
  dist = 0.1, singleSide = TRUE
))
lines(x, y, col = 2)

2. geos::geos_offset_curve

该方法对非自相交折线效果理想,但无法处理自相交情况:

# 非自相交图形表现完美,但自相交场景失效
plot(geos::geos_offset_curve(
  geos::geos_make_linestring(x, y),
  distance = 0.1
))
lines(x, y, col = 2)

3. polyclip::polylineoffset

该方法无尖刺、拐角平滑,但会合并交叉点且点顺序保留不佳:

# 无异常尖刺,拐角平滑,但交叉点被合并且点顺序丢失
pc <- polyclip::polylineoffset(list(x = x, y = y), 0.1, endtype = "openbutt")
plot(x, y, type = 'l', col = 2)
for (i in pc) {
  lines(i)
}

4. geomtextpath:::.get_offset

该方法接近需求,但拐角存在尖刺:

# 效果接近需求,但拐角处有尖刺
plot(geomtextpath:::.get_offset(x, y, d = 0.1), type = 'l')
lines(x, y, col = 2)

自定义实现:offset_round函数

基于geomtextpath:::.get_offset思路实现了圆角连接,但仍存在拐角尖刺问题:

offset_round <- function(x, y, dist, min_arc = 0.1) {
  
  start <- 1
  end   <- length(x)
  se    <- c(start, end)
  
  theta <- atan2(diff(y), diff(x)) + pi / 2
  
  # 补全首尾点的角度
  before <- c(NA, theta)
  after  <- c(theta, NA)
  before[start] <- before[start + 1]
  after[end]    <- after[end - 1]
  
  # 计算角平分线及对应长度
  bisector  <- (before + after) / 2
  bi_length <- dist / cos(bisector - after)
  
  # 根据角度差计算分段数
  delta <- (after - before) %% (2 * pi)
  n_segs <- if (sign(dist) == 1) {
    pmax(min_arc, delta - pi) %/% min_arc
  } else {
    pmin(-min_arc, delta - pi) %/% -min_arc
  }
  n_segs[se] <- 1
  
  # 扩展分段点
  idx <- rep.int(seq_along(n_segs), n_segs)
  xnew <- x[idx]
  ynew <- y[idx]
  
  # 计算圆角拐角的角度
  new_delta <- if (sign(dist) == 1) delta - 2 * pi else delta
  angle <- unlist(
    Map(seq, 0, new_delta, length.out = n_segs)
  ) + before[idx]
  
  # 单分段拐角使用角平分线角度
  singles <- n_segs == 1
  angle[singles[idx]] <- bisector[singles]
  
  # 设置对应长度
  dist  <- rep_len(dist, end)
  len <- dist[idx]
  len[singles[idx]] <- bi_length[singles]
  
  # 应用偏移变换
  xnew <- xnew + cos(angle) * len
  ynew <- ynew + sin(angle) * len
 
  list(
    x = xnew,
    y = ynew
  ) 
}

调用示例

t <- seq(0, 2 * pi, length.out = 360)

x <- c(0.25, sin(t) + seq(0, 2, length.out = 360), 1.75)
y <- c(2, cos(t), 2)

plot(x, y, type = 'l', xlim = c(-0.1, 2.1))
lines(offset_round(x, y, 0.1), col = 2)
lines(offset_round(x, y, -0.1), col = 3)

当前问题:生成的折线仍存在拐角尖刺,需要找到消除尖刺的有效方法。


内容的提问来源于stack exchange,提问作者teunbrand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 14:45:34