In chapter 14, every allowed transformation had a reverse. Now remove that restriction. We need to recognize when a drawing flips, and when an original coordinate can no longer be recovered.
Measure a transformed square
Start with a square one unit wide and one unit high, centered at the origin. Its local coordinates run from −0.5 to 0.5, and its area is 1. Stretch it by 2 horizontally and 0.5 vertically. The rectangle looks different, but its area is still 2×0.5=1.
We already describe a matrix with two columns. Call them a and b:
mat2 M = mat2(a, b);
vec2 world = M * local; // local.x * a + local.y * b
The transformed square has edge vectors a and b. Its four corners are −a/2−b/2, a/2−b/2, a/2+b/2, and −a/2+b/2. Changing the square's center would move this parallelogram without changing its area.
Now use a=(1,0) and b=(1,1). The top leans to the right. The base is still 1 and the perpendicular height is still 1, so the area remains 1. The length of b is √2, but that is the sloping side, not the height. Multiplying column lengths would overestimate the area.
Give the area a sign
For a nonzero a, the vector (−a.y,a.x) points perpendicular to it, to its left in our Y-up world. Divide this vector by length(a), then take its dot product with b. The result is a signed perpendicular height: positive on the left side of a and negative on the right.
Multiplying this height by the base length cancels the division. We get a formula that also works when a is zero:
signedAreaScale = a.x * b.y - a.y * b.x;
This number is the determinant of the 2×2 matrix. GLSL calculates it for us:
float det = determinant(M);
float area = abs(det); // Our original square has area 1.
With GLSL's column indexing, the expanded expression is M[0].x*M[1].y − M[0].y*M[1].x. Keep the order: swapping the two columns changes the sign.
For any flat region transformed by the same linear matrix, the ordinary area is multiplied by abs(det). The sign describes orientation, not a negative physical area. Our centered square is simply a convenient measuring shape.
Read the result
A positive determinant preserves orientation. For a nondegenerate pair, b lies to the left of the directed axis a. A negative determinant reverses orientation; b lies to its right. An asymmetric letter makes this reversal visible even when the square's outline looks unchanged.
Choose Flip X: a=(−1,0), b=(0,1). The determinant is −1. Area stays 1, but the letter is mirrored horizontally. Negative determinant can accompany stretching, shear, and rotation too; it does not mean that a transform is only a simple mirror.
Next flip both axes: a=(−1,0), b=(0,−1). The determinant becomes +1. Two sign changes preserve orientation. Here the drawing has turned through 180°, so it may look upside down without becoming mirrored.
For diagonal scale, the determinant is scale.x*scale.y. Horizontal shear has determinant 1. Rotation has determinant cos²(angle)+sin²(angle)=1 in exact arithmetic. These familiar cases explain why a shape can change dramatically while keeping the same area.
For a chain, determinant(A*B)=determinant(A)*determinant(B). Each step multiplies the previous area scale and orientation sign. This does not make A*B equal to B*A: the two orders in chapter 13 have the same determinant while sending points to different places.
Find the lost coordinate
Move b down until a=(1,0) and b=(1,0). The determinant is zero. Both edges now follow the same line, so the whole square collapses to the segment from (−1,0) to (1,0).
world = local.x * vec2(1.0, 0.0)
+ local.y * vec2(1.0, 0.0);
// world = vec2(local.x + local.y, 0.0)
Two different local points, (0.4,−0.1) and (0.1,0.2), both reach (0.3,0). The sum survives; the separate coordinates do not. A world pixel cannot tell which original point to retrieve. There is no unique inverse to call.
Parallel columns, opposite columns, or a zero column all give zero determinant. If both columns are zero, every point reaches the origin. The image becomes a point instead of a line. For a 2×2 matrix, determinant zero is exactly the mathematical test for non-invertibility.
The thick line and dot in our preview are schematic marks. Their screen width keeps collapse visible; it does not give the collapsed square a nonzero mathematical area.
Guard the lookup
The filled square and letter use inverse sampling. Check the determinant before calculating the inverse. Computing inverse(M) first and hiding its result afterward does not protect an invalid calculation.
float det = determinant(M);
if (abs(det) > 0.0001) {
vec2 local = inverse(M) * p;
// Sample the square and letter here.
}
// Draw the forward edges even when sampling is paused.
The cutoff is a drawing policy for this bounded demonstration. The Near collapse preset uses b=(1,0.00005), giving a nonzero determinant. Its narrow parallelogram is mathematically invertible, but we intentionally pause its fill. The forward outline remains, and the readout distinguishes this state from exact collapse.
A small determinant alone is not a general measure of numerical stability. Uniform scale 0.001 has determinant 0.000001, yet stretches both directions equally. It magnifies absolute coordinate errors on the way back, without the directional imbalance of nearly parallel columns. Conversely, scale=(1000000,0.000001), outside our controls, has determinant 1 and a huge imbalance.
Floating-point subtraction can also lose accuracy when the two products in the determinant nearly cancel. A computed zero may hide a tiny nonzero determinant. The lab uses wider arithmetic for its geometric classification and separately reports the rounded float32 guard reference. Passing this cutoff is not a promise of an accurate inverse. The lab limits each column component to [−2,2], reports its sampling policy, and avoids calling inverse in rejected states. GLSL leaves inverse results undefined for singular or poorly conditioned matrices.
Because the matrix depends only on uniforms, every fragment makes the same branch decision. The fwidth calls used for approximate edge smoothing stay inside that uniform branch. Thin shapes may still be smaller than a pixel; their numerical area is more informative than their visible thickness.