구의 일부분이 되는 붉게 표시한 원을 구할 때

원의 반지름은 rcos(t)

중심점으로부터의 높이는 rsin(t)



point.z = r*sin(t);

point.x = r*cos(t)*cos(경도);  

point.y = r*cos(t)*sin(경도);


z값은 중심점으로부터의 높이로 두고

x와 y값은 반지름( rcos(t) )에 경도의 코사인값과 사인값을 취하여 곱한다.












- 본문폭 상승

- 카테고리 목록을 우측으로 이동

- 사이드바 3단 -> 2단

- 최하단에 간단한 플래시 삽입.



지난번 초대장배포때 가져가지 않은 분이 있어서

회수 후 재배포합니다.

아래 양식에 맞게 비밀덧글을 작성하신분께 선착순으로 드립니다.(선착순 1명)


1. 메일주소
2. 개설하고자 하는 블로그의 주제나 방향

3. 기존에 운영하던 블로그 주소 (있을 경우에만)








 1. 플래시


자세한 설명은 아래에 있습니다.







2. 데이터&설명


지구에서 관측되는 화성의 움직임을 플래시로 나타내 본 것입니다.

아래와 같은 조건들을 설정했습니다.


- 공전궤도 모양

원(평면)으로 가정


- 공전궤도 반지름

지구 : 1
화성 : 1.5


-공전속도

지구 : 1.88

화성 : 1


- 궤도경사와 승교점경도

지구 : 7.3˚ 349˚
화성 : 5.7˚ 50˚


- 천구의 중심

태양 (중심을 지구로 잡으면 투영면이 움직여서 알아보기가 불편하고,

태양으로 잡아도 화성의 역행 움직임이 나타나는 모양을 설명하기에는

큰 차이가 없기 때문에 편의상 태양을 기준으로 함.) 


- 관찰자의 고도

황도를 기준으로 pi/13 라디안



지구와 화성의 공전궤도면이 일치하지 않기 때문에

화성이 충 부근에서 역행을 할 때 α모양이나 s모양이 나타나게 됩니다.


