як намалювати плавну криву через N точок за допомогою полотна javascript HTML5?


133

Для програми для малювання я зберігаю координати руху миші до масиву, а потім малюю їх з допомогою рядкаTo. Отримана лінія не є гладкою. Як я можу створити єдину криву між усіма зібраними точками?

Я googled, але я знайшов лише 3 функції для малювання ліній: Для 2-х зразкових точок просто використовуйте lineTo. В протягом 3 точок вибірки quadraticCurveTo, для 4 точок вибірки, bezierCurveTo.

(Я спробував намалювати bezierCurveToкожні 4 точки в масиві, але це призводить до перекручування кожні 4 вибіркові точки замість безперервної плавної кривої.)

Як написати функцію малювання гладкої кривої з 5 точками вибірки і далі?


5
Що ви маєте на увазі під "гладким"? Нескінченно диференційований? Двічі диференційований? Кубічні сплайни ("криві Безьє") мають багато хороших властивостей і є вдвічі диференційованими та досить простими для обчислення.
Керрек СБ

7
@ Керрек С.Б., під "гладким" я маю на увазі візуально не вдається виявити жодних куточків / загострень тощо
Хоман

@sketchfemme, чи ви рендеруєте рядки в режимі реального часу, або затримуєте візуалізацію, поки не наберете купу очок?
Crashalot

@Crashalot Я збираю очки в масив. Для використання цього алгоритму вам потрібно щонайменше 4 бали. Після цього ви можете візуалізувати в режимі реального часу на полотні, очистивши екран під час кожного дзвінка миші
Хоман

1
@sketchfemme: Не забудьте прийняти відповідь. Це добре, якщо це ваш власний .
TJ Crowder

Відповіді:


130

Проблема з приєднанням наступних зразкових точок разом із роз'єднаними функціями типу "curveTo" полягає в тому, що там, де зустрічаються криві, не є рівним. Це пояснюється тим, що дві криві розділяють кінцеву точку, але на них впливають повністю непересічні контрольні точки. Одним із варіантів є "крива до" середини між наступними двома наступними точками вибірки. Об'єднання кривих за допомогою цих нових інтерпольованих точок дає плавний перехід у кінцевих точках (те, що є кінцевою точкою для однієї ітерації, стає контрольною точкою для наступної ітерації.) Іншими словами, дві розрізнені криві мають набагато більше спільного.

Це рішення було витягнуто з книги "Фонд ActionScript 3.0 анімації: переміщення речей". с.95 - техніка візуалізації: створення декількох кривих.

Зауважте: це рішення насправді не проводиться через кожну з точок, яка була назвою мого запитання (скоріше, вона наближає криву через вибіркові точки, але ніколи не проходить через вибіркові точки), але для моїх цілей (додаток для малювання), це досить добре для мене, і візуально ви не можете сказати різницю. Там є рішення пройти через всі точки вибірки, але це набагато складніша (див http://www.cartogrammar.com/blog/actionscript-curves-update/ )

Ось креслення коду для методу наближення:

// 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 Це чудово підходило для проекту JavaScript / полотно, над яким я працюю
Метт

1
Радий допомогти. FYI, я запустив графічний майданчик із відкритим кодом html5 для малювання полотна, який є плагіном jQuery. Це повинно бути корисною відправною точкою. github.com/homanchou/sketchyPad
Хоман

4
Це добре, але як би ви зробили криву, щоб вона проходила через усі точки?
Річард

За допомогою цього алгоритму кожна послідовна крива повинна починатись із кінцевої точки попередньої кривої?
Лі Бріндлі

Дякую тобі Гоман! Це працює! Я витратив стільки днів, щоб вирішити це. Привіт від спільноти Delphi Android / iOS!
alitrun

104

Трохи запізнився, але для запису.

Домогтися плавних ліній можна за допомогою кардинальних сплайнів (ака-канонічний сплайн), щоб намалювати плавні криві, що проходять через точки.

Я зробив цю функцію для полотна - вона розбита на три функції для збільшення універсальності. Основна функція обгортки виглядає так:

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 точками у порядку: x1,y1, x2,y2, ...xn,yn .

Використовуйте його так:

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

Функція вище викликає дві підфункції, одну для обчислення згладжених точок. Це повертає масив з новими точками - це основна функція, яка обчислює згладжені точки:

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

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

Це призводить до цього:

Приклад пікс

Ви можете легко розширити полотно, щоб ви могли назвати його так:

ctx.drawCurve(myPoints);

Додайте в javascript таке:

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

Ви можете знайти більш оптимізовану версію цього в NPM ( npm i cardinal-spline-js) або на GitLab .


3
По-перше: це чудово. :-) Але дивлячись на це зображення, чи не створює (оманливе) враження, що значення насправді були нижче значення №10 на шляху між №9 та №10? (Я рахую з фактичних крапок, які я бачу, тому №1 буде тією, що знаходиться вгорі початкової низхідної траєкторії, №2 - в самому низу [найнижча точка на графіку] тощо) ... )
TJ Crowder

