UNPKG

6.31 kBMarkdownView Raw
1# Rectangle AABB Matrix
2
3Initialize a Rectangle to a 1 unit square centered at 0 x 0 transformed by a model matrix.
4
5-----
6
7Every drawable is a 1 x 1 unit square that is rotated by its direction, scaled by its skin size and scale, and offset by its rotation center and position. The square representation is made up of 4 points that are transformed by the drawable properties. Often we want a shape that simplifies those 4 points into a non-rotated shape, a axis aligned bounding box.
8
9One approach is to compare the x and y components of each transformed vector and find the minimum and maximum x component and the minimum and maximum y component.
10
11We can start from this approach and determine an alternative one that prodcues the same output with less work.
12
13Starting with transforming one point, here is a 3D point, `v`, transformation by a matrix, `m`.
14
15```js
16const v0 = v[0];
17const v1 = v[1];
18const v2 = v[2];
19
20const d = v0 * m[(0 * 4) + 3] + v1 * m[(1 * 4) + 3] + v2 * m[(2 * 4) + 3] + m[(3 * 4) + 3];
21dst[0] = (v0 * m[(0 * 4) + 0] + v1 * m[(1 * 4) + 0] + v2 * m[(2 * 4) + 0] + m[(3 * 4) + 0]) / d;
22dst[1] = (v0 * m[(0 * 4) + 1] + v1 * m[(1 * 4) + 1] + v2 * m[(2 * 4) + 1] + m[(3 * 4) + 1]) / d;
23dst[2] = (v0 * m[(0 * 4) + 2] + v1 * m[(1 * 4) + 2] + v2 * m[(2 * 4) + 2] + m[(3 * 4) + 2]) / d;
24```
25
26As this is a 2D rectangle we can cancel out the third dimension, and the determinant, 'd'.
27
28```js
29const v0 = v[0];
30const v1 = v[1];
31
32dst = [
33 v0 * m[(0 * 4) + 0] + v1 * m[(1 * 4) + 0] + m[(3 * 4) + 0,
34 v0 * m[(0 * 4) + 1] + v1 * m[(1 * 4) + 1] + m[(3 * 4) + 1
35];
36```
37
38Let's set the matrix points to shorter names for convenience.
39
40```js
41const m00 = m[(0 * 4) + 0];
42const m01 = m[(0 * 4) + 1];
43const m10 = m[(1 * 4) + 0];
44const m11 = m[(1 * 4) + 1];
45const m30 = m[(3 * 4) + 0];
46const m31 = m[(3 * 4) + 1];
47```
48
49We need 4 points with positive and negative 0.5 values so the square has sides of length 1.
50
51```js
52let p = [0.5, 0.5];
53let q = [-0.5, 0.5];
54let r = [-0.5, -0.5];
55let s = [0.5, -0.5];
56```
57
58Transform the points by the matrix.
59
60```js
61p = [
62 0.5 * m00 + 0.5 * m10 + m30,
63 0.5 * m01 + 0.5 * m11 + m31
64];
65q = [
66 -0.5 * m00 + -0.5 * m10 + m30,
67 0.5 * m01 + 0.5 * m11 + m31
68];
69r = [
70 -0.5 * m00 + -0.5 * m10 + m30,
71 -0.5 * m01 + -0.5 * m11 + m31
72];
73s = [
74 0.5 * m00 + 0.5 * m10 + m30,
75 -0.5 * m01 + -0.5 * m11 + m31
76];
77```
78
79With 4 transformed points we can build the left, right, top, and bottom values for the Rectangle. Each will use the minimum or the maximum of one of the components of all points.
80
81```js
82const left = Math.min(p[0], q[0], r[0], s[0]);
83const right = Math.max(p[0], q[0], r[0], s[0]);
84const top = Math.max(p[1], q[1], r[1], s[1]);
85const bottom = Math.min(p[1], q[1], r[1], s[1]);
86```
87
88Fill those calls with the vector expressions.
89
90```js
91const left = Math.min(
92 0.5 * m00 + 0.5 * m10 + m30,
93 -0.5 * m00 + 0.5 * m10 + m30,
94 -0.5 * m00 + -0.5 * m10 + m30,
95 0.5 * m00 + -0.5 * m10 + m30
96);
97const right = Math.max(
98 0.5 * m00 + 0.5 * m10 + m30,
99 -0.5 * m00 + 0.5 * m10 + m30,
100 -0.5 * m00 + -0.5 * m10 + m30,
101 0.5 * m00 + -0.5 * m10 + m30
102);
103const top = Math.max(
104 0.5 * m01 + 0.5 * m11 + m31,
105 -0.5 * m01 + 0.5 * m11 + m31,
106 -0.5 * m01 + -0.5 * m11 + m31,
107 0.5 * m01 + -0.5 * m11 + m31
108);
109const bottom = Math.min(
110 0.5 * m01 + 0.5 * m11 + m31,
111 -0.5 * m01 + 0.5 * m11 + m31,
112 -0.5 * m01 + -0.5 * m11 + m31,
113 0.5 * m01 + -0.5 * m11 + m31
114);
115```
116
117Pull out the `0.5 * m??` patterns.
118
119```js
120const x0 = 0.5 * m00;
121const x1 = 0.5 * m10;
122const y0 = 0.5 * m01;
123const y1 = 0.5 * m11;
124
125const left = Math.min(x0 + x1 + m30, -x0 + x1 + m30, -x0 + -x1 + m30, x0 + -x1 + m30);
126const right = Math.max(x0 + x1 + m30, -x0 + x1 + m30, -x0 + -x1 + m30, x0 + -x1 + m30);
127const top = Math.max(y0 + y1 + m31, -y0 + y1 + m31, -y0 + -y1 + m31, y0 + -y1 + m31);
128const bottom = Math.min(y0 + y1 + m31, -y0 + y1 + m31, -y0 + -y1 + m31, y0 + -y1 + m31);
129```
130
131Now each argument for the min and max calls take an expression like `(a * x0 + b * x1 + m3?)`. As each expression has the x0, x1, and m3? variables we can split the min and max calls on the addition operators. Each new call has all the coefficients of that variable.
132
133```js
134const left = Math.min(x0, -x0) + Math.min(x1, -x1) + Math.min(m30, m30);
135const right = Math.max(x0, -x0) + Math.max(x1, -x1) + Math.max(m30, m30);
136const top = Math.max(y0, -y0) + Math.max(y1, -y1) + Math.max(m31, m31);
137const bottom = Math.min(y0, -y0) + Math.min(y1, -y1) + Math.min(m31, m31);
138```
139
140The min or max of two copies of the same value will just be that value.
141
142```js
143const left = Math.min(x0, -x0) + Math.min(x1, -x1) + m30;
144const right = Math.max(x0, -x0) + Math.max(x1, -x1) + m30;
145const top = Math.max(y0, -y0) + Math.max(y1, -y1) + m31;
146const bottom = Math.min(y0, -y0) + Math.min(y1, -y1) + m31;
147```
148
149The max of a negative and positive variable will be the absolute value of that variable. The min of a negative and positive variable will the negated absolute value of that variable.
150
151```js
152const left = -Math.abs(x0) + -Math.abs(x1) + m30;
153const right = Math.abs(x0) + Math.abs(x1) + m30;
154const top = Math.abs(y0) + Math.abs(y1) + m31;
155const bottom = -Math.abs(y0) + -Math.abs(y1) + m31;
156```
157
158Pulling out the negations of the absolute values, left and right as well as top and bottom are the positive or negative sum of the absolute value of the saled and rotated unit value.
159
160```js
161const left = -(Math.abs(x0) + Math.abs(x1)) + m30;
162const right = Math.abs(x0) + Math.abs(x1) + m30;
163const top = Math.abs(y0) + Math.abs(y1) + m31;
164const bottom = -(Math.abs(y0) + Math.abs(y1)) + m31;
165```
166
167We call pull out those sums and use them twice.
168
169```js
170const x = Math.abs(x0) + Math.abs(x1);
171const y = Math.abs(y0) + Math.abs(y1);
172
173const left = -x + m30;
174const right = x + m30;
175const top = y + m31;
176const bottom = -y + m31;
177```
178
179This lets us arrive at our goal. Inlining some of our variables we get this block that will initialize a Rectangle to a unit square transformed by a matrix.
180
181```js
182const m30 = m[(3 * 4) + 0];
183const m31 = m[(3 * 4) + 1];
184
185const x = Math.abs(0.5 * m[(0 * 4) + 0]) + Math.abs(0.5 * m[(1 * 4) + 0]);
186const y = Math.abs(0.5 * m[(0 * 4) + 1]) + Math.abs(0.5 * m[(1 * 4) + 1]);
187
188const left = -x + m30;
189const right = x + m30;
190const top = y + m31;
191const bottom = -y + m31;
192```