cara menggambar kurva halus melalui titik N menggunakan kanvas HTML5 javascript?

133

Untuk aplikasi menggambar, saya menyimpan koordinat gerakan mouse ke array kemudian menggambarnya dengan lineTo. Garis yang dihasilkan tidak mulus. Bagaimana saya bisa menghasilkan kurva tunggal antara semua poin yang dikumpulkan?

Saya sudah googled tetapi saya hanya menemukan 3 fungsi untuk menggambar garis: Untuk 2 titik sampel, cukup gunakan lineTo. Untuk 3 titik sampel quadraticCurveTo, untuk 4 titik sampel bezierCurveTo,.

(Saya mencoba menggambar a bezierCurveTountuk setiap 4 poin dalam array, tetapi ini mengarah ke ketegaran setiap 4 poin sampel, alih-alih kurva halus terus menerus.)

Bagaimana cara menulis fungsi untuk menggambar kurva halus dengan 5 titik sampel dan seterusnya?

Homan
sumber
5
Apa yang Anda maksud dengan "halus"? Dapat dibedakan tanpa batas? Dua kali dapat dibedakan? Cubic splines ("Bezier curve") memiliki banyak properti bagus dan dua kali dapat dibedakan, dan cukup mudah untuk dihitung.
Kerrek SB
7
@ Gerrek SB, dengan "halus" Maksudku secara visual tidak dapat mendeteksi sudut / titik dll.
Homan
@sketchfemme, apakah Anda merender baris secara real-time, atau menunda rendering hingga setelah mengumpulkan banyak poin?
Crashalot
@ Crashalot Saya mengumpulkan poin ke dalam array. Anda membutuhkan setidaknya 4 poin untuk menggunakan algoritma ini. Setelah itu Anda dapat merender secara real time di atas kanvas dengan membersihkan layar pada setiap panggilan mouseMove
Homan
1
@sketchfemme: Jangan lupa menerima jawaban. Tidak masalah jika itu milik Anda sendiri .
TJ Crowder

Jawaban:

130

Masalah dengan menggabungkan titik-titik sampel berikutnya bersama-sama dengan fungsi tipe "curveTo" yang terpisah, adalah bahwa di mana kurva bertemu tidak mulus. Ini karena kedua kurva berbagi titik akhir tetapi dipengaruhi oleh titik kontrol yang sepenuhnya terpisah. Salah satu solusinya adalah "melengkung ke" titik tengah antara 2 titik sampel berikutnya. Bergabung dengan kurva menggunakan titik-titik interpolasi baru ini memberikan transisi yang mulus pada titik akhir (apa titik akhir untuk satu iterasi menjadi titik kontrol untuk iterasi berikutnya.) Dengan kata lain dua kurva terputus-putus memiliki lebih banyak kesamaan sekarang.

Solusi ini diekstraksi dari buku "Yayasan ActionScript 3.0 Animasi: Membuat segalanya bergerak". hal.95 - teknik rendering: membuat banyak kurva.

Catatan: solusi ini sebenarnya tidak menggambarkan masing-masing poin, yang merupakan judul pertanyaan saya (lebih tepatnya mendekati kurva melalui titik sampel tetapi tidak pernah melewati titik sampel), tetapi untuk tujuan saya (aplikasi menggambar), itu cukup baik untuk saya dan secara visual Anda tidak bisa membedakannya. Ada adalah solusi untuk pergi melalui semua titik sampel, tetapi jauh lebih rumit (lihat http://www.cartogrammar.com/blog/actionscript-curves-update/ )

Berikut ini adalah kode gambar untuk metode aproksimasi:

// 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
sumber
+1 Ini bekerja sangat baik untuk proyek JavaScript / kanvas yang sedang saya kerjakan
Matt
1
Senang bisa membantu. FYI, saya sudah memulai open source html5 canvas drawing pad yang merupakan plugin jQuery. Ini harus menjadi titik awal yang bermanfaat. github.com/homanchou/sketchyPad
Homan
4
Itu bagus, tetapi bagaimana Anda membuat kurva sehingga melewati semua poin?
Richard
Dengan algoritma ini, apakah masing-masing kurva berurutan dimaksudkan untuk memulai dari titik akhir kurva sebelumnya?
Lee Brindley
Terima kasih banyak Homan! Berhasil! Saya menghabiskan banyak hari untuk menyelesaikannya. Dan hai dari komunitas Delphi Android / iOS!
alitrun
104

Agak terlambat, tapi untuk catatan.

Anda dapat mencapai garis halus dengan menggunakan kardinal splines (alias spline kanonik) untuk menggambar kurva halus yang melewati titik-titik.

Saya membuat fungsi ini untuk kanvas - ini dibagi menjadi tiga fungsi untuk meningkatkan fleksibilitas. Fungsi pembungkus utama terlihat seperti ini:

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

Untuk menggambar kurva memiliki sebuah array dengan x, y poin dalam urutan: x1,y1, x2,y2, ...xn,yn.

Gunakan seperti ini:

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

Fungsi di atas memanggil dua sub-fungsi, satu untuk menghitung poin yang dihaluskan. Ini mengembalikan array dengan poin baru - ini adalah fungsi inti yang menghitung poin yang dihaluskan:

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

Dan untuk benar-benar menggambar titik sebagai kurva yang dihaluskan (atau garis tersegmentasi lainnya selama Anda memiliki x, y array):

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

Ini menghasilkan ini:

Contoh pix

Anda dapat dengan mudah memperluas kanvas sehingga Anda dapat menyebutnya seperti ini:

ctx.drawCurve(myPoints);

Tambahkan yang berikut ke javascript:

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

Anda dapat menemukan versi yang lebih optimal ini di NPM ( npm i cardinal-spline-js) atau di GitLab .


sumber
3
Pertama: Ini sangat cantik. :-) Tetapi melihat gambar itu, bukankah itu memberi kesan (menyesatkan) bahwa nilai-nilai tersebut sebenarnya berada di bawah nilai # 10 dalam perjalanan antara # 9 dan # 10? (Saya menghitung dari titik-titik aktual yang dapat saya lihat, jadi # 1 adalah yang berada di dekat bagian atas lintasan ke bawah awal, # 2 yang ada di paling bawah [titik terendah dalam grafik], dan seterusnya ... )
TJ Crowder
6
Hanya ingin mengatakan bahwa setelah berhari-hari mencari, ini adalah satu-satunya kegunaan yang benar-benar berfungsi persis seperti yang saya inginkan. Terima kasih banyak
cnp
4
YA YA YA Terima kasih! Saya melompat dan menari dalam sukacita.
Jeffrey Sun
1
Ada kesalahan ketik dalam kode Anda. Parameter ptsaharus pts, atau yang lain akan membuang erro.
gfaceless
2
Dulu Anda memposting solusi ini dan Anda membantu saya hari ini untuk menyelesaikan masalah besar. Terima kasih banyak!
ÂlexBay
19

