Straight lines across circles: the geometry inside Apsis

How Apsis works out exactly what a flying arrow hits, why an arrow along its ring catches the one ahead, how close arrows pass the sun, and which arrow you meant.

A close view of the Apsis rings round the sun, one arrow lying along the top of its ring with its band running dead straight to the right as the ring curves away, still touching the next arrow along

Apsis looks round, but almost everything that happens in it is straight. Arrows rest on circles and fly in lines, and the puzzle lives in the gap between the two. This is the geometry the game uses to decide what a flying arrow hits, and which arrow your finger meant.

An arrow is a capsule

To the game, an arrow is a capsule: a straight segment from its tail to its head, plus everything within 11 units of it. Every arrow is 22 units wide, a short one has a segment 44 units long, and a long one 80. The pastel pill on screen is exactly that capsule, so what you see is what collides.

Two capsules touch when the distance between their segments is at most 11 + 11 = 22, which turns every question about arrows into a question about line segments.

The path is a band

A flying arrow slides along its own direction, so its segment sweeps out a ray that starts at its tail. Thicken that ray by 11 on each side and you have every point the arrow passes over: a band exactly as wide as the arrow.

A resting arrow is in the way exactly when its segment comes within 22 units of that ray. That is why holding an arrow draws a dotted line over a faint band: the band is the precise shape of the question.

A held Apsis arrow with its dotted path over a faint band crossing several orbits

Solved exactly, not stepped

Many games move an object a little at a time and check for overlap, which is approximate. Apsis solves each flight exactly.

In two dimensions, unless two segments cross, their closest pair of points always includes an end of one of them. So the first contact is one of four cases: an end of the flying arrow meets the resting arrow’s capsule, or an end of the resting arrow meets the flying arrow’s capsule, seen from the flying arrow. Each case is a ray hitting a capsule, a small equation with an exact answer.

I checked it against the slow way. Over 2,000 random layouts, a brute-force march in steps of 0.01 units found 271 contacts, the exact answer never differed by more than the step, and all 1,566 misses agreed. The tests repeat a smaller version of that check on every run.

Why an arrow along its ring catches the one ahead

Many arrows lie along their ring, and a straight line that starts along a circle leaves it only slowly.

Take a ring of radius rr and a straight path that starts touching it. After travelling tt units, the path is r2+t2\sqrt{r^2 + t^2} from the sun, barely more than rr while tt is small. On a ring of radius 190, after 60 units the path is at 1902+602≈199.2\sqrt{190^2 + 60^2} \approx 199.2, only about 9 units outside the ring and well within an arrow’s reach.

So an arrow lying along its ring usually runs into the next arrow ahead on the same ring, and the level generator uses exactly that when it places a blocker.

An Apsis arrow frozen mid-flight, leaving the system along a straight line with a streak behind it

The closest pass by the sun

From the fourth chapter, some arrows point inward at 45 degrees and fly past the sun on their way out. The sun has a radius of 46, so an arrow clears it only if its centre line stays more than 46 + 11 = 57 units from the sun’s centre. An inward diagonal on the innermost ring, radius 90, passes the centre at 90sin⁡45∘≈63.690 \sin 45^\circ \approx 63.6 units, with 6.6 to spare. Of the 1,156 inward diagonals on one-sun skies in the game today, none passes closer than that.

When the line bends

Later chapters bend flights on purpose: a mirror turns an arrow, a gravity well swings it a quarter turn, a wormhole moves it to its twin, and a comet rides its ring round to a gate before flying out. A bent flight is traced as a route of straight legs, arcs and jumps, with the arrow’s head leading and its body following like a train on a track.

The head reaches every point of the route before the rest of the arrow does, so the arrow first touches something exactly when its head first comes within 22 units of it. On an arc, that moment is where two circles meet, or a circle meets a line, so bends are solved exactly too. And a level is only allowed when every flight in it stays clear of the edges where a bend begins, so no bend ever turns on a hair.

Which arrow did your finger mean?

A touch picks the arrow whose surface is nearest, as long as it is within 26 units. Nearest surface, not nearest centre, matters for long arrows: a touch near the end of an 80-unit arrow is closer to its surface than to a short neighbour’s centre.

How often does a quick tap pick the wrong arrow? I measured it on the current levels, aiming at the middle of every arrow with a random error spread normally by the size shown, 40 touches per arrow.

LevelsAverage gap to the nearest arrowWrong arrow, 12-unit spreadWrong arrow, 20-unit spread
1 to 2064 units0.3%2.3%
21 to 4033 units0.8%4.7%
41 to 6026 units0.8%5.6%
61 to 8024 units1.2%6.3%
81 to 20019 units1.5%7.6%
201 and up18 units1.6%8.4%

The board is about 690 pixels across on a screen 720 pixels wide, so 20 units is roughly 2 mm on a phone, a careful finger. The mistakes follow how close neighbours sit on average, which is set by how many arrows a level has, so widening the small minimum gap between arrows would change little.

The fix is in the gesture instead. A press only lifts an arrow and shows its path, and nothing counts until you let go. While the finger slides, a nearer arrow takes over the hold, but only once it is 4 units nearer than the one already held, so a finger resting halfway between two neighbours does not flick between them. And a touch that starts in open space never picks anything up, so a drag across the board cannot end in a launch.

What this means in play is in Habits that save hearts, and how the levels are built around it is in Built backwards.

On this page