gg/ox/transform.go
2024-06-03 00:36:02 +05:00

274 lines
5.2 KiB
Go

package ox
import "surdeus.su/core/gg/mx"
type Transformer interface {
GetTransform() *Transform
}
// The structure represents basic transformation
// features: positioning, rotating and scaling
// in more high level and more "GAME ENGINE" style.
type Transform struct {
// Absolute (if no parent) position and
// the scale.
position, scale mx.Vector
// The object rotation in radians.
rotation mx.Float
// The not scaled offset vector from upper left corner
// which the object should be rotated around.
around mx.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, parentMatrice, parentInverted mx.Matrice
}
// Returns the default Transform structure.
func T() *Transform {
ret := &Transform{
// Rotate around
scale: mx.V2(1),
// Rotate around the center.
around: mx.V2(.5),
}
ret.dirty = true
ret.parentDirty = true
return ret
}
// Returns whether it needs to be recalculated
// or not.
func (t *Transform) IsDirty() bool {
return t.dirty || t.parentDirty
}
// For implementing the Transformer on embedding.
func (t *Transform) GetTransform() *Transform {
return t
}
// Set the absolute object position.
func (t *Transform) SetPosition(position mx.Vector) *Transform {
t.dirty = true
t.parentDirty = true
if t.parent == nil {
t.position = position
return t
}
_, mi := t.parent.GetMatriceForParenting()
t.position = position.Apply(mi)
return t
}
// Set the absolute object rotation.
func (t *Transform) SetRotation(rotation mx.Float) *Transform {
t.dirty = true
t.parentDirty = true
if t.parent == nil {
t.rotation = rotation
return t
}
t.rotation -= t.parent.GetRotation()
return t
}
// Set the absolute object scale.
func (t *Transform) SetScale(scale mx.Vector) *Transform {
t.dirty = true
t.parentDirty = true
t.scale = scale
return t
}
func (t *Transform) AddScale(add mx.Vector) *Transform {
t.dirty = true
//t.parentDirty = true
t.scale = t.scale.Add(add)
return t
}
func (t *Transform) Scale(mul mx.Vector) *Transform {
t.dirty = true
t.scale = t.scale.Mul(mul)
return t
}
func (t *Transform) SetAround(around mx.Vector) *Transform {
t.dirty = true
t.around = around
return t
}
// Get the absolute representation of the transform.
func (t *Transform) Abs() Transform {
if t.parent == nil {
return *t
}
ret := Transform{}
ret.position = t.GetPosition()
ret.rotation = t.GetRotation()
ret.scale = t.GetScale()
ret.around = t.GetAround()
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) GetPosition() mx.Vector {
if t.parent == nil {
return t.position
}
pm, _ := t.parent.GetMatriceForParenting()
return t.position.Apply(pm)
}
// Move by the specified delta.
func (t *Transform) Move(v mx.Vector) {
t.SetPosition(t.GetPosition().Add(v))
}
// Get the absolute object scale.
func (t *Transform) GetScale() mx.Vector {
return t.scale
}
// Get the absolute object rotation.
func (t *Transform) GetRotation() mx.Float {
if t.parent == nil {
return t.rotation
}
return t.rotation + t.parent.GetRotation()
}
func (t *Transform) Rotate(rot mx.Float) *Transform {
t.dirty = true
t.parentDirty = true
t.rotation += rot
return t
}
func (t *Transform) GetAround() mx.Vector {
return t.around
}
// Returns true if the object is connected
// to some parent.
func (t *Transform) IsConnected() bool {
return t.parent != nil
}
// Connect the object to another one making
// it its parent.
func (t *Transform) Connect(parent Transformer) *Transform {
if parent == nil {
return t
}
if t.parent != nil {
t.Disconnect()
}
position := t.GetPosition()
rotation := t.GetRotation()
t.parent = parent.GetTransform()
t.SetPosition(position)
t.SetRotation(rotation)
return t
}
// Disconnect from the parent.
func (t *Transform) Disconnect() *Transform {
if t.parent == nil {
return t
}
*t = t.Abs()
return t
}
// Return the matrix and the inverted
// one for parenting children.
func (t *Transform) GetMatriceForParenting(
) (mx.Matrice, mx.Matrice) {
var m, mi mx.Matrice
if t.parentDirty {
//m.Scale(t.scale.X, t.scale.Y)
m.Rotate(t.rotation)
m.Translate(t.position.X, t.position.Y)
t.parentMatrice = m
mi = m
mi.Invert()
t.parentInverted = mi
t.parentDirty = false
} else {
m = t.parentMatrice
mi = t.parentInverted
}
if t.parent != nil {
pm, pmi := t.parent.GetMatriceForParenting()
m.Concat(pm)
pmi.Concat(mi)
mi = pmi
}
return m, mi
}
// Returns the GeoM with corresponding
// to the transfrom transformation.
func (t *Transform) GetMatrice() mx.Matrice {
var m, pm mx.Matrice
// 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.GetMatriceForParenting()
m.Concat(pm)
}
return m
}