下图说明了我想要实现的目标:
基本上我想创建两个Path
相互“接触”的对象(平行路径)。这是用于生成此图像的 XAML:
<StackPanel Orientation="Horizontal">
<StackPanel.LayoutTransform>
<ScaleTransform CenterX="0" CenterY="0" ScaleX="15" ScaleY="15" />
</StackPanel.LayoutTransform>
<Grid Margin="-5,0,0,0">
<Path Stroke="Blue">
<Path.Data>
<PathGeometry>M10,10 C20,10 10,20 20,20</PathGeometry>
</Path.Data>
</Path>
<Path Stroke="Red">
<Path.Data>
<PathGeometry>M10,11 C19,10.85 9,20.80 20,21</PathGeometry>
</Path.Data>
</Path>
</Grid>
<Grid Margin="-5,0,0,0">
<Path Stroke="Blue">
<Path.Data>
<PathGeometry>M10,10 C20,10 10,20 20,20</PathGeometry>
</Path.Data>
</Path>
<Path Stroke="Red">
<Path.Data>
<PathGeometry>M10,11 C19,11 9,21 20,21</PathGeometry>
</Path.Data>
</Path>
</Grid>
</StackPanel>
第一条曲线具有手动优化的点位置,第二条曲线具有通过考虑笔划粗细轻松计算的点位置。你可以看到第二条曲线并不完美,因为两者之间有一个空间。如何以编程方式创建两条完美“接触”的曲线,而无需手动优化每条曲线(这实际上是不可能的,因为曲线是在代码中生成的)?
简单地说,我在代码中生成了一条曲线(resp. Path
),我需要它有两种颜色。所以我认为使第二个平行Path
可以解决问题,但调整Geometry
第二个Path
(使其平行)已被证明是有问题的。
更新#1
Charles Petzold的平行线和曲线可能是解决这个问题的一种方法。它实际上工作得很好,但它会使曲线变平,当深度缩放时会产生视觉伪影,当然还有性能缺陷。
但是,该算法不会尝试找到与另一条贝塞尔曲线平行的贝塞尔曲线。该算法完全基于折线:输入是一条或多条折线,输出由每条输入折线的多条折线组成。出于这个原因,ParallelPath 需要“展平”输入几何——这意味着将整个几何(包括圆弧和贝塞尔曲线)转换为折线近似。
更新#2
所以我的一个朋友(数学博士 inceptor)分析了这个问题,并为(三阶)贝塞尔曲线创建平行曲线非常复杂且计算量大。对于平行曲线的每个点,计算机必须计算如下:
(degree 3 polynomial) + (degree 2 polynomial) / sqrt(degree 4 polynomial)
也许有一种方法可以优化这个表达式,但它仍然比标准的贝塞尔曲线更昂贵(因为平行曲线与原始贝塞尔曲线完全不同)。我希望能够为曲线设置动画,因此这种解决方案可能会占用过多的 CPU 成本。这给我们留下了几个选择:
使用Charles Petzold的折线近似,效果很好,但深度放大时会出现视觉故障。
根据 Charles Petzond 的近似值推导出我们自己的近似值。使用贝塞尔曲线而不是直线(也许弧线就足够了)。这将解决深度缩放问题,但可能很难编码(我不知道如何做到这一点)。
也许可以创建类似双色画笔的东西。这样,我们可以只使用一个
Path
来达到预期的结果(如第一张图所示)。虽然我没有在任何地方看到它,所以这可能不是一个选择。
更新#3
我发现了一些非常有趣的链接:
- 如何抵消三次贝塞尔曲线? (启发式算法)
- 三次贝塞尔曲线描边轮廓
- 贝塞尔路径加宽(Python算法)
- 偏移 Bézier 曲线(自定义折线 Bézier 曲线近似的 Java 实现;实现与 Charles Petzold 的不同)
- 平行贝塞尔曲线(为什么从数学角度看平行贝塞尔曲线是不可能的)
- 三次贝塞尔路径和偏移曲线纸的快速、精确展平(下载链接 1, 下载链接 2)
更多信息:
- Qt 框架中的 QPainterPathStroker 应该使用 Thomas F. Hain 的平行曲线算法
- 这个Java Stroker也应该能够绘制平行曲线
也许最终的解决方案?(来源在这里)
...我计算出了我对贝塞尔曲线理论的所有了解,并将不平坦的偏移发展为正确的东西,并且(怪物)在贝塞尔曲线的入门中记录了这一点
尝试#1
使第二条路径更宽一些,并在使用 Z-Index 时将其滑到第一条路径下方。 http://i51.tinypic.com/2r5vwjk.png
这行不通,Geometry
必须进行相应的转换。