Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Hermite曲线的子曲线不适合原始曲线_C#_Spline_Hermite - Fatal编程技术网

C# Hermite曲线的子曲线不适合原始曲线

C# Hermite曲线的子曲线不适合原始曲线,c#,spline,hermite,C#,Spline,Hermite,我试图在一个项目中使用Hermite曲线,对数学的实际工作原理的理解是有限的,我遇到了一些我不理解的行为。我已经用下面的最小代码示例演示了我的混淆,但基本上我希望沿着hermite曲线的次曲线(即使用原始曲线上的点和切线定义的次曲线)的点能够拟合原始曲线,但这似乎是错误的 下面的c#代码定义了一个Hermite曲线类,该类提供了用于计算沿曲线以一定比例的点的位置和切线的函数。我从互联网上的其他地方复制/粘贴了这两个函数的数学 然后,一个小测试工具执行我期望成功但没有成功的测试。我不清楚我的代码中

我试图在一个项目中使用Hermite曲线,对数学的实际工作原理的理解是有限的,我遇到了一些我不理解的行为。我已经用下面的最小代码示例演示了我的混淆,但基本上我希望沿着hermite曲线的次曲线(即使用原始曲线上的点和切线定义的次曲线)的点能够拟合原始曲线,但这似乎是错误的

下面的c#代码定义了一个Hermite曲线类,该类提供了用于计算沿曲线以一定比例的点的位置和切线的函数。我从互联网上的其他地方复制/粘贴了这两个函数的数学

然后,一个小测试工具执行我期望成功但没有成功的测试。我不清楚我的代码中是否有错误,我的数学中是否有错误,或者我是否误解了Hermite曲线的工作原理,并且这个测试实际上不应该通过

任何见解都值得赞赏

using System;
using System.Numerics;

class Program
{
    class HermiteCurve
    {
        Vector2 start;
        Vector2 startTangent;
        Vector2 end;
        Vector2 endTangent;

        public HermiteCurve(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent)
        {
            this.start = start;
            this.startTangent = startTangent;
            this.end = end;
            this.endTangent = endTangent;
        }

        public Vector2 GetPoint(float t)
        {
            var t2 = t * t;
            var t3 = t2 * t;

            return
                ( 2f*t3 - 3f*t2 + 1f) * start +
                (-2f*t3 + 3f*t2) * end +
                (t3 - 2f*t2 + t) * startTangent +
                (t3 - t2) * endTangent;
        }

        public Vector2 GetTangent(float t)
        {
            var t2 = t * t;

            return
                (6f*t2 - 6*t) * start +
                (-6f*t2 + 6*t) * end +
                (3f*t2 - 4f*t + 1) * startTangent +
                (3f*t2 - 2f*t) * endTangent;
        }
    }

    static void Main(string[] args)
    {
        Vector2 p0 = new Vector2(0, 0);
        Vector2 m0 = new Vector2(1, 0);
        Vector2 p1 = new Vector2(1, 1);
        Vector2 m1 = new Vector2(0, 1);

        HermiteCurve curve = new HermiteCurve(p0, m0, p1, m1);

        Vector2 p0prime = curve.GetPoint(0.5f);
        Vector2 m0prime = curve.GetTangent(0.5f);

        HermiteCurve curvePrime = new HermiteCurve(p0prime, m0prime, p1, m1);

        Vector2 curvePoint = curve.GetPoint(0.75f);
        Vector2 curveTangent = curve.GetTangent(0.75f);

        Vector2 curvePrimePoint = curvePrime.GetPoint(0.5f);
        Vector2 curvePrimeTangent = curvePrime.GetTangent(0.5f);

        // Why does this check fail?
        if (curvePoint != curvePrimePoint || curveTangent != curvePrimeTangent)
        {
            Console.WriteLine("fail");
            Console.WriteLine("curvePosition      - x: " + curvePoint.X + " y: " + curvePoint.Y);
            Console.WriteLine("curvePrimePosition - x: " + curvePrimePoint.X + " y: " + curvePrimePoint.Y);
            Console.WriteLine("curveTangent       - x: " + curveTangent.X + " y: " + curveTangent.Y);
            Console.WriteLine("curvePrimeTangent  - x: " + curvePrimeTangent.X + " y: " + curvePrimeTangent.Y);
        }
    }
}
程序输出:

fail
curvePosition      - x: 0.890625 y: 0.703125
curvePrimePosition - x: 0.96875 y: 0.71875
curveTangent       - x: 0.8125 y: 1.3125
curvePrimeTangent  - x: 0.25 y: 0.375

您使用的是指针相等而不是对象相等?

简单的回答是,数学并没有按照您希望的方式工作

我已经很久没有玩弄多项式曲线了,所以为了好玩,我拼凑了一个Python程序来计算“分割”hermite曲线的控制点,以及你的“错误”曲线。实际上,您最好使用de Casteljau算法

这个实现可能在很多方面都很可怕,但至少它似乎产生了正确的结果。:-)