예전에 만들었던 것(http://www.tibyte.kr/126)은 두 행성의 공전궤도를 평면상에 둬서 

이런 현상을 확인할 수 없었는데 이번에 입체로 구현해보았습니다.



3. 코드 구현


(1)

Vector3D객체에 각 천체들의 위치벡터를 저장한 후

눈으로 직접 보기 위해 Sprite객체를 움직일 때는 아래와 같은 코드를 사용함.


earth.x = earthPoint.x + sunPoint.x;

earth.y = Math.sin(viewAng)*(earthPoint.y)+sunPoint.y +

 Math.cos(viewAng)*(earthPoint.z)+sunPoint.z;


viewAng는 시점의 고도이며, 이 값에 따라 위치벡터의 y,z값이 Sprite의 y값에 적용됩니다.

모니터는 2D니까 3차원 좌표를 2차원 좌표로 변환해야 하기 때문임.



(2)

공전궤도를 기울이는 함수 inclineObt()


function inclineObt(vec3D:Vector3D,xy:Number=0, zx:Number=0):Vector3D

{

var transVec3D:Vector3D = new Vector3D();

var matrix:Matrix = new Matrix(0,0,0,0);

matrix.a = vec3D.x;

matrix.b = vec3D.z;

matrix.rotate(zx);  //

transVec3D.x = matrix.a;

transVec3D.z = matrix.b;

matrix.a = transVec3D.x;

matrix.b = vec3D.y;

matrix.rotate(xy);

transVec3D.x = matrix.a;

transVec3D.y = matrix.b;

transVec3D.w = vec3D.w;

return transVec3D;

}


궤도경사와 승교점고도 수치에 맞게 공전궤도를 기울인다.

Matrix클래스의 rotate메서드는 이차원 회전행렬 

( cos -sin)
( sin  cos) 를 대상 행렬 앞에 곱함.




(3)

천구면에 투영된 화성의 위치를 구하는 함수


function calcProjPoint(vec1:Vector3D, vec2:Vector3D):Vector3D

{

var direcVec3D:Vector3D = vec2.clone();

direcVec3D.decrementBy(vec1);

var a:Number = direcVec3D.x;

var b:Number = direcVec3D.y;

var c:Number = direcVec3D.z;

var d:Number = vec1.x;

var e:Number = vec1.y;

var f:Number = vec1.z;

/* 이차방정식의 근의 공식으로 투영된 외행성의 위치를 구한다*/

var product:Number = calcQuadratic(a*a+b*b+c*c, 2*(a*d+b*e+c*f), d*d+e*e+f*f-sphereRad*sphereRad);

direcVec3D.scaleBy(product);

return direcVec3D.add(vec1);


}


천구면은 편의상 중심점을 태양의 중심으로 맞춤.

sphereRad 는 천구의 반지름값이므로 투영된 화성의 상은 태양으로부터 항상 sphereRad위치에 있게 됨.

천구면에 투영된 화성의 위치벡터는 (지구의위치벡터 + p*지구에서화성을바라보는벡터) 이므로 p값을 먼저 구해야 함.


즉, 지구벡터+p*(화성-지구벡터)의 거리는 sphereRad와 같으므로

거리 = 거리 방정식을 세워보면 위 코드와 같은 방법으로 p(product)값을 구할 수 있다. (calcQuadratic은 근의 공식으로 이차방정식의 (큰)해를 구하는 함수) 



(4) 

3D천구 뼈대를 그리는 부분


var polygons:Vector.<Vector.<Vector3D>> = new Vector.<Vector.<Vector3D>>(19);

var model2D:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(19);

for(var i_lati:int = 0; i_lati<19; i_lati++) {

polygons[i_lati] = new Vector.<Vector3D>(36);

model2D[i_lati] = new Vector.<Point>(36);

for(var j_longi:int = 0; j_longi<36; j_longi++) {

polygons[i_lati][j_longi] = new Vector3D();

model2D[i_lati][j_longi] = new Point();

//초기위치 

polygons[i_lati][j_longi].z = radius*Math.sin(getLatitude(i_lati));

polygons[i_lati][j_longi].x = radius*Math.cos(getLatitude(i_lati))*Math.cos(getLongitude(j_longi));

polygons[i_lati][j_longi].y = radius*Math.cos(getLatitude(i_lati))*Math.sin(getLongitude(j_longi));

//시점에따른 변환

model2D[i_lati][j_longi].x = polygons[i_lati][j_longi].x + central.x;

model2D[i_lati][j_longi].y = Math.sin(viewAng)*(polygons[i_lati][j_longi].y)+central.y +

Math.cos(viewAng)*(polygons[i_lati][j_longi].z)+central.z;

}

}

function getLatitude(index:int):Number{return (index*10-90)/180*Math.PI;}

function getLongitude(index:int):Number{return (index*10)/180*Math.PI;} 



Vector에  Vector3D객체와 Point객체를를 2차원으로 할당하고

Vector3D에는 위도를 10도단위로 한층씩 훑어가면서 경선을 이루게 될 36개의 점의 xyz 3차원 좌표를 저장.

Point에는 2차원인 모니터에 표시하기 위해 위에서 구한 xyz좌표를 xy좌표로 변환 .











재미로하는 10년간의 행운그래프 입니다.

운이 별로 좋지 않게 나오면 노력으로 메꾸세요.


사용방법 : 태어난 년도와 이름을 적고 확인버튼을 누르세요.

마우스 오른쪽 버튼으로 언어전환을 할 수 있습니다. (fp11.2)









코드
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package
{
    import adobe.utils.CustomActions;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    /**
     * ...
     * @author tibyte.kr
     */
    public class Main extends Sprite
    {
        private var form1:Form1;
        private var drawingContainer:Sprite = new Sprite();
        
        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            stage.addEventListener(MouseEvent.RIGHT_CLICK, rightClick_h);
            form1 = new Form1();
            form1.name_input.text = "";
            form1.interfaceText.mouseEnabled = false;
            
            addChild(form1);
            addChild(drawingContainer);
            
            form1.ok_btn.addEventListener(MouseEvent.CLICK, btnClick_h);
        }
        
        private function rightClick_h(evt:MouseEvent):void
        {
            form1.title.gotoAndStop(form1.title.currentFrame % 2 + 1);
            form1.interfaceText.gotoAndStop(form1.interfaceText.currentFrame % 2 + 1);
        }
        
        private function calcData(year:intname:String):Array
        {
            var dataArray:Array = new Array(11);
            var md5:String = MD5.getMD5(name + year);
            var seed:int =parseInt("0x"+md5.slice(2, 6));
            var bmp:BitmapData = new BitmapData(220, 1);
            var luck:int;
            var offsets:Array = new Array(6);
            for (var j:int = 0; j < 6; j++)
            {
                offsets[j] = new Point(20*(new Date().getUTCFullYear()%30), 0);
            }
            
            /* 행운값 추출 */
            bmp.perlinNoise(120, 1, 6, seed,falsetrue, 7, true, offsets);
            for (var i:int = 0; i < 11; i+=1)
            {
                luck = Math.floor((Math.max(10, Math.min(80, (bmp.getPixel(i*20, 0) >> 16) / 255 * 100)) - 10) * (5 / 4));
                dataArray[i] = luck;
            }
            return dataArray.concat();
        }
        
        private function drawData(data:Array, dest:Sprite, form:Form1):void
        {
            dest.graphics.clear();
            
            /* 보조선 */
            dest.graphics.lineStyle(0.1, 0x666666);
            for (var i:int = 0; i < 11; i++)
            {
                dest.graphics.moveTo(calcX(i, form), calcY(100, form));
                dest.graphics.lineTo(calcX(i, form), calcY(0, form));
            }
            
            /* 그래프 */
            dest.graphics.lineStyle(2, 0xcc0033);
            dest.graphics.moveTo(calcX(0, form), calcY(data[0], form));
            for (var i:int = 1; i < 11; i++)
            {
                dest.graphics.lineStyle(2, 0xcc0033);
                dest.graphics.lineTo(calcX(i, form), calcY(data[i], form));
            }
        }
        
        private function calcY(value:int, form:Form1):Number
        {
            return form.point2_mc.y - (form.point2_mc.y - form.point1_mc.y) / 100 * value;
        }
        
        private function calcX(index:int, form:Form1):Number
        {
            return form.point1_mc.x + (form.point2_mc.x - form.point1_mc.x) / 10 * index;
        }
        
        private function btnClick_h(evt:MouseEvent):void
        {
            if (form1.name_input.text == "")
                return;
            var dataArray:Array = calcData(form1.age_input.value, form1.name_input.text);
            drawData(dataArray, drawingContainer, form1);
        
        }
    }
 
}


















