javascript HTML5 canvas kullanarak N nokta boyunca düzgün eğri nasıl çizilir?


133

Bir çizim uygulaması için, fare hareket koordinatlarını bir diziye kaydedip ardından lineTo ile çiziyorum. Ortaya çıkan çizgi düzgün değil. Toplanan tüm noktalar arasında nasıl tek bir eğri oluşturabilirim?

Googledim ancak çizgi çizmek için yalnızca 3 işlev buldum: 2 örnek nokta için kullanın lineTo. 3 numune noktası quadraticCurveToiçin 4 numune noktası için bezierCurveTo.

(Dizideki bezierCurveToher 4 nokta için bir a çizmeyi denedim , ancak bu sürekli bir düzgün eğri yerine her 4 örnek noktasında bükülmelere neden oluyor.)

5 örnek noktası ve ötesinde düzgün bir eğri çizmek için nasıl bir fonksiyon yazabilirim?


5
"Pürüzsüz" ile ne demek istiyorsun? Sonsuz türevlenebilir mi? İki kez türevlenebilir mi? Kübik spline'lar ("Bezier eğrileri") birçok iyi özelliğe sahiptir ve iki kez türevlenebilir ve hesaplanması kolay.
Kerrek SB

7
@Kerrek SB, "pürüzsüz" ile görsel olarak herhangi bir köşeyi / sivri uçları vb. Algılayamıyorum demek istiyorum.
Homan

@sketchfemme, çizgileri gerçek zamanlı olarak mı oluşturuyorsunuz, yoksa oluşturmayı birkaç nokta toplayana kadar mı erteliyorsunuz?
Crashalot

@Crashalot Noktaları bir dizi halinde topluyorum. Bu algoritmayı kullanmak için en az 4 puana ihtiyacınız var. Bundan sonra, mouseMove'un her çağrısında ekranı temizleyerek gerçek zamanlı olarak bir tuval üzerinde render edebilirsiniz
Homan

1
@sketchfemme: Bir cevabı kabul etmeyi unutmayın. Seninki ise sorun değil .
TJ Crowder

Yanıtlar:


130

Sonraki örnek noktalarının ayrık "curveTo" tipi fonksiyonlarla birleştirilmesiyle ilgili sorun, eğrilerin buluştuğu yerin düzgün olmamasıdır. Bunun nedeni, iki eğrinin bir bitiş noktasını paylaşması, ancak tamamen ayrık kontrol noktalarından etkilenmesidir. Bir çözüm, sonraki 2 örnek noktası arasındaki orta noktalara "eğri" yapmaktır. Bu yeni enterpolasyonlu noktaları kullanarak eğrileri birleştirmek, bitiş noktalarında yumuşak bir geçiş sağlar (bir yineleme için bir bitiş noktası olan, bir sonraki yineleme için bir kontrol noktası haline gelir .) Diğer bir deyişle, iki ayrık eğrinin artık çok daha fazla ortak noktası vardır.

Bu çözüm, "Temel ActionScript 3.0 Animasyonu: İşleri hareket ettirme" kitabından alınmıştır. s.95 - oluşturma teknikleri: birden çok eğri oluşturma.

Not: Bu çözüm aslında sorumun başlığı olan noktaların her birini çekmez (bunun yerine eğriyi örnek noktalarından kestirir, ancak örnek noktalarından asla geçmez), ancak amaçlarım için (bir çizim uygulaması), bu benim için yeterince iyi ve görsel olarak farkı anlayamazsın. Tüm örnek noktaların üzerinden geçmek için bir çözüm var , ancak çok daha karmaşık (bkz. Http://www.cartogrammar.com/blog/actionscript-curves-update/ )

Yaklaşım yöntemi için çizim kodu:

// 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 Bu, üzerinde çalıştığım bir JavaScript / tuval projesi için harika çalıştı
Matt

1
Yardımcı olduğuma sevindim. Bilginize, jQuery eklentisi olan açık kaynaklı bir html5 canvas çizim pedi başlattım. Yararlı bir başlangıç ​​noktası olmalı. github.com/homanchou/sketchyPad
Homan

4
Bu iyi, ancak tüm noktalardan geçmesi için eğriyi nasıl yaparsınız?
Richard

Bu algoritma ile her ardışık eğri, önceki eğrilerin bitiş noktasından başlaması mı gerekiyor?
Lee Brindley

Çok teşekkür ederim Homan! İşe yarıyor! Çözmek için çok günler harcadım. Ve Delphi Android / iOS topluluğundan merhaba!
alitrun

104

Biraz geç ama kayıt için.

Noktaların içinden geçen düzgün eğriler çizmek için kardinal spline'lar (aka kanonik spline) kullanarak düzgün çizgiler elde edebilirsiniz .

Bu işlevi tuval için yaptım - çok yönlülüğü artırmak için üç işleve ayrıldı. Ana sarmalayıcı işlevi şuna benzer:

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

Bir eğri çizmek amacıyla x, y noktaları ile bir dizi vardır: x1,y1, x2,y2, ...xn,yn.

Bunu şu şekilde kullanın:

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

Yukarıdaki işlev, biri düzleştirilmiş noktaları hesaplamak için iki alt işlevi çağırır. Bu, yeni noktalara sahip bir dizi döndürür - bu, düzleştirilmiş noktaları hesaplayan temel işlevdir:

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

Ve aslında noktaları düzleştirilmiş bir eğri (veya bir x, y dizisine sahip olduğunuz sürece diğer parçalı çizgiler) olarak çizmek için:

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

Bu şununla sonuçlanır:

Örnek pix

Tuvali kolayca genişletebilir, böylece bunun yerine şöyle diyebilirsiniz:

ctx.drawCurve(myPoints);

Aşağıdakileri javascript'e ekleyin:

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

Bunun daha optimize bir sürümünü NPM ( npm i cardinal-spline-js) veya GitLab'da bulabilirsiniz .


3
İlk olarak: Bu muhteşem. :-) Ama bu resme bakıldığında, değerlerin aslında # 9 ile # 10 arasında giderken # 10 değerinin altına düştüğü (yanıltıcı) izlenimi vermiyor mu? (Görebildiğim gerçek noktalardan sayıyorum, bu nedenle 1 numara, ilk aşağı doğru yörüngenin tepesine yakın olan, 2 numaralı en alttaki [grafikteki en düşük nokta] ve benzeri ... )
TJ Crowder

