目前在做等值线等值面相关的功能,用户可拖拽控制点修改等值线,再用等值线生成等值面。因为初始的等值线点数据太多,不利于用户操作,所以先使用道格拉斯-普克算法(Douglas–Peucker)进行等值线抽稀,再将抽稀后的控制点使用贝塞尔曲线算法进行平滑。
对于贝塞尔曲线算法的平滑过程,有人做了很详细的示意图,推荐大家看下贝塞尔曲线算法之JS获取点
可以了解到贝赛尔曲线算法平滑得到的曲线是经过起始点的,同时二阶算法需要三个点,三阶算法需要四个点,四阶算法需要五个点,以此类推。
一般的来说,三阶贝塞尔曲线就已经够用了,而且效果还不错,所以我选择了三次贝塞尔曲线平滑算法来进行控制点的平滑处理。
贝塞尔曲线平滑后的等值线是基本不经过控制点的,考虑到用户操作逻辑,以及点线关系(我的控制点是等值线抽稀得到的,所以等值线是经过控制点的),所以采用三次贝塞尔曲线过点平滑算法来进行控制点的平滑处理。
过点平滑的原理就是以相邻两个控制点为起始点,然后往起始点中间插入其他过程点(不是在起始点直线上选择点),这样平滑得到的曲线是经过起始点的,而曲线如何平滑是由插入的点来控制的,三次贝塞尔曲线需要四个点,那就需要在起始点中间插入两个点。
大致思路就是,先算出相邻原始点的中点,在把相邻中点连成的线段平移到对应的原始点,以平移后的中点作为控制点,相邻原始点为起始点画贝塞尔曲线,这样就保证了连接处的光滑。而贝塞尔曲线本身是光滑的,所以就把这些原始点用光滑曲线连起来了。具体代码及示意图如下:
代码:function createCurve(originPoint, option){
//控制点收缩系数 ,经调试0.6较好
let scale = option.tension || 0.6;
//平滑插值插入的最大点数
let maxpoints = option.pointsPerSeg
let originCount = originPoint.length
let curvePoint = []
let midpoints = []
//生成中点
for(let i = 0 ;i < originCount - 1 ; i++){
midpoints.push([
(originPoint[i][0] + originPoint[i + 1][0])/2.0,
(originPoint[i][1] + originPoint[i + 1][1])/2.0
])
}
//平移中点
let extrapoints = []
for(let i = 1 ;i < originCount - 1 ; i++){
let backi = i - 1;
let midinmid = [
(midpoints[i][0] + midpoints[backi][0])/2.0,
(midpoints[i][1] + midpoints[backi][1])/2.0
]
let offsetx = originPoint[i][0] - midinmid[0];
let offsety = originPoint[i][1] - midinmid[1];
let extraindex = 2 * i;
extrapoints[extraindex] = [
midpoints[backi][0] + offsetx,
midpoints[backi][1] + offsety
]
//朝 originPoint[i]方向收缩
let addx = (extrapoints[extraindex][0] - originPoint[i][0]) * scale;
let addy = (extrapoints[extraindex][1] - originPoint[i][1]) * scale;