Catmull-Rom 样条曲线是一种通过给定点集生成平滑曲线的插值方法。
/**
* Catmull-Rom 样条曲线实现
* @param {Array} points 控制点数组,格式为 [{x: number, y: number}, ...]
* @param {number} [tension=0.5] 张力参数 (0-1),默认0.5
* @param {number} [numOfSeg=25] 每两个控制点之间的线段数,默认25
* @param {boolean} [closed=false] 是否闭合曲线,默认false
* @returns {Array} 生成的曲线点数组
*/
function catmullRom(points, tension = 0.5, numOfSeg = 25, closed = false) {
if (points.length < 2) return points;
const result = []; // 存储结果点
const pts = [...points]; // 复制控制点
// 处理闭合曲线情况
if (closed) {
pts.unshift(points[points.length - 1]); // 在开头添加最后一个点
pts.unshift(points[points.length - 2]); // 再添加倒数第二个点
pts.push(points[0], points[1]); // 在末尾添加前两个点
} else {
// 非闭合曲线,复制第一个和最后一个点作为虚拟点
pts.unshift(points[0]);
pts.push(points[points.length - 1]);
}
// 遍历所有控制点段
for (let i = 1; i < pts.length - 2; i++) {
// 当前段的四个控制点
const p0 = pts[i - 1];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[i + 2];
// 计算每段的曲线点
for (let t = 0; t <= numOfSeg; t++) {
const t1 = t / numOfSeg;
const t2 = t1 * t1;
const t3 = t2 * t1;
// Catmull-Rom 样条公式
const x = 0.5 * ((2 * p1.x) +
(-p0.x + p2.x) * t1 +
(2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
(-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
const y = 0.5 * ((2 * p1.y) +
(-p0.y + p2.y) * t1 +
(2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
(-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
result.push({ x, y });
}
}
return result;
}
参数说明
-
points
: 控制点数组,包含 x 和 y 坐标的对象 -
tension
: 张力参数 (0-1),控制曲线的松紧程度-
0: 最松散的曲线
-
0.5: 标准 Catmull-Rom 曲线 (默认)
-
1: 最紧绷的曲线
-
-
numOfSeg
: 每两个控制点之间的线段数,值越大曲线越平滑 -
closed
: 是否闭合曲线 (首尾相连)
算法说明
Catmull-Rom 样条曲线使用四个控制点来计算曲线段:
-
对于每个内部点 P1 和 P2,使用 P0 和 P3 作为相邻点来计算曲线
-
公式基于三次多项式插值
-
曲线保证通过所有控制点 (除了闭合曲线添加的虚拟点)