comment dessiner une courbe lisse à travers N points en utilisant javascript HTML5 canvas?

133

Pour une application de dessin, j'enregistre les coordonnées du mouvement de la souris dans un tableau, puis je les dessine avec lineTo. La ligne résultante n'est pas lisse. Comment puis-je produire une seule courbe entre tous les points rassemblés?

J'ai cherché sur Google mais je n'ai trouvé que 3 fonctions pour dessiner des lignes: Pour 2 points d'échantillonnage, utilisez simplement lineTo. 3 points d' échantillonnage quadraticCurveTo, pour 4 points d' échantillonnage, bezierCurveTo.

(J'ai essayé de dessiner un bezierCurveTopour 4 points dans le tableau, mais cela conduit à des plis tous les 4 points d'échantillonnage, au lieu d'une courbe continue lisse.)

Comment écrire une fonction pour dessiner une courbe lisse avec 5 points d'échantillonnage et au-delà?

Homan
la source
5
Qu'entendez-vous par «lisse»? Infiniment différentiable? Deux fois différenciable? Les splines cubiques («courbes de Bézier») ont de nombreuses bonnes propriétés et sont deux fois différentiables et assez faciles à calculer.
Kerrek SB
7
@Kerrek SB, par "lisse", je veux dire, je ne peux pas détecter visuellement les coins / cuspides, etc.
Homan
@sketchfemme, effectuez-vous le rendu des lignes en temps réel, ou retardez-vous le rendu après avoir collecté un tas de points?
Crashalot
@Crashalot Je rassemble les points dans un tableau. Vous avez besoin d'au moins 4 points pour utiliser cet algorithme. Après cela, vous pouvez rendre en temps réel sur une toile en effaçant l'écran à chaque appel de mouseMove
Homan
1
@sketchfemme: N'oubliez pas d'accepter une réponse. C'est bien si c'est le vôtre .
TJ Crowder du

Réponses:

130

Le problème de la jonction de points d'échantillonnage ultérieurs avec des fonctions disjointes de type «curveTo» est que la rencontre des courbes n'est pas lisse. En effet, les deux courbes partagent un point final mais sont influencées par des points de contrôle complètement disjoints. Une solution consiste à «courber» les points médians entre les 2 points d'échantillonnage suivants. Joindre les courbes à l'aide de ces nouveaux points interpolés donne une transition douce aux points d'extrémité (ce qui est un point final pour une itération devient un point de contrôle pour l'itération suivante.) En d'autres termes, les deux courbes disjointes ont beaucoup plus en commun maintenant.

Cette solution a été extraite du livre "Foundation ActionScript 3.0 Animation: Faire bouger les choses". p.95 - techniques de rendu: création de plusieurs courbes.

Remarque: cette solution ne trace pas réellement à travers chacun des points, qui était le titre de ma question (elle se rapproche plutôt de la courbe à travers les points d'échantillonnage mais ne passe jamais par les points d'échantillonnage), mais pour mes besoins (une application de dessin), c'est assez bon pour moi et visuellement vous ne pouvez pas faire la différence. Il existe une solution pour parcourir tous les exemples de points, mais c'est beaucoup plus compliqué (voir http://www.cartogrammar.com/blog/actionscript-curves-update/ )

Voici le code de dessin pour la méthode d'approximation:

// 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
la source
+1 Cela a très bien fonctionné pour un projet JavaScript / canvas sur lequel je travaille
Matt
1
Heureux d'avoir pu aider. Pour info, j'ai démarré un bloc de dessin en toile html5 open source qui est un plugin jQuery. Cela devrait être un point de départ utile. github.com/homanchou/sketchyPad
Homan
4
C'est bien, mais comment feriez-vous la courbe pour qu'elle passe par tous les points?
Richard
Avec cet algorithme, chaque courbe successive est-elle censée commencer à partir du point final des courbes précédentes?
Lee Brindley
Merci beaucoup Homan! Ça marche! J'ai passé tellement de jours à le résoudre. Et bonjour de la communauté Delphi Android / iOS!
alitrun
104

Un peu tard, mais pour mémoire.

Vous pouvez obtenir des lignes lisses en utilisant des splines cardinales (aka spline canonique) pour dessiner des courbes lisses passant par les points.

J'ai créé cette fonction pour la toile - elle est divisée en trois fonctions pour augmenter la polyvalence. La fonction principale du wrapper ressemble à ceci:

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

Pour dessiner une courbe, utilisez un tableau avec x, y points dans l'ordre: x1,y1, x2,y2, ...xn,yn .

Utilisez-le comme ceci:

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

La fonction ci-dessus appelle deux sous-fonctions, une pour calculer les points lissés. Cela renvoie un tableau avec de nouveaux points - c'est la fonction principale qui calcule les points lissés:

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

Et pour dessiner réellement les points sous forme de courbe lissée (ou toute autre ligne segmentée tant que vous avez un tableau 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]);
}

