0x01 代码

#include <iostream>
#include <vector>
using namespace std;

// 定义二维坐标点结构体
struct Point {
    double x;
    double y;
};

// 计算三阶贝塞尔曲线上某个参数t对应的点坐标
Point getBezierPoint(vector<Point>& controlPoints, double t) {
    double x = pow((1 - t), 3) * controlPoints[0].x + 
               3 * t * pow((1 - t), 2) * controlPoints[1].x +
               3 * pow(t, 2) * (1 - t) * controlPoints[2].x +
               pow(t, 3) * controlPoints[3].x;
    double y = pow((1 - t), 3) * controlPoints[0].y +
               3 * t * pow((1 - t), 2) * controlPoints[1].y +
               3 * pow(t, 2) * (1 - t) * controlPoints[2].y +
               pow(t, 3) * controlPoints[3].y;
    Point p = {x, y};
    return p;
}

// 递归计算三阶贝塞尔曲线上的等间距点
void recursiveDivide(vector<Point>& controlPoints, double t0, double t1, 
                     vector<Point>& pointList, int& idx) {
    // 当参数范围小于1e-6时停止递归
    if ((t1 - t0) < 1e-6) {
        return;
    }
    // 计算参数区间的中点
    double tMid = (t0 + t1) / 2.0;
    // 计算中点对应的贝塞尔曲线上的坐标
    Point pMid = getBezierPoint(controlPoints, tMid);
    // 将中点添加到结果列表中
    pointList[idx++] = pMid;
    // 递归计算左右两段曲线上的等间距点
    recursiveDivide(controlPoints, t0, tMid, pointList, idx);
    recursiveDivide(controlPoints, tMid, t1, pointList, idx);
}

int main() {
    // 定义控制点
    vector<Point> controlPoints = {{10, 10}, {50, 60}, {100, 30}, {150, 150}};

    // 定义采样个数
    int N = 10;
    // 计算初始和末尾点
    vector<Point> pointList(N + 1);
    pointList[0] = controlPoints.front();
    pointList[N] = controlPoints.back();

    // 递归计算曲线上的等间距点
    int idx = 1;
    recursiveDivide(controlPoints, 0.0, 1.0, pointList, idx);

    // 输出等间距的点容器
    for (auto& p : pointList) {
        cout << "(" << p.x << ", " << p.y << ")" << endl;
    }

    return 0;
}

这是一个 C++ 的程序,用于计算三阶贝塞尔曲线上等间距的点。程序在 main 函数中运行,包含以下步骤:

  1. 定义四个控制点,并将它们存储在一个二维向量 controlPoints 中。
  2. 定义需要采样的点数 N,并将采样结果存储在一个二维向量 pointList 中。
  3. 调用递归函数 recursiveDivide 来计算贝塞尔曲线上的等间距点。该函数通过二分法的方式递归计算左右两个子区间的点,直到整个区间小于某个阈值。
  4. 输出等间距的点容器 pointList 中的各个元素。

请注意,贝塞尔曲线是一种可描述平滑曲线的数学模型,三阶贝塞尔曲线由四个控制点确定。对于任意参数 t∈[0,1]t \in [0, 1]t[0,1],三阶贝塞尔曲线上可以计算出对应的点坐标。本程序利用递归方式计算贝塞尔曲线上等间距的点坐标,主要涉及递归算法和贝塞尔曲线的基本原理。

0x02 公式

3阶贝塞尔曲线沿线长等距分割方法可以简述如下:

  1. 计算曲线长度,即对参数 ttt[0,1][0,1][0,1] 区间上积分 (dx/dt)2+(dy/dt)2\sqrt{(dx/dt)^2+(dy/dt)^2}(dx/dt)2+(dy/dt)2 ,其中 dx/dtdx/dtdx/dtdy/dtdy/dtdy/dt 分别为贝塞尔曲线在 xxxyyy 方向上的导数。
  2. 根据总采样点数 NNN,计算出采样点的间隔 s=L/(N−1)s=L/(N-1)s=L/(N1),其中 LLL 为曲线长度。
  3. 初始化采样点列表,包括起始点和终止点,并将起始点和终止点的参数值分别设为 t0=0t_0=0t0=0tN=1t_N=1tN=1
  4. 对于 i∈[1,N−1]i \in [1,N-1]i[1,N1],计算参数值 tit_iti 使得从起点到第 iii 个采样点的长度为 i×si \times si×s,并计算对应的点坐标。

对于3阶贝塞尔曲线,上述分割方法稍加简化后的具体计算方式如下:

  1. 计算曲线长度公式为:

L=∫01(dx/dt)2+(dy/dt)2dt L=\int_{0}^{1} \sqrt{(dx/dt)^2+(dy/dt)^2} dt L=01(dx/dt)2+(dy/dt)2 dt

其中 dx/dtdx/dtdx/dtdy/dtdy/dtdy/dt 的计算公式如下:

dx/dt=3(1−t)2(x1−x0)+6t(1−t)(x2−x1)+3t2(x3−x2) dx/dt=3(1-t)^2(x_1-x_0)+6t(1-t)(x_2-x_1)+3t^2(x_3-x_2) dx/dt=3(1t)2(x1x0)+6t(1t)(x2x1)+3t2(x3x2)

dy/dt=3(1−t)2(y1−y0)+6t(1−t)(y2−y1)+3t2(y3−y2) dy/dt=3(1-t)^2(y_1-y_0)+6t(1-t)(y_2-y_1)+3t^2(y_3-y_2) dy/dt=3(1t)2(y1y0)+6t(1t)(y2y1)+3t2(y3y2)

其中 (x0,y0)(x_0,y_0)(x0,y0)(x1,y1)(x_1,y_1)(x1,y1)(x2,y2)(x_2,y_2)(x2,y2)(x3,y3)(x_3,y_3)(x3,y3) 为3阶贝塞尔曲线的4个控制点坐标。

  1. 根据采样点数 NNN,计算出采样点的间隔 s=L/(N−1)s=L/(N-1)s=L/(N1)

  2. 初始化采样点列表,包括起始点 (x0,y0)(x_0,y_0)(x0,y0) 和终止点 (x3,y3)(x_3,y_3)(x3,y3) 并将起始点和终止点的参数值分别设为 t0=0t_0=0t0=0tN=1t_N=1tN=1

  3. 对于 i∈[1,N−1]i \in [1,N-1]i[1,N1],通过二分法计算出满足以下条件的 tit_iti

∫t0ti(dx/dt)2+(dy/dt)2dt=i×s\int_{t_0}^{t_i} \sqrt{(dx/dt)^2+(dy/dt)^2} dt = i \times st0ti(dx/dt)2+(dy/dt)2 dt=i×s

其中 (dx/dt)2+(dy/dt)2\sqrt{(dx/dt)^2+(dy/dt)^2}(dx/dt)2+(dy/dt)2 的计算公式如上。然后根据 tit_iti 计算出曲线上对应的点坐标,即 (xi,yi)(x_i,y_i)(xi,yi)

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