Computational Geometry

Computational geometry deals with geometric objects and spatial relationships. Understanding points, lines, and polygons is essential for solving geometric algorithmic problems.

Basic Geometric Structures

Let's start with fundamental geometric objects and operations:

Point Structure


struct Point {
    double x, y;
    
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    
    Point operator+(const Point& p) const { return {x + p.x, y + p.y}; }
    Point operator-(const Point& p) const { return {x - p.x, y - p.y}; }
    Point operator*(double t) const { return {x * t, y * t}; }
    Point operator/(double t) const { return {x / t, y / t}; }
    
    double dot(const Point& p) const { return x * p.x + y * p.y; }
    double cross(const Point& p) const { return x * p.y - y * p.x; }
    
    double length() const { return sqrt(x * x + y * y); }
    double length2() const { return x * x + y * y; } // Squared length
    
    Point normalize() const { 
        double len = length();
        return len > 0 ? *this / len : Point(0, 0);
    }
    
    Point rotate(double angle) const {
        double c = cos(angle), s = sin(angle);
        return {x * c - y * s, x * s + y * c};
    }
};

const double EPS = 1e-9;

bool equal(double a, double b) {
    return abs(a - b) < EPS;
}
                    

Lines and Segments

Working with lines and line segments:

Line Representation


struct Line {
    Point a, b; // Two points defining the line
    
    Line(Point a, Point b) : a(a), b(b) {}
    
    // Get direction vector
    Point direction() const { return b - a; }
    
    // Check if point lies on line
    bool contains(const Point& p) const {
        return equal((p - a).cross(direction()), 0);
    }
    
    // Distance from point to line
    double distanceToPoint(const Point& p) const {
        return abs((p - a).cross(direction())) / direction().length();
    }
    
    // Project point onto line
    Point project(const Point& p) const {
        Point d = direction();
        double t = (p - a).dot(d) / d.dot(d);
        return a + d * t;
    }
};

// Line from equation ax + by + c = 0
struct LineEquation {
    double a, b, c;
    
    LineEquation(double a, double b, double c) : a(a), b(b), c(c) {}
    
    LineEquation(Point p1, Point p2) {
        a = p2.y - p1.y;
        b = p1.x - p2.x;
        c = p2.x * p1.y - p1.x * p2.y;
    }
    
    double evaluate(Point p) const {
        return a * p.x + b * p.y + c;
    }
    
    bool contains(Point p) const {
        return equal(evaluate(p), 0);
    }
};
                    

Line Intersection


// Find intersection of two lines
pair<bool, Point> lineIntersection(Line l1, Line l2) {
    Point d1 = l1.direction(), d2 = l2.direction();
    double cross = d1.cross(d2);
    
    // Lines are parallel
    if(equal(cross, 0)) {
        return {false, Point()};
    }
    
    Point diff = l2.a - l1.a;
    double t = diff.cross(d2) / cross;
    
    return {true, l1.a + d1 * t};
}

// Check if two segments intersect
bool segmentsIntersect(Point a1, Point b1, Point a2, Point b2) {
    Point d1 = b1 - a1, d2 = b2 - a2;
    Point diff = a2 - a1;
    
    double cross = d1.cross(d2);
    
    if(equal(cross, 0)) {
        // Parallel segments - check if they overlap
        if(!equal(diff.cross(d1), 0)) return false; // Not collinear
        
        double t1 = diff.dot(d1) / d1.dot(d1);
        double t2 = (diff + d2).dot(d1) / d1.dot(d1);
        
        if(t1 > t2) swap(t1, t2);
        return t2 >= 0 && t1 <= 1;
    }
    
    double t1 = diff.cross(d2) / cross;
    double t2 = diff.cross(d1) / cross;
    
    return t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1;
}
                    

Polygon Operations

Working with polygons and calculating their properties:

Polygon Area


// Calculate polygon area using shoelace formula
double polygonArea(vector<Point>& polygon) {
    double area = 0;
    int n = polygon.size();
    
    for(int i = 0; i < n; i++) {
        int j = (i + 1) % n;
        area += polygon[i].cross(polygon[j]);
    }
    
    return abs(area) / 2.0;
}

// Check if polygon is convex
bool isConvex(vector<Point>& polygon) {
    int n = polygon.size();
    if(n < 3) return false;
    
    bool positive = false, negative = false;
    
    for(int i = 0; i < n; i++) {
        Point a = polygon[i];
        Point b = polygon[(i + 1) % n];
        Point c = polygon[(i + 2) % n];
        
        double cross = (b - a).cross(c - b);
        
        if(cross > EPS) positive = true;
        if(cross < -EPS) negative = true;
        
        if(positive && negative) return false;
    }
    
    return true;
}
                    

Point in Polygon


