BLOG main image
Category (342)
MySpace (89)
Astronomy (50)
Development (178)
Drum (25)
linux에서 subversion설정
누리에 없을 자그마한 자국
살라딘의 생각
saladin's me2DAY
3D Avata - BuddyPoke
기찬 개발이야기
[FLEX] ANT로 ASDOC 사용하기
THLIFE.net
Flash10 대응 Textcube 1.7.5.1..
텍스트큐브 공지사항
«   2008년 11월   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
325650 Visitors up to today!
Today 150 hit, Yesterday 963 hit
/Development/Flex/AIR 관련글 보기 2007년 08월 07일 03시 21분

사용자 삽입 이미지

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
BlogIcon 검쉰 | 2007년 08월 07일 09시 18분 | PERMALINK | EDIT/DEL | REPLY
직접 짜시다니! ^^;;
고맙습니다! 시간 꽤 들었을텐데요 ^^;

근데, 전 직접 마우스로 클릭해서 화살표를 그리는 걸 해보고 싶었던 겁니다 ^^;;;; 소스를 보니 어떻게 해야될지 감 잡을듯. ^^;
완성되면 소스 공유 하도록 하겠습니다. ^^

ps. 저 소스에 색 입히는 건 어떻게 하신겐가요?
제 블로그에는 일일이 손으로 했기때문에; ㅡㅡ;
간단하게 입혀주는 프로그램을 짜서 쓰시나요?
BlogIcon 지돌스타 | 2007년 08월 07일 09시 52분 | PERMALINK | EDIT/DEL
약간이나마 도움이 되셨다니 다행입니다. ^^

소스에 색 입히는 것은 후리자 님께서 만드신 테터툴즈용 플러그인을 설치했기 때문에 사용이 가능합니다.

TStory에도 적용할 수 있는지는 잘 모르겠네요 ^^;;;
BlogIcon Zet | 2007년 08월 08일 20시 52분 | PERMALINK | EDIT/DEL | REPLY
헉 엄청 어려워 보입니다 ㅠ
김준 | 2008년 03월 24일 00시 43분 | PERMALINK | EDIT/DEL | REPLY
그려진 선을 삭제하는 방법좀 없는지요...
BlogIcon 지돌스타 | 2008년 03월 24일 09시 43분 | PERMALINK | EDIT/DEL
this.addChild( arrow );
했잖아요?

this.removeChild( DisplayObject(arrow) ); 하시면 됩니다. ^^
물론 1개 선택할 수 있는 관리 컴포넌트가 필요합니다. 그것은 직접 만드셔야 하겠구요.
Name
Password
Homepage
Secret