replace Transform by much simpler Matrix

This commit is contained in:
faiface 2017-03-06 19:58:24 +01:00
parent 1083ca720d
commit f0394ec7d1
8 changed files with 95 additions and 234 deletions

View File

@ -3,9 +3,8 @@ package pixel
import (
"fmt"
"image/color"
"math"
"github.com/go-gl/mathgl/mgl32"
"github.com/go-gl/mathgl/mgl64"
)
// Batch is a Target that allows for efficient drawing of many objects with the same Picture (but
@ -16,7 +15,7 @@ import (
type Batch struct {
cont Drawer
mat mgl32.Mat3
mat Matrix
col NRGBA
}
@ -45,9 +44,9 @@ func (b *Batch) Draw(t Target) {
b.cont.Draw(t)
}
// SetTransform sets transforms used in the following draws onto the Batch.
func (b *Batch) SetTransform(t ...Transform) {
b.mat = transformToMat(t...)
// SetMatrix sets a Matrix that every point will be projected by.
func (b *Batch) SetMatrix(m Matrix) {
b.mat = m
}
// SetColorMask sets a mask color used in the following draws onto the Batch.
@ -74,10 +73,12 @@ func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
// MakePicture returns a specialized copy of the provided Picture that draws onto this Batch.
func (b *Batch) MakePicture(p Picture) TargetPicture {
return &batchPicture{
bp := &batchPicture{
Picture: p,
b: b,
}
bp.original = bp
return bp
}
type batchTriangles struct {
@ -89,15 +90,17 @@ type batchTriangles struct {
func (bt *batchTriangles) draw(bp *batchPicture) {
for i := range *bt.trans {
transPos := bt.b.mat.Mul3x1(mgl32.Vec3{
float32((*bt.orig)[i].Position.X()),
float32((*bt.orig)[i].Position.Y()),
transPos := mgl64.Mat3(bt.b.mat).Mul3x1(mgl64.Vec3{
(*bt.orig)[i].Position.X(),
(*bt.orig)[i].Position.Y(),
1,
})
(*bt.trans)[i].Position = V(float64(transPos.X()), float64(transPos.Y()))
(*bt.trans)[i].Color = (*bt.orig)[i].Color.Mul(bt.b.col)
(*bt.trans)[i].Picture = (*bt.orig)[i].Picture
(*bt.trans)[i].Intensity = (*bt.orig)[i].Intensity
if bp == nil {
(*bt.trans)[i].Picture = V(math.Inf(+1), math.Inf(+1))
(*bt.trans)[i].Intensity = 0
}
}
@ -116,15 +119,21 @@ func (bt *batchTriangles) Draw() {
type batchPicture struct {
Picture
b *Batch
original *batchPicture
b *Batch
}
func (bp *batchPicture) Slice(r Rect) Picture {
return &batchPicture{
Picture: bp.Picture.Slice(r),
Picture: bp.Picture.Slice(r),
original: bp.original,
}
}
func (bp *batchPicture) Original() Picture {
return bp.original
}
func (bp *batchPicture) Draw(t TargetTriangles) {
bt := t.(*batchTriangles)
if bp.b != bt.b {

View File

@ -4,6 +4,8 @@ import (
"fmt"
"math"
"math/cmplx"
"github.com/go-gl/mathgl/mgl64"
)
// Vec is a 2D vector type. It is unusually implemented as complex128 for convenience. Since
@ -175,3 +177,63 @@ func (r Rect) Contains(u Vec) bool {
min, max := r.Pos, r.Pos+r.Size
return min.X() <= u.X() && u.X() <= max.X() && min.Y() <= u.Y() && u.Y() <= max.Y()
}
// Matrix is a 3x3 transformation matrix that can be used for all kinds of spacial transforms, such
// as movement, scaling and rotations.
//
// Matrix has a handful of useful methods, each of which adds a transformation to the matrix. For
// example:
//
// pixel.ZM.Move(pixel.V(100, 200)).Rotate(0, math.Pi/2)
//
// This code creates a Matrix that first moves everything by 100 units horizontaly and 200 units
// vertically and then rotates everything by 90 degrees around the origin.
type Matrix [9]float64
// ZM stands for Zero-Matrix which is the identity matrix. Does nothing, no transformation.
var ZM = Matrix(mgl64.Ident3())
// Move moves everything by the delta vector.
func (m Matrix) Move(delta Vec) Matrix {
m3 := mgl64.Mat3(m)
m3 = mgl64.Translate2D(delta.XY()).Mul3(m3)
return Matrix(m3)
}
// ScaleXY scales everything around a given point by the scale factor in each axis respectively.
func (m Matrix) ScaleXY(around Vec, scale Vec) Matrix {
m3 := mgl64.Mat3(m)
m3 = mgl64.Translate2D((-around).XY()).Mul3(m3)
m3 = mgl64.Scale2D(scale.XY()).Mul3(m3)
m3 = mgl64.Translate2D(around.XY()).Mul3(m3)
return Matrix(m3)
}
// Scale scales everything around a given point by the scale factor.
func (m Matrix) Scale(around Vec, scale float64) Matrix {
return m.ScaleXY(around, V(scale, scale))
}
// Rotate rotates everything around a given point by the given angle in radians.
func (m Matrix) Rotate(around Vec, angle float64) Matrix {
m3 := mgl64.Mat3(m)
m3 = mgl64.Translate2D((-around).XY()).Mul3(m3)
m3 = mgl64.Rotate3DZ(angle).Mul3(m3)
m3 = mgl64.Translate2D(around.XY()).Mul3(m3)
return Matrix(m3)
}
// Project applies all transformations added to the Matrix to a vector u and returns the result.
func (m Matrix) Project(u Vec) Vec {
m3 := mgl64.Mat3(m)
proj := m3.Mul3x1(mgl64.Vec3{u.X(), u.Y(), 1})
return V(proj.X(), proj.Y())
}
// Unproject does the inverse operation to Project.
func (m Matrix) Unproject(u Vec) Vec {
m3 := mgl64.Mat3(m)
inv := m3.Inv()
unproj := inv.Mul3x1(mgl64.Vec3{u.X(), u.Y(), 1})
return V(unproj.X(), unproj.Y())
}

View File

@ -29,11 +29,10 @@ type Target interface {
type BasicTarget interface {
Target
// SetTransform sets a Transform that transforms the TrianglesPosition property of all
// Triangles.
SetTransform(...Transform)
// SetMatrix sets a Matrix that every point will be projected by.
SetMatrix(Matrix)
// SetMColorMask sets a color that will be multiplied with the TrianglesColor property of all
// SetColorMask sets a color that will be multiplied with the TrianglesColor property of all
// Triangles.
SetColorMask(color.Color)
}

View File

@ -116,9 +116,11 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
return cp
}
// SetTransform sets a set of Transforms that every position in triangles will be put through.
func (c *Canvas) SetTransform(t ...pixel.Transform) {
c.mat = transformToMat(t...)
// SetMatrix sets a Matrix that every point will be projected by.
func (c *Canvas) SetMatrix(m pixel.Matrix) {
for i := range m {
c.mat[i] = float32(m[i])
}
}
// SetColorMask sets a color that every color in triangles or a picture will be multiplied by.

View File

@ -4,17 +4,8 @@ import (
"math"
"github.com/faiface/pixel"
"github.com/go-gl/mathgl/mgl32"
)
func transformToMat(t ...pixel.Transform) mgl32.Mat3 {
mat := mgl32.Ident3()
for i := range t {
mat = mat.Mul3(t[i].Mat())
}
return mat
}
func discreteBounds(bounds pixel.Rect) (x, y, w, h int) {
x0 := int(math.Floor(bounds.Pos.X()))
y0 := int(math.Floor(bounds.Pos.Y()))

View File

@ -377,11 +377,9 @@ func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
return w.canvas.MakePicture(p)
}
// SetTransform sets a global transformation matrix for the Window.
//
// Transforms are applied right-to-left.
func (w *Window) SetTransform(t ...pixel.Transform) {
w.canvas.SetTransform(t...)
// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
w.canvas.SetMatrix(m)
}
// SetColorMask sets a global color mask for the Window.

View File

@ -1,190 +0,0 @@
package pixel
import "github.com/go-gl/mathgl/mgl32"
// Transform holds space transformation information. Concretely, a transformation is specified
// by position, anchor, scale and rotation.
//
// 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.
//
// 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.
//
// pixel.Position(pixel.V(100, 100)).Rotate(math.Pi / 3).Scale(1.5)
//
// Also note, that no method changes the Transform. All simply return a new, changed Transform.
type Transform struct {
pos, anc, sca Vec
rot float64
}
// ZT stands for Zero-Transform. This Transform is a neutral Transform, does not change anything.
var ZT = Transform{}.Scale(1)
// Position returns a Zero-Transform with Position set to pos.
func Position(pos Vec) Transform {
return ZT.Position(pos)
}
// Anchor returns a Zero-Transform with Anchor set to anchor.
func Anchor(anchor Vec) Transform {
return ZT.Anchor(anchor)
}
// Scale returns a Zero-Transform with Scale set to scale.
func Scale(scale float64) Transform {
return ZT.Scale(scale)
}
// ScaleXY returns a Zero-Transform with ScaleXY set to scale.
func ScaleXY(scale Vec) Transform {
return ZT.ScaleXY(scale)
}
// Rotation returns a Zero-Transform with Rotation set to angle (in radians).
func Rotation(angle float64) Transform {
return ZT.Rotation(angle)
}
// 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
return t
}
// AddPosition adds delta to the existing Position of this Transform.
func (t Transform) AddPosition(delta Vec) Transform {
t.pos += delta
return t
}
// Anchor specifies the zero vector, point originally located at anchor will be treated as zero.
// This affects Rotation and Position.
func (t Transform) Anchor(anchor Vec) Transform {
t.anc = anchor
return t
}
// AddAnchor adds delta to the existing Anchor of this Transform.
func (t Transform) AddAnchor(delta Vec) Transform {
t.anc += delta
return t
}
// Scale specifies a factor by which an object will be scaled around it's Anchor.
//
// Same as:
// t.ScaleXY(pixel.V(scale, scale)).
func (t Transform) Scale(scale float64) Transform {
t.sca = V(scale, scale)
return t
}
// MulScale multiplies the existing Scale of this Transform by factor.
//
// 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.
func (t Transform) ScaleXY(scale Vec) Transform {
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
return t
}
// AddRotation adds delta to the existing Angle of this Transform.
//
// The delta is in radians.
func (t Transform) AddRotation(delta float64) Transform {
t.rot += delta
return t
}
// 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
}
// Project transforms a vector by a transform.
func (t Transform) Project(v Vec) Vec {
mat := t.Mat()
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 {
mat := t.InvMat()
vec := mgl32.Vec3{float32(v.X()), float32(v.Y()), 1}
unp := mat.Mul3x1(vec)
return V(float64(unp.X()), float64(unp.Y()))
}
// Mat returns a transformation matrix that satisfies previously set transform properties.
func (t Transform) Mat() mgl32.Mat3 {
mat := mgl32.Ident3()
mat = mat.Mul3(mgl32.Translate2D(float32(t.pos.X()), float32(t.pos.Y())))
mat = mat.Mul3(mgl32.Rotate3DZ(float32(t.rot)))
mat = mat.Mul3(mgl32.Scale2D(float32(t.sca.X()), float32(t.sca.Y())))
mat = mat.Mul3(mgl32.Translate2D(float32(-t.anc.X()), float32(-t.anc.Y())))
return mat
}
// InvMat returns an inverse transformation matrix to the matrix returned by Mat3 method.
func (t Transform) InvMat() mgl32.Mat3 {
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
}
// 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.
// 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 {
return Anchor(center).ScaleXY(2 * zoom).MulScaleXY(V(1/screenSize.X(), 1/screenSize.Y()))
}

10
util.go
View File

@ -1,7 +1,5 @@
package pixel
import "github.com/go-gl/mathgl/mgl32"
func clamp(x, low, high float64) float64 {
if x < low {
return low
@ -11,11 +9,3 @@ func clamp(x, low, high float64) float64 {
}
return x
}
func transformToMat(t ...Transform) mgl32.Mat3 {
mat := mgl32.Ident3()
for i := range t {
mat = mat.Mul3(t[i].Mat())
}
return mat
}