Jawaban pertama tidak akan melewati semua poin. Grafik ini akan melewati semua titik dan akan menjadi kurva yang sempurna dengan titik-titik seperti [{x:, y:}] n titik-titik tersebut.

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
sumber
1
Sejauh ini, ini adalah pendekatan yang paling sederhana dan benar.
haymez
10

Seperti yang ditunjukkan Daniel Howard , Rob Spencer menjelaskan apa yang Anda inginkan di http://scaledinnovation.com/analytics/splines/aboutSplines.html .

Berikut ini demo interaktif: http://jsbin.com/ApitIxo/2/

Ini dia sebagai potongan jika jsbin turun.

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

Saya menemukan ini berfungsi dengan baik

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
sumber
6

Saya memutuskan untuk menambahkan, daripada memposting solusi saya ke posting lain. Di bawah ini adalah solusi yang saya bangun, mungkin tidak sempurna, tetapi sejauh ini hasilnya bagus.

Penting: itu akan melewati semua poin!

Jika Anda punya ide, untuk membuatnya lebih baik , silakan bagikan kepada saya. Terima kasih.

Berikut adalah perbandingan sebelum sesudah:

masukkan deskripsi gambar di sini

Simpan kode ini ke HTML untuk mengujinya.

    <!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.
sumber
5

Cobalah KineticJS - Anda dapat menentukan Spline dengan berbagai titik. Ini sebuah contoh:

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

Lihat url arsip: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/

Eric Rowell
sumber
Lib menakjubkan! Yang terbaik untuk tugas ini!
Dziad Borowy
Iya!! Saya membutuhkan fungsi blob () untuk membuat bentuk tertutup yang melewati semua titik.
AwokeKnowing
7
404 halaman tidak ditemukan.
diet
Tautan asli - 404 tidak ditemukan - lihat web.archive.org/web/20141204030628/http://…
satels
1

Sangat terlambat tetapi terilhami oleh jawaban Homan yang sangat sederhana, izinkan saya untuk mengirim solusi yang lebih umum (umum dalam arti bahwa solusi Homan crash pada array poin dengan kurang dari 3 simpul):

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
sumber
0

Untuk menambah metode splines kardinal K3N dan mungkin mengatasi kekhawatiran TJ Crowder tentang kurva 'mencelupkan' di tempat-tempat yang menyesatkan, saya memasukkan kode berikut dalam getCurvePoints()fungsi, tepat sebelumres.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;
}

Ini secara efektif menciptakan kotak pembatas (tidak terlihat) antara setiap pasangan poin berurutan dan memastikan kurva tetap berada dalam kotak pembatas ini - yaitu. jika suatu titik pada kurva berada di atas / di bawah / kiri / kanan dari kedua titik, itu mengubah posisinya berada di dalam kotak. Di sini titik tengah digunakan, tetapi ini bisa diperbaiki, mungkin menggunakan interpolasi linier.

James Pearce
sumber
0

Jika Anda ingin menentukan persamaan kurva melalui n poin maka kode berikut akan memberi Anda koefisien polinomial derajat n-1 dan menyimpan koefisien ini ke coefficients[]array (mulai dari istilah konstan). Koordinat x tidak harus berurutan. Ini adalah contoh dari polinomial 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
sumber