由于项目开发需要对等值线进行平滑处理,所以研究了线条的平滑算法,经研究查阅资料,可以使用三次B样条曲线方程对线条进行平滑处理,而平滑处理可分为近似拟合和插值拟合两种,两种拟合处理各有其优缺点,以下会做说明,可根据实际业务需要进行选取。本次项目开发最终选用了近似拟合算法处理。
一、三次B样条曲线方程
B样条曲线的总方程为:
其中是控制曲线的特征点,
则是K阶B样条基函数。
三次B样条曲线方程中基函数为:
其中表示阶乘,化成看的明白的式子就是:
将基函数带入总方程可换算出三次B样条曲线方程:
基于上述的三次B样条曲线方程可对曲线进行平滑处理。
二、三次B样条曲线近似拟合
近似拟合,平滑后的线条不一定过原始特征点,平滑处理的核心算法如下,算法中对闭合曲线和非闭合曲线分别做了处理:
// 闭合曲线
if (line[0] == line[len - 2] && line[1] == line[len - 1]) {
for (var n = 0; n < pntLen; n++) {
if (n <= pntLen - 4) {
for (var t = 0.0; t <= 1.0; t += 0.1) {
var a1 = Math.pow((1 - t), 3) / 6;
var a2 = (3 * Math.pow(t, 3) - 6 * Math.pow(t, 2) + 4) / 6;
var a3 = (-3 * Math.pow(t, 3) + 3 * Math.pow(t, 2) + 3 * t + 1) / 6;
var a4 = Math.pow(t, 3) / 6;
var x = a1*points[n].x + a2*points[n + 1].x + a3*points[n + 2].x + a4*points[n + 3].x;
var y = a1*points[n].y + a2*points[n + 1].y + a3*points[n + 2].y + a4*points[n + 3].y;
newPnts.push({x: x, y: y});
}
} else if (n == pntLen - 3) {
for (var t = 0.0; t <= 1.0; t += 0.1) {
var a1 = Math.pow((1 - t), 3) / 6;
var a2 = (3 * Math.pow(t, 3) - 6 * Math.pow(t, 2) + 4) / 6;
var a3 = (-3 * Math.pow(t, 3) + 3 * Math.pow(t, 2) + 3 * t + 1) / 6;
var a4 = Math.pow(t, 3) / 6;
var x = a1*points[n].x + a2*points[n + 1].x + a3*points[n + 2].x + a4*points[0].x;
var y = a1*points[n].y + a2*points[n + 1].y + a3*points[n + 2].y + a4*points[0].y;
newPnts.push({x: x, y: y});
}
} else if (n == pntLen - 2) {
for (var t = 0.0; t <= 1.0; t += 0.1) {
var a1 = Math.pow((1 - t), 3) / 6;
var a2 = (3 * Math.pow(t, 3) - 6 * Math.pow(t, 2) + 4) / 6;
var a3 = (-3 * Math.pow(t, 3) + 3 * Math.pow(t, 2) + 3 * t + 1) / 6;
var a4 = Math.pow(t, 3) / 6;
var x = a1*points[n].x + a2*points[n + 1].x + a3*points[0].x + a4*points[1].x;
var y = a1*points[n].y + a2*points[n + 1].y + a3*points[0].y + a4*points[1].y;
newPnts.push({x: x, y: y});
}
} else if (n == pntLen - 1) {
for (var t = 0.0; t <= 1.0; t += 0.1) {
var a1 = Math.pow((1 - t), 3) / 6;
var a2 = (3 * Math.pow(t, 3) - 6 * Math.pow(t, 2) + 4) / 6;
var a3 = (-3 * Math.pow(t, 3) + 3 * Math.pow(t, 2) + 3 * t + 1) / 6;
var a4 = Math.pow(t, 3) / 6;
var x = a1*points[n].x + a2*points[0].x + a3*points[1].x + a4*points[2].x;
var y = a1*points[n].y + a2*points[0].y + a3*points[1].y + a4*points[2].y;
newPnts.push({x: x, y: y});
}
}
}
// 不闭合
} else {
newPnts.push({x: points[0].x, y: points[0].y});
for (var n = 0; n < pntLen; n++) {
if (n <= pntLen - 4) {
for (var t = 0.0; t <= 1.0; t += 0.1) {
var a1 = Math.pow((1 - t), 3) / 6;
var a2 = (3 * Math.pow(t, 3) - 6 * Math.pow(t, 2) + 4) / 6;
var a3 = (-3 * Math.pow(t, 3) + 3 * Math.pow(t, 2) + 3 * t + 1) / 6;
var a4 = Math.pow(t, 3) / 6;
var x = a1*points[n].x + a2*points[n + 1].x + a3*points[n + 2].x + a4*points[n + 3].x;
var y = a1*points[n].y + a2*points[n + 1].y + a3*points[n + 2].y + a4*points[n + 3].y;
newPnts.push({x: x, y: y});
}
}
}
newPnts.push({x: points[pntLen - 1].x, y: points[pntLen - 1].y});
}
三次B样条曲线近似拟合处理的效果图如下,其中红色为原始未平滑处理的曲线,而蓝色为平滑后的曲线。可以看出近似拟合后的曲线平滑的效果较为满意,但是缺点是曲线不过原始特征点 。
三、三次B样条曲线插值拟合
插值拟合,平滑后的曲线过原始特征点,平滑的核心算法如下:
function getCtrlPoint(pointsArr, index, scaleA, scaleB) {
if (!scaleA || !scaleB) {
scaleA = 0.15;
scaleB = 0.15;
}
// 处理两种极端情形
if (index < 1) {
var pAx = pointsArr[0].x + (pointsArr[1].x - pointsArr[0].x) * scaleA;
var pAy = pointsArr[0].y + (pointsArr[1].y - pointsArr[0].y) * scaleA;
} else {
var pAx = pointsArr[index].x + (pointsArr[index + 1].x - pointsArr[index - 1].x) * scaleA;
var pAy = pointsArr[index].y + (pointsArr[index + 1].y - pointsArr[index - 1].y) * scaleA;
}
if (index > pointsArr.length - 3) {
var last = pointsArr.length - 1
var pBx = pointsArr[last].x - (pointsArr[last].x - pointsArr[last - 1].x) * scaleB;
var pBy = pointsArr[last].y - (pointsArr[last].y - pointsArr[last - 1].y) * scaleB;
} else {
var pBx = pointsArr[index + 1].x - (pointsArr[index + 2].x - pointsArr[index].x) * scaleB;
var pBy = pointsArr[index + 1].y - (pointsArr[index + 2].y - pointsArr[index].y) * scaleB;
}
return {
pA: {x: pAx, y: pAy},
pB: {x: pBx, y: pBy}
}
}
三次B样条曲线插值拟合处理的效果图如下,其中红色为原始未平滑处理的曲线,而蓝色为平滑后的曲线。可以看出插值拟合后的曲线平滑的效果不尽如人意,有出现线条打结的情况,但是优点是曲线过原始特征点 。
参考资料:https://blog.csdn.net/liumangmao1314/article/details/54588155