单向偏移折线问题续:消除拐角尖刺的实现方案
单向偏移折线(自相交场景优化需求)
需实现折线的单向偏移,满足以下严格要求:
- 仅向单侧偏移,可通过调整距离符号切换方向;
- 支持自相交折线偏移,且不合并交叉点;
- 拐角需平滑无异常尖刺,最好可指定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
相关产品推荐
相关产品推荐