6
Günlerce araştırdıktan sonra, aslında tam istediğim gibi çalışan tek kullanımın bu olduğunu söylemek istiyorum . Çok teşekkürler
cnp

4
EVET EVET EVET Teşekkürler! Zıpladım ve neşe içinde dans ettim.
Jeffrey Sun

1
Kodunuzda bir tür hatası var. Parametre ptsaolmalı pts, yoksa hatalar atabilir.
gfaceless

2
Uzun zaman önce bu çözümü yayınladınız ve bugün büyük bir sorunu çözmeme yardım ettiniz. Çok teşekkür ederim!
ÂlexBay

19

İlk cevap tüm noktalardan geçmeyecektir. Bu grafik tam olarak tüm noktalardan geçecek ve bu tür noktalarda [{x:, y:}] n gibi noktalarla mükemmel bir eğri olacaktır.

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
Bu, açık ara en basit ve doğru yaklaşımdır.
haymez

10

Daniel Howard'ın işaret ettiği gibi , Rob Spencer, http://scaledinnovation.com/analytics/splines/aboutSplines.html adresinde ne istediğinizi açıklıyor .

İşte etkileşimli bir demo: http://jsbin.com/ApitIxo/2/

Burada jsbin çalışmama ihtimaline karşı bir pasaj olarak.

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

Bunu güzelce çalıştığını buldum

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

Çözümümü başka bir gönderiye göndermek yerine eklemeye karar veriyorum. Aşağıda oluşturduğum çözüm mükemmel olmayabilir, ancak şu ana kadar çıktı iyi.

Önemli: tüm noktalardan geçecek!

Herhangi bir fikriniz varsa, daha iyi hale getirmek için lütfen benimle paylaşın. Teşekkürler.

İşte öncekinin karşılaştırması:

görüntü açıklamasını buraya girin

Test etmek için bu kodu HTML'ye kaydedin.

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

İnanılmaz lib! Görev için en iyisi!
Dziad Borowy

Evet!! Tüm noktalardan geçen kapalı bir şekil oluşturmak için blob () işlevine ihtiyacım vardı.
AwokeKnowing

7
404 Sayfa Bulunamadı.
dieter

Orijinal bağlantı - 404 dnot bulunamadı - bkz. Web.archive.org/web/20141204030628/http://…
uydular

1

İnanılmaz derecede geç, ancak Homan'ın zekice basit cevabından esinlenerek, daha genel bir çözüm yayınlamama izin verin (genel anlamda, Homan'ın çözümünün 3'ten az köşeli noktalarda çökmesi anlamında):

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'nin kardinal spline yöntemine eklemek ve belki de TJ Crowder'ın yanıltıcı yerlerde eğrilerin 'daldırılması' konusundaki endişelerini gidermek için, aşağıdaki kodu getCurvePoints()fonksiyona hemen öncesinde ekledimres.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;
}

Bu, her bir ardışık nokta çifti arasında etkili bir şekilde (görünmez) bir sınırlama kutusu oluşturur ve eğrinin bu sınırlayıcı kutu içinde kalmasını sağlar - yani. eğri üzerindeki bir nokta her iki noktanın üstünde / altında / solunda / sağındaysa, konumunu kutunun içinde olacak şekilde değiştirir. Burada orta nokta kullanılır, ancak bu belki doğrusal enterpolasyon kullanılarak geliştirilebilir.


0

Eğrinin denklemini n nokta üzerinden belirlemek istiyorsanız, aşağıdaki kod size n-1 dereceli polinomun katsayılarını verecek ve bu katsayıları coefficients[]diziye kaydedecektir (sabit terimden başlayarak). X koordinatlarının sıralı olması gerekmez. Bu, Lagrange polinomuna bir örnektir .

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];
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.