=================================
=================================
=================================
출처: http://blog.jidolstar.com/463
ActionScript 3.0에는 Matrix와 Transform 클래스가 있다.
Matrix는 일종의 변환행렬(Transformation Matrix)이다. 3×3 행렬로 이동(translation), 확대/축소(scaling), 회전(rotation), 기울이기(shearing)등을 구현할 수 있다. Papervision3D나 Away3D와 같이 2D화면에 3D 효과가 가능했던 것은 Bitamp과 Matrix가 있기 때문이다. 이들 클래스를 어떻게 잘 조작하느냐에 따라 풍성한 화면효과를 구현할 수 있다.
Transform은 DisplayObject 계열의 객체에 만들어진 Matrix를 적용하는 역할을 한다. 즉 Matrix는 이동/회전등의 변환수단으로 작용하고 실제 이 Matrix를 이용해서 DisplayObject에 적용하는 것은 Transform인 것이다.
DisplayObject에는 tranform 속성이 있고 transform에는 matrix 속성이 있다. 그러므로 만들어진 Matrix를 DisplayObject에 적용하기 위해 다음과 같이 시도할 수 있다.
var content: DisplayObject = new Sprite as DisplayObject;
var mat: Matrix = new Matrix();
(mat 조작 생략)
content.tranform.matrix = mat;
위와 같이 하는 것만으로도 이동,확대/축소,회전,shearing,거울효과등을 적용할 수 있게 된다.
그럼 어떻게 Matrix를 만들어야 원하는 동작을 만들어낼 수 있을까? 다행히도 Matrix는 사용자가 조작하기 아주 쉽게 만들어져 있다.
회전을 하려면 Matrix의 rotate( 라디안각도 ), 이동을 하려면 Matrix의 translate( dx, dy ), 확대/축소하려면 Matrix의 scale( sx, sy ) 함수를 사용하면 된다.
보통 이 Matrix를 이용하지 않고도 DisplayObject의 rotation, x, y, scaleX, scaleY 속성을 사용해도 될때가 있다. 사릴 이 속성들은 결국 Matrix로 구현된다. Matrix가 다루기 어렵기 때문에 쉬운 사용을 위해 기본적인 인터페이스는 DisplayObject에 만들어준 것 뿐이다. 하지만 이 외에 사진의 중심으로 회전하던가 거울효과를 적용시키던가 shearing과 같은 효과를 주려면 이들 속성만 가지고는 해결할 수 있는 방법이 없거나 있다고 하더라도 비효율적인 방법일 소지가 많다. 그러므로 고급적으로 DisplayObject를 가공하려면 Matrix의 사용방법과 그 원리에 대해서 익숙해져야 한다.
Matrix는 3×3로 구성된다고 했다. 2D인데 3×3 행렬을 이용하는 이유는 이동(translation)이 포함되어 있기 때문이다.
일단 전반적인 지식은 아래 링크들을 참고하기 바란다. 아래 내용만 잘 알아도 DisaplyObject 객체를 가지고 기하학적 변형을 위한 기초는 알 수 있다.
- ActionScript 3.0 Matrix 클래스
- Understanding the Transformation Matrix in Flash 8
- 2D Transformations
- 2D Tranformations – PPT 자료
- Transformation Pipeline
- Affine transformations
DisplayObject 객체의 중심을 그의 부모 (0,0)점에 위치하고 회전 및 확대/축소
DisplayObject객체 중심을 객체의 좌측상단점으로 이동하는 행렬 T, 회전행렬 R, 확대/축소행렬 S라고 하자.
DisplayObject는 항상 좌측상단이 기준점이 된다. 그러므로 DisplayObject의 중심점 이동->회전->확대/축소를 적용하면 되겠다. 그러므로 이들을 모두 적용할 수 있는 행렬은 다음과 같다.
M = S x R x T = R x S x T
더 명확히 표현하자면 아래와 같다. (아래식에서 각각의 변환행렬을 곱한 결과는 그 아래 실제 결과와 다르게 나왔다. 실제결과를 도출하기 위한 뭔가 다른 설정이 있는 것 같은데 본인은 발견하지 못했다. 혹시 아는분 댓글 부탁한다.)
여기서 θ는 회전각도 radian값이고 sx와 sy는 각각 x축, y축 확대/축소 비율이다. cx와 cy는 DisplayObject 객체의 중심좌표값이다.
A=[x,y,1]값을 변환을 거쳐서 나온 결과 좌표값을 A’=[x’,y’,1]이라고 한다면 다음 관계가 성립한다.
A’= M x A
결국 중요한 것은 변환행렬 M을 만들어 내는 일이다.
그럼 위 행렬을 어떻게 DisplayObject에 적용할 수 있을까?
var content: DisplayObject = new Sprite as DisplayObject;
var cx: Number = content.width / 2;
var cy: Number = content.height / 2;
var sx: Number = 2;
var sy: Number = 3;
var theta: Number = 45 * Math.PI / 180;
var mat: Matrix = new Matrix();
mat.translate(-cx, -cy);
mat.scale(sx, sy);
mat.rotate(theta);
content.tranform.matrix = mat;
위 코드처럼 하면 변환행렬 M을 DisplayObject에 적용한 것과 같다. 다음과 같이 해도 동일한 동작을 하게 된다.
var content: DisplayObject = new Sprite as DisplayObject;
var cx: Number = content.width / 2;
var cy: Number = content.height / 2;
var sx: Number = 2;
var sy: Number = 3;
var theta: Number = 45 * Math.PI / 180;
var cos: Number = Math.cos(theta);
var sin: Number = Math.sin(theta);
var mat: Matrix = new Matrix();
mat.a = sx * cos;
mat.b = sy * sin;
mat.c = –sx * sin;
mat.d = sy * cos;
mat.tx = -sx * cx * cos + sy * cy * sin;
mat.ty = -sx * cx * sin - sy * cy * cos;
content.tranform.matrix = mat;
이런 원리를 잘 알아두면 앞으로 DisplayObject의 기하학적 변형을 위한 방법을 익히는 것 뿐아니라 속도향상에도 도움이 될 수 있겠다.
예제 애플리케이션 제작
위 설명을 토대로 사진 중심으로 회전, 확대/축소등이 가능한 DisplayObject 객체를 만들고 테스트 해볼 수 있는 예제를 만들어보자.
아래 클래스는 이동/스케일링/회전/거울효과를 테스트 하기 위한 것이다. 위에서 다 설명했으므로 특별히 분석은 안하도록 하겠다.
package
{
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Matrix;
import flash.net.URLRequest;
import flash.utils.setTimeout;
public class TestSprite extends Sprite
{
private
var container: Sprite;
private
var image: Loader;
private
var originalWidth: Number = 0;
private
var originalHeight: Number = 0;
private
var _scale: Number = 1;
private
var _rotation: Number = 0;
private
var _horizontalMirror: Boolean = false;
private
var _verticalMirror: Boolean = false;
private
var isApplyMatrix: Boolean = false;
public
function TestSprite()
{
super();
container = new Sprite;
addChild(container);
image = new Loader;
container.addChild(image);
image.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
image.load(new URLRequest("http://jidolstar.com/blog/wp-content/uploads/2009/03/yaejin.jpg"));
}
private
function onComplete(event: Event): void
{
event.target.removeEventListener(Event.COMPLETE, onComplete);
//원본 사진의 크기
originalWidth = event.target.content.width;
originalHeight = event.target.content.height;
//Matrix적용
applyMatrix(container);
}
private
function applyMatrix(target: DisplayObject): void
{
var mat: Matrix = target.transform.matrix;
var cos: Number = Math.cos(_rotation * Math.PI / 180);
var sin: Number = Math.sin(_rotation * Math.PI / 180);
var cx: Number = originalWidth / 2;
var cy: Number = originalHeight / 2;
//단위행렬로 바꿈
mat.identity();
//거울 효과 적용
if (_horizontalMirror)
{
mat.a = -1;
mat.tx = originalWidth;
}
if (_verticalMirror)
{
mat.d = -1;
mat.ty = originalHeight;
}
//widget의 (0,0)위치 조정
mat.translate(-cx, -cy);
//스케일 적용
mat.scale(_scale, _scale);
//회전 적용
mat.rotate(_rotation * Math.PI / 180);
//mat.translate( cy, cy );
/*
//주석부분은 위에 mat.translate(),mat.scale(), mat.rotate()을 호출한것과 동일하게 동작한다. 단 거울효과를 적용했을때는 똑같지 않다. 같은 효과를 내려면 Matrix.concat()을 이용해 행렬곱을 실시하면 되겠다.
mat.a = _scale * cos;
mat.b = _scale * sin ;
mat.c = _scale * sin * -1;
mat.d = _scale * cos;
mat.tx = -cx * _scale * cos + cy * _scale * sin;
mat.ty = -cx * _scale * sin - cy * _scale * cos;
*/
//Matrix 적용
target.transform.matrix = mat;
this.width = target.width;
this.height = target.height;
this.dispatchEvent(new Event(Event.RESIZE));
isApplyMatrix = false;
}
public override
function set rotation(value: Number): void
{
_rotation = value;
if (!isApplyMatrix)
{
isApplyMatrix = true;
setTimeout(applyMatrix, 0, container);
}
}
public
function set scale(value: Number): void
{
_scale = value;
if (!isApplyMatrix)
{
isApplyMatrix = true;
setTimeout(applyMatrix, 0, container);
}
}
public
function set horizontalMirror(value: Boolean): void
{
_horizontalMirror = value;
if (!isApplyMatrix)
{
isApplyMatrix = true;
setTimeout(applyMatrix, 0, container);
}
}
public
function set verticalMirror(value: Boolean): void
{
_verticalMirror = value;
if (!isApplyMatrix)
{
isApplyMatrix = true;
setTimeout(applyMatrix, 0, container);
}
}
}
}
아래는 위에서 정의한 TestSprite를 사용하는 Flex Application이다.
<? xml version = "1.0" encoding = "utf-8" ? >
<mx : Application xmlns: mx = "http://www.adobe.com/2006/mxml"
layout = "vertical"
creationComplete = "init()"
backgroundGradientColors = "[0,0]" >
<mx: VBox width = "100%"
height = "100%" >
<mx: UIComponent id = "container"
width = "100%"
height = "100%"
resize = "onResize()" / >
<mx: Form >
<mx: FormItem label = "scale" >
<mx: HSlider id = "sldScale"
minimum = "0.5"
maximum = "3"
value = "1"
change = "test.scale=sldScale.value"
liveDragging = "true" / >
</mx:FormItem>
<mx: FormItem label = "rotation" >
<mx: HSlider id = "sldRotation"
minimum = "0"
maximum = "360"
value = "0"
change = "test.rotation=sldRotation.value"
liveDragging = "true" / >
</mx:FormItem>
<mx: FormItem label = "horizontal Mirror" >
<mx: CheckBox id = "chHorizontalMirror"
change = "test.horizontalMirror = chHorizontalMirror.selected" / >
</mx:FormItem>
<mx: FormItem label = "vertical Mirror" >
<mx: CheckBox id = "chVerticalMirror"
change = "test.verticalMirror = chVerticalMirror.selected" / >
</mx:FormItem>
</mx:Form>
</mx:VBox>
<mx: Script >
<
![CDATA[
private
var test: TestSprite;
private
function init(): void
{
test = new TestSprite();
container.addChild(test);
test.addEventListener(Event.RESIZE, onResizeTest);
onResize();
}
private
function onResize(): void
{
if (test)
{
test.x = container.width / 2;
test.y = container.height / 2;
}
}
private
function onResizeTest(event: Event): void
{
var w: Number = test.width;
var h: Number = test.height;
var hw: Number = w / 2;
var hh: Number = h / 2;
test.graphics.clear();
test.graphics.lineStyle(1, 0xff0000, 1);
test.graphics.drawRect(-hw, -hh, w, h);
test.graphics.moveTo(-hw, 0);
test.graphics.lineTo(hw, 0);
test.graphics.moveTo(0, -hh);
test.graphics.lineTo(0, hh);
}
]] >
</mx:Script>
<mx: Style >
global
{
color: #ffffff;
}
ToolTip
{
color: #000000;
}
</mx:Style>
</mx:Application>
위에서 보여준 것과 달리 좌측상단을 기준으로 하고 사진중심만 회전하고 싶은 경우에는 TestSprite 클래스에서 mat.rotate() 부분 아래에 mat.translate( cx, cy )만 추가하면 된다. 결국 사진중심을 회전하기 위해 이동후, 회전 및 스케일링을 거친다음에 다시 자신의 위치로 옮겨오는 작업이 추가되는 것이다.
원리를 알면 많이 고민 안하고도 적용할 수 있다는거…
산수 좀 나온다고 거부하면 다음에도 고생한다. ^^
출처: http://smilejsu.tistory.com/288 [{ Unity3D }]
=================================
=================================
=================================
'ADOBE > ActionScript' 카테고리의 다른 글
ActionScript 3.0(Flash)에서 JSON 데이터 읽기. (0) | 2020.09.20 |
---|---|
adobe air 액션스크립트 파일 쓰기 읽기, 파일탐색기 사용 관련 (0) | 2020.09.20 |
플래시 AS3.0 Textfield에 Tag를 사용하여 Embed자원 이미지를 지정하기 (0) | 2020.09.19 |
[AS] 플래시 fl.controls.DataGrid 관련 (0) | 2020.09.19 |
플래시 UI 컨트롤 리스트 fl.controls.List | fl.controls.TileList 패키지 관련 (0) | 2020.09.19 |