1cb93a386Sopenharmony_cidescribe('CanvasKit\'s Matrix Helpers', () => {
2cb93a386Sopenharmony_ci
3cb93a386Sopenharmony_ci  beforeEach(async () => {
4cb93a386Sopenharmony_ci    await LoadCanvasKit;
5cb93a386Sopenharmony_ci  });
6cb93a386Sopenharmony_ci
7cb93a386Sopenharmony_ci  const expectArrayCloseTo = (a, b, precision) => {
8cb93a386Sopenharmony_ci    precision = precision || 14; // digits of precision in base 10
9cb93a386Sopenharmony_ci    expect(a.length).toEqual(b.length);
10cb93a386Sopenharmony_ci    for (let i=0; i<a.length; i++) {
11cb93a386Sopenharmony_ci      expect(a[i]).toBeCloseTo(b[i], precision);
12cb93a386Sopenharmony_ci    }
13cb93a386Sopenharmony_ci  };
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ci  describe('3x3 matrices', () => {
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci    it('can make a translated 3x3 matrix', () => {
18cb93a386Sopenharmony_ci      expectArrayCloseTo(
19cb93a386Sopenharmony_ci        CanvasKit.Matrix.translated(5, -1),
20cb93a386Sopenharmony_ci          [1, 0,  5,
21cb93a386Sopenharmony_ci           0, 1, -1,
22cb93a386Sopenharmony_ci           0, 0,  1]);
23cb93a386Sopenharmony_ci    });
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci    it('can make a scaled 3x3 matrix', () => {
26cb93a386Sopenharmony_ci      expectArrayCloseTo(
27cb93a386Sopenharmony_ci        CanvasKit.Matrix.scaled(2, 3),
28cb93a386Sopenharmony_ci          [2, 0, 0,
29cb93a386Sopenharmony_ci           0, 3, 0,
30cb93a386Sopenharmony_ci           0, 0, 1]);
31cb93a386Sopenharmony_ci    });
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci    it('can make a rotated 3x3 matrix', () => {
34cb93a386Sopenharmony_ci      expectArrayCloseTo(
35cb93a386Sopenharmony_ci        CanvasKit.Matrix.rotated(Math.PI, 9, 9),
36cb93a386Sopenharmony_ci          [-1,  0, 18,
37cb93a386Sopenharmony_ci            0, -1, 18,
38cb93a386Sopenharmony_ci            0,  0,  1]);
39cb93a386Sopenharmony_ci    });
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    it('can make a skewed 3x3 matrix', () => {
42cb93a386Sopenharmony_ci      expectArrayCloseTo(
43cb93a386Sopenharmony_ci        CanvasKit.Matrix.skewed(4, 3, 2, 1),
44cb93a386Sopenharmony_ci          [1, 4, -8,
45cb93a386Sopenharmony_ci           3, 1, -3,
46cb93a386Sopenharmony_ci           0, 0,  1]);
47cb93a386Sopenharmony_ci    });
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci    it('can multiply 3x3 matrices', () => {
50cb93a386Sopenharmony_ci      const a = [
51cb93a386Sopenharmony_ci         0.1,  0.2,  0.3,
52cb93a386Sopenharmony_ci         0.0,  0.6,  0.7,
53cb93a386Sopenharmony_ci         0.9, -0.9, -0.8,
54cb93a386Sopenharmony_ci      ];
55cb93a386Sopenharmony_ci      const b = [
56cb93a386Sopenharmony_ci         2.0,  3.0,  4.0,
57cb93a386Sopenharmony_ci        -3.0, -4.0, -5.0,
58cb93a386Sopenharmony_ci         7.0,  8.0,  9.0,
59cb93a386Sopenharmony_ci      ];
60cb93a386Sopenharmony_ci      const expected = [
61cb93a386Sopenharmony_ci         1.7,  1.9,  2.1,
62cb93a386Sopenharmony_ci         3.1,  3.2,  3.3,
63cb93a386Sopenharmony_ci        -1.1, -0.1,  0.9,
64cb93a386Sopenharmony_ci      ];
65cb93a386Sopenharmony_ci      expectArrayCloseTo(
66cb93a386Sopenharmony_ci        CanvasKit.Matrix.multiply(a, b),
67cb93a386Sopenharmony_ci        expected);
68cb93a386Sopenharmony_ci    });
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    it('satisfies the inverse rule for 3x3 matrics', () => {
71cb93a386Sopenharmony_ci      // a matrix times its inverse is the identity matrix.
72cb93a386Sopenharmony_ci      const a = [
73cb93a386Sopenharmony_ci         0.1,  0.2,  0.3,
74cb93a386Sopenharmony_ci         0.0,  0.6,  0.7,
75cb93a386Sopenharmony_ci         0.9, -0.9, -0.8,
76cb93a386Sopenharmony_ci      ];
77cb93a386Sopenharmony_ci      const b = CanvasKit.Matrix.invert(a);
78cb93a386Sopenharmony_ci      expectArrayCloseTo(
79cb93a386Sopenharmony_ci        CanvasKit.Matrix.multiply(a, b),
80cb93a386Sopenharmony_ci        CanvasKit.Matrix.identity());
81cb93a386Sopenharmony_ci    });
82cb93a386Sopenharmony_ci
83cb93a386Sopenharmony_ci    it('maps 2D points correctly with a 3x3 matrix', () => {
84cb93a386Sopenharmony_ci        const a = [
85cb93a386Sopenharmony_ci           3,  0, -4,
86cb93a386Sopenharmony_ci           0,  2, 4,
87cb93a386Sopenharmony_ci           0,  0, 1,
88cb93a386Sopenharmony_ci        ];
89cb93a386Sopenharmony_ci        const points = [
90cb93a386Sopenharmony_ci          0, 0,
91cb93a386Sopenharmony_ci          1, 1,
92cb93a386Sopenharmony_ci        ];
93cb93a386Sopenharmony_ci        const expected = [
94cb93a386Sopenharmony_ci          -4, 4,
95cb93a386Sopenharmony_ci          -1, 6,
96cb93a386Sopenharmony_ci        ];
97cb93a386Sopenharmony_ci        expectArrayCloseTo(
98cb93a386Sopenharmony_ci          CanvasKit.Matrix.mapPoints(a, points),
99cb93a386Sopenharmony_ci          expected);
100cb93a386Sopenharmony_ci    });
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci  }); // describe 3x3
103cb93a386Sopenharmony_ci  describe('4x4 matrices', () => {
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci    it('can make a translated 4x4 matrix', () => {
106cb93a386Sopenharmony_ci      expectArrayCloseTo(
107cb93a386Sopenharmony_ci        CanvasKit.M44.translated([5, 6, 7]),
108cb93a386Sopenharmony_ci          [1, 0, 0, 5,
109cb93a386Sopenharmony_ci           0, 1, 0, 6,
110cb93a386Sopenharmony_ci           0, 0, 1, 7,
111cb93a386Sopenharmony_ci           0, 0, 0, 1]);
112cb93a386Sopenharmony_ci    });
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    it('can make a scaled 4x4 matrix', () => {
115cb93a386Sopenharmony_ci      expectArrayCloseTo(
116cb93a386Sopenharmony_ci        CanvasKit.M44.scaled([5, 6, 7]),
117cb93a386Sopenharmony_ci          [5, 0, 0, 0,
118cb93a386Sopenharmony_ci           0, 6, 0, 0,
119cb93a386Sopenharmony_ci           0, 0, 7, 0,
120cb93a386Sopenharmony_ci           0, 0, 0, 1]);
121cb93a386Sopenharmony_ci    });
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    it('can make a rotated 4x4 matrix', () => {
124cb93a386Sopenharmony_ci      expectArrayCloseTo(
125cb93a386Sopenharmony_ci        CanvasKit.M44.rotated([1,1,1], Math.PI),
126cb93a386Sopenharmony_ci          [-1/3,  2/3,  2/3, 0,
127cb93a386Sopenharmony_ci            2/3, -1/3,  2/3, 0,
128cb93a386Sopenharmony_ci            2/3,  2/3, -1/3, 0,
129cb93a386Sopenharmony_ci              0,    0,    0, 1]);
130cb93a386Sopenharmony_ci    });
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci    it('can make a 4x4 matrix looking from eye to center', () => {
133cb93a386Sopenharmony_ci      eye = [1, 0, 0];
134cb93a386Sopenharmony_ci      center = [1, 0, 1];
135cb93a386Sopenharmony_ci      up = [0, 1, 0]
136cb93a386Sopenharmony_ci      expectArrayCloseTo(
137cb93a386Sopenharmony_ci        CanvasKit.M44.lookat(eye, center, up),
138cb93a386Sopenharmony_ci          [-1,  0,  0,  1,
139cb93a386Sopenharmony_ci            0,  1,  0,  0,
140cb93a386Sopenharmony_ci            0,  0, -1,  0,
141cb93a386Sopenharmony_ci            0,  0,  0,  1]);
142cb93a386Sopenharmony_ci    });
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci    it('can make a 4x4 prespective matrix', () => {
145cb93a386Sopenharmony_ci      expectArrayCloseTo(
146cb93a386Sopenharmony_ci        CanvasKit.M44.perspective(2, 10, Math.PI/2),
147cb93a386Sopenharmony_ci          [1, 0,   0, 0,
148cb93a386Sopenharmony_ci           0, 1,   0, 0,
149cb93a386Sopenharmony_ci           0, 0, 1.5, 5,
150cb93a386Sopenharmony_ci           0, 0,  -1, 1]);
151cb93a386Sopenharmony_ci    });
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci    it('can multiply 4x4 matrices', () => {
154cb93a386Sopenharmony_ci      const a = [
155cb93a386Sopenharmony_ci         0.1,  0.2,  0.3,  0.4,
156cb93a386Sopenharmony_ci         0.0,  0.6,  0.7,  0.8,
157cb93a386Sopenharmony_ci         0.9, -0.9, -0.8, -0.7,
158cb93a386Sopenharmony_ci        -0.6, -0.5, -0.4, -0.3,
159cb93a386Sopenharmony_ci      ];
160cb93a386Sopenharmony_ci      const b = [
161cb93a386Sopenharmony_ci         2.0,  3.0,  4.0,  5.0,
162cb93a386Sopenharmony_ci        -3.0, -4.0, -5.0, -6.0,
163cb93a386Sopenharmony_ci         7.0,  8.0,  9.0, 10.0,
164cb93a386Sopenharmony_ci        -4.0, -3.0, -2.0, -1.0,
165cb93a386Sopenharmony_ci      ];
166cb93a386Sopenharmony_ci      const expected = [
167cb93a386Sopenharmony_ci         0.1,  0.7,  1.3,  1.9,
168cb93a386Sopenharmony_ci        -0.1,  0.8,  1.7,  2.6,
169cb93a386Sopenharmony_ci         1.7,  2.0,  2.3,  2.6,
170cb93a386Sopenharmony_ci        -1.3, -2.1, -2.9, -3.7,
171cb93a386Sopenharmony_ci      ];
172cb93a386Sopenharmony_ci      expectArrayCloseTo(
173cb93a386Sopenharmony_ci        CanvasKit.M44.multiply(a, b),
174cb93a386Sopenharmony_ci        expected);
175cb93a386Sopenharmony_ci    });
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci    it('satisfies the identity rule for 4x4 matrices', () => {
178cb93a386Sopenharmony_ci      const a = [
179cb93a386Sopenharmony_ci         0.1,  0.2,  0.3,  0.4,
180cb93a386Sopenharmony_ci         0.0,  0.6,  0.7,  0.8,
181cb93a386Sopenharmony_ci         0.9,  0.9, -0.8, -0.7,
182cb93a386Sopenharmony_ci        -0.6, -0.5, -0.4, -0.3,
183cb93a386Sopenharmony_ci      ];
184cb93a386Sopenharmony_ci      const b = CanvasKit.M44.invert(a)
185cb93a386Sopenharmony_ci      expectArrayCloseTo(
186cb93a386Sopenharmony_ci        CanvasKit.M44.multiply(a, b),
187cb93a386Sopenharmony_ci        CanvasKit.M44.identity());
188cb93a386Sopenharmony_ci    });
189cb93a386Sopenharmony_ci
190cb93a386Sopenharmony_ci    it('can create a camera setup matrix', () => {
191cb93a386Sopenharmony_ci      const camAngle = Math.PI / 12;
192cb93a386Sopenharmony_ci      const cam = {
193cb93a386Sopenharmony_ci        'eye'  : [0, 0, 1 / Math.tan(camAngle/2) - 1],
194cb93a386Sopenharmony_ci        'coa'  : [0, 0, 0],
195cb93a386Sopenharmony_ci        'up'   : [0, 1, 0],
196cb93a386Sopenharmony_ci        'near' : 0.02,
197cb93a386Sopenharmony_ci        'far'  : 4,
198cb93a386Sopenharmony_ci        'angle': camAngle,
199cb93a386Sopenharmony_ci      };
200cb93a386Sopenharmony_ci      const mat = CanvasKit.M44.setupCamera(CanvasKit.LTRBRect(0, 0, 200, 200), 200, cam);
201cb93a386Sopenharmony_ci      // these values came from an invocation of setupCamera visually inspected.
202cb93a386Sopenharmony_ci      const expected = [
203cb93a386Sopenharmony_ci          7.595754, 0, -0.5, 0,
204cb93a386Sopenharmony_ci          0, 7.595754, -0.5, 0,
205cb93a386Sopenharmony_ci          0, 0, 1.010050, -1324.368418,
206cb93a386Sopenharmony_ci          0, 0, -0.005, 7.595754];
207cb93a386Sopenharmony_ci      expectArrayCloseTo(mat, expected, 5);
208cb93a386Sopenharmony_ci    });
209cb93a386Sopenharmony_ci  }); // describe 4x4
210cb93a386Sopenharmony_ci});
211