=================================
=================================
=================================
출처: http://www.emanueleferonato.com/2009/09/22/drawing-arcs-with-as3/
Drawing arcs with AS3
September 22, 2009 • Actionscript 3, Flash by Emanuele Feronato • 10 Comments
If you have ever tried to draw arcs with AS3 (or AS2), you probably smashed your computer on the floor after spending hours with curveTo().
That’s not what we need when we want to draw simple arcs, without any Bezier curve.
That’s why I made my own function.
It’s not that interesting since it only uses a bit of trigonometry, and obviously I did not write it for the sake of writing a function, but at the moment with this script
package { import flash.display.Sprite; public class arc extends Sprite { var my_canvas:Sprite = new Sprite(); var deg_to_rad=0.0174532925; public function arc() { addChild(my_canvas); my_canvas.graphics.lineStyle(20,0xff0000,1); draw_arc(my_canvas,250,200,150,14,180,1); } public function draw_arc(movieclip,center_x,center_y,radius,angle_from,angle_to,precision) { var angle_diff=angle_to-angle_from; var steps=Math.round(angle_diff*precision); var angle=angle_from; var px=center_x+radius*Math.cos(angle*deg_to_rad); var py=center_y+radius*Math.sin(angle*deg_to_rad); movieclip.graphics.moveTo(px,py); for (var i:int=1; i<=steps; i++) { angle=angle_from+angle_diff/steps*i; movieclip.graphics.lineTo(center_x+radius*Math.cos(angle*deg_to_rad),center_y+radius*Math.sin(angle*deg_to_rad)); } } } } |
An arc from degree 14 to degree 180. It’s easy and simple and uses trigonometry (check this old tutorial if you think it’s a brain disease)
But the final application is a power meter like the one used in Pumpkin Story, and here it is, with a bit of the previous script and a bit of Flash artillery.
package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; public class arc extends Sprite { var my_canvas:Sprite = new Sprite(); var deg_to_rad=0.0174532925; var charging:Boolean=false; var power:int=0; public function arc() { addChild(my_canvas); stage.addEventListener(MouseEvent.MOUSE_UP, shoot); stage.addEventListener(MouseEvent.MOUSE_DOWN,charge); addEventListener(Event.ENTER_FRAME, on_enter_frame); } public function draw_arc(movieclip,center_x,center_y,radius,angle_from,angle_to,precision) { var angle_diff=angle_to-angle_from; var steps=Math.round(angle_diff*precision); var angle=angle_from; var px=center_x+radius*Math.cos(angle*deg_to_rad); var py=center_y+radius*Math.sin(angle*deg_to_rad); movieclip.graphics.moveTo(px,py); for (var i:int=1; i<=steps; i++) { angle=angle_from+angle_diff/steps*i; movieclip.graphics.lineTo(center_x+radius*Math.cos(angle*deg_to_rad),center_y+radius*Math.sin(angle*deg_to_rad)); } } public function charge(e:MouseEvent) { charging=true; } public function shoot(e:MouseEvent) { charging=false; my_canvas.graphics.clear(); power=0; } public function on_enter_frame(e:Event) { if (charging) { power++; if (power>=120) { power-=120; } my_canvas.graphics.clear(); my_canvas.graphics.lineStyle(20,0x000000,1); draw_arc(my_canvas,250,200,150,270,270+power*3,1); } } } } |
This post has 10 comments
- HiddenSpartanType declarations please.
- on September 23, 2009 at 12:52 am
- dVyperYes, the fact that you’ve provided the code and example simply isn’t good enough for us dammit!
- on September 23, 2009 at 6:05 pm
- ArthurYes, type declarations! I can see that the functions doesn’t return anything, but don’t know they’re void unless explicitly stated!
- on September 23, 2009 at 7:38 pm
- mawI also often write libraries for myself (for satisfaction), but I already know this method from “draw advanced methods” for Flash MX at Adobe. Before a year I found this: http://theflashblog.com/?p=429 – more powerful than their own package, than single functions.
- on September 23, 2009 at 10:52 pm
- Pumpkin Story prototype : Emanuele Feronato[…] quick Pumpkin Story prototype is made merging and mixing these scripts: Drawing arcs with AS3 and Creation of a Flash artillery game using […]
- on September 25, 2009 at 12:22 am
- Weekly Shared Items – 29. September, 2009 | TOXIN LABS - weblog of a german design student from wuerzburg[…] Drawing arcs with AS3 […]
- on September 29, 2009 at 6:11 am
- JohnThis is a really sweet one..
I’m rewriting it to use a timer for a slideshow I’m making. - on December 18, 2009 at 3:24 am
- invasionI was just wondering how to do this.. This is a good starting point for a circular progress bar or in my case gun reload timer. Thanks for posting.
- on May 22, 2010 at 6:12 am
- JaiHi,Thanks a ton.If I set the line cap style to “none”Then the line cap is not proper. You can see it buy running the below code.public class TimerDemo extends Sprite {
var my_canvas:Sprite = new Sprite();
var deg_to_rad=0.0174532925;
var charging:Boolean=false;
var power:int = 0;
public var timeCount:Number = 1;
var myTimer:Timer = new Timer(1000);myTimer.addEventListener(TimerEvent.TIMER, runOnce);
addChild(my_canvas);function runOnce(event:TimerEvent):void {public function draw_arc(movieclip,center_x,center_y,radius,angle_from,angle_to,precision) {
var angle_diff=angle_to-angle_from;
var steps=Math.round(angle_diff*precision);
var angle=angle_from;
var px=center_x+radius*Math.cos(angle*deg_to_rad);
var py=center_y+radius*Math.sin(angle*deg_to_rad);
movieclip.graphics.moveTo(px,py);
for (var i:int=1; i=1440) {
power -= 1440;
timeCount = 1;
}
my_canvas.graphics.clear();
my_canvas.graphics.lineStyle(10,0xCCCCCC,0.5,false,”normal”,”none”);
draw_arc(my_canvas,stage.stageWidth*0.5,stage.stageHeight*0.5,100,270,270+power*0.25,1);
}
}
}
}
//Code Ends Here// - Regards,
Jai - timerUpdate.text = String(timeCount);
timeCount++
timerUpdate.text = String(timeCount);
} - charging = true;
addEventListener(Event.ENTER_FRAME, on_enter_frame);
//stage.addEventListener(MouseEvent.MOUSE_UP, shoot);
//stage.addEventListener(MouseEvent.MOUSE_DOWN,charge);
timerUpdate.text = String(timeCount);
myTimer.start();
} - public function TimerDemo() {
- //Code Starts Here//
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer; - my_canvas.graphics.lineStyle(10,0xCCCCCC,0.5,false,”normal”,”none”);
- Now, I have updated your code and made a Polar Clock Timer that runs for a minute.
- First of all well done. Excellent work. Learned a lot from you.
- on May 30, 2012 at 1:40 pm
- JaiSorry for the above comment. it was my code problem.draw_arc(movieclip,center_x,center_y,radius,angle_from,angle_to,precision)Thanks again for a excellent tutorial.
– Jai - previously it was set to 1
- Sorted out by changing the precision to 0.2
- on May 30, 2012 at 3:06 pm
=================================
=================================
=================================
package {
import flash.display.Sprite;
import ascb.units.Converter;
import ascb.units.Unit;
import flash.events.Event;
public class NumbersAndMath2 extends Sprite {
private
var _square: Sprite;
private
var _angle: uint;
private
var _sprite: Sprite;
public
function NumbersAndMath2() {
_sprite = new Sprite();
addChild(_sprite);
_sprite.graphics.beginFill(0xffffff);
_sprite.graphics.drawRect(0, 0, 400, 400);
_sprite.graphics.endFill();
addEventListener(Event.ENTER_FRAME, move);
_square = new Sprite();
_square.graphics.lineStyle(0);
_square.graphics.drawCircle(0, 0, 20);
addChild(_square);
_angle = 0;
_square.graphics.endFill();
}
private
function move(event: Event): void {
var converter: Converter = Unit.DEGREE.getConverterTo(Unit.RADIAN);
var angleRadians: Number = converter.convert(_angle);
_square.x = Math.cos(angleRadians) * 100 + 200;
_square.y = Math.sin(angleRadians) * 100 + 200;
_angle++;
_sprite.graphics.lineStyle(1, 0, 1);
_sprite.graphics.lineTo(_square.x, _square.y);
}
}
}
=================================
=================================
=================================
출처: http://lpesign.tistory.com/159
동적으로 호를 그려야 할때가 있다.
혹은 동적으로 호를 그리면 편한상황이 있다.
자주있진 않은데 분명 있다.
하지만 지원하지 않았습니다..
지원하지 않으므로 직접 구현하는 수밖에 없다.
삼각함수를 이용하면 원을그리는 자취를 구할수있으므로
그리고자 하는 호의 각도를 알면 그 각 만큼은 그려주면 된다.
자 만들어보자!
.
.
.
하고 혹시나 검색해보니 이미 만들어 진게 있다. ASCBLibrary 의 Pen 클래스에서 지원한다.
글거보니 예전에 산 ActionScript 3.0 Cookbook 에서 나오는 바로 그 라이브러리라 하드를 뒤져보니 이미 가지고 있다.
어쨌든.
Pen클래스는 Graphics 클래스의 래퍼클래스랄까 데코레이터 랄까 ..
상속을 받지는 않지만 Graphics의 참조를 받아 (has a) 기능을 확장하고 있다.
좋아 굳
원하는건 drawAcr.
파라메터는 4개를 받는다
$nX:Number, // 호의중심 x
$nY:Number, // 호의중심 y
$nRadius:Number, //호의 반지름
$nArc:Number, //호의 각도
$nStartingAngle:Number = 0, //호가 시작하는 각도
$bRadialLines:Boolean = false //외곽선을 그릴지 결정
귀찮지 않게 각도는 degree로 받고 있다.
사용예
var shape:Shape = new Shape();
var pen:Pen = new Pen(shape.graphics);
pen.lineStyle(0,0);
pen.beginFill(0xff0000);
pen.drawArc(100,100,50,180,180,true);
addChild(shape);
100,100지점에 50반지름으로 180도부터 시작하여 180만큼 그림
활용
TweenMax를 의 ease 기능을 이용하여 호를 그린 후 마스크로 활용(사실 이거 하려고 호를 그리고 싶었음)
=================================
=================================
=================================
출처: http://actionscript.minarto.com/257
제목만 보고 충격(?)을 받으신 분들도 있을 듯 합니다.
'아니, 원 그리기라니!!! 우리가 기초도 모르는 줄 알어? actionscript를 모르는 사람도 할 수 있는거라고!!!'
라고 생각하신 분들이 많을 것 같네요. 네, 맞는 말이죠 :^)
사실 채재영씨에게 e-Book 집필에 참여하고 싶다고 메일을 보냈을 때만 해도, 쓰고 싶었던 강좌 내지 기술문서의 내용은 다음과 같았습니다.
1. 수학정석을 actionscript(이하 as)로 옮기기
2. google code 중 재밌는 코드 소개하기
3. 플래시 알바하기 (계약 / 작업 / 돈받아내기/ 신고하기 ㅡㅡ;;)
세가지 중 하나를 택일하려고 했습니다. 다른 필자분들이 게임쪽에 많이 계셔서(요즘 플래시 게임이 대세죠) 저는 뭔가 다른걸 적어보려고 했거든요.
1번 수학정석은 사실 예전에 블로그와 카페를 통해서 했었던 것이었죠. 카페에는 아마 정석 2단원까지 올렸을 겁니다.(비공개로 아직 가지고 있는 글은 몇단원 더 됩니다)
그런데, 그 글을 쓰다가 중단한 이유가 있습니다. 제가 엄청 게으른 것도 문제지만 수학이라는 것이 인터넷에 글로 올릴 때 매우 어렵습니다.
바로 수학기호 때문인데, 특수문자 입력기로도 해결이 안되는 것이 엄청 많고 그나마 올려놔도 올리고 나서 보면 다 깨지기 마련입니다. 결국에는 포토샵으로 글을 그려서(?) 포스팅을 해야하는 일이 생기더군요.
일반적인 포스팅보다 시간이 10배 이상 걸리는 셈이었죠...
블로그나 카페에 하는 포스팅이야 글 말미에,
"어, 수학기호들이 깨졌네요. 대충 알아서 봐주세요. 개념만 이해하세요."
라고 써버려도 대부분 그냥 넘어가시겠지만, 책에서도 그렇게 하는건 좀 아닌 거 같더군요.
(게다가, 수학정석 모두 단원별로 옮기려면 한 1년 걸릴 듯...)
게다가 수학정석은 저작권이 있는 책이다보니 아무래도 미묘한 문제가(?) 생길 듯도 싶었습니다. 그래서 수학정석은 패스했습니다.
2번인 구글 코드 소개는(얼굴인식, 바코드인식 등을 소개하려 했었습니다만) 사실 제가 짠 코드들이 아니고, 다른 분들이 짠 코드를 소개하는 것이잖아요?
3번은 e-book 방향성과 안 맞는 듯 하여... ㅎㅎㅎ (그런데, 제일 중요한거 아닌가...)
그래서 세가지를 제외하고 생각하다보니, 제가 신입팀원을 뽑으면 어쩌다 플래시를 가르칠 때 가르치던 것이 있었는데 그것을 보여드리고자 생각했습니다. 그게 바로 "원그리기"죠.
원래 애들에게 이걸 가르치는 순서는,
플래시사용법 > 피타고라스 정리 > 삼각함수 > 원주율 > cos/sin/tan 파형 > 원그리기 (피타고라스 부터는 애들에게 모두 증명하기 까지 가르치고 시키는 악마 선임 개발자랍니다. ㅎㅎㅎ)
순으로 나갑니다만 여기서는 지면관계상(그러면서도 서론이 기네요) 플래시 사용법 + 원그리기 만으로 진행하려 합니다..
사실 예전에 다니던 회사가 차트를 만들다 보니, 애들에게 수학적인게 부족해보여서 가르치기 시작했던 것인데 아마 이 글을 보는 분중에 저와 일했던 분들은 몇번 들어보았을 겁니다 :^) (얘들아, 보고있니? 형 왔다)
플래시 개발자분들은 디자인베이스에서 출발하신 분들과, 개발베이스에서 넘어오신 분들이 있습니다.
제가 이바닥에서 일을 하면서 느낀 것이 있다면, 신입이건 경력이건 디자인 베이스면 수학(알고리즘)이 약하고 개발베이스에서 넘어오신 분들은 에디터 이외에는 아무 것도 안건드리려는 모습이였다고 할까요? (참고로 전 플래시 애니메이터 출신이랍니다 ㅎㅎㅎ)
그러나, 디자인 베이스 분들이 수학을 모르면 중급을 넘어갈 수 없고, 개발베이스 분들은 에디터로만 개발하려고만 하면 손발이 고생한다는 것이 결론이랍니다.
개발에는 왕도도 없고, 정석도 없죠. 주어진 상황(일정, 클라이언트 요구사항, 팀장 스타일)에 맞춰 어떻게든 개발하는 것이 회사에서 인정받는 방법이랍니다.
서론이 길었습니다. 그럼 시작하겠습니다.
제가 생각해낸 원그리기 10가지 방법은 다음과 같습니다. 모두 다 다른 방법이죠.
1. 드로잉툴 이용하기
2. 이미지 불러오기
3. 드로잉툴로 마스크 씌우기
4. 이미지로 마스크 씌우기
5. 점찍기
6. lineTo
7. drawCircle
8. drawEllipse
9. 타원의 접선의 방정식 이용
10. 삼각함수 이용
이렇게 하려고 했었는데, 다른 분들과 상의 결과 글이 너무 초보내용으로만 갈 수 있을 것 같다고 해서 중간중간은 건너뛰도록 하겠습니다. (다 쓰자니 너무 길기도 하고요)
1. Tool
자, 기초부터 시작해보죠.
WIndow/Tools 메뉴를 활성화하면 나오는 도구툴입니다. 원을 선택하고선 그리시면 되겠죠. Alt, Shift 조합에 따라 여러가지 방법으로 그릴 수 있습니다. 스크린샷 보시면 알겠지만, 단축키도 보이죠? 머리가 나쁘면 손발이 고생하는 법입니다.
as로 치자면, Graphics.drawEllipse() 정도가 될 것 같은데 이게 또 그렇지가 않습니다. 내부적으로는 그런지 몰라도 Stage에 저렇게 그려놓고,
trace(getChildAt(0));
라고 친 후 컴파일을 해보면 Shape라고 나옵니다. Graphics 였다면 아무것도 안나와야겠죠? 아마도 컴파일러 단에서 컴파일 타임에 Shape로 변환해버리는 듯 싶습니다. 개발자 분들이 헷갈릴 수 있는 부분이죠.
2. Impot to Library
대부분의 업무는 플래시 개발자가 직접 뭔가를 그릴 일은 없습니다. 디자이너가 만들어준 원 이미지를 활용해야죠.
File/Import 메뉴에 마우스를 가져가보면 메뉴가 뜨게 됩니다. 제목은 Impot to Library 지만, 사실 단축키 때문에라도 Impot to Stage 를 더 자주 쓰게 된답니다. 여기서 디자이너가 만들어준 이미지를 불러와 사용하면 끝~
as로 치면 [Embed] 정도가 되겠습니다.
3. Mask
이번에는 Mask 입니다
원형 Mask 를(Image가 됐든, MovieClip이나 Graphic Symbol이 됐건) 써서 DisplayObject 를 덮으면 원이 됩니다.
'원그리기라더니, 이건 사기 아니야?'
라고 하실 분이 있을 지도 모르겠네요. :^) 하지만 Mask 를 이용해 원을 그려야 할 필요가 있을 때가 있습니다. 바로 Alpha Mask 때문이지요.
as3에서는 DisplayObject.scrollRect 속성이 생기면서 Mask 를 쓸 일이 좀 줄긴 했습니다. scrollRect 를 이용하는 것이 개발이 더 편하기도 하거니와, Mask 를 쓰면 느리거든요. 하지만, scrollRect 는 이름에서 알 수 있듯이 사각형 밖에 사용을 못하니 Mask 를 쓸 땐 또 써야죠.
4. Graphics.lineTo
자, 위 내용까지는 flash ide를 활용하는 것이었다면, 지금부터는 as로 해보겠습니다.
Graphics 로 드로잉을 해보신 분들이 있다면 제목을 보고, '원그리기 인데 웬 lineTo? curveTo를 오타쓴거 아니야?' 라고 생각하실지 모르겠습니다. 그런데 오타가 아닙니다.
graphics.lineStyle(10);
graphics.moveTo(100, 100);
graphics.lineTo(100.25, 100);
graphics.lineStyle(2);
graphics.beginFill(0);
graphics.drawCircle(200, 100, 4);
액션 패널에 위와 같이 써놓고 컴파일 해보세요. 두가지 원은 똑같은 크기와 색상을 가진 원이 됩니다.
일종의 꽁수를 사용한 방법이라고 할 수 있겠는데요.
'에이~ 그래도 이런거 배우려고 이걸 읽는건 아니잖아!!' 라고 분노하실 분들을 위해 한가지를 더 보여드리겠습니다 :^)
var i:int = 1000;
var st:uint = getTimer();
while(-- i > -1)
{
lineTo();
//drawCircle();
}
trace(getTimer() - st);
function lineTo():void
{
graphics.lineStyle(10);
graphics.moveTo(100, 100);
graphics.lineTo(100.25, 100);
}
function drawCircle():void
{
graphics.lineStyle(2);
graphics.beginFill(0);
graphics.drawCircle(200, 100, 4);
}
자, 위의 코드로 속도를 비교해보시기 바랍니다. 제가 재본 바로는 평균 5배 이상 났었고, 10000 번 이상을 돌리면 drawCircle 은 아예 플래시가 뻗어버렸답니다. 꽁수라고 해서 무조건 지양해야 하는 것일까요?
5. Graphics.drawEllipse
앗, 이미 위에서 lineTo를 설명하며 거의 비슷한 drawCircle로 다 얘기해버렸네요. 그냥 넘어가죠...라고 하고 싶지만 정작 보여드리고 싶은건 따로 있습니다.
import flash.display.GraphicsStroke;
import flash.display.GraphicsSolidFill;
import flash.display.GraphicsPath;
import flash.display.IGraphicsData;
var fill:GraphicsSolidFill = new GraphicsSolidFill(0, 1);
var stroke:GraphicsStroke = new GraphicsStroke(1);
stroke.fill = fill;
var commands:Vector.<int> = Vector.<int>([1, 2, 2, 2, 2]);
var data:Vector.<Number> = Vector.<Number>([100, 100, 200, 100, 200, 200, 100, 200, 100, 100]);
var path:GraphicsPath = new GraphicsPath(commands, data);
var gData:Vector.<IGraphicsData> = Vector.<IGraphicsData>([stroke, path]);
graphics.drawGraphicsData(gData);
flash player10 에서부터 생겨난 graphics 드로잉 방식입니다. 이로 인해 graphics의 재사용이 가능해지게 됩니다. 속도도 빨라지고요.
이녀석을 설명하려면 디자인 패턴에 대해 설명을 해야할지도 모르겠습니다. 하지만 이 글의 주제가 디자인 패턴도 아니고, 지면도 부족하고 하니 넘어가도록 하겠습니다.
아, 그런데 drawEllipse 에 왜 IGraphicsData 들이 나오냐고요? 그리고 왜 사각형을 그리냐고요?
정작 중요한건 다음의 내용을 봐주세요.
6. 타원의 접선의 방정식
자, 제목 보면 아시겠지만 지금부터는 수학시간입니다.
플래시로 원을 그리려면 어떻게 해야할까요?
엔터프레임으로 삥 돌려가며 그냥 그리자니 시간이 너무 걸릴거 같고, 결국은 curveTo로 그려야 할 것 같긴 합니다. 아마도 여기까진 다 생각하셨을 겁니다
엔터프레임으로 찍는 거면 다음과 같겠죠.
var cosF:Function = Math.cos;
var sinF:Function = Math.sin;
var radianRate:Number = Math.PI / 180;
var radius:Number;
function drawCircle($graphics:Graphics, $x:Number, $y:Number, $radius:Number, $angle:Number):void
{
var radian:Number = radianRate * $angle;
var _x:Number = $x + cosF(radian) * $radius;
var _y:Number = $y + sinF(radian) * $radius;
$graphics.lineTo(_x, _y);
}
아마 이정도로 코드를 짜시고 루프든 엔터프레임이든 돌려야겠죠. 하지만 이미 언급했듯이 루프로 돌리면 플래시가 뻗어버릴 것이 확실해보입니다.
그래서 curveTo 로 그려야하긴 하겠는데 curveTo 는 그려야할 x, y 좌표만이 아닌 제어점의 좌표 또한 찾아내야 합니다. 그것은 레퍼런스에도 나와있듯이 curveTo 가 2차원 베지어 곡선을 사용하기 때문입니다
(요 이미지는 레퍼런스에 있는 이미지를 가져왔습니다)
2차원 베지어 곡선이라 함은 시작점과 제어점 그리고 앵커포인트로 구분되어있습니다.(레퍼런스의 용어를 그대로 따라하겠습니다) 그림에서 보시다시피 제어점은 시작점과 앵커포인터의 접선의 교차점을 뜻합니다
그런 이유로 다음과 같은 코드가 나오겠죠
graphics.lineStyle(1);
graphics.moveTo(100, 100);
graphics.curveTo(200, 100, 200, 200);
graphics.curveTo(200, 300, 100, 300);
graphics.curveTo(0, 300, 0, 200);
graphics.curveTo(0, 100, 100, 100);
그런데 문제가 있습니다. 90도마다 사각형 모서리를 제어점으로 예상하여 그려보니 원보다는 둥근 사각형처럼 되어버렸습니다. 90도보다는 작게 그려야 할 듯 싶네요.
그럼 몇도를 기준으로 curveTo 를 그려야하느냐가 문제가 되는데, 그걸 찾아보자면 베지어 곡선에 대해 알아야 하고 플래시 벡터 랜더링 엔진을 파봐야 하겠죠. 그걸 설명할 공간도 아니고, 우리 선배들께서 해답을 이미 알아내주셨습니다. 바로 45도 입니다.
그럼 45도 이내의 각도로 그려야 하는데 제어점은 어떻게 찾아내느냐... 여기서부터 타원의 접선의 방정식을 사용합니다.(원의 접선의 방정식을 사용해도 되지만 타원도 그릴 수 있게 하기 위해 타원방정식을 이용하겠습니다)
구글링을 해보니 타원의 방정식은 다음과 같습니다
(참고로 이 이미지는 구글링으로 찾은 이미지입니다)
x^2 / a^2 + y^2 / b^2 = r^2
이랍니다.
그럼 시작점이 (x1, y1) 을 지나고, 앵커포인트가 (x2, y2)를 지난다고 했을 때, 시작점 (x1, y1)을 지나는 접선의 방정식은
(x1 * x) / a^2 + (y1 * y) / b^2 = r^2
이 되고, 앵커포인트의 접선의 방정식은
(x2 * x) / a^2 + (y2 * y) / b^2 = r^2
자, 방정식은 나왔습니다. 두 접선은 제어점인 (x3, y3)을 지납니다. 그럼 값을 구해보죠.
우리는 시작점 (x1, y1) 과 앵커포인트(x2, y2) 를 알고 있습니다.
x1 = cos(angle1) * a
y1 = sin(angle1) * b
x2 = cos(angle2) * a
y2 = sin(angle2) * b
이죠. 더이상 x1, y1 은 변수가 아닌 상수란 뜻입니다. 시작각도와 앵커포인터 각도인 angle1, angle2 도 알고 있고(0부터 360까지 45도씩 증가할 테니까요), 가로 반지름인 a, 세로반지름인 b 도 알고 있습니다.
그럼 변수는 x와 y만 남게 됩니다. 두값을 알아야 하지만 변수가 두개니 값을 알 수가 없습니다.
x를 왼쪽변에 넣고 나머지를 우측변으로 옮겨 보기좋게 1차원함수로 만들어보겠습니다
(x1 * x) = r^2 * a^2 - (y1 * y) / b^2 // 양변에서 (y1 * y) / b^2 를 빼줍니다
x = (r^2 * a^2 - (y1 * y) / b^2) / x1 // 양변에 a^2을 곱해주고 x1 로 나눠줍니다
그렇다면 앵커포인트의 접선공식도 마찬가지로 하여 다음의 공식이 성립합니다
(r^2 * a^2 - (y1 * y) / b^2) / x1 = (r^2 * a^2 - (y2 * y) / b^2) / x2
이제 모두 상수고 변수가 y만 남았습니다. 그렇다면 값을 구할 수 있겠죠. y만 왼쪽변에 남기고 모두 우측변으로 넘기겠습니다
x = a^2 * (y1 - y2) / (y1 * x2 - y2 * x1)
가 나옵니다. 같은 방법으로 y를 구해보면,
y = b^2 * (x1 - x2) / (y2 * x1 - y1 * x2)
이렇게 나오죠. 여기서 나온 x, y가 바로 제어점인 x3, y3 가 되겠습니다
우리는 이제 제어점 curveTo를 사용할 수 있지요. 최종 코드를 보여드리겠습니다
var commands:Vector.<int> = Vector.<int>([]);
var data:Vector.<Number> = Vector.<Number>([]);
function drawArc($graphics:Graphics, $x:Number, $y:Number, $xradius:Number, $yradius:Number, $startAngle:Number, $angle:Number):void
{
var angle:Number = Math.abs($angle);
if(angle >= 360) angle = $angle < 0 ? - 360 : 360; // 360도 이상의 값은 360도로 처리합니다
var commandPush:Function = commands.push;
var dataPush:Function = data.push;
var cosF:Function = Math.cos;
var sinF:Function = Math.sin;
var pi:Number = Math.PI / 180;
var radian:Number = $startAngle * pi;
var x:Number = cosF(radian) * $xradius;
var y:Number = sinF(radian) * $yradius;
commandPush(2);
dataPush($x + x, $y + y);
var length:uint = Math.ceil(Math.abs(angle / 45));
var i:int = - length - 1;
var addRadian:Number = angle / length * pi;
var px:Number = x;
var py:Number = y;
var pyx:Number;
var ypx:Number;
var xradius2:Number = $xradius * $xradius;
var yradius2:Number = $yradius * $yradius;
while(++ i < 0)
{
radian += addRadian;
x = cosF(radian) * $xradius; // 앵커포인트 x
y = sinF(radian) * $yradius; // 앵커포인트 y
pyx = py * x;
ypx = y * px;
commandPush(3);
dataPush($x + xradius2 * (py - y) / (pyx - ypx, $y + yradius2 * (px - x) / (ypx - pyx), $x + x, $y + y);
px = x;
py = y;
}
}
이게 최종코드입니다. 범용성을 위해 타원그리기가 아닌 호 그리기가 되어버렸습니다.
물론 실무에 쓰려면 군데군데 고쳐야 할 것입니다. (예를 들어, 2진연산의 오차 때문에, angle / 45 에서 잘못된 값이 나오는 경우가 있습니다)
게다가 개발용 에디터로 짠게 아니고, 텍스트 에디터에서 글을 작성하다보니 어딘가 틀린 곳이 있을 지도 모르겠습니다. (하지만 글의 주제가 이 코드 가져다 쓰세요가 아니니 그냥 개념만 봐주세요. 실제로 제가 실무에 사용하려고 만든 코드는 인터페이스도 적용되어 있고, 5번에서 보여드린 IGraphicsData 를 적용하고 있기 때문에 이것과는 생김새가 좀 다릅니다.)
자, 이것으로 이번 주제를 마치겠습니다... 라고 하면 아쉽겠죠? (아닌가요...ㅜㅜ)
사실 원래의 원그리기 순서는 다음과 같았습니다.
1. 드로잉툴 이용하기 - 아주 간단한 기초에서
2. 이미지 불러오기 - 디자이너와의 협업도 해야하고
3. 드로잉툴로 마스크 씌우기 - 타임라인 마스크를 쓰기도 합니다
4. 이미지로 마스크 씌우기 - 알파마스크도 씌울 수 있죠
5. 점찍기 - 하지만 기본은 플래시랍니다
6. lineTo - 때로는 꽁수도 써야할 수도 있답니다
7. drawCircle - 그래도 as는 써야겠죠
8. drawEllipse - as 를 쓰기로 한 이상 활용성이 높아야 합니다. 디자인 패턴을 써야할 수 있고요
9. 타원의 접선의 방정식 - 프로그래밍을 하는 이상 기본은 수학이죠
10. 삼각함수 - 자, 응용해볼까요?
이렇게 부제가 모두 따랐죠. 앞서 언급한대로 지면의 한계와 난이도 조절을 위해 많이 줄였지만요...
그리고 lineTo 에서와 같이 1~8번의 간단한 방법에서도 그냥 방법이 아니라 꽁수나, 속도 테스트 등을 비교해서 보여드리려고 했었는데... 역시 지면이...
(아, 10번인 삼각함수로 제어점을 찾는 방법은 인터넷에서 찾아보세요. 직접 알고리즘을 찾으면 더욱 좋고요. 숙제입니다. 삼각함수 방식은 수년전에 매크로미디아 시절 기술문서 샘플로 올라왔던 적이 있어서 전 다른 방법을 알려드린겁니다. 삼각함수 콜과 제 실수연산 방식중 어떤 것이 빠를지 비교해보는 것도 좋은 공부가 되겠지요...)
부제를 통해서 보시다시피, 원래 이 글을 통해 말하려 했던 주제는
"10분이 주어진 상황에서 원을 그려야 하는데, 1번의 방법을 안쓰고 드로잉 클래스를 만드는게 정답이겠습니까? 아니면 시간은 한달이 주어졌는데도 툴로 원하나 그려놓고 탱자탱자 놀고 있는게 정답이겠습니까?
프로그래밍을 하는데 있어서 왕도는 없습니다.
단순한 원 하나를 그리는 데에도 수십, 수백 가지의 방법이 있고 제대로 공부하려면 기초적인 산수 / 수학부터 시작합니다. 무조건 빨리 만들어내는 것도, 무조건 복잡한 알고리즘을 해결해 클래스를 만들어 내는 것도 정답은 아니지요.
주어진 시간, 상황에 맞는 개발을 해야 인정받는 일꾼(?)이 되지 않겠습니까..."
라는 거창한 주제를 생각했었습니다. 너무 거창하죠? :^)
어떻게 잘들 보셨는지 모르겠습니다. 필자분들과 얘기가 나왔던 것처럼 처음에 Tool 부터 시작해서 너무 초보 수준으로 간건 아닐까 싶기도 하고요.
그런데, 아무래도 첫타석이다보니 e-book 의 방향성이나 독자분들의 수준이 어떻게 될지도 알 수가 없어서 나름 고심하여 적은거랍니다.
다음 글에서는 갑자기 난이도가 확 뛸지도 모르겠습니다. (아, 제가 계속 e-book 필진으로 생존해 있는다면요...ㅠㅠ)
p.s. 삼각함수 방식에 이은 두번째 숙제, 원을 그릴 수 있는 또 다른 방법들은 뭐가 있을까 아이디어를 내보세요. 뭐, Tool 처럼 아주 간단한 방법도 좋고, 픽셀벤더 등의 외부 쓰레드를 이용하는 방법이라던가...등등등 글을 올려주시면 선착순으로 칭찬해드리겠습니다.
=================================
=================================
=================================
댓글 영역