2013-01-05 2 views
3

html5 canvas에서 타원을 그리고 싶습니다. stackoverflow에서 좋은 방법을 발견했습니다.하지만 다른 질문이 있습니다.베 지어 커브와 타원의 관계?

function drawEllipse(ctx, x, y, w, h) { 
    var kappa = 0.5522848; 
     ox = (w/2) * kappa, // control point offset horizontal 
     oy = (h/2) * kappa, // control point offset vertical 
     xe = x + w,   // x-end 
     ye = y + h,   // y-end 
     xm = x + w/2,  // x-middle 
     ym = y + h/2;  // y-middle 

    ctx.beginPath(); 
    ctx.moveTo(x, ym); 
    ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); 
    ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); 
    ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); 
    ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); 
    ctx.closePath(); 
    ctx.stroke(); 
} 

상기 링크의 상기 방법은 타원을 그릴을 사용 후에 bezierCurveTo 가지고 있지만 후에 bezierCurveTo 4 회 그리는 갖는다. 하지만 난 그냥 2 후에 bezierCurveTo는 ellipse.like을 그릴 수 있다고 생각이 :

enter image description here

하지만 난 누군가가 나에게 "컨트롤 포인트"및 "타원 포인트의 관계를 말할 수있는, 수학에 약한 해요 "제발? 또는 타원형을 그리기 위해 4 개의 베 지어 커브를 그려야합니다. 내가 틀렸다면 누군가가 날 수정 확신 있도록

감사 모두

답변

4

내 배경은 수학이 아니라 나의 이해에서 우리는 단지와 타원의 꽤 좋은 근사을 그릴 수 2 차 3 차 베 지어 곡선 그러나 좌표가 약간 까다 롭습니다.

은 타원형 포인트와 나는 그것을 가장 잘 보간 또는 처음부터 익숙하다면 내가 선택한 지점에서 this video을보고 응답 할 생각 제어점 사이의 관계에 대한 귀하의 질문에 대답하는 경우 그렇지 않습니다. 짧아도 걱정하지 마십시오.

우리가 다루어야 할 한가지 문제는 위에서 시작하여 제어점과 동일한 너비와 높이의 직사각형 모서리가있는 타원의 아래쪽에 베 지어 커브를 할 때, 타원 너비가 사각형보다 작아집니다. . 우리가 원하는 크기의 75 배. 따라서 우리는 제어점을 그에 따라 확장 할 수 있습니다.

우리의 제어점의 X는과 같이 조정된다

(폭 타원의 폭 가정 및 우리가 얻는 두 가지로 나누어있어 그 기원 오프셋)

var cpx = (width/.75)/2; 

Put together a visualization 당신은 재생할 수있는 너비와 높이 및 그려진 타원을 확인하십시오.

빨간색 타원은 우리가 어떻게 그려지기를 원하는지, 내부 타원은 어떻게 제어 포인트를 재배치하지 않으면 그려지는지를 나타냅니다. 선은 비디오에 나타난 De Casteljau의 알고리즘을 보여줍니다. 여기

는 가 enter image description here

+0

좋은 일! 대단히 감사합니다! – LIXer

1

당신은이 http://pomax.github.io/bezierinfo/#circles_cubic에서 수학을 기반 약간 더 설명 찾을 수 시각화의 스크린 샷,하지만 요점은 이상 분기 차례 입방 베 지어 곡선을 사용하여 일반적으로 좋은되지 않는 것입니다 생각. 고맙게도 4 개의 곡선을 사용하면 필요한 제어점을 쉽게 찾을 수 있습니다. 원으로 시작하십시오.이 경우 1/4 원은 (1,0) - (1,0.55228) - (0.55228,1) - (0,1)으로 타원에 대한 좌표가 조정됩니다. +/- 기호를 스왑 한 상태로 4 번 그려 전체 원을 그리며 치수를 스케일하여 타원을 얻은 다음 완료하십시오.

두 개의 커브를 사용하면 좌표가 (1,0) - (1,4/3) - (- 1,4/3) - (- 1,0)이되어 타원에 맞게 조정됩니다 . 그것은 여전히 ​​응용 프로그램에서 충분히보기 흉하지 만 보일 수 있습니다.

+0

놀라운 리소스! 실제로 타원을 그리는 데 2 ​​개의 베 지어 곡선을 사용할 수 없습니다. 수학은 2 차 미분 방정식이 잘 끝나지 않았습니까? – Alex

0

어느 정도의 베 지어 곡선으로 원을 만들 수 없다는 것이 수학적으로 증명 될 수 있습니다. 그것을 대략적으로 "거의 원"으로 만들 수 있습니다.

[0,0] 주위에 1/4의 원형을 그려 넣기를 원한다고 가정 해 보겠습니다. 큐빅 좌표는 다음과 같습니다.

[0 , 1 ] 
[0.55, 1 ] 
[1 , 0.55] 
[1 , 0 ] 

아주 좋은 근사값입니다. ellpise를 얻기 위해 직선으로 변환하십시오.

2

타원을 그리려면 두 개의 3 차 베 지어 곡선 만 있으면됩니다. 여기에 제공된 원래의 함수 인수를 사용 DerekR의 코드의 단순화 된 버전입니다 - 당신은 x와 y는 타원의 중심하려는 가정 : BKH에

jsFiddle

function drawEllipse(ctx, x, y, w, h) { 
    var width_over_2 = w/2; 
    var width_two_thirds = w * 2/3; 
    var height_over_2 = h/2; 

    ctx.beginPath(); 
    ctx.moveTo(x, y - height_over_2); 
    ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2); 
    ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y -height_over_2); 
    ctx.closePath(); 
    ctx.stroke(); 
} 
1

큰 감사합니다. 회전 각도를 사용하여 타원 그리기를 완료하려면 두 개의 베 지어 곡선으로 코드를 사용했습니다. 또한 베 지어 곡선으로 그어진 타원과 기본 ellipse() 함수 (지금은 Chrome에서만 구현 됨) 사이에 demo이라는 비교를 만들었습니다.

function drawEllipseByBezierCurves(ctx, x, y, radiusX, radiusY, rotationAngle) { 
var width_two_thirds = radiusX * 4/3; 

var dx1 = Math.sin(rotationAngle) * radiusY; 
var dy1 = Math.cos(rotationAngle) * radiusY; 
var dx2 = Math.cos(rotationAngle) * width_two_thirds; 
var dy2 = Math.sin(rotationAngle) * width_two_thirds; 

var topCenterX = x - dx1; 
var topCenterY = y + dy1; 
var topRightX = topCenterX + dx2; 
var topRightY = topCenterY + dy2; 
var topLeftX = topCenterX - dx2; 
var topLeftY = topCenterY - dy2; 

var bottomCenterX = x + dx1; 
var bottomCenterY = y - dy1; 
var bottomRightX = bottomCenterX + dx2; 
var bottomRightY = bottomCenterY + dy2; 
var bottomLeftX = bottomCenterX - dx2; 
var bottomLeftY = bottomCenterY - dy2; 

ctx.beginPath(); 
ctx.moveTo(bottomCenterX, bottomCenterY); 
ctx.bezierCurveTo(bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY); 
ctx.bezierCurveTo(topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY); 
ctx.closePath(); 
ctx.stroke(); 

}