代码中的问题是从原始Hermite曲线中获取一阶导数向量,并直接使用它们创建子曲线。以这种方式创建的子曲线实际上与原始Hermite曲线的[0.5,1]部分不同。您可以尝试绘制曲线,您将看到原始曲线和子曲线不匹配

要使子曲线与原始曲线的[0.5,1.0]部分匹配,必须使用“m0prime/2”和“m1/2”创建“CurveTime”。一旦你这样做了,你会发现计算出的“curvePoint”和“curvePrimePoint”将是相同的,“CurvePrimeTranscent”将是“curveTangent”的一半


简而言之,三次Hermite曲线和三次Bezier曲线本质上是相同的东西(三次多项式曲线),但具有不同的表示形式。三次Hermite曲线的定义包含点和向量,而三次Bezier曲线的定义仅包含点。所以,确实三次厄米曲线有时会引起混乱。但是三次贝塞尔曲线有其缺点,而且中间的两个控制点不在曲线上,这使得使用三次贝塞尔曲线时“点插值”问题更难解决。

Vector2是一种值类型。您的答案看起来像是一条要求澄清的注释。是一个吗?啊,即使如此,你也不应该对不精确的类型进行相等处理,你应该计算距离并断言它小于某个小值,比如10e-5或诸如此类。谢谢@Joey为添加程序输出所做的澄清编辑。我应该补充一点,一个相当明显的问题是GetTangent规范化了切向量。输入中切线的绝对值是重要的,而不仅仅是它们的方向!然而,我的直觉是,你对这些东西的参数空间是如何工作的过于乐观了。这个问题涉及到同样的问题,我想:感谢@TurePålsson,我并没有真正关注切线向量的大小这一事实,尽管回想起来这似乎是显而易见的。我已使用此更改更新了问题。但是,检查仍然失败,现在切线值之间的距离更大。这对我来说很不直观。仍然希望能对理解我的误解有所帮助。非常感谢您的回答,并花时间编写此代码。我会把它弄得一团糟,希望能对正在发生的事情有一些直觉。我还将研究de Casteljau算法。
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import numpy.polynomial.polynomial as npp

# Hermite basis matrix
B = np.array([[1, 0, -3,  2],
              [0, 1, -2,  1],
              [0, 0,  3, -2],
              [0, 0, -1,  1]])

# Create the parameter space, for plotting. T has one column for each point
# that we plot, with the first row t^0, second row t^1 etc.
step = 0.01
Tt = np.arange(0.0, 1.01, step)
T = np.vstack([np.ones(Tt.shape[0]), Tt, Tt ** 2, Tt ** 3])

# Control points for first curve. One column for each point/tangent.
C1 = np.array([[0, 1, 1, 0],
               [0, 0, 1, 1]])

# Coefficients for first curve. @ is matrix multiplication. A1 will be a
# matrix with two rows, one for the "x" polynomial and one for the "y"
# polynomial, and four columns, one for each power in the polynomial.
# So, x(t) = Σ A[0,k] t^k and y(t) = Σ A[1,k] t^k.
A1 = C1 @ B

# Points for first curve.
X1 = A1 @ T

# Parameter value where we will split the curve.
t0 = 0.5

# Evaluate the first curve and its tangent at t=t0. The 'polyval' function
# evaluates a polynomial; 'polyder' computes the derivative of a polynomial.
# Reshape and transpose business because I want my COordinates to be COlumns,
# but polyval/polyder wants coefficients to be columns...
p = npp.polyval(t0, A1.T).reshape(2, 1)
d = npp.polyval(t0, npp.polyder(A1.T, 1)).reshape(2, 1)

# Control points for second, "wrong", curve (last two points are same as C1)
C2 = np.hstack([p, d, C1[:,2:]])

# Coefficients for second, "wrong", curve
A2 = C2 @ B

# Points for second, "wrong", curve
X2 = A2 @ T

# We want to create a new curve, such that that its parameter
# u ∈ [t0, 1] maps to t ∈ [0,1], so we let t = k * u + m.
# To get the curve on [0, t0] instead, set k=t0, m=0.
k = 1 - t0
m = t0

k2 = k * k; k3 = k2 * k; m2 = m * m; m3 = m2 * m

# This matrix maps a polynomial from "t" space to "u" space.
KM = np.array([[1,  0,          0,          0],
               [m,  k,          0,          0],
               [m2, 2 * k * m,  k2,         0],
               [m3, 3 * k * m2, 3 * k2 * m, k3]])

# A3 are the coefficients for a polynomial which gives the same values
# on the range [0, 1] as the A1 coefficients give on the range [t0, 1].
A3 = A1 @ KM
X3 = A3 @ T

# Compute the control points corresponding to the A3 coefficients, by
# multiplying by the inverse of the B matrix.
C3 = A3 @ np.linalg.inv(B)
# C3 = (np.linalg.solve(B.T, A3.T)).T # Possibly better version!
print(C3)

# Plot curves
fig, ax = plt.subplots()
ax.plot(X1[0,:], X1[1,:], linewidth=3)
ax.plot(X2[0,:], X2[1,:])
ax.plot(X3[0,:], X3[1,:])

ax.set_aspect('equal')
ax.grid()

plt.show()