10월과 11월에 충전된 티스토리 초대장을 7장 배포합니다.

아래 형식에 맞게 비밀덧글을 남겨주시면 검토후 일곱 분을 선정하여

티스토리 초대장을 보내드리겠습니다.


1. 메일주소
2. 개설하고자 하는 블로그의 주제나 방향

3. 기존에 운영하던 블로그 주소 (있을 경우에만)


마감기한 : 24시간 이내 (선착순 아님)










 FlashPlayer 11.2부터 브라우저에 실린 swf파일에서도

마우스 우클릭 기능을 쓸 수 있기에 한번 시도해 보았다.


마우스 왼쪽버튼 클릭 : 분홍색으로 전환
마우스 오른쪽버튼 클릭 : 푸른색으로 전환



 

예제파일 제공 : kzc123 님











 코딩을 하면서 각종 상황에 대한 테스트가 필요했기 때문에

Valve社의 게임에서 볼 수 있는 것과 비슷한 콘솔 창을 구현하기로 했다.  


 콘솔 창은 싱글턴 패턴으로 프로그래밍했는데

액션스크립트는 생성자 함수의 접근제한자로 private를 둘 수 없기 때문에

해당 클래스 파일의 패키지 외부에 클래스 하나를 더 두고

싱글턴 클래스 생성자의 매개변수로 그 클래스 타입을 넣어주어야 한다.

아래와 같은 모양이다.


package {

public class Singleton {

private static var instance...

public function Singleton(block:SingletonBlocker)

{

if (block == null) throw new Error...

}

public static function getInstance()...

}

}

