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