2023-11-20 18:01:35 +00:00
|
|
|
package math
|
2023-11-20 05:20:31 +00:00
|
|
|
|
|
|
|
import (
|
2023-11-21 17:38:17 +00:00
|
|
|
"fmt"
|
2023-11-20 05:20:31 +00:00
|
|
|
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf/types"
|
|
|
|
"gonum.org/v1/gonum/mat"
|
|
|
|
"math"
|
2023-11-21 17:38:17 +00:00
|
|
|
"runtime"
|
2023-11-20 05:20:31 +00:00
|
|
|
)
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
// MatrixTransform The transformation matrix used by Flash display objects.
|
|
|
|
// The matrix is a 2x3 affine transformation matrix. A Vector2(x, y) is transformed by the matrix in the following way:
|
|
|
|
//
|
|
|
|
// [a c tx] * [x] = [a*x + c*y + tx]
|
|
|
|
// [b d ty] [y] [b*x + d*y + ty]
|
|
|
|
// [0 0 1 ] [1] [1 ]
|
|
|
|
//
|
|
|
|
// Objects in Flash can only move in units of types.Twip, or 1/20 pixels.
|
|
|
|
//
|
|
|
|
// [SWF19 pp.22-24](https://web.archive.org/web/20220205011833if_/https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=22)
|
2023-11-20 05:20:31 +00:00
|
|
|
type MatrixTransform struct {
|
|
|
|
matrix *mat.Dense
|
|
|
|
}
|
|
|
|
|
|
|
|
var DefaultScale = Vector2[float64]{
|
|
|
|
X: 1,
|
|
|
|
Y: 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
var DefaultRotateSkew = Vector2[float64]{
|
|
|
|
X: 0,
|
|
|
|
Y: 0,
|
|
|
|
}
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
var DefaultTranslation = Vector2[float64]{
|
2023-11-20 05:20:31 +00:00
|
|
|
X: 0,
|
|
|
|
Y: 0,
|
|
|
|
}
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
func NewMatrixTransform(scale, rotateSkew, translation Vector2[float64]) MatrixTransform {
|
2023-11-20 05:20:31 +00:00
|
|
|
return MatrixTransform{
|
2023-11-21 01:55:09 +00:00
|
|
|
matrix: mat.NewDense(3, 3, []float64{
|
2023-11-22 04:19:00 +00:00
|
|
|
/* a */ /* c */ /* tx */
|
|
|
|
scale.X, rotateSkew.Y, translation.X,
|
|
|
|
/* b */ /* d */ /* ty */
|
|
|
|
rotateSkew.X, scale.Y, translation.Y,
|
2023-11-21 17:38:17 +00:00
|
|
|
0, 0, 1,
|
2023-11-20 05:20:31 +00:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ScaleTransform(scale Vector2[float64]) MatrixTransform {
|
|
|
|
return NewMatrixTransform(scale, DefaultRotateSkew, DefaultTranslation)
|
|
|
|
}
|
|
|
|
|
|
|
|
func RotateTransform(angle float64) MatrixTransform {
|
|
|
|
//TODO: check sin sign location
|
|
|
|
sin, cos := math.Sincos(angle)
|
|
|
|
return NewMatrixTransform(NewVector2(cos, cos), NewVector2(-sin, sin), DefaultTranslation)
|
|
|
|
}
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
func TranslateTransform[T ~int64 | ~float64](translate Vector2[T]) MatrixTransform {
|
|
|
|
return NewMatrixTransform(DefaultScale, DefaultRotateSkew, translate.Float64())
|
2023-11-20 05:20:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func IdentityTransform() MatrixTransform {
|
|
|
|
return NewMatrixTransform(DefaultScale, DefaultRotateSkew, DefaultTranslation)
|
|
|
|
}
|
|
|
|
|
|
|
|
func SkewXTransform(angle float64) MatrixTransform {
|
|
|
|
return NewMatrixTransform(DefaultScale, NewVector2(math.Tan(angle), 0), DefaultTranslation)
|
|
|
|
}
|
|
|
|
|
|
|
|
func SkewYTransform(angle float64) MatrixTransform {
|
|
|
|
return NewMatrixTransform(DefaultScale, NewVector2(0, math.Tan(angle)), DefaultTranslation)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) Multiply(o MatrixTransform) MatrixTransform {
|
|
|
|
var r mat.Dense
|
|
|
|
r.Mul(m.matrix, o.matrix)
|
|
|
|
return MatrixTransform{
|
|
|
|
matrix: &r,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-20 18:01:35 +00:00
|
|
|
var identityTransform = IdentityTransform()
|
|
|
|
|
|
|
|
func (m MatrixTransform) IsIdentity() bool {
|
|
|
|
return m.EqualsExact(identityTransform)
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:38:17 +00:00
|
|
|
// GetA Gets the ScaleX factor
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) GetA() float64 {
|
|
|
|
return m.matrix.At(0, 0)
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:38:17 +00:00
|
|
|
// GetB Gets the RotateSkewX factor
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) GetB() float64 {
|
|
|
|
return m.matrix.At(1, 0)
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:38:17 +00:00
|
|
|
// GetC Gets the RotateSkewY factor
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) GetC() float64 {
|
|
|
|
return m.matrix.At(0, 1)
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:38:17 +00:00
|
|
|
// GetD Gets the ScaleY factor
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) GetD() float64 {
|
|
|
|
return m.matrix.At(1, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) GetTX() float64 {
|
|
|
|
return m.matrix.At(0, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) GetTY() float64 {
|
|
|
|
return m.matrix.At(1, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) GetMatrix() mat.Matrix {
|
|
|
|
return m.matrix
|
|
|
|
}
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
func (m MatrixTransform) GetMatrixWithoutTranslation() mat.Matrix {
|
|
|
|
return m.matrix.Slice(0, 2, 0, 2)
|
|
|
|
}
|
|
|
|
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) GetTranslation() Vector2[float64] {
|
|
|
|
return m.ApplyToVector(NewVector2[float64](0, 0), true)
|
|
|
|
}
|
|
|
|
|
2023-11-22 04:19:00 +00:00
|
|
|
func MatrixTransformApplyToVector[T ~int64 | ~float64](m MatrixTransform, v Vector2[T], applyTranslation bool) Vector2[T] {
|
|
|
|
return Vector2ToType[float64, T](m.ApplyToVector(v.Float64(), applyTranslation))
|
|
|
|
}
|
|
|
|
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) ApplyToVector(v Vector2[float64], applyTranslation bool) Vector2[float64] {
|
2023-11-21 01:55:09 +00:00
|
|
|
var r mat.VecDense
|
2023-11-20 05:20:31 +00:00
|
|
|
if applyTranslation {
|
2023-11-22 04:19:00 +00:00
|
|
|
/*
|
|
|
|
[a c tx] * [x] = [a*x + c*y + tx]
|
|
|
|
[b d ty] [y] [b*x + d*y + ty]
|
|
|
|
[0 0 1 ] [1] [1 ]
|
|
|
|
*/
|
2023-11-21 01:55:09 +00:00
|
|
|
r.MulVec(m.matrix, mat.NewVecDense(3, []float64{v.X, v.Y, 1}))
|
2023-11-20 05:20:31 +00:00
|
|
|
} else {
|
2023-11-22 04:19:00 +00:00
|
|
|
/*
|
|
|
|
[a c] * [x] = [a*x + c*y]
|
|
|
|
[b d] [y] [b*x + d*y]
|
|
|
|
*/
|
|
|
|
r.MulVec(m.GetMatrixWithoutTranslation(), mat.NewVecDense(2, []float64{v.X, v.Y}))
|
2023-11-20 05:20:31 +00:00
|
|
|
}
|
2023-11-22 04:19:00 +00:00
|
|
|
return NewVector2[float64](r.AtVec(0), r.AtVec(1))
|
2023-11-20 05:20:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) EqualsExact(o MatrixTransform) bool {
|
|
|
|
return mat.Equal(m.matrix, o.matrix)
|
|
|
|
}
|
|
|
|
|
|
|
|
const TransformCompareEpsilon = 1e-12
|
|
|
|
|
|
|
|
func (m MatrixTransform) Equals(o MatrixTransform, epsilon float64) bool {
|
|
|
|
return mat.EqualApprox(m.matrix, o.matrix, epsilon)
|
|
|
|
}
|
2023-11-21 17:38:17 +00:00
|
|
|
|
2023-11-20 05:20:31 +00:00
|
|
|
func (m MatrixTransform) EqualsWithoutTranslation(o MatrixTransform, epsilon float64) bool {
|
2023-11-22 04:19:00 +00:00
|
|
|
return mat.EqualApprox(m.GetMatrixWithoutTranslation(), o.GetMatrixWithoutTranslation(), epsilon)
|
2023-11-21 17:38:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m MatrixTransform) String() string {
|
|
|
|
return fmt.Sprintf("%#v", mat.Formatted(m.matrix, mat.FormatPython()))
|
2023-11-20 05:20:31 +00:00
|
|
|
}
|
2023-11-21 01:55:09 +00:00
|
|
|
|
|
|
|
func MatrixTransformFromSWF(m types.MATRIX) MatrixTransform {
|
2023-11-21 17:38:17 +00:00
|
|
|
t := NewMatrixTransform(
|
2023-11-22 04:19:00 +00:00
|
|
|
NewVector2(m.ScaleX.Float64(), m.ScaleY.Float64()),
|
|
|
|
NewVector2(m.RotateSkew0.Float64(), m.RotateSkew1.Float64()),
|
|
|
|
NewVector2(m.TranslateX.Float64(), m.TranslateY.Float64()),
|
2023-11-21 01:55:09 +00:00
|
|
|
)
|
2023-11-22 06:27:56 +00:00
|
|
|
|
2023-11-21 17:38:17 +00:00
|
|
|
if m.HasRotate && m.HasScale && m.TranslateX != 0 {
|
2023-11-22 04:19:00 +00:00
|
|
|
fmt.Printf("\n\nScale: %s vs %f, %f\n", NewVector2(m.ScaleX.Float64(), m.ScaleY.Float64()), t.GetA(), t.GetD())
|
|
|
|
fmt.Printf("Skew: %s vs %f, %f\n", NewVector2(m.RotateSkew0.Float64(), m.RotateSkew1.Float64()), t.GetB(), t.GetC())
|
|
|
|
fmt.Printf("Translation: %s vs %f, %f\n", NewVector2(m.TranslateX, m.TranslateY), t.GetTX(), t.GetTY())
|
2023-11-21 17:38:17 +00:00
|
|
|
fmt.Printf("%s\n\n", t.String())
|
2023-11-22 04:19:00 +00:00
|
|
|
fmt.Printf("%#v\n\n", mat.Formatted(t.GetMatrixWithoutTranslation(), mat.FormatPython()))
|
2023-11-21 17:38:17 +00:00
|
|
|
runtime.KeepAlive(m)
|
|
|
|
}
|
|
|
|
return t
|
2023-11-21 01:55:09 +00:00
|
|
|
}
|