class SingletonBlocker{}



 프로젝트의 클래스에서 콘솔 창에 뭔가를 표시하는 함수를 호출하면 

콘솔창이 없을 경우 새로 창을 띄우고 있으면 그 창에 내용을 출력한다.


 콘솔 창은 NativeWindow클래스를 사용하여 새 윈도우를 띄우게 되는데,

이 윈도의 크기가 원하는 대로 되지 않았다. 액션스크립트 도움말을 보면 

NativeWindow객체의 stage.stageWidth 나 Height속성을 조절하면 창 크기가 

그에 맞게 바뀐다고 하는데 전혀 그렇지가 않는 것이었다..


 해결책은 역시 도움말에 나와있었는데  

_mainWindow.stage.scaleMode = StageScaleMode.NO_SCALE;

_mainWindow.stage.align = StageAlign.TOP_LEFT;

이 두 구문이 있어야 제대로 창크기 조절이 된다.



코드를 컴파일후 실행시키니 제대로 출력이 나왔다.






 다음은 게임에서 사용할 월드 데이터 파일을 제작하는 간단한 툴을 만드는 것이었다.

FileStream클래스와 File클래스로 파일쓰기를 한다.


File객체를 만들 때, 파일쓰기 하려는 위치가 어플리케이션이 있는 폴더라면

var file:File = File.applicationDirectory;

와 같이 초기화해야 한다고 해서 그렇게 해 보니 보안문제로 오류가 났다.-_-;


이 경우에는 

var file:File = new File(File.applicationDirectory.nativePath);

file = file.resolvePath("하위폴더"+File.separator+파일이름+".확장자");

요렇게 하니까 되더라..


 그 다음 이렇게 해서 생성된 월드 데이터파일에 어플리케이션을 연결해 보기로 했다.

이 작업은 그리 어렵지 않게 할 수 있다.

if(NativeApplication.nativeApplication.isSetAsDefaultApplication("확장자")==false)

NativeApplication.nativeApplication.setAsDefaultApplication("확장자");

연결이 되어 있지 않으면 '이 어플리케이션'을 해당 file의 연결 프로그램으로 설정한다.

단, 응용 프로그램 설명자(xml파일)의 fileTypes노드에 파일의 정보가 선언되어 있어야 한다.


 빌드를 하니 128x128 아이콘 파일의 크기가 맞지 않는다는 오류가 계속 뜨는데

src폴더 내의 파일을 수정해도 빌드만 하면 다시 129x129크기의 파일로 바뀌어 버렸다.

bin-debug폴더에 들어있는 파일을 지우니 제대로 빌드가 되었다.



(▲ 데이터파일의 아이콘이 바뀐 모습. 실행하면 맵에디터가 열리고 해당 파일을 불러들인다.) 



하나 빼먹은 게 있어 추가로 적는데,

프로그램 연결은 연결프로그램으로 쓸 어플리케이션이 컴퓨터에 설치되어 있는 상태여야 한다. (디버그모드에서는 확인할 수 없다.)








 





스크린샷 첨부.


















티스토리 블로그 글쓰기창이 새 에디터로 바뀌면서

플래시를 삽입할 때 불편함을 느끼셨을 것입니다.


기존 에디터와는 달리 플래시 무비의 크기를 조절하는 인터페이스가 없기 때문입니다.

HTML태그를 봐도 width와 height속성을 어디에 넣어야 할 지 쉽게 알 수 없게 되어있고요.


이럴 때 아래 과정을 거치면 쉽게 플래시의 크기와 위치를 조절할 수 있습니다.







1. 파일 버튼을 눌러서 올릴 플래시를 선택.






2. 파일을 올렸으면 HTML모드로 들어갑니다.






3.  cfile 부터 swf부분까지를 복사합니다.





4. 복사가 되었으면 [## 부터   ##]부분까지를 지우고 다음 태그로 대체합니다.


<embed width="가로크기" height="세로크기" src="/attachment/복사했던 주소" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer">




5. 다시 HTML모드를 해제하면 지정한 크기로 멀티미디어 그림이 나타납니다.






이렇게 해서 새에디터에서도 원하는 크기로 플래시를 넣을 수 있습니다!











+ Recent posts