사용자 삽입 이미지

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 
by <!--r'i"z&i\n+#]]x juree23 2008/06/14 11:04
| 1 ... 722 723 724 725 726 727 728 729 730 ... 800 |