1// Copyright 2010 The draw2d Authors. All rights reserved. 2// created: 21/11/2010 by Laurent Le Goff 3 4package draw2d 5 6import ( 7 "math" 8) 9 10// Matrix represents an affine transformation 11type Matrix [6]float64 12 13const ( 14 epsilon = 1e-6 15) 16 17// Determinant compute the determinant of the matrix 18func (tr Matrix) Determinant() float64 { 19 return tr[0]*tr[3] - tr[1]*tr[2] 20} 21 22// Transform applies the transformation matrix to points. It modify the points passed in parameter. 23func (tr Matrix) Transform(points []float64) { 24 for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { 25 x := points[i] 26 y := points[j] 27 points[i] = x*tr[0] + y*tr[2] + tr[4] 28 points[j] = x*tr[1] + y*tr[3] + tr[5] 29 } 30} 31 32// TransformPoint applies the transformation matrix to point. It returns the point the transformed point. 33func (tr Matrix) TransformPoint(x, y float64) (xres, yres float64) { 34 xres = x*tr[0] + y*tr[2] + tr[4] 35 yres = x*tr[1] + y*tr[3] + tr[5] 36 return xres, yres 37} 38 39func minMax(x, y float64) (min, max float64) { 40 if x > y { 41 return y, x 42 } 43 return x, y 44} 45 46// Transform applies the transformation matrix to the rectangle represented by the min and the max point of the rectangle 47func (tr Matrix) TransformRectangle(x0, y0, x2, y2 float64) (nx0, ny0, nx2, ny2 float64) { 48 points := []float64{x0, y0, x2, y0, x2, y2, x0, y2} 49 tr.Transform(points) 50 points[0], points[2] = minMax(points[0], points[2]) 51 points[4], points[6] = minMax(points[4], points[6]) 52 points[1], points[3] = minMax(points[1], points[3]) 53 points[5], points[7] = minMax(points[5], points[7]) 54 55 nx0 = math.Min(points[0], points[4]) 56 ny0 = math.Min(points[1], points[5]) 57 nx2 = math.Max(points[2], points[6]) 58 ny2 = math.Max(points[3], points[7]) 59 return nx0, ny0, nx2, ny2 60} 61 62// InverseTransform applies the transformation inverse matrix to the rectangle represented by the min and the max point of the rectangle 63func (tr Matrix) InverseTransform(points []float64) { 64 d := tr.Determinant() // matrix determinant 65 for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { 66 x := points[i] 67 y := points[j] 68 points[i] = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d 69 points[j] = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d 70 } 71} 72 73// InverseTransformPoint applies the transformation inverse matrix to point. It returns the point the transformed point. 74func (tr Matrix) InverseTransformPoint(x, y float64) (xres, yres float64) { 75 d := tr.Determinant() // matrix determinant 76 xres = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d 77 yres = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d 78 return xres, yres 79} 80 81// VectorTransform applies the transformation matrix to points without using the translation parameter of the affine matrix. 82// It modify the points passed in parameter. 83func (tr Matrix) VectorTransform(points []float64) { 84 for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { 85 x := points[i] 86 y := points[j] 87 points[i] = x*tr[0] + y*tr[2] 88 points[j] = x*tr[1] + y*tr[3] 89 } 90} 91 92// NewIdentityMatrix creates an identity transformation matrix. 93func NewIdentityMatrix() Matrix { 94 return Matrix{1, 0, 0, 1, 0, 0} 95} 96 97// NewTranslationMatrix creates a transformation matrix with a translation tx and ty translation parameter 98func NewTranslationMatrix(tx, ty float64) Matrix { 99 return Matrix{1, 0, 0, 1, tx, ty} 100} 101 102// NewScaleMatrix creates a transformation matrix with a sx, sy scale factor 103func NewScaleMatrix(sx, sy float64) Matrix { 104 return Matrix{sx, 0, 0, sy, 0, 0} 105} 106 107// NewRotationMatrix creates a rotation transformation matrix. angle is in radian 108func NewRotationMatrix(angle float64) Matrix { 109 c := math.Cos(angle) 110 s := math.Sin(angle) 111 return Matrix{c, s, -s, c, 0, 0} 112} 113 114// NewMatrixFromRects creates a transformation matrix, combining a scale and a translation, that transform rectangle1 into rectangle2. 115func NewMatrixFromRects(rectangle1, rectangle2 [4]float64) Matrix { 116 xScale := (rectangle2[2] - rectangle2[0]) / (rectangle1[2] - rectangle1[0]) 117 yScale := (rectangle2[3] - rectangle2[1]) / (rectangle1[3] - rectangle1[1]) 118 xOffset := rectangle2[0] - (rectangle1[0] * xScale) 119 yOffset := rectangle2[1] - (rectangle1[1] * yScale) 120 return Matrix{xScale, 0, 0, yScale, xOffset, yOffset} 121} 122 123// Inverse computes the inverse matrix 124func (tr *Matrix) Inverse() { 125 d := tr.Determinant() // matrix determinant 126 tr0, tr1, tr2, tr3, tr4, tr5 := tr[0], tr[1], tr[2], tr[3], tr[4], tr[5] 127 tr[0] = tr3 / d 128 tr[1] = -tr1 / d 129 tr[2] = -tr2 / d 130 tr[3] = tr0 / d 131 tr[4] = (tr2*tr5 - tr3*tr4) / d 132 tr[5] = (tr1*tr4 - tr0*tr5) / d 133} 134 135func (tr Matrix) Copy() Matrix { 136 var result Matrix 137 copy(result[:], tr[:]) 138 return result 139} 140 141// Compose multiplies trToConcat x tr 142func (tr *Matrix) Compose(trToCompose Matrix) { 143 tr0, tr1, tr2, tr3, tr4, tr5 := tr[0], tr[1], tr[2], tr[3], tr[4], tr[5] 144 tr[0] = trToCompose[0]*tr0 + trToCompose[1]*tr2 145 tr[1] = trToCompose[1]*tr3 + trToCompose[0]*tr1 146 tr[2] = trToCompose[2]*tr0 + trToCompose[3]*tr2 147 tr[3] = trToCompose[3]*tr3 + trToCompose[2]*tr1 148 tr[4] = trToCompose[4]*tr0 + trToCompose[5]*tr2 + tr4 149 tr[5] = trToCompose[5]*tr3 + trToCompose[4]*tr1 + tr5 150} 151 152// Scale adds a scale to the matrix 153func (tr *Matrix) Scale(sx, sy float64) { 154 tr[0] = sx * tr[0] 155 tr[1] = sx * tr[1] 156 tr[2] = sy * tr[2] 157 tr[3] = sy * tr[3] 158} 159 160// Translate adds a translation to the matrix 161func (tr *Matrix) Translate(tx, ty float64) { 162 tr[4] = tx*tr[0] + ty*tr[2] + tr[4] 163 tr[5] = ty*tr[3] + tx*tr[1] + tr[5] 164} 165 166// Rotate adds a rotation to the matrix. angle is in radian 167func (tr *Matrix) Rotate(angle float64) { 168 c := math.Cos(angle) 169 s := math.Sin(angle) 170 t0 := c*tr[0] + s*tr[2] 171 t1 := s*tr[3] + c*tr[1] 172 t2 := c*tr[2] - s*tr[0] 173 t3 := c*tr[3] - s*tr[1] 174 tr[0] = t0 175 tr[1] = t1 176 tr[2] = t2 177 tr[3] = t3 178} 179 180// GetTranslation 181func (tr Matrix) GetTranslation() (x, y float64) { 182 return tr[4], tr[5] 183} 184 185// GetScaling 186func (tr Matrix) GetScaling() (x, y float64) { 187 return tr[0], tr[3] 188} 189 190// GetScale computes a scale for the matrix 191func (tr Matrix) GetScale() float64 { 192 x := 0.707106781*tr[0] + 0.707106781*tr[1] 193 y := 0.707106781*tr[2] + 0.707106781*tr[3] 194 return math.Sqrt(x*x + y*y) 195} 196 197// ******************** Testing ******************** 198 199// Equals tests if a two transformation are equal. A tolerance is applied when comparing matrix elements. 200func (tr1 Matrix) Equals(tr2 Matrix) bool { 201 for i := 0; i < 6; i = i + 1 { 202 if !fequals(tr1[i], tr2[i]) { 203 return false 204 } 205 } 206 return true 207} 208 209// IsIdentity tests if a transformation is the identity transformation. A tolerance is applied when comparing matrix elements. 210func (tr Matrix) IsIdentity() bool { 211 return fequals(tr[4], 0) && fequals(tr[5], 0) && tr.IsTranslation() 212} 213 214// IsTranslation tests if a transformation is is a pure translation. A tolerance is applied when comparing matrix elements. 215func (tr Matrix) IsTranslation() bool { 216 return fequals(tr[0], 1) && fequals(tr[1], 0) && fequals(tr[2], 0) && fequals(tr[3], 1) 217} 218 219// fequals compares two floats. return true if the distance between the two floats is less than epsilon, false otherwise 220func fequals(float1, float2 float64) bool { 221 return math.Abs(float1-float2) <= epsilon 222} 223