12

下图说明了我想要实现的目标:

基本上我想创建两个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 成本。这给我们留下了几个选择:

  1. 使用Charles Petzold的折线近似,效果很好,但深度放大时会出现视觉故障。

  2. 根据 Charles Petzond 的近似值推导出我们自己的近似值。使用贝塞尔曲线而不是直线(也许弧线就足够了)。这将解决深度缩放问题,但可能很难编码(我不知道如何做到这一点)。

  3. 也许可以创建类似双色画笔的东西。这样,我们可以只使用一个Path来达到预期的结果(如第一张图所示)。虽然我没有在任何地方看到它,所以这可能不是一个选择。

更新#3

我发现了一些非常有趣的链接:

更多信息:

  • Qt 框架中的 QPainterPathStroker 应该使用 Thomas F. Hain 的平行曲线算法
  • 这个Java Stroker也应该能够绘制平行曲线

也许最终的解决方案?(来源在这里

...我计算出了我对贝塞尔曲线理论的所有了解,并将不平坦的偏移发展为正确的东西,并且(怪物)在贝塞尔曲线的入门中记录了这一点


尝试#1

使第二条路径更宽一些,并在使用 Z-Index 时将其滑到第一条路径下方。 http://i51.tinypic.com/2r5vwjk.png

这行不通,Geometry必须进行相应的转换。

4

6 回答 6

6

你为什么不使用一个四次贝塞尔曲线,而不是使用两个二次曲线的复合?你熟悉贝塞尔曲线数学吗?它们在图形中受到青睐,因为它们在计算上非常便宜。我最近创建了一个程序,在其中动画细胞运动(只是为了好玩):

在此处输入图像描述

该程序可以轻松地在高清监视器上全屏运行,其中包含 100 个动画和移动的 blob。这都是 GDI+。

至于平行贝塞尔曲线,根据维基百科,它真的不能做到:http ://en.wikipedia.org/wiki/B%C3%A9zier_curve

因此,您可能必须对启发式方法感到满意。

编辑1:

为了避免你的曲线是完全随机的,为什么不创建每条曲线的轮廓然后填充路径呢?一条路径的“底部”曲线将是另一条路径的“顶部”曲线。

编辑2:

好的,根据要求,这是我设想可以计算“铁路轨道”解决方案的方式:

在此处输入图像描述

于 2011-04-12T01:05:26.703 回答
4

你说你想创建两个相互接触的 Path 对象,但你没有说明路径是如何生成的。我的回答会假设你已经有一个由某种算法生成的路径,并且你想把它变成两条新的路径。

我会从尝试使用笔触切换到使用填充。如果您可以在第二张图片中自动创建红色路径,您可以同样创建由两者组成的连接路径,然后填充它而不是用笔画绘制它。然后你在两个方向上做同样的事情。

我为您的示例得到的结果如下所示:

显示连接路径的图像

<StackPanel Orientation="Horizontal">
    <StackPanel.LayoutTransform>
        <ScaleTransform CenterX="0" CenterY="0" ScaleX="15" ScaleY="15" />
    </StackPanel.LayoutTransform>

    <Grid Margin="-5,0,0,0">
        <Path Fill="Blue" Stroke="Transparent">
            <Path.Data>
                <PathGeometry>M10,10 C20,10 10,20 20,20 L20,19 C11,19 21,9 10,9</PathGeometry>
                <!--          |←    original path    →| |←  generated part   →| -->
            </Path.Data>
        </Path>
        <Path Fill="Red" Stroke="Transparent">
            <Path.Data>
                <PathGeometry>M10,10 C20,10 10,20 20,20 L20,21 C9,21 19,11 10,11</PathGeometry>
                <!--          |←    original path    →| |←   generated part   →| -->
            </Path.Data>
        </Path>
    </Grid>
</StackPanel>
于 2011-04-09T21:05:51.377 回答
1

考虑一个稍微不同的方法来解决这个问题......

假设几何上有“大量”点。通过在较低缩放级别对几何点进行采样来使用更高质量的插值方法之一可能是可行的。随着缩放的增加,您可以增加采样率,而只渲染曲线的子集;这样,计算量应该在所有缩放级别上保持相对恒定。关键是屏幕上有恒定数量的像素,一旦准确度超过某个阈值,您就可以开始采样点。

于 2011-04-12T03:08:28.523 回答
1

阅读此... Silverlight - 史诗图形失败(由两个三角形组成的矩形):(

更新

试试这个(有点矫枉过正,但它可能会帮助你)

<Grid Margin="-5,0,0,0">
    <Path Stroke="Blue" Data="M10,10 C20,10 10,20 20,20 M20,21 C9,21 19,11 10,11"
            Fill="Blue" Clip="M10,10 C20,10 10,20 20,20 L20,21 C9,21 19,11 10,11"/>
    <Path Stroke="Blue" Data="M10,10 C20,10 10,20 20,20"/>
    <Path Stroke="Red" Data="M10,11 C19,11 9,21 20,21"/>
</Grid>

在此处输入图像描述

于 2011-04-13T11:46:00.417 回答
0

假设您需要两条宽度为 5 的线:

如果一个像素的差异不是太大,您可以先用红色绘制一条宽度为 11 的曲线,然后用蓝色绘制一条宽度为 1 的相同路径的曲线,然后用蓝色填充其中一侧。

您会将线分成两半并着色一半,问题是分成两半需要一个像素:(

但是如果你选择一个像 10 这样的均匀宽度会发生什么?中间像素在哪里?也许你可以利用一些东西......

于 2011-04-13T01:59:46.210 回答
0

是否可以使第二条路径(生成的)更宽一些,并使用 z-index 将其滑到第一条路径的下方(后面)?这样,您将获得无缝加入。

于 2011-04-07T22:14:04.723 回答