Almost all interview geometry reduces to one primitive: the 2D cross product.
cross(o, a, b) = (a.x − o.x)(b.y − o.y) − (a.y − o.y)(b.x − o.x)
Its sign tells you everything:
> 0 counter-clockwise turn · < 0 clockwise · = 0 collinear
Focus on recognizing:
“Do lines intersect / is it a valid triangle / convex hull?” → orientation via cross product
Core Template (Orientation)
The cross-product sign test — left turn, right turn, collinear — every pattern below is built on it:
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Orientation Test
Does P→Q→R turn left, right, or stay collinear?
Cross product of vectors PQ and QR: sign((Qx-Px)(Ry-Qy) - (Qy-Py)(Rx-Qx)). Positive = counter-clockwise (left), negative = clockwise (right), zero = collinear. This single primitive powers convex hull, segment intersection, and point-in-polygon.
1
# cross product of vectors PQ and QR
2
val = (qx-px)*(ry-qy) - (qy-py)*(rx-qx)
3
val > 0 → counterclockwise turn
4
val < 0 → clockwise turn
5
val == 0 → collinear points
record Point(long x, long y) {}
public long cross(Point o, Point a, Point b) {
return (a.x - o.x) * (b.y - o.y)
- (a.y - o.y) * (b.x - o.x);
}
public boolean onSegment(Point p, Point a, Point b) {
return cross(a, b, p) == 0
&& Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
&& Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y);
}def cross(o, a, b):
return ((a[0] - o[0]) * (b[1] - o[1])
- (a[1] - o[1]) * (b[0] - o[0]))
def on_segment(p, a, b):
return (cross(a, b, p) == 0
and min(a[0], b[0]) <= p[0] <= max(a[0], b[0])
and min(a[1], b[1]) <= p[1] <= max(a[1], b[1]))struct Point { long long x, y; };
long long cross(Point o, Point a, Point b) {
return (a.x - o.x) * (b.y - o.y)
- (a.y - o.y) * (b.x - o.x);
}
bool onSegment(Point p, Point a, Point b) {
return cross(a, b, p) == 0
&& min(a.x, b.x) <= p.x && p.x <= max(a.x, b.x)
&& min(a.y, b.y) <= p.y && p.y <= max(a.y, b.y);
}function cross(o, a, b) {
return (
(a[0] - o[0]) * (b[1] - o[1]) -
(a[1] - o[1]) * (b[0] - o[0])
);
}
function onSegment(p, a, b) {
return (
cross(a, b, p) === 0 &&
Math.min(a[0], b[0]) <= p[0] && p[0] <= Math.max(a[0], b[0]) &&
Math.min(a[1], b[1]) <= p[1] && p[1] <= Math.max(a[1], b[1])
);
}Segment intersection = four orientation checks + collinear on-segment cases.
Pattern 1: Convex Hull (Andrew’s Monotone Chain)
Sort points, then build lower and upper hulls keeping only counter-clockwise turns:
public List<Point> hull(List<Point> pts) {
List<Point> p = new ArrayList<>(pts);
p.sort(Comparator.comparingLong((Point q) -> q.x)
.thenComparingLong(q -> q.y));
int n = p.size();
if (n < 3) return p;
List<Point> h = new ArrayList<>();
for (int i = 0; i < n; i++) {
while (h.size() >= 2
&& cross(h.get(h.size() - 2), h.get(h.size() - 1), p.get(i)) <= 0)
h.remove(h.size() - 1);
h.add(p.get(i));
}
// repeat reversed for upper hull, concatenate, drop duplicate endpoints
return h;
}def hull(pts):
pts = sorted(set(pts))
if len(pts) < 3:
return pts
def half(points):
h = []
for q in points:
while len(h) >= 2 and cross(h[-2], h[-1], q) <= 0:
h.pop()
h.append(q)
return h
return half(pts)[:-1] + half(pts[::-1])[:-1]vector<Point> hull(vector<Point> p) {
sort(p.begin(), p.end(), [](Point a, Point b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
int n = p.size();
if (n < 3) return p;
vector<Point> h;
for (int i = 0; i < n; i++) {
while (h.size() >= 2
&& cross(h[h.size()-2], h[h.size()-1], p[i]) <= 0)
h.pop_back();
h.push_back(p[i]);
}
// repeat reversed for upper hull, concatenate
return h;
}function hull(points) {
const pts = [...new Set(points.map(String))]
.map((s) => s.split(",").map(Number))
.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
if (pts.length < 3) return pts;
const half = (list) => {
const h = [];
for (const q of list) {
while (h.length >= 2 && cross(h.at(-2), h.at(-1), q) <= 0)
h.pop();
h.push(q);
}
return h;
};
return [...half(pts).slice(0, -1), ...half([...pts].reverse()).slice(0, -1)];
}Hull = sort + stack + pop-while-not-a-left-turn.
Pattern 2: Distances Without sqrt
Compare squared distances — dx² + dy² — and only sqrt at the very end (or never):
public long distSq(Point a, Point b) {
long dx = a.x - b.x, dy = a.y - b.y;
return dx * dx + dy * dy;
}def dist_sq(a, b):
return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2long long distSq(Point a, Point b) {
long long dx = a.x - b.x, dy = a.y - b.y;
return dx * dx + dy * dy;
}function distSq(a, b) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
return dx * dx + dy * dy;
}Compare squared, output exact integers, avoid float entirely.
Common Mistakes
Floating-point coordinates.
Use integer/long coordinates wherever possible. Floats break == 0 collinearity checks.
Overflow in cross product.
Coordinates up to 10^9 make each term ~10^18 — needs 64-bit multiplication (long/long long).
Forgetting collinear edge cases.
Segments touching at an endpoint or overlapping collinearly need the explicit onSegment check, not just sign tests.
Complexity
| Operation | Time |
|---|---|
| Orientation | O(1) |
| Segment intersect | O(1) |
| Convex hull | O(n log n) |
Premium Content
Unlock Geometry and all premium lessons with a subscription.
From ₹199.99/year — See plans