// Check if point is inside polygon (ray casting algorithm)
bool pointInPolygon(Point p, vector<Point>& polygon) {
    int n = polygon.size();
    bool inside = false;
    
    for(int i = 0, j = n - 1; i < n; j = i++) {
        Point pi = polygon[i], pj = polygon[j];
        
        if(((pi.y > p.y) != (pj.y > p.y)) &&
           (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x)) {
            inside = !inside;
        }
    }
    
    return inside;
}

// Winding number algorithm (more robust)
int windingNumber(Point p, vector<Point>& polygon) {
    int wn = 0;
    int n = polygon.size();
    
    for(int i = 0; i < n; i++) {
        Point a = polygon[i];
        Point b = polygon[(i + 1) % n];
        
        if(a.y <= p.y) {
            if(b.y > p.y) { // Upward crossing
                if((b - a).cross(p - a) > 0) wn++;
            }
        } else {
            if(b.y <= p.y) { // Downward crossing
                if((b - a).cross(p - a) < 0) wn--;
            }
        }
    }
    
    return wn;
}
                    

Distance and Projections

Computing distances and projections between geometric objects:


// Distance between two points
double pointDistance(Point a, Point b) {
    return (b - a).length();
}

// Distance from point to segment
double pointToSegmentDistance(Point p, Point a, Point b) {
    Point ap = p - a, ab = b - a;
    
    double t = ap.dot(ab) / ab.dot(ab);
    t = max(0.0, min(1.0, t)); // Clamp to [0, 1]
    
    Point closest = a + ab * t;
    return (p - closest).length();
}

// Distance between two segments
double segmentDistance(Point a1, Point b1, Point a2, Point b2) {
    // Check if segments intersect
    if(segmentsIntersect(a1, b1, a2, b2)) return 0;
    
    // Check all endpoint to segment distances
    double dist = pointToSegmentDistance(a1, a2, b2);
    dist = min(dist, pointToSegmentDistance(b1, a2, b2));
    dist = min(dist, pointToSegmentDistance(a2, a1, b1));
    dist = min(dist, pointToSegmentDistance(b2, a1, b1));
    
    return dist;
}

// Closest pair of points (divide and conquer)
double closestPairRec(vector<Point>& px, vector<Point>& py) {
    int n = px.size();
    if(n <= 3) {
        double minDist = 1e18;
        for(int i = 0; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                minDist = min(minDist, pointDistance(px[i], px[j]));
            }
        }
        return minDist;
    }
    
    int mid = n / 2;
    Point midPoint = px[mid];
    
    vector<Point> pyl, pyr;
    for(Point p : py) {
        if(p.x <= midPoint.x) pyl.push_back(p);
        else pyr.push_back(p);
    }
    
    vector<Point> pxl(px.begin(), px.begin() + mid);
    vector<Point> pxr(px.begin() + mid, px.end());
    
    double dl = closestPairRec(pxl, pyl);
    double dr = closestPairRec(pxr, pyr);
    double d = min(dl, dr);
    
    vector<Point> strip;
    for(Point p : py) {
        if(abs(p.x - midPoint.x) < d) {
            strip.push_back(p);
        }
    }
    
    for(int i = 0; i < strip.size(); i++) {
        for(int j = i + 1; j < strip.size() && (strip[j].y - strip[i].y) < d; j++) {
            d = min(d, pointDistance(strip[i], strip[j]));
        }
    }
    
    return d;
}
                    

Complex Numbers for Geometry

Using complex numbers can simplify many 2D geometric operations:


typedef complex<double> Point2D;

// Rotate point around origin by angle
Point2D rotate(Point2D p, double angle) {
    return p * polar(1.0, angle);
}

// Rotate point around center by angle
Point2D rotateAround(Point2D p, Point2D center, double angle) {
    return center + rotate(p - center, angle);
}

// Cross product using complex numbers
double cross(Point2D a, Point2D b) {
    return imag(conj(a) * b);
}

// Check if points are in counter-clockwise order
bool ccw(Point2D a, Point2D b, Point2D c) {
    return cross(b - a, c - a) > 0;
}

// Line intersection using complex numbers
Point2D lineIntersection(Point2D a1, Point2D b1, Point2D a2, Point2D b2) {
    Point2D d1 = b1 - a1, d2 = b2 - a2;
    Point2D diff = a2 - a1;
    
    double t = cross(diff, d2) / cross(d1, d2);
    return a1 + d1 * t;
}
                    

Geometry Tips

  • Always use appropriate epsilon for floating-point comparisons
  • Complex numbers can simplify rotation and geometric calculations
  • Use integer coordinates when possible to avoid precision issues
  • Cross product is key for many orientation and intersection problems

CodeForces Challenge Problems

Practice computational geometry:

Medium

Basic Geometry

Practice point and line operations in 2D space.

Geometry 2D
Solve Problem