双色路径对象

发布于 2024-10-30 18:59:22 字数 5110 浏览 1 评论 0原文

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

基本上我想创建两个 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>

第一条曲线具有手动优化的点位置,第二条曲线具有通过考虑笔划粗细轻松计算的点位置。您可以看到第二条曲线并不完美,因为两条曲线之间有一个空间。如何以编程方式创建两条完美的“接触”曲线,而不需要手动优化每条曲线(这实际上是不可能的,因为曲线是在代码中生成的)?

简而言之,我在代码中生成一条曲线(分别是Path),并且我需要它有两种颜色。因此,我认为制作第二个并行 Path 可以解决问题,但调整第二个 PathGeometry (使其平行)已被证明是有问题的。

更新 #1

平行线和曲线,作者:Charles Petzold< /strong> 可能是解决这个问题的一种方法。它实际上工作得很好,但它展平曲线,这在深度缩放时会产生视觉伪影,当然还有性能缺陷。

但是,该算法不会尝试找到与另一条贝塞尔曲线平行的贝塞尔曲线。相反,该算法完全基于折线:输入是一条或多条折线,输出由每条输入折线的多条折线组成。因此,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

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

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

The following image illustrates what I am trying to achieve:

Basically I want to create two Path objects that "touch" each other (parallel paths). This is XAML used to generate this image:

<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>

The first curve has hand-optimized point positions, the second has point positions easily calculated by taking stroke thickness into consideration. You can see the second curve is not perfect, because there is a space between the two. How can I create two perfectly "touching" curves programmatically, without hand-optimizing every curve (which is actually not possible because the curves are generated in code)?

Simply put, I generate one curve (resp. Path) in code, and I need it to have two colors. So I thought making second parallel Path would do the trick, but adjusting Geometry of the second Path (to make it parallel) has proven to be problematic.

Update #1

Parallel Lines and Curves by Charles Petzold might be one way to solve this problem. It actually works pretty well, but it flattens the curves, which creates visual artifacts when deeply zoomed, and of course there is a performance drawback.

The algorithm does not, however, attempt to find a Bézier curve that is parallel to another Bézier curve. The algorithm is instead based entirely on polylines: The input is one or more polylines and the output consists of multiple polylines for each input polyline. For this reason, ParallelPath needs to "flatten" the input geometry—which means converting the entire geometry (including arcs and Bézier curves) into a polyline approximation.

Update #2

So a friend of mine (math Ph.D. inceptor) has analyzed this problem and creating parallel curve to the (third-order) Bézier curve is very complex and computationally expensive. For each point of the parallel curve, computer would have to compute something like this:

(degree 3 polynomial) + (degree 2 polynomial) / sqrt(degree 4 polynomial)

Maybe there is a way to optimize this expression, but it would still be MUCH MORE computationally expensive than a standard Bézier curve (because the parallel curve is completely different curve than the original Bézier curve). I want to be able to animate the curve, so this solution would be probably too much CPU expensive. This leaves us with a couple of options:

  1. Use Charles Petzold's polyline approximation, which works wonders, but there are visual glitches when deeply zoomed.

  2. Derive our own approximation based on Charles Petzond's one. Use Bézier curves instead of lines (maybe arcs would be enough). This would solve the deep zoom problem, but it's probably quite hard to code (I have no clue how to do this).

  3. Maybe it is possible to create something like two-color brush. This way, we could use just a single Path to achieve desired result (as shown by the first image). I haven't seen it anywhere though, so this is probably not an option.

Update #3

I've found some pretty interesting links:

More info:

  • QPainterPathStroker from Qt framework is supposed to be using the Thomas F. Hain's algorithm for parallel curves
  • This Java Stroker is also supposed to be capable of drawing parallel curves

Maybe the final solution? (source here)

... I worked out all I knew about Bezier curve theory, and developed the unflattened offsetting to something that is correct, and (monster) documented that on A primer on Bezier curves


Attempt #1

