Orientation & the Cross Product
In short: given three points a, b, c in order, the path a -> b -> c
either bends left, bends right, or runs dead straight. The cross product
of vector (b - a) and vector (c - a) answers this with one number: its
sign is the turn direction, and its size is twice the area of the triangle
the three points span. Positive means a left (counter-clockwise) turn,
negative means a right (clockwise) turn, and exactly zero means the three
points are collinear — they lie on one straight line. Every lesson in
this section reduces to asking this one question about three points.

Which way does the van turn?
Meridian's route planner reads three consecutive stops at a time and asks whether the van bends left or right between them:
depot -> warehouse runs straight east; mall sits north of warehouse, so
the van bends left there — orientation returns 1. Swap mall for a stop
south of the line, (2, -3), and the sign flips to -1: the van bends
right. Nothing about the distance between the points matters here, only
which side of the line a -> b the point c lands on.
Spotting a dead-straight stretch of road
A straight stretch is the one case where orientation has to say "neither":
def orientation(a, b, c):
cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])
if cross > 0:
return 1
if cross < 0:
return -1
return 0
print(orientation((-3, -3), (0, 0), (3, 3)))This works perfectly because every coordinate here is a whole number, so the cross product's arithmetic is exact. Real map data rarely is: stop positions usually arrive as floating-point latitude/longitude, and floating point arithmetic does not promise an exact zero even when three points are mathematically dead straight.
Three genuinely collinear points report themselves as NOT collinear. Fix the comparison so it tolerates floating-point rounding.
With floats, "collinear" has to mean "close enough to zero", decided by an epsilon you choose on purpose — never left as a silent default. Too loose an epsilon calls a real turn collinear; too tight and real floating-point noise reads as a turn. Every example and every test in this section uses whole-number coordinates for exactly this reason: the results are exact, and the epsilon problem never has to be papered over to get a clean answer.
Quick practice
a, b, c = (0, 0), (0, 5), (5, 5)
cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])
print(cross)def orientation(a, b, c):
cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])
if cross > 0:
return 1
if cross < 0:
return -1
return 0
print(orientation((2, 2), (2, 2), (2, 2)))🎯 Your turn
Build orientation(a, b, c). Return 1 for a left turn, -1 for a right
turn, 0 if the three points are collinear — using only the sign of the
cross product.
orientation((0, 0), (4, 0), (4, 3))→1orientation((0, 0), (4, 0), (2, -3))→-1orientation((0, 0), (4, 0), (2, 0))→0
Hint: cross = (b[0]-a[0]) * (c[1]-a[1]) - (b[1]-a[1]) * (c[0]-a[0]).
Return 1 if cross > 0, -1 if cross < 0, otherwise 0.
Press ▶ Run, try the Live App chips, then ✓ Check.
