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

hfhan

``````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] = [

]

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] = [

]

}

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次贝塞尔曲线）

``````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] = [

]

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] = [

]

}

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];

let fn = bezier3func;

let cp = intersects(controlPoint.slice(0, 2), controlPoint.slice(-2))

if(cp && isContains(controlPoint[0], controlPoint[1], cp)){

controlPoint[1] = cp

}

if(cp && isContains(controlPoint[2], controlPoint[3], cp)){

controlPoint[2] = cp

}

if(controlPoint[1][0] == controlPoint[2][0] && controlPoint[1][1] == controlPoint[2][1]){

fn = bezier2func

controlPoint.splice(1, 1)

}

for(var n = maxpoints; n >= 0; n--){

//存入曲线点

curvePoint.push( fn(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]

}

//二次贝塞尔曲线

function bezier2func(uu, controlP){

let partX0 = controlP[0][0] * uu * uu;

let partX1 = 2 * controlP[1][0] * uu * (1 - uu);

let partX2 = controlP[2][0] * (1 - uu) * (1 - uu);

let partX = partX0 + partX1 + partX2;

let partY0 = controlP[0][1] * uu * uu;

let partY1 = 2 * controlP[1][1] * uu * (1 - uu);

let partY2 = controlP[2][1] * (1 - uu) * (1 - uu);

let partY = partY0 + partY1 + partY2;

return [partX, partY]

}

/**

* Find a point that intersects LineStrings with two coordinates each

* 找到一个点，该点与每个线串有两个坐标相交

*/

function intersects(coords1, coords2) {

if (coords1.length !== 2) {

throw new Error("<intersects> line1 must only contain 2 coordinates");

}

if (coords2.length !== 2) {

throw new Error("<intersects> line2 must only contain 2 coordinates");

}

const x1 = coords1[0][0];

const y1 = coords1[0][1];

const x2 = coords1[1][0];

const y2 = coords1[1][1];

const x3 = coords2[0][0];

const y3 = coords2[0][1];

const x4 = coords2[1][0];

const y4 = coords2[1][1];

//斜率交叉相乘 k1 = (y4 - y3) / (x4 - x3) ... k2 = (y2 - y1) / (x2 - x1)

//k1 k2 同乘 (x4 - x3) * (x2 - x1) 得到denom

const denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));

const numeA = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3));

const numeB = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3));

if (denom === 0) { //斜率一样，平行线

return null;

}

const uA = numeA / denom;

const uB = numeB / denom;

const x = x1 + (uA * (x2 - x1));

const y = y1 + (uA * (y2 - y1));

return [x, y];

}

function isContains(sp, ep, p) {

return (

(p[0] > ep[0] && p[0] < sp[0]) || (p[0] > sp[0] && p[0] < ep[0])

) && (

(p[1] > ep[1] && p[1] < sp[1]) || (p[1] > sp[1] && p[1] < ep[1])

)

}``````

18.7k 声望
19.7k 粉丝
0 条评论