Computational geometry is a field of study that focuses on developing algorithms and data structures for solving problems that involve geometric shapes and structures. The field has applications in a variety of areas, including computer graphics, robotics, geographic information systems, and more.
In this article, we will explore some of the key concepts and applications of computational geometry.
Geometric Problems:
One of the key goals of computational geometry is to find efficient solutions to geometric problems that arise in various fields. Some common geometric problems include:
- The intersection of two lines or planes
- Convex hull of a set of points
- Triangulation of a polygon
- Voronoi diagram of a set of points
- Delaunay triangulation of a set of points
- Point location in a planar subdivision
The solution to these Geometric Problems:
To solve these problems, computational geometry uses a variety of algorithms and data structures, the most commonly used algorithms include:
1) Sweep line algorithm:
A method for solving a wide range of geometric problems, including computing the intersection of two lines or planes and triangulating a polygon. The algorithm works by sweeping a line or plane across the geometry and updating a data structure as it goes.
Use Cases:
- Closest pair of points problem: Finding the closest pair of points among a set of points in a 2D plane.
- Line segment intersection problem: Finding whether two or more line segments intersect in a 2D plane.
- Rectangle intersection problem: Finding whether two or more rectangles intersect in a 2D plane.
- Bentley-Ottmann algorithm: Computing the intersection of line segments in a plane.
Line segment intersection problem using Sweep line algorithm:
C++
// C++ code to implement the sweep // line algorithm #include <bits/stdc++.h> using namespace std; struct Point { double x, y; }; struct Line { Point p, q; }; // Function to check if two // lines intersect bool doIntersect(Line l1, Line l2) { // Implementation of intersection // checking algorithm } // Function to compute the intersection // point of two lines Point computeIntersection(Line l1, Line l2) { double x1 = l1.p.x, y1 = l1.p.y; double x2 = l1.q.x, y2 = l1.q.y; double x3 = l2.p.x, y3 = l2.p.y; double x4 = l2.q.x, y4 = l2.q.y; double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); double num1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); double num2 = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); if (denom == 0) { // Lines are parallel return { INFINITY, INFINITY }; } double ua = num1 / denom; double ub = num2 / denom; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { // Intersection point lies on // both line segments double x = x1 + ua * (x2 - x1); double y = y1 + ua * (y2 - y1); return { x, y }; } // Lines do not intersect return { INFINITY, INFINITY }; } // Driver's code int main() { Line l1 = { { 0, 0 }, { 1, 1 } }; Line l2 = { { 0, 1 }, { 1, 0 } }; if (doIntersect(l1, l2)) { // Function call Point p = computeIntersection(l1, l2); cout << "Intersection point: (" << p.x << ", " << p.y << ")" << endl; } else { cout << "Lines do not intersect" << endl; } return 0; } |
Python
# Python Code to implement the Sweep Line Algorithm class Point: def __init__( self , x, y): self .x = x self .y = y class Line: def __init__( self , p, q): self .p = p self .q = q def doIntersect(l1, l2): # Implementation of intersection checking algorithm pass # You can implement this function as needed def computeIntersection(l1, l2): x1, y1 = l1.p.x, l1.p.y x2, y2 = l1.q.x, l1.q.y x3, y3 = l2.p.x, l2.p.y x4, y4 = l2.q.x, l2.q.y denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) num1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) num2 = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) if denom = = 0 : # Lines are parallel return float ( 'inf' ), float ( 'inf' ) ua = num1 / denom ub = num2 / denom if 0 < = ua < = 1 and 0 < = ub < = 1 : # Intersection point lies on both line segments x = x1 + ua * (x2 - x1) y = y1 + ua * (y2 - y1) return x, y # Lines do not intersect return float ( 'inf' ), float ( 'inf' ) # Driver's code if __name__ = = "__main__" : l1 = Line(Point( 0 , 0 ), Point( 1 , 1 )) l2 = Line(Point( 0 , 1 ), Point( 1 , 0 )) if doIntersect(l1, l2): # Function call p = computeIntersection(l1, l2) print ( "Intersection point: ({}, {})" . format (p[ 0 ], p[ 1 ])) else : print ( "Lines do not intersect" ) |
Lines do not intersect
Limitations of the Sweep line algorithm:
- The sweep line algorithm assumes that the objects being swept are not overlapping in the same event point. If this assumption is violated, the algorithm may fail.
- The algorithm may require sorting and searching operations that have a high computational cost.
2) Graham Scan Algorithm:
- Graham scan is a well-known algorithm in computational geometry that is used to find the convex hull of a given set of points. The algorithm was proposed by Ronald Graham in 1972.
- The basic idea of the algorithm is to start with the point with the lowest y-coordinate (and lowest x-coordinate in case of a tie), and then sort the remaining points in order of the angle they make with the horizontal line passing through the starting point. This can be done using the polar angle of each point with respect to the starting point.
- Once the points are sorted, the algorithm proceeds in a clockwise direction around the points, adding each point to the convex hull if it does not create a counter-clockwise turn with the last two points in the hull. If it does create a counter-clockwise turn, then the last point added to the hull is removed, and the process is repeated until the new point does not create a counter-clockwise turn.
- The Graham scan algorithm has a time complexity of O(n log n), where n is the number of input points. The algorithm is widely used in applications such as computer graphics, geographic information systems, and robotics.
Use Cases:
- Convex hull problem: Finding the convex hull of a set of points in a 2D plane.
- Rotating calipers algorithm: Finding the width and diameter of a convex polygon.
- Maximum distance problem: Finding the maximum distance between any two points in a set of points in a 2D plane.
Let’s see the implementation of Graham’s algorithm for the convex hull problem:
C++
// C++ code to implement the graham // scan algorithm #include <algorithm> #include <iostream> #include <stack> #include <vector> using namespace std; struct Point { int x, y; }; Point p0; // Comparator function for sorting points // in counterclockwise order around p0 int compare( const void * vp1, const void * vp2) { Point* p1 = (Point*)vp1; Point* p2 = (Point*)vp2; int orientation = (p1->y - p0.y) * (p2->x - p1->x) - (p1->x - p0.x) * (p2->y - p1->y); if (orientation == 0) { return (p1->x + p1->y < p2->x + p2->y) ? -1 : 1; } return (orientation < 0) ? -1 : 1; } // Returns the square of the Euclidean // distance between two points int distSq(Point p1, Point p2) { return (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); } // Returns the orientation of three // points (p, q, r) int orientation(Point p, Point q, Point r) { int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); if (val == 0) { return 0; } return (val > 0) ? 1 : 2; } // Computes the convex hull of a set of n // points using the Graham's // Scan algorithm void convexHull(Point points[], int n) { // Find the point with the // lowest y-coordinate int ymin = points[0].y; int min = 0; for ( int i = 1; i < n; i++) { int y = points[i].y; if ((y < ymin) || (ymin == y && points[i].x < points[min].x)) { ymin = points[i].y; min = i; } } // Swap the lowest point with // the first point swap(points[0], points[min]); p0 = points[0]; // Sort the remaining points in // counterclockwise order around p0 qsort (&points[1], n - 1, sizeof (Point), compare); // If two or more points have the same // angle with p0, remove all but // the farthest int m = 1; for ( int i = 1; i < n; i++) { while (i < n - 1 && orientation(p0, points[i], points[i + 1]) == 0) { i++; } points[m] = points[i]; m++; } // If there are less than 3 unique // points, there is no convex hull if (m < 3) { return ; } // Use a stack to keep track of the // vertices of the convex hull stack<Point> hull; hull.push(points[0]); hull.push(points[1]); hull.push(points[2]); // Process the remaining points for ( int i = 3; i < n; i++) { while (hull.size() > 1 && orientation(hull.top(), hull.top(), points[i]) != 2) { hull.pop(); } hull.push(points[i]); } // Print the vertices of the // convex hull while (!hull.empty()) { Point p = hull.top(); cout << "(" << p.x << ", " << p.y << ")" << endl; hull.pop(); } } // Driver's code int main() { // Test the algorithm with a set of points Point points[] = { { 0, 3 }, { 1, 1 }, { 2, 2 }, { 4, 4 }, { 0, 0 }, { 1, 2 }, { 3, 1 }, { 3, 3 } }; int n = sizeof (points) / sizeof (points[0]); cout << "Vertices of the convex hull:" << endl; convexHull(points, n); return 0; } |
Vertices of the convex hull: (0, 3) (0, 0)
Limitations of the Graham scan algorithm:
- The Graham scan algorithm only works for 2D convex hull problems.
- The algorithm has a computational complexity of O(n*logn), which can be a limiting factor for large datasets.
- The algorithm is sensitive to degenerate cases where many points lie in a straight line.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!