como desenhar curvas suaves através de N pontos usando a tela HTML5 em javascript?

133

Para um aplicativo de desenho, estou salvando as coordenadas de movimento do mouse em uma matriz e desenhando-as com lineTo. A linha resultante não é suave. Como posso produzir uma única curva entre todos os pontos reunidos?

Eu pesquisei no Google, mas encontrei apenas 3 funções para desenhar linhas: para 2 pontos de amostra, basta usar lineTo. Para 3 pontos de amostra quadraticCurveTo, para 4 pontos de amostra bezierCurveTo,.

(Tentei desenhar um bezierCurveTopara cada 4 pontos na matriz, mas isso leva a dobras a cada 4 pontos de amostra, em vez de uma curva suave contínua.)

Como escrevo uma função para desenhar uma curva suave com 5 pontos de amostra e além?

Homan
fonte
5
O que você quer dizer com "suave"? Infinitamente diferenciável? Duas vezes diferenciável? As splines cúbicas ("curvas de Bezier") têm muitas boas propriedades e são duas vezes diferenciáveis ​​e fáceis de calcular.
Kerrek SB
7
@Kerrek SB, por "suave" quero dizer que visualmente não consigo detectar cantos / cúspides etc.
Homan
@sketchfemme, você está renderizando as linhas em tempo real ou atrasando a renderização até depois de coletar vários pontos?
`` Crashalot
@Crashalot Estou coletando os pontos em uma matriz. Você precisa de pelo menos 4 pontos para usar esse algoritmo. Depois disso, você pode render em tempo real em uma tela de limpar a tela em cada chamada de mouseMove
Homan
1
@sketchfemme: Não se esqueça de aceitar uma resposta. Tudo bem se for seu .
TJ Crowder

Respostas:

130

O problema de unir os pontos de amostra subsequentes às funções disjuntas do tipo "curveTo" é que o local onde as curvas se encontram não é suave. Isso ocorre porque as duas curvas compartilham um ponto final, mas são influenciadas por pontos de controle totalmente disjuntos. Uma solução é "curvar-se" para os pontos médios entre os próximos 2 pontos de amostra subsequentes. Unir as curvas usando esses novos pontos interpolados fornece uma transição suave nos pontos finais (o que é um ponto final para uma iteração se torna um ponto de controle para a próxima iteração.) Em outras palavras, as duas curvas desconexas têm muito mais em comum agora.

Esta solução foi extraída do livro "Foundation ActionScript 3.0 Animation: Making Things Move". p.95 - técnicas de renderização: criando múltiplas curvas.

Nota: esta solução, na verdade, não passa por cada um dos pontos, que era o título da minha pergunta (ela aproxima a curva pelos pontos de amostra, mas nunca passa pelos pontos de amostra), mas, para meus propósitos (um aplicativo de desenho), é bom o suficiente para mim e, visualmente, você não percebe a diferença. Não é uma solução para passar por todos os pontos de amostragem, mas é muito mais complicado (ver http://www.cartogrammar.com/blog/actionscript-curves-update/ )

Aqui está o código do desenho para o método de aproximação:

// 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);
Homan
fonte
+1 Isso funcionou muito bem para um JavaScript / canvas projeto que eu estou trabalhando em
Matt
1
Feliz por ajudar. Para sua informação, iniciei um bloco de desenho de tela html5 de código aberto que é um plug-in jQuery. Deve ser um ponto de partida útil. github.com/homanchou/sketchyPad
Homan
4
Isso é bom, mas como você faria a curva para que ela passasse por todos os pontos?
Richard
Com este algoritmo, cada curva sucessiva deve começar a partir do ponto final das curvas anteriores?
Lee Brindley
Muito obrigado Homan! Funciona! Passei tantos dias para resolvê-lo. E oi da comunidade Delphi Android / iOS!
alitrun
104

Um pouco tarde, mas para constar.

Você pode obter linhas suaves usando splines cardinais (também conhecidas como splines canônicas) para desenhar curvas suaves que atravessam os pontos.

Eu criei essa função para tela - ela é dividida em três funções para aumentar a versatilidade. A função principal do wrapper é assim:

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);
    }
}

Para desenhar uma curva tem uma matriz com x, y pontos na ordem: x1,y1, x2,y2, ...xn,yn.

Use-o assim:

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

A função acima chama duas subfunções, uma para calcular os pontos suavizados. Isso retorna uma matriz com novos pontos - esta é a função principal que calcula os pontos suavizados:

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

E para desenhar os pontos como uma curva suavizada (ou qualquer outra linha segmentada, desde que você tenha uma matriz 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]);
}

