关于bezier:对三次贝塞尔曲线过点平滑中尖角和交叉现象的优化

目前在做等值线等值面相干的性能,用户可拖拽控制点批改等值线,再用等值线生成等值面。因为初始的等值线点数据太多,不利于用户操作,所以先应用道格拉斯-普克算法(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; extrapoints[extraindex] = [ originPoint[i][0] + addx, originPoint[i][1] + addy ] let extranexti = extraindex + 1; extrapoints[extranexti] = [ midpoints[i][0] + offsetx, midpoints[i][1] + offsety ] //朝 originPoint[i]方向膨胀 addx = (extrapoints[extranexti][0] - originPoint[i][0]) * scale; addy = (extrapoints[extranexti][1] - originPoint[i][1]) * scale; extrapoints[extranexti] = [ originPoint[i][0] + addx, originPoint[i][1] + addy ] } let controlPoint = [] //生成4控制点,产生贝塞尔曲线 for(let i = 1 ;i < originCount - 2 ; i++){ controlPoint[0] = originPoint[i]; let extraindex = 2 * i; controlPoint[1] = extrapoints[extraindex + 1]; let extranexti = extraindex + 2; controlPoint[2] = extrapoints[extranexti]; let nexti = i + 1; controlPoint[3] = originPoint[nexti]; for(let n = maxpoints; n >= 0; n--){ //存入曲线点 curvePoint.push( bezier3func(n / maxpoints, controlPoint) ); } } return curvePoint} //三次贝塞尔曲线 function bezier3func(uu, controlP){ let partX0 = controlP[0][0] * uu * uu * uu; let partX1 = 3 * controlP[1][0] * uu * uu * (1 - uu); let partX2 = 3 * controlP[2][0] * uu * (1 - uu) * (1 - uu); let partX3 = controlP[3][0] * (1 - uu) * (1 - uu) * (1 - uu); let partX = partX0 + partX1 + partX2 + partX3; let partY0 = controlP[0][1] * uu * uu * uu; let partY1 = 3 * controlP[1][1] * uu * uu * (1 - uu); let partY2 = 3 * controlP[2][1] * uu * (1 - uu) * (1 - uu); let partY3 = controlP[3][1] * (1 - uu) * (1 - uu) * (1 - uu); let partY = partY0 + partY1 + partY2 + partY3; return [partX, partY]}c++版的能够看穿过已知点画平滑曲线(3次贝塞尔曲线) ...

October 9, 2020 · 5 min · jiezi