자바 스크립트 HTML5 캔버스를 사용하여 N 점을 통해 부드러운 곡선을 그리는 방법은 무엇입니까?


133

그리기 응용 프로그램의 경우 마우스 움직임 좌표를 배열에 저장 한 다음 lineTo를 사용하여 그립니다. 결과 라인이 부드럽 지 않습니다. 수집 된 모든 점 사이에 단일 곡선을 생성하려면 어떻게해야합니까?

나는 봤지만 선 그리기를위한 3 개의 함수 만 찾았습니다 .2 개의 샘플 포인트에는 간단히을 사용하십시오 lineTo. 3 샘플 포인트를 들어 quadraticCurveTo, 4 샘플 포인트, bezierCurveTo.

( bezierCurveTo배열에서 4 포인트마다 a 를 그리려고 시도했지만 연속 부드러운 곡선 대신 4 샘플 포인트마다 꼬임이 발생했습니다.)

5 개 이상의 샘플 포인트로 부드러운 곡선을 그리는 함수를 작성하려면 어떻게해야합니까?


5
"부드럽다"는 무슨 뜻입니까? 무한한 차별화? 두 번 차별화 할 수 있습니까? 큐빅 스플라인 ( "베 지어 곡선")은 많은 좋은 특성을 가지고 있으며 두 배로 구분할 수 있으며 계산하기 쉽습니다.
Kerrek SB

7
@Kerrek SB, "부드러운"나는 시각적으로 어떤 모서리 나 뾰족한 부분도 감지 할 수 없음을 의미합니다.
Homan

@sketchfemme, 실시간으로 선을 렌더링합니까, 아니면 많은 점을 수집 한 후 렌더링을 지연합니까?
Crashalot

@Crashalot 포인트를 배열로 수집하고 있습니다. 이 알고리즘을 사용하려면 최소한 4 점이 필요합니다. 그 후 mouseMove
Homan

1
@sketchfemme : 답을받는 것을 잊지 마십시오. 그것이 당신 자신이라면 괜찮습니다 .
TJ Crowder

답변:


130

후속 샘플 포인트를 분리 된 "curveTo"유형 함수와 함께 결합 할 때의 문제는 곡선이 만나는 곳이 부드럽 지 않다는 것입니다. 두 곡선이 종점을 공유하지만 완전히 분리 된 제어점의 영향을 받기 때문입니다. 한 가지 해결책은 다음 2 개의 후속 샘플 포인트 사이의 중간 점을 "곡선"시키는 것입니다. 이 새로운 보간 된 점을 사용하여 곡선을 결합하면 끝점에서 부드럽게 전환됩니다 (한 반복의 끝 점이 다음 반복 의 제어점이 됩니다). 즉, 두 개의 분리 된 곡선은 이제 훨씬 더 공통적입니다.

이 솔루션은 "Foundation ActionScript 3.0 애니메이션 : 사물 이동"책에서 추출되었습니다. p.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 / 캔버스 프로젝트에 적합했습니다.
Matt

1
도움이되어 기쁘다. 참고로, jQuery 플러그인 인 오픈 소스 html5 캔버스 드로잉 패드를 시작했습니다. 유용한 시작점이되어야합니다. github.com/homanchou/sketchyPad
Homan

4
그것은 좋지만 어떻게 모든 점을 통과하도록 곡선을 만들 것입니까?
Richard

이 알고리즘을 사용하면 각 연속 곡선이 이전 곡선 끝점에서 시작합니까?
Lee Brindley

호만 감사합니다! 효과가있다! 나는 그것을 해결하기 위해 너무 많은 시간을 보냈다. 그리고 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]);
}

결과는 다음과 같습니다.

예 pix

캔버스를 쉽게 확장하여 대신 다음과 같이 호출 할 수 있습니다.

ctx.drawCurve(myPoints);

자바 스크립트에 다음을 추가하십시오.

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
우선 : 이것은 화려합니다. :-) 그러나 그 이미지를 보면 # 9와 # 10 사이의 값이 실제로 값 # 10 아래로 떨어 졌다는 (오도적인) 인상을주지 않습니까? (내가 볼 수있는 실제 점에서 세고 있습니다. 따라서 # 1은 초기 하향 궤적의 상단 근처에 있고 # 2는 맨 아래에있는 것입니다 (그래프에서 가장 낮은 지점). )
TJ Crowder

6
며칠 동안 검색 한 후에 실제로 이것이 원하는 대로 작동하는 유일한 유틸리티였습니다 . 정말 감사합니다
cnp

4
예 예 예 감사합니다! 나는 뛰어 올라 기쁨으로 춤을 추었다.
Jeffrey Sun

1
코드에 유형 오류가 있습니다. 매개 변수 ptsa는이어야합니다 pts. 그렇지 않으면 오류가 발생합니다.
gfaceless

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 2016 년

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 () 함수가 필요했습니다.
AwokeKinging

7
404 페이지를 찾을 수 없습니다.
dieter

원래 링크 - 404 dnot 발견 - 볼 web.archive.org/web/20141204030628/http://...
satels

1

엄청나게 늦었지만 Homan의 놀랍도록 간단한 답변에서 영감을 얻어보다 일반적인 솔루션 (Homan의 솔루션이 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 좌표는 순서대로있을 필요는 없습니다. 이것은 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];
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.