geom.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. package ebiten
  15. import (
  16. "fmt"
  17. "math"
  18. )
  19. // GeoMDim is a dimension of a GeoM.
  20. const GeoMDim = 3
  21. // A GeoM represents a matrix to transform geometry when rendering an image.
  22. //
  23. // The initial value is identity.
  24. type GeoM struct {
  25. a_1 float64 // The actual 'a' value minus 1
  26. b float64
  27. c float64
  28. d_1 float64 // The actual 'd' value minus 1
  29. tx float64
  30. ty float64
  31. }
  32. // String returns a string representation of GeoM.
  33. func (g *GeoM) String() string {
  34. 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)
  35. }
  36. // Reset resets the GeoM as identity.
  37. func (g *GeoM) Reset() {
  38. g.a_1 = 0
  39. g.b = 0
  40. g.c = 0
  41. g.d_1 = 0
  42. g.tx = 0
  43. g.ty = 0
  44. }
  45. // Apply pre-multiplies a vector (x, y, 1) by the matrix.
  46. // In other words, Apply calculates GeoM * (x, y, 1)^T.
  47. // The return value is x and y values of the result vector.
  48. func (g *GeoM) Apply(x, y float64) (float64, float64) {
  49. return (g.a_1+1)*x + g.b*y + g.tx, g.c*x + (g.d_1+1)*y + g.ty
  50. }
  51. func (g *GeoM) elements32() (a, b, c, d, tx, ty float32) {
  52. return float32(g.a_1) + 1, float32(g.b), float32(g.c), float32(g.d_1) + 1, float32(g.tx), float32(g.ty)
  53. }
  54. // Element returns a value of a matrix at (i, j).
  55. func (g *GeoM) Element(i, j int) float64 {
  56. switch {
  57. case i == 0 && j == 0:
  58. return g.a_1 + 1
  59. case i == 0 && j == 1:
  60. return g.b
  61. case i == 0 && j == 2:
  62. return g.tx
  63. case i == 1 && j == 0:
  64. return g.c
  65. case i == 1 && j == 1:
  66. return g.d_1 + 1
  67. case i == 1 && j == 2:
  68. return g.ty
  69. default:
  70. panic("ebiten: i or j is out of index")
  71. }
  72. }
  73. // Concat multiplies a geometry matrix with the other geometry matrix.
  74. // This is same as multiplying the matrix other and the matrix g in this order.
  75. func (g *GeoM) Concat(other GeoM) {
  76. a := (other.a_1+1)*(g.a_1+1) + other.b*g.c
  77. b := (other.a_1+1)*g.b + other.b*(g.d_1+1)
  78. tx := (other.a_1+1)*g.tx + other.b*g.ty + other.tx
  79. c := other.c*(g.a_1+1) + (other.d_1+1)*g.c
  80. d := other.c*g.b + (other.d_1+1)*(g.d_1+1)
  81. ty := other.c*g.tx + (other.d_1+1)*g.ty + other.ty
  82. g.a_1 = a - 1
  83. g.b = b
  84. g.c = c
  85. g.d_1 = d - 1
  86. g.tx = tx
  87. g.ty = ty
  88. }
  89. // Scale scales the matrix by (x, y).
  90. func (g *GeoM) Scale(x, y float64) {
  91. a := (g.a_1 + 1) * x
  92. b := g.b * x
  93. tx := g.tx * x
  94. c := g.c * y
  95. d := (g.d_1 + 1) * y
  96. ty := g.ty * y
  97. g.a_1 = a - 1
  98. g.b = b
  99. g.c = c
  100. g.d_1 = d - 1
  101. g.tx = tx
  102. g.ty = ty
  103. }
  104. // Translate translates the matrix by (tx, ty).
  105. func (g *GeoM) Translate(tx, ty float64) {
  106. g.tx += tx
  107. g.ty += ty
  108. }
  109. // Rotate rotates the matrix clockwise by theta.
  110. // The unit is radian.
  111. func (g *GeoM) Rotate(theta float64) {
  112. if theta == 0 {
  113. return
  114. }
  115. sin, cos := math.Sincos(theta)
  116. a := cos*(g.a_1+1) - sin*g.c
  117. b := cos*g.b - sin*(g.d_1+1)
  118. tx := cos*g.tx - sin*g.ty
  119. c := sin*(g.a_1+1) + cos*g.c
  120. d := sin*g.b + cos*(g.d_1+1)
  121. ty := sin*g.tx + cos*g.ty
  122. g.a_1 = a - 1
  123. g.b = b
  124. g.c = c
  125. g.d_1 = d - 1
  126. g.tx = tx
  127. g.ty = ty
  128. }
  129. // Skew skews the matrix by (skewX, skewY). The unit is radian.
  130. func (g *GeoM) Skew(skewX, skewY float64) {
  131. sx := math.Tan(skewX)
  132. sy := math.Tan(skewY)
  133. a := (g.a_1 + 1) + g.c*sx
  134. b := g.b + (g.d_1+1)*sx
  135. c := (g.a_1+1)*sy + g.c
  136. d := g.b*sy + (g.d_1 + 1)
  137. tx := g.tx + g.ty*sx
  138. ty := g.ty + g.tx*sy
  139. g.a_1 = a - 1
  140. g.b = b
  141. g.c = c
  142. g.d_1 = d - 1
  143. g.tx = tx
  144. g.ty = ty
  145. }
  146. func (g *GeoM) det2x2() float64 {
  147. return (g.a_1+1)*(g.d_1+1) - g.b*g.c
  148. }
  149. // IsInvertible returns a boolean value indicating
  150. // whether the matrix g is invertible or not.
  151. func (g *GeoM) IsInvertible() bool {
  152. return g.det2x2() != 0
  153. }
  154. // Invert inverts the matrix.
  155. // If g is not invertible, Invert panics.
  156. func (g *GeoM) Invert() {
  157. det := g.det2x2()
  158. if det == 0 {
  159. panic("ebiten: g is not invertible")
  160. }
  161. a := (g.d_1 + 1) / det
  162. b := -g.b / det
  163. c := -g.c / det
  164. d := (g.a_1 + 1) / det
  165. tx := (-(g.d_1+1)*g.tx + g.b*g.ty) / det
  166. ty := (g.c*g.tx + -(g.a_1+1)*g.ty) / det
  167. g.a_1 = a - 1
  168. g.b = b
  169. g.c = c
  170. g.d_1 = d - 1
  171. g.tx = tx
  172. g.ty = ty
  173. }
  174. // SetElement sets an element at (i, j).
  175. func (g *GeoM) SetElement(i, j int, element float64) {
  176. e := element
  177. switch {
  178. case i == 0 && j == 0:
  179. g.a_1 = e - 1
  180. case i == 0 && j == 1:
  181. g.b = e
  182. case i == 0 && j == 2:
  183. g.tx = e
  184. case i == 1 && j == 0:
  185. g.c = e
  186. case i == 1 && j == 1:
  187. g.d_1 = e - 1
  188. case i == 1 && j == 2:
  189. g.ty = e
  190. default:
  191. panic("ebiten: i or j is out of index")
  192. }
  193. }