MS PowerPoint를 보면 화살표를 만들어 배치하고 화살표를 이동, 회전, 크기 등을 자유롭게 조정하는 기능이 있다. 나는 Flex로 간단하게 이러한 기능을 구현하는 것을 만들어 보았다.
일단 아래 실행화면을 보면 알 수 있듯이 두께조정, 색선택을 한 후 Create Arrow 버튼을 누르면 화면에 한개의 화살표가 생성된다. 이 생성된 화살표를 선택하면 이동이 가능하고 화살표의 양 끝단을 선택하여 움직이면 회전 및 크기 조정이 가능하다.
기능의 개선여지는 충분히 많다.
1. 선택시에만 양끝단 원이 보이도록하고 다른 화살표는 원을 없애준다. 2. 선택후 색과 두께를 자유롭게 선택할 수 있게 3. 화살표 모양을 선택할 수 있게 4. Thumbnail이 있고 그것을 드래그 해서 화면에 배치해서 화살표를 생성시킬 수 있도록 5. 선택후 화살표를 삭제할 수 있도록
화살표를 회전할 때는 고등학교 수학시간때 배운 회전행렬(http://blog.naver.com/elsacred/50008430063 )을 이용했다. 코드를 보시면 아시겠지만 그리 효율적이지 못하다. 이 글을 보시고 기능 개선 및 추가해주셔서 공개해주면 고맙겠다. 또 필요한 곳에서 유용하게 쓰길 바란다.
실행화면
실행코드
ArrowTest.mxml (Language : xml)
<?xml version ="1.0" encoding ="utf-8" ?> <mx:Application xmlns:mx ="http://www.adobe.com/2006/mxml" layout ="absolute" > <mx:Script> <![CDATA[ private var arrow:Arrow; //화살표 생성 private function createArrow( thickness:Number, color:uint ):void { arrow = new Arrow( 100, 100, thickness, color ); arrow.x = 200; arrow.y = 300; this.addChild( arrow ); } ]]> </mx:Script> <mx:NumericStepper id ="lineThickness" x ="10" y ="10" value ="3" maximum ="5" minimum ="1" /> <mx:ColorPicker id ="lineColor" x ="76" y ="10" selectedColor ="#ff0000" /> <mx:Button x ="106" y ="10" label ="Create Arrow" click ="createArrow( lineThickness.value, lineColor.selectedColor )" /> </mx:Application>
Arrow.as (Language : java)
package { import mx.core.UIComponent; import mx.events.ResizeEvent; import flash.display.Sprite; import flash.display.SpreadMethod; import mx.events.FlexEvent; import flash.events.MouseEvent; import flash.ui.Mouse; public class Arrow
extends UIComponent
{ private var header:Sprite;
//화살표 머리 부분 Selecter private var footer:Sprite;
//화살표 발부분 Selecter private var arrow:Sprite;
//화살표 그림 private var thickness:
Number ;
//화살표 두께 private var color:uint;
//화살표 색 //생성자 public function Arrow
( width:
Number , height:
Number , thickness:
Number =
1.0 , color:uint=0x000000
) { super ( ) ;
this .
thickness = thickness;
this .
color = color;
//화살표 머리 생성 header =
new Sprite
( ) ;
header.
addEventListener ( MouseEvent .
MOUSE_DOWN , onMouseDown
) ;
header.
graphics .
clear ( ) ;
header.
graphics .
lineStyle ( 1.0 , 0x000000,
0.5 ) ;
header.
graphics .
beginFill ( 0xffffff,
0.5 ) ;
header.
graphics .
drawCircle ( 0 ,
0 ,
5 ) ;
header.
graphics .
endFill ( ) ;
header.
x = width;
header.
y = height;
//화살표 발 생성 footer =
new Sprite
( ) ;
footer.
graphics .
clear ( ) ;
footer.
graphics .
lineStyle ( 1.0 , 0x000000,
0.5 ) ;
footer.
graphics .
beginFill ( 0xffffff,
0.5 ) ;
footer.
graphics .
drawCircle ( 0 ,
0 ,
5 ) ;
footer.
graphics .
endFill ( ) ;
footer.
x =
0 ;
footer.
y =
0 ;
footer.
addEventListener ( MouseEvent .
MOUSE_DOWN , onMouseDown
) ;
//화살표 생성 arrow =
new Sprite;
arrow.
addEventListener ( MouseEvent .
MOUSE_DOWN , onMouseDown
) ;
draw
( ) ;
//addChild의 순서는 중요하다. 이는 Event target과 관련된다. this .
addChild ( arrow
) ;
this .
addChild ( header
) ;
this .
addChild ( footer
) ;
} //화살표를 그려준다. (회전행렬 사용 ) private function draw
( ) :
void { var x1:
Number = header.
x ;
//화살표 앞쪽 x축값 var y1:
Number = header.
y ;
//화살표 앞쪽 y축값 var x2:
Number = footer.
x ;
//화살표 뒤쪽 x축값 var y2:
Number = footer.
y ;
//화살표 뒤쪽 y축값 var angle:
Number =
Math .
atan2 ( y1-y2, x1-x2
) ;
//화살표의 회전각도 var headSize:
Number =
this .
thickness* 3 ;
//화살표의 헤더부분 길이 var Degree2Radian:
Number =
Math .
PI /
180 ;
//Degree -> Radian을 바꿔줄때 이 값을 곱함 var cos1:
Number =
Math .
cos ( Degree2Radian *
45 ) ;
//화살표 촉의 x축값은 cos(45도 ) 임 var sin1:
Number =
Math .
sin ( Degree2Radian *
45 ) ;
//화살표 촉의 y축값은 sin(45도) 임 var cos2:
Number =
Math .
cos ( angle
) ;
//화살표 회전에 대한 cos 값 var sin2:
Number =
Math .
sin ( angle
) ;
//화살표 회전에 대한 sin 값 var length:
Number =
Math .
sqrt ( Math .
abs ( x1-x2
) *
Math .
abs ( x1-x2
) +
Math .
abs ( y1-y2
) *
Math .
abs ( y1-y2
) ) ;
//화살표 길이 var dx:
Number = length - headSize * cos1;
//화살표 촉 양 끝단 x축 값 var dy1:
Number = headSize * sin1;
//화살표 촉 양 끝단 y축 값 1 var dy2:
Number = -headSize * sin1;
//화살표 촉 양 끝단 y축 값 2 //회전에 의한 화살표 양 끝단의 위치 (2차 회전행렬 공식 이용 ) var newX1:
Number = x2 +
( dx * cos2 - dy1 * sin2
) ;
var newY1:
Number = y2 +
( dx *
( +sin2
) + dy1 * cos2
) ;
var newX2:
Number = x2 +
( dx * cos2 - dy2 * sin2
) ;
var newY2:
Number = y2 +
( dx *
( +sin2
) + dy2 * cos2
) ;
//계산된 좌표값을 이용해 화살표를 다시 그림 arrow.
graphics .
clear ( ) ;
arrow.
graphics .
lineStyle ( this .
thickness ,
this .
color ,
1.0 ) ;
arrow.
graphics .
moveTo ( x1, y1
) ;
arrow.
graphics .
lineTo ( x2, y2
) ;
arrow.
graphics .
moveTo ( x1, y1
) ;
arrow.
graphics .
lineTo ( newX1, newY1
) ;
arrow.
graphics .
moveTo ( x1, y1
) ;
arrow.
graphics .
lineTo ( newX2, newY2
) ;
} //마우스 Down private function onMouseDown
( e:
MouseEvent ) :
void { var sprite:Sprite = e.
target as Sprite;
if ( null != sprite
) { if ( sprite == header
) { sprite.
addEventListener ( MouseEvent .
MOUSE_MOVE , onMouseMove
) ;
sprite.
addEventListener ( MouseEvent .
MOUSE_UP , onMouseUp
) ;
sprite.
startDrag ( ) ;
trace
( "화살표 앞쪽 선택함" ) ;
} else if ( sprite == footer
) { sprite.
addEventListener ( MouseEvent .
MOUSE_MOVE , onMouseMove
) ;
sprite.
addEventListener ( MouseEvent .
MOUSE_UP , onMouseUp
) ;
sprite.
startDrag ( ) ;
trace
( "화살표 뒷쪽 선택함" ) ;
} else { this .
startDrag ( ) ;
this .
addEventListener ( MouseEvent .
MOUSE_MOVE , onMouseMove
) ;
this .
addEventListener ( MouseEvent .
MOUSE_UP , onMouseUp
) ;
trace
( "화살표 중간 선택함" ) ;
} } } //마우스 Move private function onMouseMove
( e:
MouseEvent ) :
void { draw
( ) ;
e.
updateAfterEvent ( ) ;
} //마우스 Up private function onMouseUp
( e:
MouseEvent ) :
void { var sprite:Sprite = e.
target as Sprite;
if ( null != sprite
) { sprite.
removeEventListener ( MouseEvent .
MOUSE_MOVE , onMouseMove
) ;
sprite.
removeEventListener ( MouseEvent .
MOUSE_UP , onMouseUp
) ;
sprite.
stopDrag ( ) ;
} else { this .
removeEventListener ( MouseEvent .
MOUSE_MOVE , onMouseMove
) ;
this .
removeEventListener ( MouseEvent .
MOUSE_UP , onMouseUp
) ;
this .
stopDrag ( ) ;
} draw
( ) ;
} } }
참고사이트회전행렬 :
http://blog.naver.com/elsacred/50008430063
글쓴이 : 지돌스타 (http://blog.jidolstar.com/188 ) 참고 : 위 소스는 비효율적이다. UIComponent에서는 draw함수 대신 updateDisplayList()를 사용하면 더욱 좋은 포퍼먼스를 낼 수 있다.
Trackback Address :: http://blog.jidolstar.com/trackback/188