gg/transform.go

247 lines
4.6 KiB
Go

package gg
import (
//"github.com/hajimehoshi/ebiten/v2"
//"math"
)
type Transformer interface {
GetTransform() *Transform
}
// The structure represents basic transformation
// features: positioning, rotating and scaling.
type Transform struct {
// Absolute (if no parent) position and
// the scale.
position, scale Vector
// The object rotation in radians.
rotation Float
// The not scaled offset vector from upper left corner
// which the object should be rotated around.
around Vector
// If is not nil then the upper values will be relational to
// the parent ones.
parent *Transform
// Dirty is true if we anyhow changed matrix.
dirty, parentDirty bool
matrix, parentMatrix, parentInverted Matrix
}
// For implementing the Transformer on embedding.
func (t *Transform) GetTransform() *Transform {
return t
}
// Returns the default Transform structure.
func T() Transform {
ret := Transform{
// Rotate around
scale: Vector{1, 1},
// Rotate around the center.
around: V(.5, .5),
rotation: EqualityThreshold,
}
return ret
}
// Set the absolute object position.
func (t *Transform) SetPosition(position Vector) {
t.dirty = true
t.parentDirty = true
if t.parent == nil {
t.position = position
return
}
_, mi := t.parent.MatrixForParenting()
t.position = position.Apply(mi)
}
// Set the absolute object rotation.
func (t *Transform) SetRotation(rotation Float) {
t.dirty = true
t.parentDirty = true
if t.parent == nil {
t.rotation = rotation
return
}
t.rotation -= t.parent.Rotation()
}
// Set the absolute object scale.
func (t *Transform) SetScale(scale Vector) {
t.dirty = true
t.parentDirty = true
t.scale = scale
}
func (t *Transform) AddScale(add ...Vector) {
t.dirty = true
//t.parentDirty = true
t.scale = t.scale.Add(add...)
}
func (t *Transform) SetAround(around Vector) {
t.dirty = true
t.around = around
}
// Get the absolute representation of the transform.
func (t *Transform) Abs() Transform {
if t.parent == nil {
return *t
}
ret := Transform{}
ret.position = t.Position()
ret.rotation = t.Rotation()
ret.scale = t.Scale()
ret.around = t.Around()
ret.dirty = true
ret.parentDirty = true
return ret
}
func (t *Transform) Rel() Transform {
ret := *t
ret.parent = nil
return ret
}
// Get the absolute object position.
func (t *Transform) Position() Vector {
if t.parent == nil {
return t.position
}
pm, _ := t.parent.MatrixForParenting()
return t.position.Apply(pm)
}
func (t *Transform) Move(v ...Vector) {
t.SetPosition(t.Position().Add(v...))
}
// Get the absolute object scale.
func (t *Transform) Scale() Vector {
return t.scale
}
// Get the absolute object rotation.
func (t *Transform) Rotation() Float {
if t.parent == nil {
return t.rotation
}
return t.rotation + t.parent.Rotation()
}
func (t *Transform) Rotate(add Float) {
t.dirty = true
t.parentDirty = true
t.rotation += add
}
func (t *Transform) Around() Vector {
return t.around
}
// Returns true if the object is connected
// to some parent.
func (t *Transform) Connected() bool {
return t.parent != nil
}
// Connect the object to another one making
// it its parent.
func (t *Transform) Connect(parent Transformer) {
if parent == nil {
return
}
if t.parent != nil {
t.Disconnect()
}
position := t.Position()
rotation := t.Rotation()
t.parent = parent.GetTransform()
t.SetPosition(position)
t.SetRotation(rotation)
}
// Disconnect from the parent.
func (t *Transform) Disconnect() {
if t.parent == nil {
return
}
*t = t.Abs()
}
// Return the matrix and the inverted one for parenting children.
func (t *Transform) MatrixForParenting() (Matrix, Matrix) {
var m, mi Matrix
if t.parentDirty {
//m.Scale(t.scale.X, t.scale.Y)
m.Rotate(t.rotation)
m.Translate(t.position.X, t.position.Y)
t.parentMatrix = m
mi = m
mi.Invert()
t.parentInverted = mi
t.parentDirty = false
} else {
m = t.parentMatrix
mi = t.parentInverted
}
if t.parent != nil {
pm, pmi := t.parent.MatrixForParenting()
m.Concat(pm)
pmi.Concat(mi)
mi = pmi
}
return m, mi
}
// Returns the GeoM with corresponding
// to the transfrom transformation.
func (t *Transform)Matrix() Matrix {
var m, pm Matrix
// Calculating only if we changed the structure anyhow.
if t.dirty {
// Scale first.
m.Scale(t.scale.X, t.scale.Y)
// Then move and rotate.
m.Translate(
-t.around.X * t.scale.X,
-t.around.Y * t.scale.Y,
)
m.Rotate(t.rotation)
// And finally move to the absolute position.
m.Translate(t.position.X, t.position.Y)
t.matrix = m
// Setting the flag so we d
t.dirty = false
} else {
m = t.matrix
}
if t.parent != nil {
pm, _ = t.parent.MatrixForParenting()
m.Concat(pm)
}
return m
}