Make second path a bit wider, and slide it underneath the first path while using Z-Index.
http://i51.tinypic.com/2r5vwjk.png

This won't work, the Geometry must be transformed accordingly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

南风几经秋 2024-11-06 18:59:22

与其使用一条四次贝塞尔曲线,为什么不直接使用两个二次贝塞尔曲线的复合曲线呢?您熟悉贝塞尔曲线数学吗?它们在图形学中受到青睐因为它们的计算成本相当低。我最近创建了一个程序,其中我对细胞运动进行动画处理(只是为了好玩):

在此处输入图像描述

该程序可以轻松运行高清显示器全屏显示 100 个动态且移动的斑点。而且都是 GDI+。

至于平行贝塞尔曲线,根据维基百科,它实际上无法完成: http:// /en.wikipedia.org/wiki/B%C3%A9zier_curve

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

编辑1:

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

编辑2:

好的,根据要求,我设想的是如何计算“类似铁路轨道”的解决方案:

在此输入图像描述

Instead of using one fourth-degree Bezier curve, why don't you just use a compund of two quadratic ones? Are you familiar with Bezier curve mathematics? They are favored in graphics because they are quite computationally cheap. I recently created a program where I animated cell movement (just for fun):

enter image description here

The program could easily run in fullscreen on an HD monitor with 100 blobs animated and moving about. And it was all GDI+.

As for parallel Bezier curves, according to Wikipedia it can't really be done: http://en.wikipedia.org/wiki/B%C3%A9zier_curve

So you'll probably have to be happy with an heuristic approach.

EDIT 1:

Lest your curves are completely random, why not create the outline of each curve and then fill the path? The "bottom" curve of one path will be the "top" curve of the other.

EDIT 2:

Ok, as requested, here's how I envision that a "railroadtrack-like" solution can be calculated:

enter image description here

临风闻羌笛 2024-11-06 18:59:22

您说您想要创建两个相互接触的 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>

You said you want to create two Path objects that touch each other, but you didn’t state how the paths are generated. My answer will assume that you have a Path already generated by some algorithm, and you want to turn this into two new paths.

I would switch from trying to use strokes to using fills. If you can automatically create the red path in your second picture, you can equally create a joined path consisting of both and then fill it instead of drawing it with a stroke. Then you do the same thing in two directions.

The result I get for your example looks like this:

Image showing the joined paths

<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>
红墙和绿瓦 2024-11-06 18:59:22

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

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

Consider a slightly different approach to the problem...

Assuming a 'large' number of points on the geometry.
It may be feasible to use one of the higher quality interpolaton methods by sampling the geometry points at lower zoom levels. As the zoom increases you can increase the sampling rate and instead render only a subset of the curve; This way the amount of computation should stays relatively constant at all zoom levels.
The key is that there is a constant number of pixels on the screen and you can start sampling points once accuracy passes some threshold.

叫思念不要吵 2024-11-06 18:59:22

读这个...
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>

在此处输入图像描述

Read this...
Silverlight - Epic Graphical Fail (rectangle by two triangles) :(

Update

Try this (it's little overkilling but it may help you)

<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>

enter image description here

玩物 2024-11-06 18:59:22

是否可以使第二条路径(生成的)更宽一点,并使用 z 索引将其滑动到第一条路径的下方(后面)?这样您就可以实现无缝连接。

Is it possible to make the second path(generated) a bit wider and slide it underneath (behind) the first path using the z-index? You'll get a seamless join that way.

败给现实 2024-11-06 18:59:22

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

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

你可以把线分成两半并给一半上色,问题是分成两半需要一个像素:(

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

Lets say you need two lines of width 5:

If one pixel of difference is not too much you could first draw a curve with let's say width 11 in red, then draw a curve with same path with width 1 in blue, then fill one of the sides with blue.

You would have split the line at half and colored one half, problem is that the splitting in half takes one pixel :(

But what would happen if you chose an even width like 10? Where would the middle pixel be? Maybe you can exploit something...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文