使用仿射坐标创建带有自定义引擎的 Spine2d 库 Space
Creating a Spine2d library with a custom engine using Affine Coord Space
对于我当前的项目,我们正在使用自定义脚本语言(天知道我们为什么要这样做)来进行游戏开发。保留细节,引擎基本上解释和导出为 Flash 或 iOS.
因此,在这个项目中,我的任务是创建一个 Spine 库来协助制作动画。大多数情况下,它并不太难,因为我们的引擎与 AS3 非常相似,我可以直接翻译它。
我现在遇到的主要问题是渲染。该脚本语言的创建者决定专门使用仿射坐标 space 系统来渲染位置等。我试图全神贯注于它,但对它的工作原理知之甚少,我正在努力弄清楚。 我需要做的就是通过 x 和 y 手动设置位置,并通过角度手动设置旋转。任何帮助将不胜感激。
无论如何,这是我渲染实际脊柱库内容的代码(这是基于 AS3 库中的 SkeletonSprite.as class):
package engine.spine.render;
//These have the same functionality as the Spine2d library for AS3
import engine.spine.IGBone;
import engine.spine.IGSkeleton;
import engine.spine.IGSkeletonData;
import engine.spine.IGSlot;
import engine.spine.atlas.IGAtlasRegion;
import engine.spine.attachments.IGRegionAttachment;
//This stuff is our engine libraries
import engine.graphics.*;
import engine.gui.*;
import engine.math.*;
import engine.tween.*;
import engine.util.IGFSM;
import engine.IGApplication;
public class IGSpineWidget extends IGUIWidget
{
private var m_skeleton : IGSkeleton;
private var m_lastTime : int;
private var m_image : IGImage;
private var m_wrappers : Map<IGRegionAttachment, IGSpinePosition> = new Map<IGRegionAttachment, IGSpinePosition>(); // Works the same way as a Dictionary in AS3
public function IGSpineWidget (skeletonData : IGSkeletonData, image : IGImage)
{
IGBone.yDown = true;
m_skeleton = new IGSkeleton(skeletonData);
m_skeleton.updateWorldTransform();
m_image = image;
}
protected override function render(g : IGGraphics) : void
{
m_skeleton.update(IGApplication.delta_time) ;
var drawOrder : Vector<IGSlot> = skeleton.drawOrder;
for (var i : int = 0; i < drawOrder.length; i++)
{
g.push();
{
var slot : IGSlot = drawOrder[i];
if (slot.attachment != null && slot.attachment.type != IGRegionAttachment.Region()) { continue; }
var regionAttachment : IGRegionAttachment = IGRegionAttachment(slot.attachment);
var wrapper : IGSpinePosition = m_wrappers[regionAttachment];
if(regionAttachment != null) {
if (wrapper == null)
{
wrapper = new IGSpinePosition();
var region : IGAtlasRegion = IGAtlasRegion(regionAttachment.rendererObject);
var regionHeight : double = region.rotate ? region.width : region.height;
var regionWidth : double = region.rotate ? region.height : region.width;
wrapper.x = region.x;
wrapper.y = region.y;
wrapper.height = regionHeight;
wrapper.width = regionWidth;
// Rotate and scale using default registration point (top left corner, y-down, cw) instead of image center
wrapper.affine.rotate(regionAttachment.rotation * Math.PI / 180); //This rotates the position of the drawn object, but I need to be able to set the actual rotation instead of translating a rotation
wrapper.affine.scale(regionAttachment.scaleX * (regionAttachment.width / region.width), regionAttachment.scaleY * (regionAttachment.height / region.height));
// Position using attachment translation, shifted as if scale and rotation were at image center
var radians : double = -regionAttachment.rotation * Math.PI / 180;
var cos : double = Math.cos(radians);
var sin : double = Math.sin(radians);
var shiftX : double = -regionAttachment.width / 2 * regionAttachment.scaleX;
var shiftY : double = -regionAttachment.height / 2 * regionAttachment.scaleY;
if (region.rotate)
{
wrapper.affine.rotate(90); // Again, I need to += the rotation by 90 degrees, but I dont have that functionality
shiftX += regionHeight * (regionAttachment.width / region.width);
}
wrapper.affine.translate(0, 0);//(regionAttachment.x + shiftX * cos - shiftY * sin, -regionAttachment.y + shiftX * sin + shiftY * cos);
m_wrappers[regionAttachment] = wrapper;
}
var bone : IGBone = slot.bone;
var flipX : int = skeleton.flipX ? -1 : 1;
var flipY : int = skeleton.flipY ? -1 : 1;
//This is the key part. I need to be able to set the wrapper's affine2d's x and y position, the the rotation by angle (ie. rotation = someAngle)
//wrapper.affine.translate(bone.worldX, bone.worldY);
//wrapper.affine.rotate(bone.worldRotationX * flipX * flipY);
wrapper.scaleX = bone.worldScaleX * flipX;
wrapper.scaleY = bone.worldScaleY * flipY;
g.scale(wrapper.scaleX/4, wrapper.scaleY/4);
g.translate(1000, 1600); // Set the position of the widget
g.rotate(bone.worldRotationX * flipX * flipY);
g.multTransform(wrapper.affine);
g.drawSubImage(m_image, wrapper.x, wrapper.y, wrapper.width, wrapper.height, 0, 0, wrapper.width, wrapper.height);
}
}
g.pop();
}
}
public function get skeleton () : IGSkeleton {
return m_skeleton;
}
}
这是 Affine2d class:
package engine.math;
public class IGAffine2D
{
/////////////////////////////////////////////////////////////////////
// State
/////////////////////////////////////////////////////////////////////
public var m0:double;
public var m1:double;
public var m2:double;
public var m3:double;
public var m4:double;
public var m5:double;
/////////////////////////////////////////////////////////////////////
// Construction and Initialsation
/////////////////////////////////////////////////////////////////////
public final function IGAffine2D()
{
this.init();
}
public final function isIdentity() : bool
{
return m0 == 1 && m2 == 0 && m4 == 0 &&
m1 == 0 && m3 == 1 && m5 == 0;
}
public final function init() : IGAffine2D
{
m0 = 1.0;
m1 = 0.0;
m2 = 0.0;
m3 = 1.0;
m4 = 0.0;
m5 = 0.0;
return this;
}
public final function initColumnMajor(
a0 : double, a1 : double, a2 : double,
a3 : double, a4 : double, a5 : double) : IGAffine2D {
// represents the following 3x3 (2d affine) matrix
// m0 m2 m4
// m1 m3 m5
// 0 0 1
m0 = a0;
m1 = a1;
m2 = a2;
m3 = a3;
m4 = a4;
m5 = a5;
return this;
}
public final function initFromTransform(transform:IGAffine2D) : IGAffine2D
{
m0 = transform.m0;
m1 = transform.m1;
m2 = transform.m2;
m3 = transform.m3;
m4 = transform.m4;
m5 = transform.m5;
return this;
}
public final function initWithInverseFromTransform(other : IGAffine2D) : IGAffine2D
{
// this is 65 ops
var a : double= other.m0;
var b : double= other.m2;
var c : double= other.m1;
var d : double= other.m3;
var det_inv : double = 1.0 / (a*d - b*c);
m0 = d * det_inv;
m1 = -c * det_inv;
m2 = -b * det_inv;
m3 = a * det_inv;
var x : double= other.m4;
var y : double= other.m5;
m4 = -(x * m0 + y * m2);
m5 = -(x * m1 + y * m3);
return this;
}
//////////////////////////////////////////////////////////////////////
// Getting Properties of the Transform
//////////////////////////////////////////////////////////////////////
public final function get translate_x() : double
{
return m4;
}
public final function get translate_y() : double
{
return m5;
}
public final function get scale_x() : double
{
return m0;
}
public final function get scale_y() : double
{
return m3;
}
/**
* Determines whether or not the translation applied by this matrix
* will result in the coordinate being integer bound for a point
* around the origin
**/
public final function isIntegerTranslate() : bool
{
var tx : int = m4;
var ty : int = m5;
if (m0 == 1 && m1 == 0 && m2 == 0 && m3 == 1 && m4 == tx && m5 == ty) {
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Performing Transformations
//////////////////////////////////////////////////////////////////////
public final function translate(dx:double, dy:double): void
{
m4 += (m0 * dx) + (m2 * dy);
m5 += (m1 * dx) + (m3 * dy);
}
public final function rotate (theta: double) : void
{
if(theta == 0) {
return;
}
var st :double = Math.sin(theta);
var ct :double = Math.cos(theta);
var r00 :double = (m0 * ct) + (m2 * st);
var r01 :double = (m0 * -st) + (m2 * ct);
var r02 :double = m4;
var r10 :double = (m1 * ct) + (m3 * st);
var r11 :double = (m1 * -st) + (m3 * ct);
var r12 :double = m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
}
public final function scale(sx : double, sy : double) : void
{
m0 *= sx;
m1 *= sx;
m2 *= sy;
m3 *= sy;
}
// [m00 m01 m02] [1 shx 0]
// [m10 m11 m12] [shy 1 0]
// [ 0 0 1] [0 0 1]
public final function shear(shx:double, shy:double) : void
{
var r00 : double = m0 + m2 * shy;
var r01 : double = m0 * shx + m2;
var r02 : double = m4;
var r10 : double = m1 + m3 * shy;
var r11 : double = m1 * shx + m3;
var r12 : double = m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
}
/**
* Performs a Translate/Scale/Rotate transformation around a pivot point.
* OPTIMIZED VERSION OF:
* - translate(dx + px, dy + py);
* - rotate(theta);
* - scale(sx, sy);
* - translate(-px, -py);
**/
public final function TRS(dx : double, dy : double,
px : double, py : double, // positive pivot point
sx : double, sy : double, theta : double): IGAffine2D
{
// early abort for standard case
if (sx == 1.0 && sy == 1.0 && theta == 0) {
m4 += dx;
m5 += dy;
return this;
}
var l0 : double = m0;
var l1 : double = m1;
var l2 : double = m2;
var l3 : double = m3;
var l4 : double = m4 + (dx + px)*l0 + (dy + py)*l2;
var l5 : double = m5 + (dx + px)*l1 + (dy + py)*l3;
if (theta != 0)
{
var s : double = -Math.sin(theta);
var c : double = Math.cos(theta);
var r0 : double = c*sx;
var r1 : double = -s*sy;
var r2 : double = s*sx;
var r3 : double = c*sy;
m0 = l0 * r0 + l2 * r1;
m1 = l1 * r0 + l3 * r1;
m2 = l0 * r2 + l2 * r3;
m3 = l1 * r2 + l3 * r3;
m4 = l4 - m0 * px - m2 * py;
m5 = l5 - m1 * px - m3 * py;
}
else
{
m0 = l0 * sx ;
m1 = l1 * sx ;
m2 = l2 * sy;
m3 = l3 * sy;
m4 = l4 - (m0 * px) - (m2 * py);
m5 = l5 - (m1 * px) - (m3 * py);
}
return this;
}
public final function initFromTransformWithOffset(other : IGAffine2D, dx : double, dy : double) : IGAffine2D
{
m0 = other.m0;
m1 = other.m1;
m2 = other.m2;
m3 = other.m3;
m4 = other.m4 + dx * m0 + dy * m2;
m5 = other.m5 + dx * m1 + dy * m3;
return this;
}
public final function initFromTransformWithTRS(
other : IGAffine2D,
dx : double, dy : double,
px : double, py : double, // positive pivot point
sx : double, sy : double, theta : double): IGAffine2D
{
var s :double = 0;
var c :double = 1;
if (theta != 0)
{
s = -Math.sin(theta);
c = Math.cos(theta);
}
var l0 : double = other.m0;
var l1 : double = other.m1;
var l2 : double = other.m2;
var l3 : double = other.m3;
var l4 : double = other.m4 + (dx + px)*l0 + (dy + py)*l2;
var l5 : double = other.m5 + (dx + px)*l1 + (dy + py)*l3;
var r0 : double = c*sx;
var r1 : double = -s*sy;
var r2 : double = s*sx;
var r3 : double = c*sy;
m0 = l0 * r0 + l2 * r1;
m1 = l1 * r0 + l3 * r1;
m2 = l0 * r2 + l2 * r3;
m3 = l1 * r2 + l3 * r3;
m4 = l4 - m0 * px - m2 * py;
m5 = l5 - m1 * px - m3 * py;
return this;
}
//////////////////////////////////////////////////////////////////////
// Performing Other Operations
//////////////////////////////////////////////////////////////////////
public final function invert(): IGAffine2D
{
var a : double= m0;
var b : double= m2;
var c : double= m1;
var d : double= m3;
var det_inv : double = 1.0 / (a*d - b*c);
m0 = d * det_inv;
m1 = -c * det_inv;
m2 = -b * det_inv;
m3 = a * det_inv;
var x : double= m4;
var y : double= m5;
m4 = -x * m0 + -y * m2;
m5 = -x * m1 + -y * m3;
return this;
}
public final function concat(t : IGAffine2D): IGAffine2D
{
var r00 : double = (m0 * t.m0) + (m2 * t.m1);
var r01 : double = (m0 * t.m2) + (m2 * t.m3);
var r02 : double = (m0 * t.m4) + (m2 * t.m5) + m4;
var r10 : double = (m1 * t.m0) + (m3 * t.m1);
var r11 : double = (m1 * t.m2) + (m3 * t.m3);
var r12 : double = (m1 * t.m4) + (m3 * t.m5) + m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
return this;
}
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
public final function transformVector2(pin : IGVector2, pout : IGVector2): void
{
var x:double = pin.x * m0 + pin.y * m2 + m4;
var y:double = pin.x * m1 + pin.y * m3 + m5;
pout.x = x;
pout.y = y;
}
public final function debug() : String
{
return m0 + " \t" + m2 + "\t" + m4 + "\n" + m1 + "\t" + m3 + "\t" + m5;
}
}
我已经弄明白了。在每次我需要根据位置重绘的调用中,我重置仿射坐标,然后平移并旋转一次到正确的位置。
对于我当前的项目,我们正在使用自定义脚本语言(天知道我们为什么要这样做)来进行游戏开发。保留细节,引擎基本上解释和导出为 Flash 或 iOS.
因此,在这个项目中,我的任务是创建一个 Spine 库来协助制作动画。大多数情况下,它并不太难,因为我们的引擎与 AS3 非常相似,我可以直接翻译它。
我现在遇到的主要问题是渲染。该脚本语言的创建者决定专门使用仿射坐标 space 系统来渲染位置等。我试图全神贯注于它,但对它的工作原理知之甚少,我正在努力弄清楚。 我需要做的就是通过 x 和 y 手动设置位置,并通过角度手动设置旋转。任何帮助将不胜感激。
无论如何,这是我渲染实际脊柱库内容的代码(这是基于 AS3 库中的 SkeletonSprite.as class):
package engine.spine.render;
//These have the same functionality as the Spine2d library for AS3
import engine.spine.IGBone;
import engine.spine.IGSkeleton;
import engine.spine.IGSkeletonData;
import engine.spine.IGSlot;
import engine.spine.atlas.IGAtlasRegion;
import engine.spine.attachments.IGRegionAttachment;
//This stuff is our engine libraries
import engine.graphics.*;
import engine.gui.*;
import engine.math.*;
import engine.tween.*;
import engine.util.IGFSM;
import engine.IGApplication;
public class IGSpineWidget extends IGUIWidget
{
private var m_skeleton : IGSkeleton;
private var m_lastTime : int;
private var m_image : IGImage;
private var m_wrappers : Map<IGRegionAttachment, IGSpinePosition> = new Map<IGRegionAttachment, IGSpinePosition>(); // Works the same way as a Dictionary in AS3
public function IGSpineWidget (skeletonData : IGSkeletonData, image : IGImage)
{
IGBone.yDown = true;
m_skeleton = new IGSkeleton(skeletonData);
m_skeleton.updateWorldTransform();
m_image = image;
}
protected override function render(g : IGGraphics) : void
{
m_skeleton.update(IGApplication.delta_time) ;
var drawOrder : Vector<IGSlot> = skeleton.drawOrder;
for (var i : int = 0; i < drawOrder.length; i++)
{
g.push();
{
var slot : IGSlot = drawOrder[i];
if (slot.attachment != null && slot.attachment.type != IGRegionAttachment.Region()) { continue; }
var regionAttachment : IGRegionAttachment = IGRegionAttachment(slot.attachment);
var wrapper : IGSpinePosition = m_wrappers[regionAttachment];
if(regionAttachment != null) {
if (wrapper == null)
{
wrapper = new IGSpinePosition();
var region : IGAtlasRegion = IGAtlasRegion(regionAttachment.rendererObject);
var regionHeight : double = region.rotate ? region.width : region.height;
var regionWidth : double = region.rotate ? region.height : region.width;
wrapper.x = region.x;
wrapper.y = region.y;
wrapper.height = regionHeight;
wrapper.width = regionWidth;
// Rotate and scale using default registration point (top left corner, y-down, cw) instead of image center
wrapper.affine.rotate(regionAttachment.rotation * Math.PI / 180); //This rotates the position of the drawn object, but I need to be able to set the actual rotation instead of translating a rotation
wrapper.affine.scale(regionAttachment.scaleX * (regionAttachment.width / region.width), regionAttachment.scaleY * (regionAttachment.height / region.height));
// Position using attachment translation, shifted as if scale and rotation were at image center
var radians : double = -regionAttachment.rotation * Math.PI / 180;
var cos : double = Math.cos(radians);
var sin : double = Math.sin(radians);
var shiftX : double = -regionAttachment.width / 2 * regionAttachment.scaleX;
var shiftY : double = -regionAttachment.height / 2 * regionAttachment.scaleY;
if (region.rotate)
{
wrapper.affine.rotate(90); // Again, I need to += the rotation by 90 degrees, but I dont have that functionality
shiftX += regionHeight * (regionAttachment.width / region.width);
}
wrapper.affine.translate(0, 0);//(regionAttachment.x + shiftX * cos - shiftY * sin, -regionAttachment.y + shiftX * sin + shiftY * cos);
m_wrappers[regionAttachment] = wrapper;
}
var bone : IGBone = slot.bone;
var flipX : int = skeleton.flipX ? -1 : 1;
var flipY : int = skeleton.flipY ? -1 : 1;
//This is the key part. I need to be able to set the wrapper's affine2d's x and y position, the the rotation by angle (ie. rotation = someAngle)
//wrapper.affine.translate(bone.worldX, bone.worldY);
//wrapper.affine.rotate(bone.worldRotationX * flipX * flipY);
wrapper.scaleX = bone.worldScaleX * flipX;
wrapper.scaleY = bone.worldScaleY * flipY;
g.scale(wrapper.scaleX/4, wrapper.scaleY/4);
g.translate(1000, 1600); // Set the position of the widget
g.rotate(bone.worldRotationX * flipX * flipY);
g.multTransform(wrapper.affine);
g.drawSubImage(m_image, wrapper.x, wrapper.y, wrapper.width, wrapper.height, 0, 0, wrapper.width, wrapper.height);
}
}
g.pop();
}
}
public function get skeleton () : IGSkeleton {
return m_skeleton;
}
}
这是 Affine2d class:
package engine.math;
public class IGAffine2D
{
/////////////////////////////////////////////////////////////////////
// State
/////////////////////////////////////////////////////////////////////
public var m0:double;
public var m1:double;
public var m2:double;
public var m3:double;
public var m4:double;
public var m5:double;
/////////////////////////////////////////////////////////////////////
// Construction and Initialsation
/////////////////////////////////////////////////////////////////////
public final function IGAffine2D()
{
this.init();
}
public final function isIdentity() : bool
{
return m0 == 1 && m2 == 0 && m4 == 0 &&
m1 == 0 && m3 == 1 && m5 == 0;
}
public final function init() : IGAffine2D
{
m0 = 1.0;
m1 = 0.0;
m2 = 0.0;
m3 = 1.0;
m4 = 0.0;
m5 = 0.0;
return this;
}
public final function initColumnMajor(
a0 : double, a1 : double, a2 : double,
a3 : double, a4 : double, a5 : double) : IGAffine2D {
// represents the following 3x3 (2d affine) matrix
// m0 m2 m4
// m1 m3 m5
// 0 0 1
m0 = a0;
m1 = a1;
m2 = a2;
m3 = a3;
m4 = a4;
m5 = a5;
return this;
}
public final function initFromTransform(transform:IGAffine2D) : IGAffine2D
{
m0 = transform.m0;
m1 = transform.m1;
m2 = transform.m2;
m3 = transform.m3;
m4 = transform.m4;
m5 = transform.m5;
return this;
}
public final function initWithInverseFromTransform(other : IGAffine2D) : IGAffine2D
{
// this is 65 ops
var a : double= other.m0;
var b : double= other.m2;
var c : double= other.m1;
var d : double= other.m3;
var det_inv : double = 1.0 / (a*d - b*c);
m0 = d * det_inv;
m1 = -c * det_inv;
m2 = -b * det_inv;
m3 = a * det_inv;
var x : double= other.m4;
var y : double= other.m5;
m4 = -(x * m0 + y * m2);
m5 = -(x * m1 + y * m3);
return this;
}
//////////////////////////////////////////////////////////////////////
// Getting Properties of the Transform
//////////////////////////////////////////////////////////////////////
public final function get translate_x() : double
{
return m4;
}
public final function get translate_y() : double
{
return m5;
}
public final function get scale_x() : double
{
return m0;
}
public final function get scale_y() : double
{
return m3;
}
/**
* Determines whether or not the translation applied by this matrix
* will result in the coordinate being integer bound for a point
* around the origin
**/
public final function isIntegerTranslate() : bool
{
var tx : int = m4;
var ty : int = m5;
if (m0 == 1 && m1 == 0 && m2 == 0 && m3 == 1 && m4 == tx && m5 == ty) {
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Performing Transformations
//////////////////////////////////////////////////////////////////////
public final function translate(dx:double, dy:double): void
{
m4 += (m0 * dx) + (m2 * dy);
m5 += (m1 * dx) + (m3 * dy);
}
public final function rotate (theta: double) : void
{
if(theta == 0) {
return;
}
var st :double = Math.sin(theta);
var ct :double = Math.cos(theta);
var r00 :double = (m0 * ct) + (m2 * st);
var r01 :double = (m0 * -st) + (m2 * ct);
var r02 :double = m4;
var r10 :double = (m1 * ct) + (m3 * st);
var r11 :double = (m1 * -st) + (m3 * ct);
var r12 :double = m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
}
public final function scale(sx : double, sy : double) : void
{
m0 *= sx;
m1 *= sx;
m2 *= sy;
m3 *= sy;
}
// [m00 m01 m02] [1 shx 0]
// [m10 m11 m12] [shy 1 0]
// [ 0 0 1] [0 0 1]
public final function shear(shx:double, shy:double) : void
{
var r00 : double = m0 + m2 * shy;
var r01 : double = m0 * shx + m2;
var r02 : double = m4;
var r10 : double = m1 + m3 * shy;
var r11 : double = m1 * shx + m3;
var r12 : double = m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
}
/**
* Performs a Translate/Scale/Rotate transformation around a pivot point.
* OPTIMIZED VERSION OF:
* - translate(dx + px, dy + py);
* - rotate(theta);
* - scale(sx, sy);
* - translate(-px, -py);
**/
public final function TRS(dx : double, dy : double,
px : double, py : double, // positive pivot point
sx : double, sy : double, theta : double): IGAffine2D
{
// early abort for standard case
if (sx == 1.0 && sy == 1.0 && theta == 0) {
m4 += dx;
m5 += dy;
return this;
}
var l0 : double = m0;
var l1 : double = m1;
var l2 : double = m2;
var l3 : double = m3;
var l4 : double = m4 + (dx + px)*l0 + (dy + py)*l2;
var l5 : double = m5 + (dx + px)*l1 + (dy + py)*l3;
if (theta != 0)
{
var s : double = -Math.sin(theta);
var c : double = Math.cos(theta);
var r0 : double = c*sx;
var r1 : double = -s*sy;
var r2 : double = s*sx;
var r3 : double = c*sy;
m0 = l0 * r0 + l2 * r1;
m1 = l1 * r0 + l3 * r1;
m2 = l0 * r2 + l2 * r3;
m3 = l1 * r2 + l3 * r3;
m4 = l4 - m0 * px - m2 * py;
m5 = l5 - m1 * px - m3 * py;
}
else
{
m0 = l0 * sx ;
m1 = l1 * sx ;
m2 = l2 * sy;
m3 = l3 * sy;
m4 = l4 - (m0 * px) - (m2 * py);
m5 = l5 - (m1 * px) - (m3 * py);
}
return this;
}
public final function initFromTransformWithOffset(other : IGAffine2D, dx : double, dy : double) : IGAffine2D
{
m0 = other.m0;
m1 = other.m1;
m2 = other.m2;
m3 = other.m3;
m4 = other.m4 + dx * m0 + dy * m2;
m5 = other.m5 + dx * m1 + dy * m3;
return this;
}
public final function initFromTransformWithTRS(
other : IGAffine2D,
dx : double, dy : double,
px : double, py : double, // positive pivot point
sx : double, sy : double, theta : double): IGAffine2D
{
var s :double = 0;
var c :double = 1;
if (theta != 0)
{
s = -Math.sin(theta);
c = Math.cos(theta);
}
var l0 : double = other.m0;
var l1 : double = other.m1;
var l2 : double = other.m2;
var l3 : double = other.m3;
var l4 : double = other.m4 + (dx + px)*l0 + (dy + py)*l2;
var l5 : double = other.m5 + (dx + px)*l1 + (dy + py)*l3;
var r0 : double = c*sx;
var r1 : double = -s*sy;
var r2 : double = s*sx;
var r3 : double = c*sy;
m0 = l0 * r0 + l2 * r1;
m1 = l1 * r0 + l3 * r1;
m2 = l0 * r2 + l2 * r3;
m3 = l1 * r2 + l3 * r3;
m4 = l4 - m0 * px - m2 * py;
m5 = l5 - m1 * px - m3 * py;
return this;
}
//////////////////////////////////////////////////////////////////////
// Performing Other Operations
//////////////////////////////////////////////////////////////////////
public final function invert(): IGAffine2D
{
var a : double= m0;
var b : double= m2;
var c : double= m1;
var d : double= m3;
var det_inv : double = 1.0 / (a*d - b*c);
m0 = d * det_inv;
m1 = -c * det_inv;
m2 = -b * det_inv;
m3 = a * det_inv;
var x : double= m4;
var y : double= m5;
m4 = -x * m0 + -y * m2;
m5 = -x * m1 + -y * m3;
return this;
}
public final function concat(t : IGAffine2D): IGAffine2D
{
var r00 : double = (m0 * t.m0) + (m2 * t.m1);
var r01 : double = (m0 * t.m2) + (m2 * t.m3);
var r02 : double = (m0 * t.m4) + (m2 * t.m5) + m4;
var r10 : double = (m1 * t.m0) + (m3 * t.m1);
var r11 : double = (m1 * t.m2) + (m3 * t.m3);
var r12 : double = (m1 * t.m4) + (m3 * t.m5) + m5;
m0 = r00;
m2 = r01;
m4 = r02;
m1 = r10;
m3 = r11;
m5 = r12;
return this;
}
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
public final function transformVector2(pin : IGVector2, pout : IGVector2): void
{
var x:double = pin.x * m0 + pin.y * m2 + m4;
var y:double = pin.x * m1 + pin.y * m3 + m5;
pout.x = x;
pout.y = y;
}
public final function debug() : String
{
return m0 + " \t" + m2 + "\t" + m4 + "\n" + m1 + "\t" + m3 + "\t" + m5;
}
}
我已经弄明白了。在每次我需要根据位置重绘的调用中,我重置仿射坐标,然后平移并旋转一次到正确的位置。