From f0394ec7d1bfd6abc581367a4517a3b1af7d1eb0 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Mon, 6 Mar 2017 19:58:24 +0100
Subject: [PATCH] replace Transform by much simpler Matrix

---
 batch.go          |  35 +++++----
 geometry.go       |  62 +++++++++++++++
 interface.go      |   7 +-
 pixelgl/canvas.go |   8 +-
 pixelgl/util.go   |   9 ---
 pixelgl/window.go |   8 +-
 transform.go      | 190 ----------------------------------------------
 util.go           |  10 ---
 8 files changed, 95 insertions(+), 234 deletions(-)
 delete mode 100644 transform.go

diff --git a/batch.go b/batch.go
index b52e129..f830be1 100644
--- a/batch.go
+++ b/batch.go
@@ -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 {
diff --git a/geometry.go b/geometry.go
index c6e1c69..b2dfdb7 100644
--- a/geometry.go
+++ b/geometry.go
@@ -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())
+}
diff --git a/interface.go b/interface.go
index d58cee7..a61c247 100644
--- a/interface.go
+++ b/interface.go
@@ -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)
 }
diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go
index 16ae1b2..d0bd0d8 100644
--- a/pixelgl/canvas.go
+++ b/pixelgl/canvas.go
@@ -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.
diff --git a/pixelgl/util.go b/pixelgl/util.go
index 94138ee..af05410 100644
--- a/pixelgl/util.go
+++ b/pixelgl/util.go
@@ -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()))
diff --git a/pixelgl/window.go b/pixelgl/window.go
index 5cd63ec..6d194f4 100644
--- a/pixelgl/window.go
+++ b/pixelgl/window.go
@@ -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.
diff --git a/transform.go b/transform.go
deleted file mode 100644
index e1798a9..0000000
--- a/transform.go
+++ /dev/null
@@ -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()))
-}
diff --git a/util.go b/util.go
index ce46294..24e6199 100644
--- a/util.go
+++ b/util.go
@@ -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
-}
-- 
GitLab