2016-12-01 17:38:49 -06:00
|
|
|
package pixel
|
|
|
|
|
|
|
|
import "github.com/go-gl/mathgl/mgl32"
|
|
|
|
|
2016-12-30 10:43:26 -06:00
|
|
|
// Transform holds space transformation information. Concretely, a transformation is specified
|
|
|
|
// by position, anchor, scale and rotation.
|
2016-12-01 17:38:49 -06:00
|
|
|
//
|
2016-12-30 10:43:26 -06:00
|
|
|
// All points are first rotated around the anchor. Then they are multiplied by the scale. If
|
|
|
|
// the scale factor is 2, the object becomes 2x bigger. Finally, all points are moved, so that
|
|
|
|
// the original anchor is located precisely at the position.
|
2016-12-01 17:38:49 -06:00
|
|
|
//
|
2017-01-25 11:06:52 -06:00
|
|
|
// Create a Transform object with Position/Anchor/Rotation/... function. This sets the position
|
|
|
|
// one of it's properties. Then use methods, like Scale and Rotate to change scale, rotation and
|
|
|
|
// achor. The order in which you apply these methods is irrelevant.
|
2016-12-01 17:38:49 -06:00
|
|
|
//
|
|
|
|
// pixel.Position(pixel.V(100, 100)).Rotate(math.Pi / 3).Scale(1.5)
|
2017-01-25 11:06:52 -06:00
|
|
|
//
|
|
|
|
// Also note, that no method changes the Transform. All simply return a new, changed Transform.
|
2016-12-01 17:38:49 -06:00
|
|
|
type Transform struct {
|
2016-12-02 10:36:36 -06:00
|
|
|
pos, anc, sca Vec
|
|
|
|
rot float64
|
2016-12-01 17:38:49 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// ZT stands for Zero-Transform. This Transform is a neutral Transform, does not change anything.
|
2017-01-25 11:08:23 -06:00
|
|
|
var ZT = Transform{}.Scale(1)
|
2017-01-25 11:06:52 -06:00
|
|
|
|
|
|
|
// Position returns a Zero-Transform with Position set to pos.
|
|
|
|
func Position(pos Vec) Transform {
|
|
|
|
return ZT.Position(pos)
|
2016-12-01 17:38:49 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Anchor returns a Zero-Transform with Anchor set to anchor.
|
|
|
|
func Anchor(anchor Vec) Transform {
|
|
|
|
return ZT.Anchor(anchor)
|
2016-12-18 18:29:08 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Scale returns a Zero-Transform with Scale set to scale.
|
|
|
|
func Scale(scale float64) Transform {
|
|
|
|
return ZT.Scale(scale)
|
2016-12-18 18:29:08 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// ScaleXY returns a Zero-Transform with ScaleXY set to scale.
|
|
|
|
func ScaleXY(scale Vec) Transform {
|
|
|
|
return ZT.ScaleXY(scale)
|
2016-12-18 18:29:08 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Rotation returns a Zero-Transform with Rotation set to angle (in radians).
|
|
|
|
func Rotation(angle float64) Transform {
|
|
|
|
return ZT.Rotation(angle)
|
2016-12-18 18:29:08 -06:00
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Position moves an object by the specified vector. A zero vector will end up precisely at pos.
|
|
|
|
func (t Transform) Position(pos Vec) Transform {
|
|
|
|
t.pos = pos
|
2016-12-02 10:48:19 -06:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// AddPosition adds delta to the existing Position of this Transform.
|
|
|
|
func (t Transform) AddPosition(delta Vec) Transform {
|
2016-12-01 17:38:49 -06:00
|
|
|
t.pos += delta
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Anchor specifies the zero vector, point originally located at anchor will be treated as zero.
|
|
|
|
// This affects Rotation and Position.
|
2016-12-01 17:38:49 -06:00
|
|
|
func (t Transform) Anchor(anchor Vec) Transform {
|
|
|
|
t.anc = anchor
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// AddAnchor adds delta to the existing Anchor of this Transform.
|
|
|
|
func (t Transform) AddAnchor(delta Vec) Transform {
|
2016-12-01 17:38:49 -06:00
|
|
|
t.anc += delta
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// Scale specifies a factor by which an object will be scaled around it's Anchor.
|
2016-12-01 17:38:49 -06:00
|
|
|
//
|
2017-01-25 11:06:52 -06:00
|
|
|
// Same as:
|
|
|
|
// t.ScaleXY(pixel.V(scale, scale)).
|
2016-12-01 17:38:49 -06:00
|
|
|
func (t Transform) Scale(scale float64) Transform {
|
2017-01-25 11:06:52 -06:00
|
|
|
t.sca = V(scale, scale)
|
2016-12-02 10:36:36 -06:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// MulScale multiplies the existing Scale of this Transform by factor.
|
2016-12-02 10:36:36 -06:00
|
|
|
//
|
2017-01-25 11:06:52 -06:00
|
|
|
// Same as:
|
|
|
|
// t.MulScaleXY(pixel.V(factor, factor)).
|
|
|
|
func (t Transform) MulScale(factor float64) Transform {
|
|
|
|
t.sca = t.sca.Scaled(factor)
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// ScaleXY specifies a factor in each dimension, by which an object will be scaled around it's
|
|
|
|
// Anchor.
|
2016-12-02 10:36:36 -06:00
|
|
|
func (t Transform) ScaleXY(scale Vec) Transform {
|
2017-01-25 11:06:52 -06:00
|
|
|
t.sca = scale
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// MulScaleXY multiplies the existing ScaleXY of this Transform by factor, component-wise.
|
|
|
|
func (t Transform) MulScaleXY(factor Vec) Transform {
|
|
|
|
t.sca = V(
|
|
|
|
t.sca.X()*factor.X(),
|
|
|
|
t.sca.Y()*factor.Y(),
|
|
|
|
)
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rotation specifies an angle by which an object will be rotated around it's Anchor.
|
|
|
|
//
|
|
|
|
// The angle is in radians.
|
|
|
|
func (t Transform) Rotation(angle float64) Transform {
|
|
|
|
t.rot = angle
|
2016-12-01 17:38:49 -06:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// AddRotation adds delta to the existing Angle of this Transform.
|
2016-12-01 17:38:49 -06:00
|
|
|
//
|
2017-01-25 11:06:52 -06:00
|
|
|
// The delta is in radians.
|
|
|
|
func (t Transform) AddRotation(delta float64) Transform {
|
|
|
|
t.rot += delta
|
2016-12-01 17:38:49 -06:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2017-01-25 11:06:52 -06:00
|
|
|
// GetPosition returns the Position of the Transform.
|
|
|
|
func (t Transform) GetPosition() Vec {
|
|
|
|
return t.pos
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAnchor returns the Anchor of the Transform.
|
|
|
|
func (t Transform) GetAnchor() Vec {
|
|
|
|
return t.anc
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetScaleXY returns the ScaleXY of the Transform.
|
|
|
|
func (t Transform) GetScaleXY() Vec {
|
|
|
|
return t.sca
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRotation returns the Rotation of the Transform.
|
|
|
|
func (t Transform) GetRotation() float64 {
|
|
|
|
return t.rot
|
|
|
|
}
|
|
|
|
|
2016-12-21 13:31:09 -06:00
|
|
|
// Project transforms a vector by a transform.
|
|
|
|
func (t Transform) Project(v Vec) Vec {
|
2016-12-21 13:32:52 -06:00
|
|
|
mat := t.Mat()
|
2016-12-21 13:31:09 -06:00
|
|
|
vec := mgl32.Vec3{float32(v.X()), float32(v.Y()), 1}
|
|
|
|
pro := mat.Mul3x1(vec)
|
|
|
|
return V(float64(pro.X()), float64(pro.Y()))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unproject does the inverse operation to Project.
|
|
|
|
func (t Transform) Unproject(v Vec) Vec {
|
2016-12-21 13:32:52 -06:00
|
|
|
mat := t.InvMat()
|
2016-12-21 13:31:09 -06:00
|
|
|
vec := mgl32.Vec3{float32(v.X()), float32(v.Y()), 1}
|
|
|
|
unp := mat.Mul3x1(vec)
|
|
|
|
return V(float64(unp.X()), float64(unp.Y()))
|
|
|
|
}
|
|
|
|
|
2016-12-21 13:32:52 -06:00
|
|
|
// Mat returns a transformation matrix that satisfies previously set transform properties.
|
|
|
|
func (t Transform) Mat() mgl32.Mat3 {
|
2016-12-01 17:38:49 -06:00
|
|
|
mat := mgl32.Ident3()
|
|
|
|
mat = mat.Mul3(mgl32.Translate2D(float32(t.pos.X()), float32(t.pos.Y())))
|
|
|
|
mat = mat.Mul3(mgl32.Rotate3DZ(float32(t.rot)))
|
2016-12-02 10:36:36 -06:00
|
|
|
mat = mat.Mul3(mgl32.Scale2D(float32(t.sca.X()), float32(t.sca.Y())))
|
2016-12-02 18:50:14 -06:00
|
|
|
mat = mat.Mul3(mgl32.Translate2D(float32(-t.anc.X()), float32(-t.anc.Y())))
|
2016-12-01 17:38:49 -06:00
|
|
|
return mat
|
|
|
|
}
|
2016-12-02 10:48:19 -06:00
|
|
|
|
2016-12-21 13:32:52 -06:00
|
|
|
// InvMat returns an inverse transformation matrix to the matrix returned by Mat3 method.
|
|
|
|
func (t Transform) InvMat() mgl32.Mat3 {
|
2016-12-21 13:31:09 -06:00
|
|
|
mat := mgl32.Ident3()
|
|
|
|
mat = mat.Mul3(mgl32.Translate2D(float32(t.anc.X()), float32(t.anc.Y())))
|
|
|
|
mat = mat.Mul3(mgl32.Scale2D(float32(1/t.sca.X()), float32(1/t.sca.Y())))
|
|
|
|
mat = mat.Mul3(mgl32.Rotate3DZ(float32(-t.rot)))
|
|
|
|
mat = mat.Mul3(mgl32.Translate2D(float32(-t.pos.X()), float32(-t.pos.Y())))
|
|
|
|
return mat
|
|
|
|
}
|
|
|
|
|
2016-12-30 10:43:26 -06:00
|
|
|
// Camera is a convenience function, that returns a Transform that acts like a camera. Center is
|
|
|
|
// the position in the world coordinates, that will be projected onto the center of the screen.
|
2016-12-02 10:48:19 -06:00
|
|
|
// One unit in world coordinates will be projected onto zoom pixels.
|
|
|
|
//
|
|
|
|
// It is possible to apply additional rotations, scales and moves to the returned transform.
|
|
|
|
func Camera(center, zoom, screenSize Vec) Transform {
|
2017-01-25 11:06:52 -06:00
|
|
|
return Anchor(center).ScaleXY(2 * zoom).MulScaleXY(V(1/screenSize.X(), 1/screenSize.Y()))
|
2016-12-02 10:48:19 -06:00
|
|
|
}
|