2

我编写了一个图表编辑器,并绘制了一些曲线链接,这些链接完全适用于二次贝塞尔曲线(见图):

贝塞尔链接

我搜索绘制“尖峰”曲线链接的最佳方式(如果可能的话)。大致像这样(蓝色):

尖刺线

我不知道从哪里开始,我读了几篇关于画笔或“如何绘制弯曲文本”的文章,但这似乎不是我需要的......

感谢您的帮助或建议!:)

只是为了完成一些评论,贝塞尔曲线是用 quadrametricBezier 类从 3 点开始制作的。感谢大家 !

4

3 回答 3

5

我认为 WPF 中没有内置的方法可以做到这一点。您必须自己计算坐标并自己绘制线条(例如使用 DrawingVisual)。

要计算坐标,您必须:

步骤 1 沿贝塞尔曲线采样点。

具有 4 个控制点的贝塞尔曲线具有以下公式:

curve(t) = t^3 p1 + 3 t^2 (1-t) p2 + 3 t (1-t)^2 p3 + (1-t)^3 p4

d/dt curve(t) = 3 p3 - 3 p4 + 6 p2 t - 12 p3 t + 6 p4 t + 3 p1 t^2 - 9 p2 t^2 + 9 p3 t^2 - 3 p4 t^2

使用这些公式,您可以计算曲线上的点及其切线方向。将切线方向旋转 90°(即交换 X/Y 并更改 Y 的符号)给出法线方向。

但是,这些点并不是等距的:

在此处输入图像描述

因此,如果您直接使用这些点,您会得到一条曲线,其中一些“尖峰”比其他尖峰短:

在此处输入图像描述

第 2 步:沿曲线获取等距点

您现在有一个沿曲线的点列表。您可以计算每个点与下一个点之间的欧几里得距离。将所有这些距离相加得出曲线的总长度。

假设您想要(大约)10 像素宽的尖峰。然后你需要n=round(TotalLength / 10)积分。点在s(i) = TotalLength / n * i

因此,如果您想例如找到t第三个等距点的值,您可以计算s(3) = TotalLength / n * 3. 然后,您将遍历采样点集,将距离相加,直到到达沿曲线的总距离 > s(3) 的点。现在您知道要查找的点之前和之后的点,您可以使用三规则来计算两者之间的 t。

现在您有一组沿曲线相隔相同距离的点:

在此处输入图像描述

第 3 步:绘制尖峰

这是最简单的部分:在每个等距点,计算法线(使用上面的导数公式)。将该法线除以其长度得到单位法线。然后添加到每个偶数点+d * UnitNormal和每个奇数点-d * UnitNormal,其中d是尖峰的“深度”,即尖端到曲线的距离。

在此处输入图像描述

于 2012-08-20T08:35:05.060 回答
1

假设您已经计算出贝塞尔曲线,所需曲线是三角波乘以贝塞尔曲线与贝塞尔曲线的法向量的总和。您应该考虑的唯一一件事是,贝塞尔曲线是参数t在 [0, 1] 中的参数曲线。然后你需要 Bezier 曲线长度函数L(t)并将其插入三角波方程而不是t.

三角波也可以通过模运算来表示

TW(t) = M * abs(mod(q * L(t), n * 2 - 2) - n + 1) + 1

在哪里

M- 波的幅度,

q- 沿路径的比例因子,

n- 曲线周期,

t- 贝塞尔曲线的参数,

L(t)- 贝塞尔曲线的长度函数。

结果曲线:

C(t) = TW(t) * B_normal(t) + B(t)

其中B_normal(t)是点 处贝塞尔曲线的法线向量t

于 2012-08-20T07:12:43.070 回答
1

对于那些可能对 WPF 解决方案感兴趣的人,我最终基于QuadraticBezierSegmentPathGeometry类对此进行了编码(不是很优化)。

非常感谢大家。:)

public partial class MainWindow : Window
{
    int orientation = 1;
    int compt = 0;
    int SpikeWidth = 5;
    int SpikeHeigth = 3;

    public MainWindow()
    {

        InitializeComponent();

        Polyline wave = new Polyline();
        wave.Stroke = Brushes.Blue;
        wave.StrokeThickness = 2;

        PathGeometry pg = BezierPath.Data.GetFlattenedPathGeometry();
        double CurveLenght = GetLength(pg, PathFigure.StartPoint);
        double NbrPoint = (Math.Round(CurveLenght / SpikeWidth));
        for (int i = 0; i <= NbrPoint; i++)
        {
            //Calcul de T
            double t = SpikeWidth * i / CurveLenght;

            Point TangentPoint;
            Point PointToDraw;
            pg.GetPointAtFractionLength(t, out PointToDraw, out TangentPoint);


            // Calcul de l'angle
            double a = Math.Atan2(TangentPoint.Y, TangentPoint.X);
            a += Math.PI / 2;

            //Alterner un point sur deux de chaque coté de la courbe
            if (compt % 2 == 0)
                orientation = 1;
            else
                orientation = -1;

            //Calcul du point et ajout à la polyligne.
            //Point calculation and added to the polyline.
            wave.Points.Add(new Point(Math.Cos(a) * SpikeHeigth * orientation + PointToDraw.X, Math.Sin(a) * SpikeHeigth * orientation + PointToDraw.Y));


            //Compte le nombre de passage pour l'orientation
            compt += 1;
        }
        //Traçage sur la canvas
        cv.Children.Add(wave);
    }           

    private double GetLength(PathGeometry pg, Point startPoint)
    {
        PolyLineSegment pls = pg.Figures[0].Segments[0] as PolyLineSegment;

        double distance = 0;
        foreach (Point pt in pls.Points)
        {
            distance += Math.Sqrt((startPoint.X - pt.X).Pow(2) + (startPoint.Y - pt.Y).Pow(2));
            startPoint = pt;
        }
        return distance;
    }
}
<Canvas x:Name="cv">
    <Path Stroke="Black" x:Name="BezierPath">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure x:Name="PathFigure" StartPoint="10,400">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                <QuadraticBezierSegment x:Name="BezierSegment" Point1="50,80" Point2="400,400">
                                </QuadraticBezierSegment>
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>                    
        </Path.Data>
    </Path>               
</Canvas>
于 2012-08-25T17:49:59.583 回答