Isso resulta nisso:

Pix de exemplo

Você pode estender a tela facilmente para poder chamá-la assim:

ctx.drawCurve(myPoints);

Adicione o seguinte ao javascript:

if (CanvasRenderingContext2D != 'undefined') {
    CanvasRenderingContext2D.prototype.drawCurve = 
        function(pts, tension, isClosed, numOfSegments, showPoints) {
       drawCurve(this, pts, tension, isClosed, numOfSegments, showPoints)}
}

Você pode encontrar uma versão mais otimizada disso no NPM ( npm i cardinal-spline-js) ou no GitLab .


fonte
3
Primeiro: isso é lindo. :-) Mas olhando para essa imagem, não dá a impressão (enganosa) de que os valores realmente ficaram abaixo do valor # 10 no caminho entre os # 9 e # 10? (Estou contando a partir dos pontos reais que consigo ver, então o número 1 seria o mais próximo do topo da trajetória descendente inicial, o número 2, o mais abaixo [ponto mais baixo do gráfico], e assim por diante ... )
TJ Crowder
6
Só quero dizer que, após dias de pesquisa, esse foi o único utilitário que realmente funcionou exatamente como eu queria. Muito obrigado
cnp
4
SIM SIM SIM Obrigado! Eu pulei e dancei de alegria.
Jeffrey Dom
1
Há um erro de tipo no seu código. Parâmetro ptsadeve ser pts, ou então lançaria erros.
Gfaceless
2
Há muito tempo, você postou esta solução e me ajudou hoje a resolver um grande problema. Muito obrigado!
ÂlexBay
19

A primeira resposta não passará por todos os pontos. Este gráfico passará exatamente por todos os pontos e será uma curva perfeita com os pontos como [{x:, y:}] em tais pontos.

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);
}
Abhishek Kanthed
fonte
1
Essa é de longe a abordagem mais simples e correta.
haymez
10

Como Daniel Howard aponta , Rob Spencer descreve o que você deseja em http://scaledinnovation.com/analytics/splines/aboutSplines.html .

Aqui está uma demonstração interativa: http://jsbin.com/ApitIxo/2/

Aqui está como um trecho caso o jsbin esteja inoperante.

<!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>

Daniel Patru
fonte
7

Eu achei que isso funcionava bem

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();
}
Roy Aarts
fonte
6

Decido adicionar, em vez de postar minha solução em outro post. Abaixo está a solução que eu construo, pode não ser perfeita, mas até agora a saída é boa.

Importante: passará por todos os pontos!

Se você tem alguma idéia, para melhorar , compartilhe comigo. Obrigado.

Aqui está a comparação do antes depois:

insira a descrição da imagem aqui

Salve este código em HTML para testá-lo.

    <!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>

Eric K.
fonte
5

Experimente o KineticJS - você pode definir um Spline com uma matriz de pontos. Aqui está um exemplo:

URL antigo: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/

Consulte o URL do arquivo: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/

Eric Rowell
fonte
Lib incrível! O melhor para a tarefa!
Dziad Borowy
sim!! Eu precisava da função blob () para criar uma forma fechada que atravesse todos os pontos.
precisa saber é o seguinte
7
404 Página Não Encontrada.
Dieter #
Link original - 404 não encontrado - consulte web.archive.org/web/20141204030628/http://…
satels
1

Incrivelmente atrasado, mas inspirado pela resposta brilhantemente simples de Homan, permita-me postar uma solução mais geral (geral no sentido de que a solução de Homan trava em conjuntos de pontos com menos de 3 vértices):

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);
}
mxl
fonte
0

Para adicionar ao método de splines cardinais do K3N e talvez abordar as preocupações de TJ Crowder sobre curvas 'mergulhando' em lugares enganosos, inseri o seguinte código na getCurvePoints()função, pouco antesres.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;
}

Isso efetivamente cria uma caixa delimitadora (invisível) entre cada par de pontos sucessivos e garante que a curva permaneça dentro dessa caixa delimitadora - ie. se um ponto na curva estiver acima / abaixo / esquerda / direita dos dois pontos, ele altera sua posição para estar dentro da caixa. Aqui o ponto médio é usado, mas isso poderia ser melhorado, talvez usando interpolação linear.

James Pearce
fonte
0

Se você deseja determinar a equação da curva através de n pontos, o código a seguir fornecerá os coeficientes do polinômio de grau n-1 e salvará esses coeficientes na coefficients[]matriz (começando no termo constante). As coordenadas x não precisam estar em ordem. Este é um exemplo de um polinômio de Lagrange .

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];
}
Kevin Bertman
fonte