geom.go (4879B)
1 // Copyright 2014 Hajime Hoshi 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ebiten 16 17 import ( 18 "fmt" 19 "math" 20 ) 21 22 // GeoMDim is a dimension of a GeoM. 23 const GeoMDim = 3 24 25 // A GeoM represents a matrix to transform geometry when rendering an image. 26 // 27 // The initial value is identity. 28 type GeoM struct { 29 a_1 float64 // The actual 'a' value minus 1 30 b float64 31 c float64 32 d_1 float64 // The actual 'd' value minus 1 33 tx float64 34 ty float64 35 } 36 37 // String returns a string representation of GeoM. 38 func (g *GeoM) String() string { 39 return fmt.Sprintf("[[%f, %f, %f], [%f, %f, %f]]", g.a_1+1, g.b, g.tx, g.c, g.d_1+1, g.ty) 40 } 41 42 // Reset resets the GeoM as identity. 43 func (g *GeoM) Reset() { 44 g.a_1 = 0 45 g.b = 0 46 g.c = 0 47 g.d_1 = 0 48 g.tx = 0 49 g.ty = 0 50 } 51 52 // Apply pre-multiplies a vector (x, y, 1) by the matrix. 53 // In other words, Apply calculates GeoM * (x, y, 1)^T. 54 // The return value is x and y values of the result vector. 55 func (g *GeoM) Apply(x, y float64) (float64, float64) { 56 return (g.a_1+1)*x + g.b*y + g.tx, g.c*x + (g.d_1+1)*y + g.ty 57 } 58 59 func (g *GeoM) elements32() (a, b, c, d, tx, ty float32) { 60 return float32(g.a_1) + 1, float32(g.b), float32(g.c), float32(g.d_1) + 1, float32(g.tx), float32(g.ty) 61 } 62 63 // Element returns a value of a matrix at (i, j). 64 func (g *GeoM) Element(i, j int) float64 { 65 switch { 66 case i == 0 && j == 0: 67 return g.a_1 + 1 68 case i == 0 && j == 1: 69 return g.b 70 case i == 0 && j == 2: 71 return g.tx 72 case i == 1 && j == 0: 73 return g.c 74 case i == 1 && j == 1: 75 return g.d_1 + 1 76 case i == 1 && j == 2: 77 return g.ty 78 default: 79 panic("ebiten: i or j is out of index") 80 } 81 } 82 83 // Concat multiplies a geometry matrix with the other geometry matrix. 84 // This is same as muptiplying the matrix other and the matrix g in this order. 85 func (g *GeoM) Concat(other GeoM) { 86 a := (other.a_1+1)*(g.a_1+1) + other.b*g.c 87 b := (other.a_1+1)*g.b + other.b*(g.d_1+1) 88 tx := (other.a_1+1)*g.tx + other.b*g.ty + other.tx 89 c := other.c*(g.a_1+1) + (other.d_1+1)*g.c 90 d := other.c*g.b + (other.d_1+1)*(g.d_1+1) 91 ty := other.c*g.tx + (other.d_1+1)*g.ty + other.ty 92 93 g.a_1 = a - 1 94 g.b = b 95 g.c = c 96 g.d_1 = d - 1 97 g.tx = tx 98 g.ty = ty 99 } 100 101 // Scale scales the matrix by (x, y). 102 func (g *GeoM) Scale(x, y float64) { 103 a := (g.a_1 + 1) * x 104 b := g.b * x 105 tx := g.tx * x 106 c := g.c * y 107 d := (g.d_1 + 1) * y 108 ty := g.ty * y 109 110 g.a_1 = a - 1 111 g.b = b 112 g.c = c 113 g.d_1 = d - 1 114 g.tx = tx 115 g.ty = ty 116 } 117 118 // Translate translates the matrix by (tx, ty). 119 func (g *GeoM) Translate(tx, ty float64) { 120 g.tx += tx 121 g.ty += ty 122 } 123 124 // Rotate rotates the matrix by theta. 125 // The unit is radian. 126 func (g *GeoM) Rotate(theta float64) { 127 if theta == 0 { 128 return 129 } 130 131 sin, cos := math.Sincos(theta) 132 133 a := cos*(g.a_1+1) - sin*g.c 134 b := cos*g.b - sin*(g.d_1+1) 135 tx := cos*g.tx - sin*g.ty 136 c := sin*(g.a_1+1) + cos*g.c 137 d := sin*g.b + cos*(g.d_1+1) 138 ty := sin*g.tx + cos*g.ty 139 140 g.a_1 = a - 1 141 g.b = b 142 g.c = c 143 g.d_1 = d - 1 144 g.tx = tx 145 g.ty = ty 146 } 147 148 // Skew skews the matrix by (skewX, skewY). The unit is radian. 149 func (g *GeoM) Skew(skewX, skewY float64) { 150 sx := math.Tan(skewX) 151 sy := math.Tan(skewY) 152 153 a := (g.a_1 + 1) + g.c*sx 154 b := g.b + (g.d_1+1)*sx 155 c := (g.a_1+1)*sy + g.c 156 d := g.b*sy + (g.d_1 + 1) 157 tx := g.tx + g.ty*sx 158 ty := g.ty + g.tx*sy 159 160 g.a_1 = a - 1 161 g.b = b 162 g.c = c 163 g.d_1 = d - 1 164 g.tx = tx 165 g.ty = ty 166 } 167 168 func (g *GeoM) det2x2() float64 { 169 return (g.a_1+1)*(g.d_1+1) - g.b*g.c 170 } 171 172 // IsInvertible returns a boolean value indicating 173 // whether the matrix g is invertible or not. 174 func (g *GeoM) IsInvertible() bool { 175 return g.det2x2() != 0 176 } 177 178 // Invert inverts the matrix. 179 // If g is not invertible, Invert panics. 180 func (g *GeoM) Invert() { 181 det := g.det2x2() 182 if det == 0 { 183 panic("ebiten: g is not invertible") 184 } 185 186 a := (g.d_1 + 1) / det 187 b := -g.b / det 188 c := -g.c / det 189 d := (g.a_1 + 1) / det 190 tx := (-(g.d_1+1)*g.tx + g.b*g.ty) / det 191 ty := (g.c*g.tx + -(g.a_1+1)*g.ty) / det 192 193 g.a_1 = a - 1 194 g.b = b 195 g.c = c 196 g.d_1 = d - 1 197 g.tx = tx 198 g.ty = ty 199 } 200 201 // SetElement sets an element at (i, j). 202 func (g *GeoM) SetElement(i, j int, element float64) { 203 e := element 204 switch { 205 case i == 0 && j == 0: 206 g.a_1 = e - 1 207 case i == 0 && j == 1: 208 g.b = e 209 case i == 0 && j == 2: 210 g.tx = e 211 case i == 1 && j == 0: 212 g.c = e 213 case i == 1 && j == 1: 214 g.d_1 = e - 1 215 case i == 1 && j == 2: 216 g.ty = e 217 default: 218 panic("ebiten: i or j is out of index") 219 } 220 }