Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/main/java/com/thealgorithms/geometry/LineIntersection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.thealgorithms.geometry;

/**
* Utility class to check if two line segments intersect.
*
* <p>This class provides methods to determine whether two given line segments
* intersect or not, using orientation tests.
*
* <p>Time Complexity: O(1)
*
* @author Sandeep
*/
public final class LineIntersection {

private LineIntersection() {
}

/**
* Represents a point in 2D space.
*/
public static final class Point {
public final double x;
public final double y;

public Point(double x, double y) {
this.x = x;
this.y = y;
}
}

private static int orientation(Point p, Point q, Point r) {
double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
if (val == 0.0) {
return 0; // collinear
}
return (val > 0.0) ? 1 : 2; // clockwise or counterclockwise
}

private static boolean onSegment(Point p, Point q, Point r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x)
&& q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}

/**
* Checks whether two line segments (p1,q1) and (p2,q2) intersect.
*
* @param p1 starting point of first segment
* @param q1 ending point of first segment
* @param p2 starting point of second segment
* @param q2 ending point of second segment
* @return true if the segments intersect, false otherwise
*/
public static boolean doIntersect(Point p1, Point q1, Point p2, Point q2) {
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);

if (o1 != o2 && o3 != o4) {
return true;
}

if (o1 == 0 && onSegment(p1, p2, q1)) return true;
if (o2 == 0 && onSegment(p1, q2, q1)) return true;
if (o3 == 0 && onSegment(p2, p1, q2)) return true;
if (o4 == 0 && onSegment(p2, q1, q2)) return true;

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.thealgorithms.geometry;

import java.util.*;

public final class ClosestPairOfPoints {
private ClosestPairOfPoints() {}

public static double closestPair(List<Point> points) {
List<Point> sortedByX = new ArrayList<>(points);
sortedByX.sort(Comparator.comparingDouble(p -> p.x));
return divide(sortedByX);
}

private static double divide(List<Point> pts) {
int n = pts.size();
if (n <= 3) return bruteForce(pts);

int mid = n / 2;
Point midPoint = pts.get(mid);

double dl = divide(pts.subList(0, mid));
double dr = divide(pts.subList(mid, n));
double d = Math.min(dl, dr);

List<Point> strip = new ArrayList<>();
for (Point p : pts) {
if (Math.abs(p.x - midPoint.x) < d) strip.add(p);
}

strip.sort(Comparator.comparingDouble(p -> p.y));
for (int i = 0; i < strip.size(); ++i) {
for (int j = i + 1; j < strip.size() && (strip.get(j).y - strip.get(i).y) < d; ++j) {
d = Math.min(d, strip.get(i).distance(strip.get(j)));
}
}
return d;
}

private static double bruteForce(List<Point> pts) {
double min = Double.POSITIVE_INFINITY;
for (int i = 0; i < pts.size(); ++i) {
for (int j = i + 1; j < pts.size(); ++j) {
min = Math.min(min, pts.get(i).distance(pts.get(j)));
}
}
return min;
}
}
110 changes: 110 additions & 0 deletions src/main/java/com/thealgorithms/geometry/delaunayTriangulation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.thealgorithms.geometry;

import java.util.*;

