Created
June 8, 2016 01:13
-
-
Save akiross/bb91afee64688c89db96df48c0e55357 to your computer and use it in GitHub Desktop.
Affine transformations in go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
// This example shows how affine transformations can be combined yielding | |
// a single matrix which represent complex transformations. | |
// Feed the output into gnuplot to see the points: | |
// go build affines.go | |
// ./affines | gnuplot -p -e "plot '-' u 1:2:3 w p pt 7 palette" | |
import ( | |
"fmt" | |
"github.com/gonum/matrix/mat64" | |
"math" | |
) | |
// Structure for 2D points and affine transformations | |
// The Z coordinate is (should) be used for normalization | |
// Points have z=1, vectors have z=0 | |
// Should be easy to provide FromEngo() and (Point)ToEngo() conversion | |
type Point struct { | |
x, y, z float64 | |
} | |
func (p Point) String() string { | |
return fmt.Sprintf("%v %v", p.x, p.y) | |
} | |
// A 3x3 matrix, used to represent a transformation of a 2D point | |
type Matrix struct { | |
v []float64 | |
} | |
// Multiply m for another matrix | |
func (m *Matrix) Mul(q Matrix) { | |
Q := mat64.NewDense(3, 3, q.v) | |
M := mat64.NewDense(3, 3, m.v) | |
M.Mul(M, Q) | |
} | |
// Matrix that produces no changes in the points | |
func Ident() Matrix { | |
return Matrix{[]float64{1, 0, 0, 0, 1, 0, 0, 0, 1}} | |
} | |
// Multiply a matrix and a point yielding a new point | |
func Mul(m Matrix, p Point) Point { | |
return Point{ | |
m.v[0]*p.x + m.v[1]*p.y + m.v[2]*p.z, | |
m.v[3]*p.x + m.v[4]*p.y + m.v[5]*p.z, | |
m.v[6]*p.x + m.v[7]*p.y + m.v[8]*p.z, | |
} | |
} | |
// Produce a translation matrix that translates the points | |
func Trn(x, y float64) Matrix { | |
return Matrix{[]float64{ | |
1, 0, x, | |
0, 1, y, | |
0, 0, 1, | |
}} | |
} | |
// Returns a matrix that rotates the points around the origin (0,0) | |
func Rot(a float64) Matrix { | |
cos, sin := math.Cos(a), math.Sin(a) | |
return Matrix{[]float64{ | |
cos, -sin, 0, | |
sin, cos, 0, | |
0, 0, 1, | |
}} | |
} | |
func main() { | |
// A bunch of points that we want to transform. These can be many | |
p := []Point{ | |
Point{0, 0, 1}, Point{1, 1, 1}, Point{0.5, 2, 1}, | |
} | |
// The original points | |
for i := range p { | |
fmt.Println(p[i], "0") | |
} | |
// A transformation matrix, which may represent a bunch of consecutive transformations | |
// In this case, I want to rotate my points 90 degrees around the center (0.5, 0.5) | |
// The final transformation will be a chain of: | |
// 1. translate the points so that (0.5, 0.5) is at the origin | |
// 2. rotate 90 degrees | |
// 3. translate back to (0.5, 0.5) | |
// NOTE: The matrices multiplication order is be reversed | |
m := Trn(0.5, 0.5) // Step 3: move back to original point | |
m.Mul(Rot(3.141592 * 0.5)) // Step 2: rotate | |
m.Mul(Trn(-0.5, -0.5)) // Step 1: center point (0.5, 0.5) | |
// Center of rotation | |
fmt.Println("0.5 0.5 1") | |
// Now I have a transformation matrix and I can apply it to all my points | |
for i := range p { | |
fmt.Println(Mul(m, p[i]), "2") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment