W przypadku aplikacji do rysowania zapisuję współrzędne ruchu myszy w tablicy, a następnie rysuję je za pomocą lineTo. Wynikowa linia nie jest gładka. Jak mogę utworzyć pojedynczą krzywą między wszystkimi zebranymi punktami?
Wyszukałem w Google, ale znalazłem tylko 3 funkcje do rysowania linii: W przypadku 2 punktów próbkowania po prostu użyj lineTo
. Do 3 punktów próbki quadraticCurveTo
, z 4 punktów na próbki bezierCurveTo
.
(Próbowałem narysować a bezierCurveTo
dla każdych 4 punktów w tablicy, ale prowadzi to do załamań co 4 punkty próbkowania, zamiast ciągłej gładkiej krzywej.)
Jak napisać funkcję rysującą gładką krzywą z 5 punktami próbkowania i więcej?
Odpowiedzi:
Problem z łączeniem kolejnych punktów próbkowania wraz z rozłącznymi funkcjami typu „curveTo” polega na tym, że miejsce, w którym spotykają się krzywe, nie jest gładkie. Dzieje się tak, ponieważ dwie krzywe mają wspólny punkt końcowy, ale wpływają na nie całkowicie rozłączne punkty kontrolne. Jednym z rozwiązań jest „zakrzywienie” do punktów środkowych między następnymi 2 kolejnymi punktami próbkowania. Łączenie krzywych za pomocą tych nowych interpolowanych punktów zapewnia płynne przejście w punktach końcowych (punkt końcowy dla jednej iteracji staje się punktem kontrolnym dla następnej iteracji). Innymi słowy, dwie rozłączne krzywe mają teraz znacznie więcej wspólnego.
To rozwiązanie zostało wyodrębnione z książki „Foundation ActionScript 3.0 Animation: Making Things Move”. s.95 - techniki renderowania: tworzenie wielu krzywych.
Uwaga: to rozwiązanie właściwie nie rysuje przez każdy z punktów, co było tytułem mojego pytania (raczej aproksymuje krzywą przez punkty próbkowania, ale nigdy nie przechodzi przez punkty próbkowania), ale dla moich celów (aplikacja do rysowania), to jest dla mnie wystarczająco dobre i wizualnie nie możesz odróżnić. Jest to rozwiązanie, aby przejść przez wszystkie punkty próbkowania, ale jest o wiele bardziej skomplikowane (patrz http://www.cartogrammar.com/blog/actionscript-curves-update/ )
Oto kod rysowania metody aproksymacji:
// 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);
źródło
Trochę późno, ale dla przypomnienia.
Możesz uzyskać gładkie linie, używając splajnów kardynalnych (inaczej splajnów kanonicznych) do rysowania gładkich krzywych przechodzących przez punkty.
Zrobiłem tę funkcję dla płótna - jest podzielona na trzy funkcje, aby zwiększyć wszechstronność. Główna funkcja opakowania wygląda następująco:
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); } }
Zwrócić krzywą mieć tablicę X, Y punktów w kolejności:
x1,y1, x2,y2, ...xn,yn
.Użyj tego w ten sposób:
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);
Powyższa funkcja wywołuje dwie podfunkcje, jedną do obliczenia wygładzonych punktów. Zwraca tablicę z nowymi punktami - to jest główna funkcja, która oblicza wygładzone punkty:
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; }
Aby faktycznie narysować punkty jako wygładzoną krzywą (lub dowolną inną segmentowaną linię, o ile masz tablicę x, y):
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]); }
Pokaż fragment kodu
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 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; } 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]); }
canvas { border: 1px solid red; }
<canvas id="c"><canvas>
Skutkuje to:
Możesz łatwo rozszerzyć płótno, aby zamiast tego nazwać je w ten sposób:
Dodaj do javascript:
if (CanvasRenderingContext2D != 'undefined') { CanvasRenderingContext2D.prototype.drawCurve = function(pts, tension, isClosed, numOfSegments, showPoints) { drawCurve(this, pts, tension, isClosed, numOfSegments, showPoints)} }
Możesz znaleźć bardziej zoptymalizowaną wersję tego w NPM (
npm i cardinal-spline-js
) lub w GitLab .źródło
Pierwsza odpowiedź nie przejdzie przez wszystkie punkty. Ten wykres przejdzie dokładnie przez wszystkie punkty i będzie doskonałą krzywą z punktami jako [{x:, y:}] n takich punktów.
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); }
źródło
Jak zauważa Daniel Howard , Rob Spencer opisuje, czego chcesz, pod adresem http://scaledinnovation.com/analytics/splines/aboutSplines.html .
Oto interaktywne demo: http://jsbin.com/ApitIxo/2/
Tutaj jest to fragment kodu na wypadek, gdyby jsbin nie działał.
<!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>Demo smooth connection</title> </head> <body> <div id="display"> Click to build a smooth path. (See Rob Spencer's <a href="http://scaledinnovation.com/analytics/splines/aboutSplines.html">article</a>) <br><label><input type="checkbox" id="showPoints" checked> Show points</label> <br><label><input type="checkbox" id="showControlLines" checked> Show control lines</label> <br> <label> <input type="range" id="tension" min="-1" max="2" step=".1" value=".5" > Tension <span id="tensionvalue">(0.5)</span> </label> <div id="mouse"></div> </div> <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> <script> 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(); } } </script> </body> </html>
źródło
Okazało się, że to działa ładnie
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(); }
źródło
Postanawiam dodać, zamiast publikować moje rozwiązanie w innym poście. Poniżej przedstawiam rozwiązanie, które buduję, może nie jest idealne, ale jak na razie wyjście jest dobre.
Ważne: przejdzie przez wszystkie punkty!
Jeśli masz jakiś pomysł, aby to ulepszyć , podziel się ze mną. Dzięki.
Oto porównanie przed po:
Zapisz ten kod w formacie HTML, aby go przetestować.
<!DOCTYPE html> <html> <body> <canvas id="myCanvas" width="1200" height="700" style="border:1px solid #d3d3d3;">Your browser does not support the HTML5 canvas tag.</canvas> <script> var cv = document.getElementById("myCanvas"); var ctx = cv.getContext("2d"); function gradient(a, b) { return (b.y-a.y)/(b.x-a.x); } function bzCurve(points, f, t) { //f = 0, will be straight line //t suppose to be 1, but changing the value can control the smoothness too if (typeof(f) == 'undefined') f = 0.3; if (typeof(t) == 'undefined') t = 0.6; ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); var m = 0; var dx1 = 0; var dy1 = 0; var preP = points[0]; for (var i = 1; i < points.length; i++) { var curP = points[i]; nexP = points[i + 1]; if (nexP) { m = gradient(preP, nexP); dx2 = (nexP.x - curP.x) * -f; dy2 = dx2 * m * t; } else { dx2 = 0; dy2 = 0; } ctx.bezierCurveTo(preP.x - dx1, preP.y - dy1, curP.x + dx2, curP.y + dy2, curP.x, curP.y); dx1 = dx2; dy1 = dy2; preP = curP; } ctx.stroke(); } // Generate random data var lines = []; var X = 10; var t = 40; //to control width of X for (var i = 0; i < 100; i++ ) { Y = Math.floor((Math.random() * 300) + 50); p = { x: X, y: Y }; lines.push(p); X = X + t; } //draw straight line ctx.beginPath(); ctx.setLineDash([5]); ctx.lineWidth = 1; bzCurve(lines, 0, 1); //draw smooth line ctx.setLineDash([0]); ctx.lineWidth = 2; ctx.strokeStyle = "blue"; bzCurve(lines, 0.3, 1); </script> </body> </html>
źródło
Wypróbuj KineticJS - możesz zdefiniować splajn za pomocą tablicy punktów. Oto przykład:
Stary adres URL: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
Zobacz adres URL archiwum: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
źródło
Niezwykle późno, ale zainspirowana genialnie prostą odpowiedzią Homana, pozwólcie mi opublikować bardziej ogólne rozwiązanie (ogólne w tym sensie, że rozwiązanie Homana rozbija się na tablicach punktów z mniej niż 3 wierzchołkami):
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); }
źródło
Aby dodać do kardynalnej metody splajnów K3N i być może rozwiązać obawy TJ Crowdera dotyczące „zanurzania” krzywych w mylących miejscach, wstawiłem następujący kod do
getCurvePoints()
funkcji, tuż przedres.push(x);
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; }
To skutecznie tworzy (niewidoczną) ramkę graniczną między każdą parą kolejnych punktów i zapewnia, że krzywa pozostaje w tej ramce ograniczającej - tj. jeśli punkt na krzywej znajduje się powyżej / poniżej / po lewej / prawej stronie obu punktów, zmienia swoje położenie tak, aby znalazł się w ramce. Tutaj używany jest punkt środkowy, ale można to poprawić, być może stosując interpolację liniową.
źródło
Jeśli chcesz wyznaczyć równanie krzywej przez n punktów, to poniższy kod poda współczynniki wielomianu stopnia n-1 i zapisze te współczynniki do
coefficients[]
tablicy (zaczynając od stałego członu). Współrzędne x nie muszą być w porządku. To jest przykład wielomianu Lagrange'a .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]; }
źródło
Ten kod jest dla mnie idealny:
this.context.beginPath(); this.context.moveTo(data[0].x, data[0].y); for (let i = 1; i < data.length; i++) { this.context.bezierCurveTo( data[i - 1].x + (data[i].x - data[i - 1].x) / 2, data[i - 1].y, data[i - 1].x + (data[i].x - data[i - 1].x) / 2, data[i].y, data[i].x, data[i].y); }
masz poprawną gładką linię i prawidłowe punkty końcowe UWAGA! (y = "wysokość płótna" - y);
źródło