matrix.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Implements SVG style matrix transformations.
  2. // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
  3. // Copyright 2018 All rights reserved.
  4. package rasterx
  5. import (
  6. "math"
  7. "golang.org/x/image/math/fixed"
  8. )
  9. // Matrix2D represents an SVG style matrix
  10. type Matrix2D struct {
  11. A, B, C, D, E, F float64
  12. }
  13. // matrix3 is a full 3x3 float64 matrix
  14. // used for inverting
  15. type matrix3 [9]float64
  16. func otherPair(i int) (a, b int) {
  17. switch i {
  18. case 0:
  19. a, b = 1, 2
  20. case 1:
  21. a, b = 0, 2
  22. case 2:
  23. a, b = 0, 1
  24. }
  25. return
  26. }
  27. func (m *matrix3) coFact(i, j int) float64 {
  28. ai, bi := otherPair(i)
  29. aj, bj := otherPair(j)
  30. a, b, c, d := m[ai+aj*3], m[bi+bj*3], m[ai+bj*3], m[bi+aj*3]
  31. return a*b - c*d
  32. }
  33. func (m *matrix3) Invert() *matrix3 {
  34. var cofact matrix3
  35. for i := 0; i < 3; i++ {
  36. for j := 0; j < 3; j++ {
  37. sign := float64(1 - (i+j%2)%2*2) // "checkerboard of minuses" grid
  38. cofact[i+j*3] = m.coFact(i, j) * sign
  39. }
  40. }
  41. deteriminate := m[0]*cofact[0] + m[1]*cofact[1] + m[2]*cofact[2]
  42. // transpose cofact
  43. for i := 0; i < 2; i++ {
  44. for j := i + 1; j < 3; j++ {
  45. cofact[i+j*3], cofact[j+i*3] = cofact[j+i*3], cofact[i+j*3]
  46. }
  47. }
  48. for i := 0; i < 9; i++ {
  49. cofact[i] /= deteriminate
  50. }
  51. return &cofact
  52. }
  53. // Invert returns the inverse matrix
  54. func (a Matrix2D) Invert() Matrix2D {
  55. n := &matrix3{a.A, a.C, a.E, a.B, a.D, a.F, 0, 0, 1}
  56. n = n.Invert()
  57. return Matrix2D{A: n[0], C: n[1], E: n[2], B: n[3], D: n[4], F: n[5]}
  58. }
  59. // Mult returns a*b
  60. func (a Matrix2D) Mult(b Matrix2D) Matrix2D {
  61. return Matrix2D{
  62. A: a.A*b.A + a.C*b.B,
  63. B: a.B*b.A + a.D*b.B,
  64. C: a.A*b.C + a.C*b.D,
  65. D: a.B*b.C + a.D*b.D,
  66. E: a.A*b.E + a.C*b.F + a.E,
  67. F: a.B*b.E + a.D*b.F + a.F}
  68. }
  69. // Identity is the identity matrix
  70. var Identity = Matrix2D{1, 0, 0, 1, 0, 0}
  71. // TFixed transforms a fixed.Point26_6 by the matrix
  72. func (a Matrix2D) TFixed(x fixed.Point26_6) (y fixed.Point26_6) {
  73. y.X = fixed.Int26_6((float64(x.X)*a.A + float64(x.Y)*a.C) + a.E*64)
  74. y.Y = fixed.Int26_6((float64(x.X)*a.B + float64(x.Y)*a.D) + a.F*64)
  75. return
  76. }
  77. // Transform multiples the input vector by matrix m and outputs the results vector
  78. // components.
  79. func (a Matrix2D) Transform(x1, y1 float64) (x2, y2 float64) {
  80. x2 = x1*a.A + y1*a.C + a.E
  81. y2 = x1*a.B + y1*a.D + a.F
  82. return
  83. }
  84. // TransformVector is a modidifed version of Transform that ignores the
  85. // translation components.
  86. func (a Matrix2D) TransformVector(x1, y1 float64) (x2, y2 float64) {
  87. x2 = x1*a.A + y1*a.C
  88. y2 = x1*a.B + y1*a.D
  89. return
  90. }
  91. //Scale matrix in x and y dimensions
  92. func (a Matrix2D) Scale(x, y float64) Matrix2D {
  93. return a.Mult(Matrix2D{
  94. A: x,
  95. B: 0,
  96. C: 0,
  97. D: y,
  98. E: 0,
  99. F: 0})
  100. }
  101. //SkewY skews the matrix in the Y dimension
  102. func (a Matrix2D) SkewY(theta float64) Matrix2D {
  103. return a.Mult(Matrix2D{
  104. A: 1,
  105. B: math.Tan(theta),
  106. C: 0,
  107. D: 1,
  108. E: 0,
  109. F: 0})
  110. }
  111. //SkewX skews the matrix in the X dimension
  112. func (a Matrix2D) SkewX(theta float64) Matrix2D {
  113. return a.Mult(Matrix2D{
  114. A: 1,
  115. B: 0,
  116. C: math.Tan(theta),
  117. D: 1,
  118. E: 0,
  119. F: 0})
  120. }
  121. //Translate translates the matrix to the x , y point
  122. func (a Matrix2D) Translate(x, y float64) Matrix2D {
  123. return a.Mult(Matrix2D{
  124. A: 1,
  125. B: 0,
  126. C: 0,
  127. D: 1,
  128. E: x,
  129. F: y})
  130. }
  131. //Rotate rotate the matrix by theta
  132. func (a Matrix2D) Rotate(theta float64) Matrix2D {
  133. return a.Mult(Matrix2D{
  134. A: math.Cos(theta),
  135. B: math.Sin(theta),
  136. C: -math.Sin(theta),
  137. D: math.Cos(theta),
  138. E: 0,
  139. F: 0})
  140. }
  141. // MatrixAdder is an adder that applies matrix M to all points
  142. type MatrixAdder struct {
  143. Adder
  144. M Matrix2D
  145. }
  146. // Reset sets the matrix M to identity
  147. func (t *MatrixAdder) Reset() {
  148. t.M = Identity
  149. }
  150. // Start starts a new path
  151. func (t *MatrixAdder) Start(a fixed.Point26_6) {
  152. t.Adder.Start(t.M.TFixed(a))
  153. }
  154. // Line adds a linear segment to the current curve.
  155. func (t *MatrixAdder) Line(b fixed.Point26_6) {
  156. t.Adder.Line(t.M.TFixed(b))
  157. }
  158. // QuadBezier adds a quadratic segment to the current curve.
  159. func (t *MatrixAdder) QuadBezier(b, c fixed.Point26_6) {
  160. t.Adder.QuadBezier(t.M.TFixed(b), t.M.TFixed(c))
  161. }
  162. // CubeBezier adds a cubic segment to the current curve.
  163. func (t *MatrixAdder) CubeBezier(b, c, d fixed.Point26_6) {
  164. t.Adder.CubeBezier(t.M.TFixed(b), t.M.TFixed(c), t.M.TFixed(d))
  165. }