Skip to content

Instantly share code, notes, and snippets.

@akiross
Created June 8, 2016 01:13
Show Gist options
  • Save akiross/bb91afee64688c89db96df48c0e55357 to your computer and use it in GitHub Desktop.
Save akiross/bb91afee64688c89db96df48c0e55357 to your computer and use it in GitHub Desktop.
Affine transformations in go
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