/**
* Delaunay Triangulation using the Bowyer–Watson algorithm.
*
* Delaunay triangulation ensures that no point lies inside the circumcircle
* of any triangle in the triangulation.
*
* Time Complexity: O(n^2) average
*
* Reference: https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
*/
public final class DelaunayTriangulation {

private DelaunayTriangulation() {}

public static List<Triangle> triangulate(List<Point> points) {
List<Triangle> triangles = new ArrayList<>();

// 1. Create super triangle large enough to encompass all points
double minX = points.stream().mapToDouble(p -> p.x).min().orElse(0);
double minY = points.stream().mapToDouble(p -> p.y).min().orElse(0);
double maxX = points.stream().mapToDouble(p -> p.x).max().orElse(0);
double maxY = points.stream().mapToDouble(p -> p.y).max().orElse(0);

double dx = maxX - minX;
double dy = maxY - minY;
double deltaMax = Math.max(dx, dy);
double midx = (minX + maxX) / 2;
double midy = (minY + maxY) / 2;

Point p1 = new Point(midx - 20 * deltaMax, midy - deltaMax);
Point p2 = new Point(midx, midy + 20 * deltaMax);
Point p3 = new Point(midx + 20 * deltaMax, midy - deltaMax);
Triangle superTriangle = new Triangle(p1, p2, p3);
triangles.add(superTriangle);

// 2. Add points one by one
for (Point p : points) {
List<Triangle> badTriangles = new ArrayList<>();

for (Triangle t : triangles) {
if (t.isPointInsideCircumcircle(p)) {
badTriangles.add(t);
}
}

List<Edge> polygon = new ArrayList<>();
for (Triangle t : badTriangles) {
for (Edge e : t.getEdges()) {
boolean shared = false;
for (Triangle t2 : badTriangles) {
if (t2 != t && t2.hasEdge(e)) {
shared = true;
break;
}
}
if (!shared) polygon.add(e);
}
}

triangles.removeAll(badTriangles);
for (Edge e : polygon) {
triangles.add(new Triangle(e.p1, e.p2, p));
}
}

// 3. Remove triangles that share vertices with super triangle
triangles.removeIf(t -> t.hasVertex(p1) || t.hasVertex(p2) || t.hasVertex(p3));
return triangles;
}

/** Helper record for representing an Edge. */
public record Edge(Point p1, Point p2) {}

/** Helper class for representing a Triangle. */
public static class Triangle {
final Point a, b, c;

public Triangle(Point a, Point b, Point c) {
this.a = a;
this.b = b;
this.c = c;
}

public boolean hasVertex(Point p) {
return p.equals(a) || p.equals(b) || p.equals(c);
}

public boolean hasEdge(Edge e) {
return hasVertex(e.p1) && hasVertex(e.p2);
}

public List<Edge> getEdges() {
return List.of(new Edge(a, b), new Edge(b, c), new Edge(c, a));
}

public boolean isPointInsideCircumcircle(Point p) {
double ax = a.x - p.x, ay = a.y - p.y;
double bx = b.x - p.x, by = b.y - p.y;
double cx = c.x - p.x, cy = c.y - p.y;
double det = (ax * ax + ay * ay) * (bx * cy - by * cx)
- (bx * bx + by * by) * (ax * cy - ay * cx)
+ (cx * cx + cy * cy) * (ax * by - ay * bx);
return det > 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.thealgorithms.geometry;

/**
* Line Segment Intersection detection using orientation method.
*
* Time Complexity: O(1)
*/
public final class LineSegmentIntersection {
private LineSegmentIntersection() {}

public static boolean doIntersect(Point p1, Point q1, Point p2, Point q2) {
int o1 = Point.orientation(p1, q1, p2);
int o2 = Point.orientation(p1, q1, q2);
int o3 = Point.orientation(p2, q2, p1);
int o4 = Point.orientation(p2, q2, q1);

if (o1 != o2 && o3 != o4) return true;

// Collinear cases
if (o1 == 0 && onSegment(p1, p2, q1)) return true;
if (o2 == 0 && onSegment(p1, q2, q1)) return true;
if (o3 == 0 && onSegment(p2, p1, q2)) return true;
if (o4 == 0 && onSegment(p2, q1, q2)) return true;

return false;
}

private static boolean onSegment(Point p, Point q, Point r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x)
&& q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/thealgorithms/geometry/rotatingCalipers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.thealgorithms.geometry;

import java.util.List;

/**
* Rotating Calipers algorithm to find the farthest pair of points (diameter)
* from a convex polygon.
*
* Time Complexity: O(n)
*
* Reference: https://cp-algorithms.com/geometry/rotating_calipers.html
*/
public final class RotatingCalipers {
private RotatingCalipers() {}

public static double findDiameter(List<Point> points) {
int n = points.size();
if (n < 2) return 0;

int j = 1;
double maxDist = 0;

for (int i = 0; i < n; i++) {
Point nextI = points.get((i + 1) % n);
while (Point.cross(nextI.subtract(points.get(i)), points.get((j + 1) % n).subtract(points.get(j))) > 0) {
j = (j + 1) % n;
}
maxDist = Math.max(maxDist, points.get(i).distance(points.get(j)));
}

return maxDist;
}
}
44 changes: 44 additions & 0 deletions src/main/java/com/thealgorithms/geometry/voronoiDiagram.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.thealgorithms.geometry;

import java.awt.geom.Line2D;
import java.util.*;

/**
* A simplified Voronoi Diagram generator using perpendicular bisectors.
*
* This implementation computes the Voronoi edges between each pair of sites
* by finding their perpendicular bisectors (not a full Fortune’s algorithm).
*
* Time Complexity: O(n^2)
*/
public final class VoronoiDiagram {

private VoronoiDiagram() {}

public static List<Line2D.Double> computeVoronoiEdges(List<Point> points) {
List<Line2D.Double> edges = new ArrayList<>();

for (int i = 0; i < points.size(); i++) {
for (int j = i + 1; j < points.size(); j++) {
Point p1 = points.get(i);
Point p2 = points.get(j);
Point mid = new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);

double dx = p2.x - p1.x;
double dy = p2.y - p1.y;

// Perpendicular slope
double length = Math.sqrt(dx * dx + dy * dy);
double ux = -dy / length;
double uy = dx / length;

// Extend line in both directions
double scale = 1000;
Point start = new Point(mid.x + ux * scale, mid.y + uy * scale);
Point end = new Point(mid.x - ux * scale, mid.y - uy * scale);
edges.add(new Line2D.Double(start.x, start.y, end.x, end.y));
}
}
return edges;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.thealgorithms.geometry;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class LineIntersectionTest {

@Test
void testIntersectingSegments() {
LineIntersection.Point p1 = new LineIntersection.Point(1, 1);
LineIntersection.Point q1 = new LineIntersection.Point(4, 4);
LineIntersection.Point p2 = new LineIntersection.Point(1, 4);
LineIntersection.Point q2 = new LineIntersection.Point(4, 1);
assertTrue(LineIntersection.doIntersect(p1, q1, p2, q2));
}

@Test
void testNonIntersectingSegments() {
LineIntersection.Point p1 = new LineIntersection.Point(1, 1);
LineIntersection.Point q1 = new LineIntersection.Point(2, 2);
LineIntersection.Point p2 = new LineIntersection.Point(3, 3);
LineIntersection.Point q2 = new LineIntersection.Point(4, 4);
assertFalse(LineIntersection.doIntersect(p1, q1, p2, q2));
}

@Test
void testCollinearOverlappingSegments() {
LineIntersection.Point p1 = new LineIntersection.Point(1, 1);
LineIntersection.Point q1 = new LineIntersection.Point(5, 5);
LineIntersection.Point p2 = new LineIntersection.Point(2, 2);
LineIntersection.Point q2 = new LineIntersection.Point(6, 6);
assertTrue(LineIntersection.doIntersect(p1, q1, p2, q2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.thealgorithms.geometry;

import static org.junit.jupiter.api.Assertions.*;
import java.util.Arrays;
import org.junit.jupiter.api.Test;

public class ClosestPairOfPointsTest {
@Test
void testClosestPair() {
var pts = Arrays.asList(
new Point(2, 3), new Point(12, 30),
new Point(40, 50), new Point(5, 1),
new Point(12, 10), new Point(3, 4)
);
double result = ClosestPairOfPoints.closestPair(pts);
assertEquals(Math.sqrt(2), result, 1e-6);
}
}
Loading
Loading