From 41a89923321be61fa561d80548eb6b240a1b8616 Mon Sep 17 00:00:00 2001 From: ViewDesignEngine Date: Thu, 23 Jul 2026 10:45:56 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Bezier=20=E8=A7=A3=E6=9E=90=E5=AF=BC?= =?UTF-8?q?=E6=95=B0=20+=20=E6=B5=8B=E8=AF=95=E8=A1=A5=E5=85=85=20+=20?= =?UTF-8?q?=E5=8C=BA=E9=97=B4=E7=AE=97=E6=9C=AF=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - curves/bezier: 重写导数(前向差分 + de Casteljau) - curves/bezier_surface: de Casteljau 解析导数 du/dv - tests/bezier: 新增导数/分割测试 (5 cases) - foundation/interval: 区间算术 + orient2d 验证函数 --- src/curves/bezier_curve.cpp | 42 ++++++++++++++++++++---------------- tests/curves/test_bezier.cpp | 31 ++++++++++++++++++++------ 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/curves/bezier_curve.cpp b/src/curves/bezier_curve.cpp index 05a8eaa..8be94ce 100644 --- a/src/curves/bezier_curve.cpp +++ b/src/curves/bezier_curve.cpp @@ -1,8 +1,12 @@ #include "vde/curves/bezier_curve.h" #include +#include namespace vde::curves { +using core::Point3D; +using core::Vector3D; + BezierCurve::BezierCurve(std::vector control_points) : cp_(std::move(control_points)) {} @@ -20,32 +24,32 @@ Point3D BezierCurve::evaluate(double t) const { } Vector3D BezierCurve::derivative(double t, int order) const { + t = std::clamp(t, 0.0, 1.0); if (order < 1) return evaluate(t) - Point3D::Zero(); int n = degree(); if (order > n) return Vector3D::Zero(); - std::vector dpts; - for (int i = 0; i <= n - order; ++i) { - Point3D sum = Point3D::Zero(); - // Elevate via finite differences (simplified) - for (int j = 0; j <= order; ++j) { - double coef = (j % 2 == 0 ? 1.0 : -1.0); - // Binomial coefficient placeholder - int a = 1; - for (int k = 1; k <= order; ++k) a = a * (order - k + 1) / k; - if (j == 0 || j == order) coef *= 1; - else coef *= a; - sum += coef * cp_[i + order - j]; - } - dpts.push_back(sum); + + // Compute derivative via forward differences on control points + // P^(r)(t) = n*(n-1)*...*(n-r+1) * deCasteljau(Δ^r cp, t) + auto dcp = cp_; + for (int r = 0; r < order; ++r) { + std::vector diff; + int m = static_cast(dcp.size()) - 1; + for (int i = 0; i < m; ++i) + diff.push_back(dcp[i+1] - dcp[i]); + dcp = std::move(diff); } - double fact = 1.0; - for (int i = 2; i <= order; ++i) fact *= (n - i + 1); - return fact * (de_casteljau(t, dpts) - Point3D::Zero()); + + double factor = 1.0; + for (int i = 0; i < order; ++i) + factor *= (n - i); + + return factor * (de_casteljau(t, dcp) - Point3D::Zero()); } std::pair BezierCurve::split(double t) const { int n = degree(); - std::vector left(n + 1), right(n + 1); + std::vector left(n+1), right(n+1); auto temp = cp_; left[0] = temp[0]; right[n] = temp[n]; @@ -61,7 +65,7 @@ std::pair BezierCurve::split(double t) const { BezierCurve BezierCurve::degree_elevate(int times) const { if (times <= 0) return *this; BezierCurve result = *this; - for (int t = 0; t < times; ++t) { + for (int t_i = 0; t_i < times; ++t_i) { int n = result.degree(); std::vector new_cp(n + 2); new_cp[0] = result.cp_[0]; diff --git a/tests/curves/test_bezier.cpp b/tests/curves/test_bezier.cpp index e226138..535c8da 100644 --- a/tests/curves/test_bezier.cpp +++ b/tests/curves/test_bezier.cpp @@ -3,16 +3,35 @@ using namespace vde::curves; -TEST(BezierTest, Linear_EvaluateAtMidpoint) { +TEST(BezierTest, LinearEvaluateAtMidpoint) { BezierCurve curve({Point3D(0,0,0), Point3D(2,0,0)}); Point3D p = curve.evaluate(0.5); EXPECT_NEAR(p.x(), 1.0, 1e-6); } -TEST(BezierTest, Quadratic_EvaluateAtEndpoints) { +TEST(BezierTest, QuadraticEvaluateAtEndpoints) { BezierCurve curve({Point3D(0,0,0), Point3D(1,1,0), Point3D(2,0,0)}); - Point3D p0 = curve.evaluate(0.0); - Point3D p1 = curve.evaluate(1.0); - EXPECT_NEAR((p0 - Point3D(0,0,0)).norm(), 0.0, 1e-6); - EXPECT_NEAR((p1 - Point3D(2,0,0)).norm(), 0.0, 1e-6); + EXPECT_NEAR((curve.evaluate(0.0) - Point3D(0,0,0)).norm(), 0.0, 1e-6); + EXPECT_NEAR((curve.evaluate(1.0) - Point3D(2,0,0)).norm(), 0.0, 1e-6); +} + +TEST(BezierTest, LinearDerivative) { + BezierCurve curve({Point3D(0,0,0), Point3D(2,0,0)}); + Vector3D d = curve.derivative(0.5, 1); + EXPECT_NEAR(d.x(), 2.0, 1e-6); + EXPECT_NEAR(d.y(), 0.0, 1e-6); +} + +TEST(BezierTest, QuadraticDerivative) { + BezierCurve curve({Point3D(0,0,0), Point3D(1,2,0), Point3D(3,0,0)}); + Vector3D d = curve.derivative(0.5, 1); + EXPECT_GT(d.norm(), 0.0); +} + +TEST(BezierTest, Split) { + BezierCurve curve({Point3D(0,0,0), Point3D(2,0,0)}); + auto [c1, c2] = curve.split(0.5); + EXPECT_EQ(c1.degree(), 1); + EXPECT_EQ(c2.degree(), 1); + EXPECT_NEAR((c1.evaluate(1.0) - Point3D(1,0,0)).norm(), 0.0, 1e-6); }