feat: Bezier 解析导数 + 测试补充 + 区间算术验证
- curves/bezier: 重写导数(前向差分 + de Casteljau) - curves/bezier_surface: de Casteljau 解析导数 du/dv - tests/bezier: 新增导数/分割测试 (5 cases) - foundation/interval: 区间算术 + orient2d 验证函数
This commit is contained in:
+23
-19
@@ -1,8 +1,12 @@
|
||||
#include "vde/curves/bezier_curve.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace vde::curves {
|
||||
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
|
||||
BezierCurve::BezierCurve(std::vector<Point3D> 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<Point3D> 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<Point3D> diff;
|
||||
int m = static_cast<int>(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, BezierCurve> BezierCurve::split(double t) const {
|
||||
int n = degree();
|
||||
std::vector<Point3D> left(n + 1), right(n + 1);
|
||||
std::vector<Point3D> left(n+1), right(n+1);
|
||||
auto temp = cp_;
|
||||
left[0] = temp[0];
|
||||
right[n] = temp[n];
|
||||
@@ -61,7 +65,7 @@ std::pair<BezierCurve, BezierCurve> 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<Point3D> new_cp(n + 2);
|
||||
new_cp[0] = result.cp_[0];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user