如何处理可变宽度(“缓冲区”)的折线偏移?

Gra*_*ton 6 c# c++ geometry clipper nettopologysuite

我的问题陈述:

我有一条多段线,我想沿着这些点在一个方向上进行可变宽度偏移。我该怎么做?对于折线,我只需要支持直线,不需要支持曲线或圆弧。

多段线可以是闭合的,也可以是开放的,并且偏移仅在一个方向上——为了便于讨论,我们假设它在左侧方向。

在此输入图像描述上图 几乎概括了我想要做的事情;唯一的问题是,它在整个折线中是统一的偏移,因此我想要可变的偏移。

这个问题比乍看起来要棘手得多。有一些库并没有完全做到这一点。让我们一一分析一下。

快艇

Clipper 可以处理多边形缓冲区,这意味着在两个方向上创建偏移线,最后围绕该线形成多边形。所以不适合我的需求。此外,它不处理变量缓冲。

快船队开发者在论坛上对此进行了一些讨论,但遗憾的是没有任何结果。

网络拓扑套件

NetTopologySuite有一个VariableBuffer类,它可以处理可变偏移量。但不幸的是,NetTopologySuite 只能处理多边形缓冲(将一条线转换为包含该线的多边形),而不能处理折线偏移(其中折线在单个方向上偏移)。

另外,似乎使用上述方法 NetTopologySuite 会在两个方向上“炸毁”多边形,并且需要设置BufferParameters.IsSingleSided=true以便具有单边多边形偏移,但目前还不清楚如何将其与 结合使用VariableBuffer

骑士轮廓

Cavalier countours与大多数库不同,它只能在一个方向上进行折线偏移(这就是我想要的),这样就不会形成多边形。这就是我想要的,但不幸的是,它不能进行可变宽度偏移。

如何调整当前的库以满足我的需求?

似乎没有简单的方法可以做到这一点。有什么想法可以做到这一点吗?

欢迎任何基于 C#、C++ 或 C 库构建的解决方案。

Jer*_*tke 1

事实上,对于可变偏移量,问题会更加复杂。在具有单个偏移的情况下,点 p 到折线的距离定义为折线到 p 的最近点的距离(参见https://github.com/jbuckmccready/CavalierContours/blob中的pointValidForOffset示例) /master/include/cavc/polylineoffset.hpp

对可变偏移量的概括并不明显。

对于简单折线(无曲线),一个简单的解决方案是:设F为初始折线。

  • 绘制G,远离变量偏移量的折线(详见下文)
  • 将F的第一个点与G的第一个点连接(创建线段A
  • 将F的最后一个点与G的最后一个点连接(创建线段B
  • 我们得到一个闭合多边形P
  • 为了管理交叉点,删除循环:计算P与其自身的并集,然后保留不是F的轮廓,也不是线段AB

要绘制远离变量 offset 的折线G :如果将代码放在http://paperjs.org/“Sketch ”中,则此代码有效,结果如下: 具有固定偏移量(红色)的多段线和具有可变大小偏移量(绿色)的多段线

/* Computes a polyline with a fixed offset (red) 
 * and another with a variable size offset (green)
 */
    const points = [
        new Point(30, 40),
        new Point(100, 80),
        new Point(200, 60),
        new Point(250, 50),
        new Point(300, 70),
        new Point(350, 90),
        new Point(400, 60),
        new Point(450, 50),
        new Point(500, 70),
        new Point(550, 90),
        ];
    
    let poly1 = new Path();
    poly1.strokeColor = 'black';
    points.forEach(p =>  poly1.add(p));
    
    const fixOffs = 5;
    const offsets = [
        10, 20, 30, 20, 10, 10, 20, 20, 30, 30];
    
    let fix = [];
    let variable = [];
    
    const size = points.length;
    for(let i=0; i<size; ++i){
        let tangent;
        if(i===0){ // if first point
            tangent = points[1] - points[0];
        }else if(i+1===size){ // if last point
            tangent = points[i] - points[i-1];
        }else{ // mean of segment before and segment after point i
            tangent = (points[i] - points[i-1]) * 0.5 +
                      (points[i+1] - points[i]) * 0.5;
        }
        let normal = new Point(-tangent.y, tangent.x); // perpendicular vector
        normal = normal / normal.length; // normalize
        fix.push(points[i] + normal * fixOffs);
        variable.push(points[i] + normal * offsets[i]);
    }
    
    let polyFix = new Path();
    polyFix.strokeColor = 'red';
    fix.forEach(p =>  polyFix.add(p));
    
    let polyVar = new Path();
    polyVar.strokeColor = 'green';
    variable.forEach(p =>  polyVar.add(p));
Run Code Online (Sandbox Code Playgroud)