Il en résulte ceci:

Exemple de pix

Vous pouvez facilement étendre le canevas pour pouvoir l'appeler comme ceci à la place:

ctx.drawCurve(myPoints);

Ajoutez ce qui suit au javascript:

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

Vous pouvez trouver une version plus optimisée de ceci sur NPM ( npm i cardinal-spline-js) ou sur GitLab .


la source
3
Tout d'abord: c'est magnifique. :-) Mais en regardant cette image, cela ne donne-t-il pas l'impression (trompeuse) que les valeurs sont en fait passées sous la valeur # 10 en route entre # 9 et # 10? (Je compte à partir des points réels que je peux voir, donc le n ° 1 serait celui près du haut de la trajectoire descendante initiale, le n ° 2 celui tout en bas [point le plus bas du graphique], et ainsi de suite ... )
TJ Crowder
6
Je veux juste dire qu'après des jours de recherche, c'était le seul utilitaire qui fonctionnait exactement comme je le voulais. Merci beaucoup
cnp
4
OUI OUI OUI Merci! J'ai sauté et dansé de joie.
Jeffrey Sun
1
Il y a une erreur de type dans votre code. Le paramètre ptsadevrait être pts, sinon il générerait des erreurs.
gfaceless
2
Il y a longtemps, vous avez publié cette solution et vous m'avez aidé aujourd'hui à résoudre un gros problème. Merci beaucoup!
ÂlexBay
19

La première réponse ne passera pas par tous les points. Ce graphe passera exactement par tous les points et sera une courbe parfaite avec les points comme [{x:, y:}] n tels points.

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
la source
1
C'est de loin l'approche la plus simple et la plus correcte.
haymez
10

Comme le souligne Daniel Howard , Rob Spencer décrit ce que vous voulez sur http://scaledinnovation.com/analytics/splines/aboutSplines.html .

Voici une démo interactive: http://jsbin.com/ApitIxo/2/

Ici, c'est comme un extrait au cas où jsbin serait en panne.

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

J'ai trouvé que cela fonctionnait bien

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
la source
6

Je décide d'ajouter, plutôt que de publier ma solution dans un autre article. Voici la solution que je construis, peut-être pas parfaite, mais jusqu'à présent, le rendement est bon.

Important: il passera par tous les points!

Si vous avez une idée, pour l' améliorer , partagez-la avec moi. Merci.

Voici la comparaison d'avant après:

entrez la description de l'image ici

Enregistrez ce code au format HTML pour le tester.

    <!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.
la source
5

Essayez KineticJS - vous pouvez définir une spline avec un tableau de points. Voici un exemple:

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

Voir l'url de l'archive: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/

Eric Rowell
la source
Incroyable lib! Le meilleur pour la tâche!
Dziad Borowy
Oui!! J'avais besoin de la fonction blob () pour créer une forme fermée qui passe par tous les points.
AwokeKnowing le
7
404 Page non trouvée.
dieter
Lien d'origine - 404 non trouvé - voir web.archive.org/web/20141204030628/http
//...
1

Incroyablement tardive mais inspirée par la réponse brillamment simple de Homan, permettez-moi de poster une solution plus générale (générale dans le sens où la solution de Homan plante sur des tableaux de points avec moins de 3 sommets):

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
la source
0

Pour ajouter à la méthode des splines cardinales de K3N et peut-être répondre aux préoccupations de TJ Crowder concernant le `` trempage '' des courbes à des endroits trompeurs, j'ai inséré le code suivant dans la getCurvePoints()fonction, juste avantres.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;
}

Cela crée effectivement une boîte englobante (invisible) entre chaque paire de points successifs et garantit que la courbe reste dans cette boîte englobante - c.-à-d. si un point de la courbe est au-dessus / en dessous / à gauche / à droite des deux points, il modifie sa position pour être dans la boîte. Ici, le point médian est utilisé, mais cela pourrait être amélioré, peut-être en utilisant une interpolation linéaire.

James Pearce
la source
0

Si vous souhaitez déterminer l'équation de la courbe passant par n points, le code suivant vous donnera les coefficients du polynôme de degré n-1 et enregistrera ces coefficients dans le coefficients[]tableau (à partir du terme constant). Les coordonnées x n'ont pas à être dans l'ordre. Ceci est un exemple de polynôme 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
la source