6
Просто хочу сказати, що після днів пошуку це була єдина утиліта, яка насправді працювала саме так, як я хотів. Велике спасибі
cnp

4
ТАК ТАК ДЯКУЮ! Я підскочив і від радості танцював.
Джеффрі нд

1
У коді є помилка типу. Параметр ptsaповинен бути pts, інакше він би кидав помилки.
липня

2
Давно ви опублікували це рішення, і ви сьогодні допомогли мені вирішити велике питання. Велике спасибі!
ÂlexBay

19

Перша відповідь не пройде через усі моменти. Цей графік точно пройде через усі точки і буде ідеальною кривою з точками як [{x:, y:}] n таких точок.

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

1
Це, безумовно, найпростіший і правильний підхід.
haymez

10

Як зазначає Даніель Говард , Роб Спенсер описує те, що ви хочете, на сторінці http://scaledinnovation.com/analytics/splines/aboutSplines.html .

Ось інтерактивна демонстрація: http://jsbin.com/ApitIxo/2/

Ось це як фрагмент, якщо jsbin не працює.

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


7

Я виявив, що це добре працює

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

6

Я вирішую додати, а не розміщувати своє рішення на іншій посаді. Нижче наведено рішення, яке я будую, можливо, не буде ідеальним, але поки результат хороший.

Важливо: воно пройде через усі пункти!

Якщо у вас є ідея, щоб зробити її кращою , будь ласка, поділіться зі мною. Дякую.

Ось порівняння раніше після:

введіть тут опис зображення

Збережіть цей код у HTML, щоб перевірити його.

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


5

Спробуйте KineticJS - ви можете визначити сплайн із масивом точок. Ось приклад:

Стара URL-адреса: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/

Дивіться URL-адресу архіву: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/


Дивовижна лайка! Найкращий для виконання завдання!
Dziad Borowy

так!! Мені потрібна була функція blob (), щоб зробити закриту форму, яка проходить через усі точки.
AwokeKnowing

7
404. Сторінку не знайдено.
дітер

Оригінальне посилання - знайдено 404 вузла - див. Web.archive.org/web/20141204030628/http://…
супутники

1

Неймовірно пізня, але натхненна геніально простою відповіддю Хомана, дозволяють мені опублікувати більш загальне рішення (загальне в тому сенсі, що рішення Хомана розбивається на масиви точок з меншими 3 вершинами):

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

0

Щоб додати до методу кардинальних сплайнів K3N і, можливо, вирішити занепокоєння TJ Crowder щодо кривих "занурення" в оманливих місцях, я вставив наступний код у getCurvePoints()функцію, безпосередньо перед цимres.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;
}

Це ефективно створює (невидимий) обмежувальний ящик між кожною парою послідовних точок і забезпечує криву, що залишається всередині цього обмежувального поля - тобто. якщо точка на кривій знаходиться вище / нижче / ліво / праворуч від обох точок, вона змінює своє положення, щоб бути в межах поля. Тут використовується середина, але це можна вдосконалити, можливо, використовуючи лінійну інтерполяцію.


0

Якщо ви хочете визначити рівняння кривої через n точок, то наступний код дасть вам коефіцієнти многочлена ступеня n-1 і збереже ці коефіцієнти до coefficients[]масиву (починаючи з постійного члена). Координати x не повинні бути в порядку. Це приклад многочлена Лагранжа .

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];
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.