OpenLayer4——贝塞尔曲线插值算法
这是早期项目中用到的代码,经过 AI 优化调整,可能需要适当调整一下。
/**
* 生成贝塞尔曲线插值点
*
* 阶乘数为数组长度减一
*
* @param {Array} points - 控制点数组,格式 [[x0, y0], [x1, y1], ...]
* @param {Array} res - 存储结果的数组(可选)
* @param {number} step - 步长,取值越小曲线越精细
* @returns {Array} 贝塞尔曲线上的点坐标数组
*/
function createBezierCurvePoints(points, res, step = 0.2) {
// 贝塞尔曲线阶数
let n = points.length - 1;
// 阶乘查找表
const factorials = [1]; // 0! = 1
for (let i = 1; i <= n; i++) {
factorials[i] = factorials[i - 1] * i;
}
for (let t = 0; t <= 1; t = t + step) {
res.push(multiplyBezier(points, n, factorials, t));
}
}
/**
* 高阶贝塞尔曲线
*
* @param points {Array} - 控制点数组,格式 [[x0, y0], [x1, y1], ...]
* @param n {number} - 阶乘数
* @param factorials {Array} - 阶乘查找表,提前算好阶乘的结果,减少重复计算
* @param t {number} - 参数 t ∈ [0, 1]
* @returns [x,y] ret
*/
function multiplyBezier(points, n, factorials, t) {
let x = 0, y = 0;
for (let i = 0; i <= n; i++) {
const comb = factorials[n] / (factorials[i] * factorials[n - i]);
x += comb * Math.pow((1 - t), n - i) * Math.pow(t, i) * points[i][0];
y += comb * Math.pow((1 - t), n - i) * Math.pow(t, i) * points[i][1];
}
return [x, y];
}
/**
* 2 阶贝塞尔曲线
*
* @param p0 - 点 0
* @param p1 - 点 1
* @param p2 - 点 2
* @param t {number} - 参数 t ∈ [0, 1]
* @returns [x,y] ret
*/
function quadraticBezier(p0, p1, p2, t) {
// 使用数学公式计算贝塞尔曲线上的点
const temp = 1 - t;
const x = Math.pow(temp, 2) * p0[0] + 2 * temp * t * p1[0] + Math.pow(t, 2) * p2[0];
const y = Math.pow(temp, 2) * p0[1] + 2 * temp * t * p1[1] + Math.pow(t, 2) * p2[1];
return [x, y];
}
/**
* 3 阶贝塞尔曲线
*
* @param p0 - 点 0
* @param p1 - 点 1
* @param p2 - 点 2
* @param p3 - 点 3
* @param t {number} - 参数 t ∈ [0, 1]
* @returns [x,y] ret
*/
function cubicBezier(p0, p1, p2, p3, t) {
const temp = 1 - t;
const x = p0[0] * temp * temp * temp + 3 * p1[0] * t * temp * temp + 3 * p2[0] * t * t * temp + p3[0] * t * t * t;
const y = p0[1] * temp * temp * temp + 3 * p1[1] * t * temp * temp + 3 * p2[1] * t * t * temp + p3[1] * t * t * t;
return [x, y];
}
/**
* p1 到 p2 进行 n 等分,取第 m 个点的坐标
*
* @param n - p1 - p2 进行 n 等分
* @param m - 取第 m 个点的坐标
* @param p1 - 点 1
* @param p2 - 点 2
*/
function dividedPoint(n, m, p1, p2) {
return [(p2[0] - p1[0]) / n * m + p1[0], (p2[1] - p1[1]) / n * m + p1[1]];
}
/**
* 平滑转角
* 使用高阶贝塞尔曲线,正方形会变圆(水滴形);
*
* 有时候,只是希望转角圆润一些,而不是整体变为曲线,
*
* 简单说,就是类似于 css 中设置的 border-radio 的效果
*
* @example
* ```
* 原始数据:(0,0),(5,0),(5,5)
* 计算结果:(4,0),(5,0),(5,1)
*
* 需要从 (4,0) 开始绘制曲线,结束于 (5,1) 点
* ```
* @param p0 - 点 0
* @param p1 - 点 1
* @param p2 - 点 2
* @param res - 结果数组
* @param segments 分段数,越平滑
*/
function smoothCorner(p0, p1, p2, res, segments = 20) {
const denominator = 10;
//计算 4/5 等分点
const point1 = dividedPoint(denominator, denominator - 1, p0, p1);
const point2 = p1;
//计算 1/5 等分点
const point3 = dividedPoint(denominator, 1, p1, p2);
//开始点
res.push(point1);
//计算曲线坐标点
for (let i = 0; i <= segments; i++) {
const t = i / segments;
res.push(quadraticBezier(point1, point2, point3, t));
}
//结束点
res.push(point3);
}
/**
* 使所有的转角变平滑
*
* @param line 线段数组
* @returns {*|*[]}
*/
function smoothLine(line) {
if (line.length < 3) {
console.error('图形点的数量不允许小于3:' + line);
return line;
} else {
const n = line.length - 2, res = [];
// first point
res.push(line[0]);
for (let i = 0; i < n; i++) {
smoothCorner(line[i], line[i + 1], line[i + 2], res);
}
// last point
res.push(line[line.length - 1]);
return res;
}
}
export default {
// example n-bezier
createBezierCurvePoints,
// bezier - core
multiplyBezier, quadraticBezier, cubicBezier,
// smooth-corner
dividedPoint, smoothCorner, smoothLine
};
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
浙公网安备 33010602011771号