Rotating an image sounds like it should work forwards — take each pixel, work out where it lands, put it there. It doesn't: rotated positions are fractional, several source pixels can round to the same destination, and others get nothing, leaving holes.
Every real implementation runs backwards instead. For each output pixel, ask where it came from. Every output gets exactly one answer, and no holes are possible.
Task: write rotate_nearest(image, degrees) returning a rotated grid of the same size.
With the centre at cy = (rows - 1) / 2, cx = (cols - 1) / 2, and θ the angle in radians, for each output pixel (r, c):
dx = c - cx, dy = r - cy.src_c = round(sx + cx), src_r = round(sy + cy).0.degrees is in degrees — convert with math.radians.round().0 — that's the black triangles you see on any naively rotated photo.Note the sign pattern in step 2: it's the rotation matrix for -θ, not θ. Using the forward matrix produces an image rotated the wrong way, which is an easy bug to ship because the output still looks like a rotation.
"Nearest neighbour" is the crudest resampling there is — it just grabs whichever pixel is closest, so diagonal edges come out visibly jagged. Bilinear interpolation on those same fractional coordinates is the usual upgrade.