how to draw smooth curve through N points using javascript HTML5 canvas?
对于绘图应用程序,我将鼠标移动坐标保存到数组中,然后使用lineTo进行绘制。 结果线不平滑。 如何在所有收集的点之间生成一条曲线?
我已经用谷歌搜索过,但是我只发现了3个绘制线条的函数:对于2个采样点,只需使用
(我尝试为数组中的每4个点绘制一个
我该如何编写一个函数绘制5个采样点及更多的平滑曲线?
将随后的采样点与不相交的" curveTo"类型的函数连接在一起的问题在于,曲线相交的位置不平滑。这是因为两条曲线共享一个端点,但受到完全不相交的控制点的影响。一种解决方案是"弯曲"到下两个后续采样点之间的中点。使用这些新的内插点将曲线连接起来,可以在端点处实现平滑过渡(一次迭代的终点变成下一次迭代的控制点。)换句话说,两条不相交的曲线现在有很多共同点。
该解决方案摘自《 Foundation ActionScript 3.0动画:使事物移动》一书。第95页-渲染技术:创建多条曲线。
注意:此解决方案实际上并没有绘制出每个点,这是我的问题的标题(而是通过采样点近似绘制曲线,但从不通过采样点),但是出于我的目的(绘图应用程序),对我来说已经足够了,从视觉上讲,您无法分辨出区别。有一种解决方案可以遍历所有样本点,但是要复杂得多(请参阅http://www.cartogrammar.com/blog/actionscript-curves-update/)
这是近似方法的图形代码:
1 2 3 4 5 6 7 8 9 10 11 12 | // move to the first point ctx.moveTo(points[0].x, points[0].y); for (i = 1; i < points.length - 2; i ++) { var xc = (points[i].x + points[i + 1].x) / 2; var yc = (points[i].y + points[i + 1].y) / 2; ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc); } // curve through the last two points ctx.quadraticCurveTo(points[i].x, points[i].y, points[i+1].x,points[i+1].y); |
有点晚了,但是为了记录。
您可以通过使用基本样条曲线(也称为规范样条曲线)绘制穿过点的平滑曲线来获得平滑线。
我为画布制作了此功能-它分为三个功能以增加多功能性。主要包装函数如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function drawCurve(ctx, ptsa, tension, isClosed, numOfSegments, showPoints) { showPoints = showPoints ? showPoints : false; ctx.beginPath(); drawLines(ctx, getCurvePoints(ptsa, tension, isClosed, numOfSegments)); if (showPoints) { ctx.stroke(); ctx.beginPath(); for(var i=0;i<ptsa.length-1;i+=2) ctx.rect(ptsa[i] - 2, ptsa[i+1] - 2, 4, 4); } } |
要绘制曲线,请按以下顺序排列一个带有x,y点的数组:
像这样使用它:
1 2 3 4 5 | var myPoints = [10,10, 40,30, 100,10]; //minimum two points var tension = 1; drawCurve(ctx, myPoints); //default tension=0.5 drawCurve(ctx, myPoints, tension); |
上面的函数调用两个子函数,一个子函数计算平滑点。这将返回一个包含新点的数组-这是计算平滑点的核心功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | function getCurvePoints(pts, tension, isClosed, numOfSegments) { // use input value if provided, or use a default value tension = (typeof tension != 'undefined') ? tension : 0.5; isClosed = isClosed ? isClosed : false; numOfSegments = numOfSegments ? numOfSegments : 16; var _pts = [], res = [], // clone array x, y, // our x,y coords t1x, t2x, t1y, t2y, // tension vectors c1, c2, c3, c4, // cardinal points st, t, i; // steps based on num. of segments // clone array so we don't change the original // _pts = pts.slice(0); // The algorithm require a previous and next point to the actual point array. // Check if we will draw closed or open curve. // If closed, copy end points to beginning and first points to end // If open, duplicate first points to befinning, end points to end if (isClosed) { _pts.unshift(pts[pts.length - 1]); _pts.unshift(pts[pts.length - 2]); _pts.unshift(pts[pts.length - 1]); _pts.unshift(pts[pts.length - 2]); _pts.push(pts[0]); _pts.push(pts[1]); } else { _pts.unshift(pts[1]); //copy 1. point and insert at beginning _pts.unshift(pts[0]); _pts.push(pts[pts.length - 2]); //copy last point and append _pts.push(pts[pts.length - 1]); } // ok, lets start.. // 1. loop goes through point array // 2. loop goes through each segment between the 2 pts + 1e point before and after for (i=2; i < (_pts.length - 4); i+=2) { for (t=0; t <= numOfSegments; t++) { // calc tension vectors t1x = (_pts[i+2] - _pts[i-2]) * tension; t2x = (_pts[i+4] - _pts[i]) * tension; t1y = (_pts[i+3] - _pts[i-1]) * tension; t2y = (_pts[i+5] - _pts[i+1]) * tension; // calc step st = t / numOfSegments; // calc cardinals c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1; c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st; c4 = Math.pow(st, 3) - Math.pow(st, 2); // calc x and y cords with common control vectors x = c1 * _pts[i] + c2 * _pts[i+2] + c3 * t1x + c4 * t2x; y = c1 * _pts[i+1] + c2 * _pts[i+3] + c3 * t1y + c4 * t2y; //store points in array res.push(x); res.push(y); } } return res; } |
并将点实际绘制为平滑曲线(或任何其他分段线,只要您具有x,y数组):
1 2 3 4 | function drawLines(ctx, pts) { ctx.moveTo(pts[0], pts[1]); for(i=2;i<pts.length-1;i+=2) ctx.lineTo(pts[i], pts[i+1]); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | var ctx = document.getElementById("c").getContext("2d"); function drawCurve(ctx, ptsa, tension, isClosed, numOfSegments, showPoints) { ctx.beginPath(); drawLines(ctx, getCurvePoints(ptsa, tension, isClosed, numOfSegments)); if (showPoints) { ctx.beginPath(); for(var i=0;i<ptsa.length-1;i+=2) ctx.rect(ptsa[i] - 2, ptsa[i+1] - 2, 4, 4); } ctx.stroke(); } var myPoints = [10,10, 40,30, 100,10, 200, 100, 200, 50, 250, 120]; //minimum two points var tension = 1; drawCurve(ctx, myPoints); //default tension=0.5 drawCurve(ctx, myPoints, tension); function getCurvePoints(pts, tension, isClosed, numOfSegments) { // use input value if provided, or use a default value\t tension = (typeof tension != 'undefined') ? tension : 0.5; isClosed = isClosed ? isClosed : false; numOfSegments = numOfSegments ? numOfSegments : 16; var _pts = [], res = [],\t// clone array x, y,\t\t\t// our x,y coords t1x, t2x, t1y, t2y,\t// tension vectors c1, c2, c3, c4,\t\t// cardinal points st, t, i;\t\t// steps based on num. of segments // clone array so we don't change the original // _pts = pts.slice(0); // The algorithm require a previous and next point to the actual point array. // Check if we will draw closed or open curve. // If closed, copy end points to beginning and first points to end // If open, duplicate first points to befinning, end points to end if (isClosed) { _pts.unshift(pts[pts.length - 1]); _pts.unshift(pts[pts.length - 2]); _pts.unshift(pts[pts.length - 1]); _pts.unshift(pts[pts.length - 2]); _pts.push(pts[0]); _pts.push(pts[1]); } else { _pts.unshift(pts[1]);\t//copy 1. point and insert at beginning _pts.unshift(pts[0]); _pts.push(pts[pts.length - 2]);\t//copy last point and append _pts.push(pts[pts.length - 1]); } // ok, lets start.. // 1. loop goes through point array // 2. loop goes through each segment between the 2 pts + 1e point before and after for (i=2; i < (_pts.length - 4); i+=2) { for (t=0; t <= numOfSegments; t++) { // calc tension vectors t1x = (_pts[i+2] - _pts[i-2]) * tension; t2x = (_pts[i+4] - _pts[i]) * tension; t1y = (_pts[i+3] - _pts[i-1]) * tension; t2y = (_pts[i+5] - _pts[i+1]) * tension; // calc step st = t / numOfSegments; // calc cardinals c1 = 2 * Math.pow(st, 3) \t- 3 * Math.pow(st, 2) + 1; c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); c3 = \t Math.pow(st, 3)\t- 2 * Math.pow(st, 2) + st; c4 = \t Math.pow(st, 3)\t- \t Math.pow(st, 2); // calc x and y cords with common control vectors x = c1 * _pts[i]\t+ c2 * _pts[i+2] + c3 * t1x + c4 * t2x; y = c1 * _pts[i+1]\t+ c2 * _pts[i+3] + c3 * t1y + c4 * t2y; //store points in array res.push(x); res.push(y); } } return res; } function drawLines(ctx, pts) { ctx.moveTo(pts[0], pts[1]); for(i=2;i<pts.length-1;i+=2) ctx.lineTo(pts[i], pts[i+1]); } |
1 | canvas { border: 1px solid red; } |
1 | <canvas id="c"><canvas> |
结果是:
您可以轻松扩展画布,因此可以这样调用它:
1 | ctx.drawCurve(myPoints); |
将以下内容添加到javascript中:
1 2 3 4 5 | if (CanvasRenderingContext2D != 'undefined') { CanvasRenderingContext2D.prototype.drawCurve = function(pts, tension, isClosed, numOfSegments, showPoints) { drawCurve(this, pts, tension, isClosed, numOfSegments, showPoints)} } |
您可以在NPM(
第一个答案并不能说明所有问题。该图将精确地穿过所有点,并且将是一条完美曲线,其中点[[x:,y:}] n个这样的点。
1 2 3 4 5 6 7 8 9 10 11 12 13 | var points = [{x:1,y:1},{x:2,y:3},{x:3,y:4},{x:4,y:2},{x:5,y:6}] //took 5 example points ctx.moveTo((points[0].x), points[0].y); for(var i = 0; i < points.length-1; i ++) { var x_mid = (points[i].x + points[i+1].x) / 2; var y_mid = (points[i].y + points[i+1].y) / 2; var cp_x1 = (x_mid + points[i].x) / 2; var cp_x2 = (x_mid + points[i+1].x) / 2; ctx.quadraticCurveTo(cp_x1,points[i].y ,x_mid, y_mid); ctx.quadraticCurveTo(cp_x2,points[i+1].y ,points[i+1].x,points[i+1].y); } |
正如Daniel Howard所指出的,Rob Spencer在http://scaledinnovation.com/analytics/splines/aboutSplines.html上描述了您想要的东西。
这是一个交互式演示:http://jsbin.com/ApitIxo/2/
这是jsbin关闭时的摘要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | <!DOCTYPE html> <html> <head> <meta charset=utf-8 /> Demo smooth connection </head> <body> Click to build a smooth path. (See Rob Spencer's article) <label><input type="checkbox" id="showPoints" checked> Show points</label> <label><input type="checkbox" id="showControlLines" checked> Show control lines</label> <label> <input type="range" id="tension" min="-1" max="2" step=".1" value=".5"> Tension <span id="tensionvalue">(0.5)</span> </label> <canvas id="canvas"></canvas> <style> html { position: relative; height: 100%; width: 100%; } body { position: absolute; left: 0; right: 0; top: 0; bottom: 0; } canvas { outline: 1px solid red; } #display { position: fixed; margin: 8px; background: white; z-index: 1; } </style> function update() { $("tensionvalue").innerHTML="("+$("tension").value+")"; drawSplines(); } $("showPoints").onchange = $("showControlLines").onchange = $("tension").onchange = update; // utility function function $(id){ return document.getElementById(id); } var canvas=$("canvas"), ctx=canvas.getContext("2d"); function setCanvasSize() { canvas.width = parseInt(window.getComputedStyle(document.body).width); canvas.height = parseInt(window.getComputedStyle(document.body).height); } window.onload = window.onresize = setCanvasSize(); function mousePositionOnCanvas(e) { var el=e.target, c=el; var scaleX = c.width/c.offsetWidth || 1; var scaleY = c.height/c.offsetHeight || 1; if (!isNaN(e.offsetX)) return { x:e.offsetX*scaleX, y:e.offsetY*scaleY }; var x=e.pageX, y=e.pageY; do { x -= el.offsetLeft; y -= el.offsetTop; el = el.offsetParent; } while (el); return { x: x*scaleX, y: y*scaleY }; } canvas.onclick = function(e){ var p = mousePositionOnCanvas(e); addSplinePoint(p.x, p.y); }; function drawPoint(x,y,color){ ctx.save(); ctx.fillStyle=color; ctx.beginPath(); ctx.arc(x,y,3,0,2*Math.PI); ctx.fill() ctx.restore(); } canvas.onmousemove = function(e) { var p = mousePositionOnCanvas(e); $("mouse").innerHTML = p.x+","+p.y; }; var pts=[]; // a list of x and ys // given an array of x,y's, return distance between any two, // note that i and j are indexes to the points, not directly into the array. function dista(arr, i, j) { return Math.sqrt(Math.pow(arr[2*i]-arr[2*j], 2) + Math.pow(arr[2*i+1]-arr[2*j+1], 2)); } // return vector from i to j where i and j are indexes pointing into an array of points. function va(arr, i, j){ return [arr[2*j]-arr[2*i], arr[2*j+1]-arr[2*i+1]] } function ctlpts(x1,y1,x2,y2,x3,y3) { var t = $("tension").value; var v = va(arguments, 0, 2); var d01 = dista(arguments, 0, 1); var d12 = dista(arguments, 1, 2); var d012 = d01 + d12; return [x2 - v[0] * t * d01 / d012, y2 - v[1] * t * d01 / d012, x2 + v[0] * t * d12 / d012, y2 + v[1] * t * d12 / d012 ]; } function addSplinePoint(x, y){ pts.push(x); pts.push(y); drawSplines(); } function drawSplines() { clear(); cps = []; // There will be two control points for each"middle" point, 1 ... len-2e for (var i = 0; i < pts.length - 2; i += 1) { cps = cps.concat(ctlpts(pts[2*i], pts[2*i+1], pts[2*i+2], pts[2*i+3], pts[2*i+4], pts[2*i+5])); } if ($("showControlLines").checked) drawControlPoints(cps); if ($("showPoints").checked) drawPoints(pts); drawCurvedPath(cps, pts); } function drawControlPoints(cps) { for (var i = 0; i < cps.length; i += 4) { showPt(cps[i], cps[i+1],"pink"); showPt(cps[i+2], cps[i+3],"pink"); drawLine(cps[i], cps[i+1], cps[i+2], cps[i+3],"pink"); } } function drawPoints(pts) { for (var i = 0; i < pts.length; i += 2) { showPt(pts[i], pts[i+1],"black"); } } function drawCurvedPath(cps, pts){ var len = pts.length / 2; // number of points if (len < 2) return; if (len == 2) { ctx.beginPath(); ctx.moveTo(pts[0], pts[1]); ctx.lineTo(pts[2], pts[3]); ctx.stroke(); } else { ctx.beginPath(); ctx.moveTo(pts[0], pts[1]); // from point 0 to point 1 is a quadratic ctx.quadraticCurveTo(cps[0], cps[1], pts[2], pts[3]); // for all middle points, connect with bezier for (var i = 2; i < len-1; i += 1) { // console.log("to", pts[2*i], pts[2*i+1]); ctx.bezierCurveTo( cps[(2*(i-1)-1)*2], cps[(2*(i-1)-1)*2+1], cps[(2*(i-1))*2], cps[(2*(i-1))*2+1], pts[i*2], pts[i*2+1]); } ctx.quadraticCurveTo( cps[(2*(i-1)-1)*2], cps[(2*(i-1)-1)*2+1], pts[i*2], pts[i*2+1]); ctx.stroke(); } } function clear() { ctx.save(); // use alpha to fade out ctx.fillStyle ="rgba(255,255,255,.7)"; // clear screen ctx.fillRect(0,0,canvas.width,canvas.height); ctx.restore(); } function showPt(x,y,fillStyle) { ctx.save(); ctx.beginPath(); if (fillStyle) { ctx.fillStyle = fillStyle; } ctx.arc(x, y, 5, 0, 2*Math.PI); ctx.fill(); ctx.restore(); } function drawLine(x1, y1, x2, y2, strokeStyle){ ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); if (strokeStyle) { ctx.save(); ctx.strokeStyle = strokeStyle; ctx.stroke(); ctx.restore(); } else { ctx.save(); ctx.strokeStyle ="pink"; ctx.stroke(); ctx.restore(); } } </body> </html> |
我发现这很好用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function drawCurve(points, tension) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); var t = (tension != null) ? tension : 1; for (var i = 0; i < points.length - 1; i++) { var p0 = (i > 0) ? points[i - 1] : points[0]; var p1 = points[i]; var p2 = points[i + 1]; var p3 = (i != points.length - 2) ? points[i + 2] : p2; var cp1x = p1.x + (p2.x - p0.x) / 6 * t; var cp1y = p1.y + (p2.y - p0.y) / 6 * t; var cp2x = p2.x - (p3.x - p1.x) / 6 * t; var cp2y = p2.y - (p3.y - p1.y) / 6 * t; ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y); } ctx.stroke(); } |
我决定添加内容,而不是将解决方案发布到另一篇文章中。
以下是我构建的解决方案,可能并不完美,但到目前为止输出效果很好。
重要提示:它将贯穿所有要点!
如果您有任何想法,请使其改善。谢谢。
以下是之前和之后的比较:
将此代码保存为HTML进行测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <!DOCTYPE html> <html> <body> \t<canvas id="myCanvas" width="1200" height="700" style="border:1px solid #d3d3d3;">Your browser does not support the HTML5 canvas tag.</canvas> \t \t\tvar cv = document.getElementById("myCanvas"); \t\tvar ctx = cv.getContext("2d"); \t\tfunction gradient(a, b) { \t\t\treturn (b.y-a.y)/(b.x-a.x); \t\t} \t\tfunction bzCurve(points, f, t) { \t\t\t//f = 0, will be straight line \t\t\t//t suppose to be 1, but changing the value can control the smoothness too \t\t\tif (typeof(f) == 'undefined') f = 0.3; \t\t\tif (typeof(t) == 'undefined') t = 0.6; \t\t\tctx.beginPath(); \t\t\tctx.moveTo(points[0].x, points[0].y); \t\t\tvar m = 0; \t\t\tvar dx1 = 0; \t\t\tvar dy1 = 0; \t\t\tvar preP = points[0]; \t\t\tfor (var i = 1; i < points.length; i++) { \t\t\t\tvar curP = points[i]; \t\t\t\tnexP = points[i + 1]; \t\t\t\tif (nexP) { \t\t\t\t\tm = gradient(preP, nexP); \t\t\t\t\tdx2 = (nexP.x - curP.x) * -f; \t\t\t\t\tdy2 = dx2 * m * t; \t\t\t\t} else { \t\t\t\t\tdx2 = 0; \t\t\t\t\tdy2 = 0; \t\t\t\t} \t\t\t\tctx.bezierCurveTo(preP.x - dx1, preP.y - dy1, curP.x + dx2, curP.y + dy2, curP.x, curP.y); \t\t\t\tdx1 = dx2; \t\t\t\tdy1 = dy2; \t\t\t\tpreP = curP; \t\t\t} \t\t\tctx.stroke(); \t\t} \t\t// Generate random data \t\tvar lines = []; \t\tvar X = 10; \t\tvar t = 40; //to control width of X \t\tfor (var i = 0; i < 100; i++ ) { \t\t\tY = Math.floor((Math.random() * 300) + 50); \t\t\tp = { x: X, y: Y }; \t\t\tlines.push(p); \t\t\tX = X + t; \t\t} \t\t//draw straight line \t\tctx.beginPath(); \t\tctx.setLineDash([5]); \t\tctx.lineWidth = 1; \t\tbzCurve(lines, 0, 1); \t\t//draw smooth line \t\tctx.setLineDash([0]); \t\tctx.lineWidth = 2; \t\tctx.strokeStyle ="blue"; \t\tbzCurve(lines, 0.3, 1); \t </body> </html> |
尝试一下KineticJS-您可以使用点数组定义样条线。这是一个例子:
旧网址:http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
参见存档网址:https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
太晚了,但受到Homan出色的简单答案的启发,让我发布了一个更通用的解决方案(一般意义上来说,Homan的解决方案在少于3个顶点的点阵列上发生崩溃):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function smooth(ctx, points) { if(points == undefined || points.length == 0) { return true; } if(points.length == 1) { ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[0].x, points[0].y); return true; } if(points.length == 2) { ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[1].x, points[1].y); return true; } ctx.moveTo(points[0].x, points[0].y); for (var i = 1; i < points.length - 2; i ++) { var xc = (points[i].x + points[i + 1].x) / 2; var yc = (points[i].y + points[i + 1].y) / 2; ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc); } ctx.quadraticCurveTo(points[i].x, points[i].y, points[i+1].x, points[i+1].y); } |
如果要通过n个点确定曲线方程,则以下代码将为您提供n-1阶多项式的系数,并将这些系数保存到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var xPoints=[2,4,3,6,7,10]; //example coordinates var yPoints=[2,5,-2,0,2,8]; var coefficients=[]; for (var m=0; m<xPoints.length; m++) coefficients[m]=0; for (var m=0; m<xPoints.length; m++) { var newCoefficients=[]; for (var nc=0; nc<xPoints.length; nc++) newCoefficients[nc]=0; if (m>0) { newCoefficients[0]=-xPoints[0]/(xPoints[m]-xPoints[0]); newCoefficients[1]=1/(xPoints[m]-xPoints[0]); } else { newCoefficients[0]=-xPoints[1]/(xPoints[m]-xPoints[1]); newCoefficients[1]=1/(xPoints[m]-xPoints[1]); } var startIndex=1; if (m==0) startIndex=2; for (var n=startIndex; n<xPoints.length; n++) { if (m==n) continue; for (var nc=xPoints.length-1; nc>=1; nc--) { newCoefficients[nc]=newCoefficients[nc]*(-xPoints[n]/(xPoints[m]-xPoints[n]))+newCoefficients[nc-1]/(xPoints[m]-xPoints[n]); } newCoefficients[0]=newCoefficients[0]*(-xPoints[n]/(xPoints[m]-xPoints[n])); } for (var nc=0; nc<xPoints.length; nc++) coefficients[nc]+=yPoints[m]*newCoefficients[nc]; } |
为了添加到K3N的基本样条方法中,并可能解决T. J. Crowder对在误导位置"浸入"曲线的担忧,我在
1 2 3 4 5 6 | if ((y < _pts[i+1] && y < _pts[i+3]) || (y > _pts[i+1] && y > _pts[i+3])) { y = (_pts[i+1] + _pts[i+3]) / 2; } if ((x < _pts[i] && x < _pts[i+2]) || (x > _pts[i] && x > _pts[i+2])) { x = (_pts[i] + _pts[i+2]) / 2; } |
这有效地在每对连续点之间创建一个(不可见的)边界框,并确保曲线停留在该边界框内-即。如果曲线上的一个点在两个点的上方/下方/左侧/右侧,则其位置将更改为位于框内。这里使用了中点,但是可以通过使用线性插值来改善。