Jump to content

Check if two polygons overlap

From Rosetta Code
Task
Check if two polygons overlap
You are encouraged to solve this task according to the task description, using any language you may know.

Self-explanatory: given two polygons (as a list of their vertices), check whether they overlap.

Related tasks




Translation of: Go – but using operators instead of functions and representing a polygon as a row of points instead of a [][]REAL
BEGIN # test for overlapping 2D polygons                                     #
      # - based on a translation Go which is a translation of Wren           #

    # In the following a polygon is represented as a row of vertices         #
    # and a vertex ( POINT ) by a pair of x, y coordinates in the plane      #

    MODE POINT      = STRUCT( REAL x,   y   );
    MODE PROJECTION = STRUCT( REAL min, max );
    MODE POLYGON    = FLEX[ 1 : 0 ]POINT;

    PRIO DOT = 3;
    OP   DOT = ( POINT v, other )REAL:
         ( x OF v * x OF other ) + ( y OF v * y OF other );

    # returns the axes of the polygon defined by poly                        #
    OP  AXES = ( POLYGON poly )[]POINT:
        BEGIN
            [ LWB poly : UPB poly ]POINT result;
            FOR i FROM LWB poly TO UPB poly DO
                INT j = IF i = UPB poly THEN LWB poly ELSE i + 1 FI;
                POINT vertex1 = poly[ i ];
                POINT vertex2 = poly[ j ];
                POINT edge    = ( x OF vertex1 - x OF vertex2, y OF vertex1 - y OF vertex2 );
                result[ i ]  := ( - y OF edge, x OF edge )
            OD;
            result
         END # AXES # ;

    # returns the projection of poly onto axis                               #
    PRIO PROJECTONTO = 3;
    OP   PROJECTONTO = ( POLYGON poly, POINT axis )PROJECTION:
         BEGIN
             REAL min := axis DOT poly[ LWB poly ];
             REAL max := min;
             FOR i FROM LWB poly + 1 TO UPB poly DO
                 REAL p = axis DOT poly[ i ];
                 IF   p < min THEN
                     min := p
                 ELIF p > max THEN
                     max := p
                 FI
             OD;
             PROJECTION( min, max )
         END # PROJECTONTO # ;

    PRIO OVERLAPS = 5;
    # returns TRUE if the projections proj1 and proj2 overlap,               #
    #         FALSE otherrwise                                               #
    OP   OVERLAPS = ( PROJECTION proj1, proj2 )BOOL:
         IF   max OF proj1 < min OF proj2 THEN FALSE
         ELIF max OF proj2 < min OF proj1 THEN FALSE
         ELSE                                  TRUE
         FI # OVERLAPS # ;

    # returns TRUE if the ppolygons poly1 and poly2 overlap,                 #
    #         FALSE otherrwise                                               #
    OP   OVERLAPS = ( POLYGON poly1, poly2 )BOOL:
         BEGIN
            []POINT axes1 = AXES poly1, axes2 = AXES poly2;
            BOOL does overlap := TRUE;
            FOR a FROM LWB axes1 TO UPB axes1 WHILE does overlap DO
                does overlap := ( poly1 PROJECTONTO axes1[ a ] )
                       OVERLAPS ( poly2 PROJECTONTO axes1[ a ] )
            OD;
            FOR a FROM LWB axes2 TO UPB axes2 WHILE does overlap DO
                does overlap := ( poly1 PROJECTONTO axes2[ a ] )
                       OVERLAPS ( poly2 PROJECTONTO axes2[ a ] )
            OD;
            does overlap
         END # OVERLAPS # ;

    # returns x as a string without trailing 0 decoimals                     #
    OP TOSTRING = ( REAL x )STRING:
         BEGIN
            STRING v       := fixed( x, -14, 11 );
            INT    end pos := UPB v;
            WHILE IF end pos < LWB v THEN FALSE ELSE v[ end pos ] = "0" FI DO
                end pos -:= 1
            OD;
            IF end pos >= LWB v THEN
                IF v[ end pos ] = "." THEN end pos -:= 1 FI
            FI;
            INT start pos := LWB v;
            WHILE IF start pos > end pos THEN FALSE ELSE v[ start pos ] = " " FI DO
                start pos +:= 1
            OD;
            IF end pos < start pos THEN "0" ELSE v[ start pos : end pos ] FI
         END # TOSTRING # ;

    # returns a string representation of the POINT p                         #
    OP   TOSTRING = ( POINT p )STRING: "( " + TOSTRING x OF p + ", " + TOSTRING y OF p + " )";

    # returns a string representation of the points of p                     #
    OP   TOSTRING = ( POLYGON p )STRING:
         BEGIN
            STRING result    := "(", separator := "";
            FOR i FROM LWB p TO UPB p DO
                result   +:= separator + " " + TOSTRING p[ i ];
                separator := ","
            OD;
            result + " )"
         END # TOSTRING # ;


    BEGIN # test cases                                                       #
        POLYGON poly1 = ( ( 0, 0 ), ( 0, 2 ), ( 1, 4 ), ( 2, 2 ), ( 2, 0 ) )
              , poly2 = ( ( 4, 0 ), ( 4, 2 ), ( 5, 4 ), ( 6, 2 ), ( 6, 0 ) )
              , poly3 = ( ( 1, 0 ), ( 1, 2 ), ( 5, 4 ), ( 9, 2 ), ( 9, 0 ) )
              ;
        []STRING yn = []STRING( "no", "yes" )[ AT 0 ];
        print( ( "poly1 = ", TOSTRING poly1, newline ) );
        print( ( "poly2 = ", TOSTRING poly2, newline ) );
        print( ( "poly3 = ", TOSTRING poly3, newline ) );
        print( ( newline ) );
        print( ( "poly1 and poly2 overlap? ", yn[ ABS ( poly1 OVERLAPS poly2 ) ], newline ) );
        print( ( "poly1 and poly3 overlap? ", yn[ ABS ( poly1 OVERLAPS poly3 ) ], newline ) );
        print( ( "poly2 and poly3 overlap? ", yn[ ABS ( poly2 OVERLAPS poly3 ) ], newline ) )
    END
END
Output:
poly1 = ( ( 0, 0 ), ( 0, 2 ), ( 1, 4 ), ( 2, 2 ), ( 2, 0 ) )
poly2 = ( ( 4, 0 ), ( 4, 2 ), ( 5, 4 ), ( 6, 2 ), ( 6, 0 ) )
poly3 = ( ( 1, 0 ), ( 1, 2 ), ( 5, 4 ), ( 9, 2 ), ( 9, 0 ) )

poly1 and poly2 overlap? no
poly1 and poly3 overlap? yes
poly2 and poly3 overlap? yes

C

Translation of: Wren
#include <stdio.h>
#include <stdbool.h>

typedef struct {
    double x;
    double y;
} Vector2;

typedef struct {
    double min;
    double max;
} Projection;

double dot(Vector2 v1, Vector2 v2) {
    return v1.x * v2.x + v1.y * v2.y;
}

/* In the following a polygon is represented as an array of vertices
   and a vertex by a pair of x, y coordinates in the plane. */

void getAxes(double poly[][2], size_t len, Vector2 axes[len]) {
    int i, j;
    Vector2 vector1, vector2, edge;
    for (i = 0; i < len; ++i) {
        vector1 = (Vector2){poly[i][0], poly[i][1]};
        j = (i + 1 == len) ? 0 : i + 1;
        vector2 = (Vector2){poly[j][0], poly[j][1]};
        edge = (Vector2){vector1.x - vector2.x, vector1.y - vector2.y};
        axes[i].x = -edge.y;
        axes[i].y = edge.x;
    }
}

Projection projectOntoAxis(double poly[][2], size_t len, Vector2 axis) {
    int i;
    Vector2 vector0, vector;
    double min, max, p;
    vector0 = (Vector2){poly[0][0], poly[0][1]};
    min = dot(axis, vector0);
    max = min;
    for (i = 1; i < len; ++i) {
        vector = (Vector2){poly[i][0], poly[i][1]};
        p = dot(axis, vector);
        if (p < min) {
            min = p;
        } else if (p > max) {
            max = p;
        }
    }
    return (Projection){min, max};
}

bool projectionsOverlap(Projection proj1, Projection proj2) {
    if (proj1.max < proj2.min) return false;
    if (proj2.max < proj1.min) return false;
    return true;
}

bool polygonsOverlap(double poly1[][2], double poly2[][2], size_t len1, size_t len2) {
    int i;
    Vector2 axis, axes1[len1], axes2[len2];
    Projection proj1, proj2;
    getAxes(poly1, len1, axes1);
    getAxes(poly2, len2, axes2);
    for (i = 0; i < len1; ++i) {
        axis = axes1[i];
        proj1 = projectOntoAxis(poly1, len1, axis);
        proj2 = projectOntoAxis(poly2, len2, axis);
        if (!projectionsOverlap(proj1, proj2)) return false;
    }
    for (i = 0; i < len2; ++i) {
        axis = axes2[i];
        proj1 = projectOntoAxis(poly1, len1, axis);
        proj2 = projectOntoAxis(poly2, len2, axis);
        if (!projectionsOverlap(proj1, proj2)) return false;
    }
    return true;
}

void printPoly(double poly[][2], size_t len) {
    int i, j;
    printf("{ ");
    for (i = 0; i < len; ++i) {
        printf("{");
        for (j = 0; j < 2; ++j) {
            printf("%g", poly[i][j]);
            if (j == 0) printf(", ");
        }
        printf("}");
        if (i < len-1) printf(", ");
    }
    printf(" }\n");
}

int main() {
    double poly1[][2] = { {0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0} };
    double poly2[][2] = { {4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0} };
    double poly3[][2] = { {1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0} };
    printf("poly1 = ");
    printPoly(poly1, 5);
    printf("poly2 = ");
    printPoly(poly2, 5);
    printf("poly3 = ");
    printPoly(poly3, 5);
    printf("\n");
    printf("poly1 and poly2 overlap? %s\n", polygonsOverlap(poly1, poly2, 5, 5) ? "true" : "false");
    printf("poly1 and poly3 overlap? %s\n", polygonsOverlap(poly1, poly3, 5, 5) ? "true" : "false");
    printf("poly2 and poly3 overlap? %s\n", polygonsOverlap(poly2, poly3, 5, 5) ? "true" : "false");
    return 0;
}
Output:
poly1 = { {0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0} }
poly2 = { {4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0} }
poly3 = { {1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0} }

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true

An implementation of the Separating Axis Theorem algorithm for convex polygons.

#include <iostream>
#include <limits>
#include <string>
#include <vector>

class Point {
public:
	float x;
	float y;
};

class Projection {
public:
	float min;
	float max;

	const bool overlaps(const Projection& other) {
		return ! ( max < other.min || other.max < min );
	}
};

class Vector {
public:
	float x;
	float y;

	const float scalarProduct(const Vector& other) {
		return x * other.x + y * other.y;
	}

	const Vector edgeWith(const Vector& other) {
		return Vector(x - other.x, y - other.y);
	}

	const Vector perpendicular() {
		return Vector(-y, x);
	}

	const std::string to_string() {
		return "(" + std::to_string(x) + ", " + std::to_string(y) + ") ";
	}
};

class Polygon {
public:
	Polygon(const std::vector<Point>& points) {
		computeVertices(points);
		computeAxes();
	}

	const bool overlaps(Polygon& other) {
		std::vector<Vector> allAxes = axes;
		allAxes.insert(allAxes.end(), other.axes.begin(), other.axes.end());

		for ( Vector& axis : allAxes ) {
			Projection projection1 = projectionOnAxis(axis);
			Projection projection2 = other.projectionOnAxis(axis);
			if ( ! projection1.overlaps(projection2) ) {
				return false;
			}
		}

		return true;
	}

	const Projection projectionOnAxis(Vector& axis) {
		float min = std::numeric_limits<float>::infinity();
		float max = -std::numeric_limits<float>::infinity();

		for ( const Vector& vertex : vertices ) {
			double p = axis.scalarProduct(vertex);
			if ( p < min ) {
				min = p;
			}
			if ( p > max ) {
			  max = p;
			}
		}

		return Projection(min, max);
	}

	const std::string to_string() {
		std::string result = "[ ";
		for ( Vector& vertex : vertices ) {
			result += vertex.to_string();
		}

		result += "]";
		return result;
	}

private:
	void computeVertices(const std::vector<Point>& points) {
		for ( const Point& point : points ) {
			vertices.emplace_back(Vector(point.x, point.y));
		}
	}

	void computeAxes() {
		for ( size_t i = 0; i < vertices.size(); i++ ) {
			Vector vertex1 = vertices[i];
			Vector vertex2 = vertices[( i + 1 ) % vertices.size()];
			Vector edge = vertex1.edgeWith(vertex2);
			axes.emplace_back(edge.perpendicular());
		}
	}

	std::vector<Vector> vertices;
	std::vector<Vector> axes;
};

int main() {
	Polygon polygon1(std::vector<Point> { Point(0.0, 0.0), Point(0.0, 2.0), Point(1.0, 4.0),
										  Point(2.0, 2.0), Point(2.0, 0.0) } );

	Polygon polygon2(std::vector<Point> { Point(4.0, 0.0), Point(4.0, 2.0), Point(5.0, 4.0),
					                      Point(6.0, 2.0), Point(6.0, 0.0) } );

	Polygon polygon3(std::vector<Point> { Point(1.0, 0.0), Point(1.0, 2.0), Point(5.0, 4.0),
					                      Point(9.0, 2.0), Point(9.0, 0.0) } );

	std::cout << "polygon1: " << polygon1.to_string() << std::endl;
	std::cout << "polygon2: " << polygon2.to_string() << std::endl;
	std::cout << "polygon3: " << polygon3.to_string() << std::endl;
	std::cout << std::boolalpha << std::endl;
	std::cout << "polygon1 and polygon2 overlap? " << polygon1.overlaps(polygon2) << std::endl;
	std::cout << "polygon1 and polygon3 overlap? " << polygon1.overlaps(polygon3) << std::endl;
	std::cout << "polygon2 and polygon3 overlap? " << polygon2.overlaps(polygon3) << std::endl;
}
Output:
polygon1: [ (0.000000, 0.000000) (0.000000, 2.000000) (1.000000, 4.000000) (2.000000, 2.000000) (2.000000, 0.000000) ]
polygon2: [ (4.000000, 0.000000) (4.000000, 2.000000) (5.000000, 4.000000) (6.000000, 2.000000) (6.000000, 0.000000) ]
polygon3: [ (1.000000, 0.000000) (1.000000, 2.000000) (5.000000, 4.000000) (9.000000, 2.000000) (9.000000, 0.000000) ]

polygon1 and polygon2 overlap? false
polygon1 and polygon3 overlap? true
polygon2 and polygon3 overlap? true

Run it

Translation of: Go
func dot a[] b[] .
   return a[1] * b[1] + a[2] * b[2]
.
proc addAxes &poly[][] &r[][] .
   for i to len poly[][]
      v1[] = poly[i][]
      j = (i + 1) mod1 len poly[][]
      v2[] = poly[j][]
      r[][] &= [ -(v1[2] - v2[2]) v1[1] - v2[1] ]
   .
.
proc projectAxis &poly[][] &axis[] &min &max .
   min = 1 / 0
   max = -1 / 0
   for p[] in poly[][]
      p = dot axis[] p[]
      min = lower min p
      max = higher max p
   .
.
func polyOverlap &poly1[][] &poly2[][] .
   axes[][] = [ ]
   addAxes poly1[][] axes[][]
   addAxes poly2[][] axes[][]
   for ax[] in axes[][]
      projectAxis poly1[][] ax[] min1 max1
      projectAxis poly2[][] ax[] min2 max2
      if max1 < min2 or max2 < min1 : return 0
   .
   return 1
.
coord_translate 5 5
coord_scale 10
glinewidth 0.05
proc polyDraw &poly[][] col .
   gcolor col
   gpenup
   for p[] in poly[][]
      glineto p[1] p[2]
   .
   glineto poly[1][1] poly[1][2]
.
proc rectToPoly &r[] &p[][] .
   p[][] = [ [ r[1] r[2] ] [ r[1] + r[3] r[2] ] [ r[1] + r[3] r[2] + r[4] ] [ r[1] r[2] + r[4] ] ]
.
poly1[][] = [ [ 0 0 ] [ 0 2 ] [ 1 4 ] [ 2 2 ] [ 2 0 ] ]
poly2[][] = [ [ 4 0 ] [ 4 2 ] [ 5 4 ] [ 6 2 ] [ 6 0 ] ]
poly3[][] = [ [ 1 0 ] [ 1 2 ] [ 5 4 ] [ 9 2 ] [ 9 0 ] ]
# 
polyDraw poly1[][] 900
polyDraw poly2[][] 090
polyDraw poly3[][] 009
# 
print polyOverlap poly1[][] poly2[][]
print polyOverlap poly1[][] poly3[][]
print polyOverlap poly2[][] poly3[][]
Output:
0
1
1

This is a generalisation of the Fortran solution of the "Determine if two triangles overlap" task. That task was limited to 2 triangles, here the 2 shapes to check can be any number of points. (It is just the main program that calls the overlapCheck subroutine with pentagonal shapes only)

! Check if two polygons overlap
!
! tested with Intel ifx (IFX) 2025.2.1 20250806             on Kubuntu 25.10
!             GNU gfortran (Ubuntu 15.2.0-4ubuntu4)  15.2.0 on Kubuntu 25.10
!             VSI Fortran x86-64 V8.7-001                   on OpenVMS V9.2-3
! U.B., March 2026
!
program Polygon_overlap
implicit none

type :: Point
    real :: x, y
end type Point

type(Point), dimension(:), allocatable :: Polygon1, Polygon2, Polygon3

! VSI Fortran V8.7 is still Fortran 95 standard only and cannot handle implicit allocation by assignment.
! These explicit allocate statements would not be necessary when using Fortran 2003, e.g. current 
! gfortran 15.2.0 and ifx 2025.2.1
!
allocate (Polygon1(5))
allocate (polygon2(5))
allocate (polygon3(5))

Polygon1 = [Point (0.0, 0.0), Point(0.0, 2.0), Point(1.0, 4.0), Point(2.0, 2.0), Point(2.0, 0.0) ]
Polygon2 = [Point (4.0, 0.0), Point(4.0, 2.0), Point(5.0, 4.0), Point(6.0, 2.0), Point(6.0, 0.0) ]
Polygon3 = [Point (1.0, 0.0), Point(1.0, 2.0), Point(5.0, 4.0), Point(9.0, 2.0), Point(9.0, 0.0) ]

! Print the input data
write (*, '("Polygon1 = ", 4 ("(",F3.1, ",",F3.1,"), "), "(",F3.1, ",",F3.1,")" )')   Polygon1
write (*, '("Polygon2 = ", 4 ("(",F3.1, ",",F3.1,"), "), "(",F3.1, ",",F3.1,")" )')   Polygon2
write (*, '("Polygon3 = ", 4 ("(",F3.1, ",",F3.1,"), "), "(",F3.1, ",",F3.1,")" )')   Polygon3
write (*,*)

! ===================================================================================
! Attention: overlapCheck () does not check if the polygons represent a convex shape!
! ===================================================================================
!
! the overlapCheck subroutine prints the result.
call overlapCheck (Polygon1, 'Polygon1', size(Polygon1), Polygon2, 'Polygon2', size(Polygon2))
call overlapCheck (Polygon1, 'Polygon1', size(Polygon1), Polygon3, 'Polygon3', size(Polygon3))
call overlapCheck (Polygon2, 'Polygon2', size(Polygon2), Polygon3, 'Polygon3', size(Polygon3))


contains

subroutine overlapCheck (PolA, nameA, sizPolA,  PolB, nameB, sizPolB) 

implicit none

integer, intent(in) :: sizPolA, sizPolB
type(Point), intent(in)  :: polA(sizPolA), polB(sizPolB)
character(len=*) :: nameA, nameB

logical :: overlaps
integer :: ii

write (*,'(A, " and ", A)', advance='no') nameA, nameB

overlaps = check_overlap(PolA, sizPolA,  PolB, sizPolB)

if (overlaps) then
  write (*,'(A)') "        Overlap"
else
  write (*,'(A)') " do not Overlap"
end if

end subroutine overlapCheck



logical function check_overlap(a, sizeA,  b, sizeB)

implicit none
integer, intent(in) :: sizeA, sizeB

type(Point), intent(in) :: a(sizeA), b(sizeB)
type(Point) :: axes(sizeA+sizeB)
integer :: i
real :: minA, maxA, minB, maxB

! 1. GENERATE SEARCH AXES
! According to SAT, if two convex shapes are separated, 
! they must be separated by an axis perpendicular to one of their edges.        

axes(1:sizeA) = get_normals(a, sizeA)
axes(sizeA+1:sizeA+SizeB ) = get_normals(b, sizeB)

! Assume they overlap until proven otherwise
check_overlap = .true.

! 2. TEST EACH AXIS
do i=1, sizeA+sizeB
  call project(a, sizeA, axes(i), minA, maxA)
  call project(b, sizeB, axes(i), minB, maxB)

  ! 3. CHECK FOR GAP
  ! If the intervals (shadows) do not overlap, we found a "separating axis".
  ! If even one such axis exists, the triangles cannot be touching.
  if (maxA < minB .or. maxB < minA) then
     check_overlap = .false.    ! No overlap
      return                    ! Exit early: No need to check other axes
  end if
end do
end function check_overlap

function get_normals(p, sizeP) result(n)

implicit none
integer, intent(in) :: sizeP
type(Point), intent(in) :: p(sizeP)
type(Point) :: n(sizeP)
integer :: i, j
real :: dx, dy
do i = 1, sizeP
    j = mod(i, sizeP) + 1

    ! Vector representing the edge between two vertices
    dx = p(j)%x - p(i)%x
    dy = p(j)%y - p(i)%y

    ! The normal vector (perpendicular) is (-dy, dx)
    n(i) = Point(-dy, dx) 
end do
end function get_normals

subroutine project(p, sizeP, axis, minP, maxP)

implicit none
integer, intent(in) :: sizeP
type(Point), intent(in) :: p(sizeP), axis
real, intent(out) :: minP, maxP
real :: dot_val
integer :: i

! Project the first vertex using the Dot Product: (A.x * B.x) + (A.y * B.y)
minP = (p(1)%x * axis%x) + (p(1)%y * axis%y)
maxP = minP

! Project remaining vertices and find the min/max bounds of the shadow
do i = 2, 3
  dot_val = (p(i)%x * axis%x) + (p(i)%y * axis%y)
  if (dot_val < minP) minP = dot_val
  if (dot_val > maxP) maxP = dot_val
end do
end subroutine project

end program Polygon_overlap
Output:
Polygon1 = (0.0,0.0), (0.0,2.0), (1.0,4.0), (2.0,2.0), (2.0,0.0)
Polygon2 = (4.0,0.0), (4.0,2.0), (5.0,4.0), (6.0,2.0), (6.0,0.0)
Polygon3 = (1.0,0.0), (1.0,2.0), (5.0,4.0), (9.0,2.0), (9.0,0.0)
 
Polygon1 and Polygon2 do not Overlap
Polygon1 and Polygon3        Overlap
Polygon2 and Polygon3        Overlap
Translation of: C
Type Vector2
    x As Double
    y As Double
End Type

Type Projection
    min As Double
    max As Double
End Type

Function dot(v1 As Vector2, v2 As Vector2) As Double
    Return v1.x * v2.x + v1.y * v2.y
End Function

Sub getAxes(poly() As Vector2, axes() As Vector2)
    Dim As Integer i, j
    Dim As Vector2 vector1, vector2, edge
    
    For i = 0 To Ubound(poly)
        vector1 = poly(i)
        j = Iif((i + 1 = Ubound(poly)+1), 0, i + 1)
        vector2 = poly(j)
        edge.x = vector1.x - vector2.x
        edge.y = vector1.y - vector2.y
        axes(i).x = -edge.y
        axes(i).y = edge.x
    Next i
End Sub

Function projectOntoAxis(poly() As Vector2, axis As Vector2) As Projection
    Dim As Vector2 vector
    Dim As Double min, max, p
    vector = poly(0)
    min = dot(axis, vector)
    max = min
    
    For i As Integer = 1 To Ubound(poly)
        vector = poly(i)
        p = dot(axis, vector)
        If p < min Then
            min = p
        Elseif p > max Then
            max = p
        End If
    Next i
    Return Type<Projection>(min, max)
End Function

Function projectionsOverlap(proj1 As Projection, proj2 As Projection) As Boolean
    If proj1.max < proj2.min Then Return False
    If proj2.max < proj1.min Then Return False
    Return True
End Function

Function polygonsOverlap(poly1() As Vector2, poly2() As Vector2) As Boolean
    Dim As Integer i
    Dim As Vector2 axis
    Dim As Projection proj1, proj2
    Dim As Vector2 axes1(Ubound(poly1)), axes2(Ubound(poly2))
    
    getAxes(poly1(), axes1())
    getAxes(poly2(), axes2())
    For i = 0 To Ubound(poly1)
        axis = axes1(i)
        proj1 = projectOntoAxis(poly1(), axis)
        proj2 = projectOntoAxis(poly2(), axis)
        If projectionsOverlap(proj1, proj2) = 0 Then Return False
    Next i
    For i = 0 To Ubound(poly2)
        axis = axes2(i)
        proj1 = projectOntoAxis(poly1(), axis)
        proj2 = projectOntoAxis(poly2(), axis)
        If projectionsOverlap(proj1, proj2) = 0 Then Return False
    Next i
    Return True
End Function

Sub printPoly(poly() As Vector2)
    Print "{";
    For i As Integer = 0 To Ubound(poly)
        Print "{" & poly(i).x & ", " & poly(i).y & "}";
        If i < Ubound(poly) Then Print ", ";
    Next i
    Print "}"
End Sub

Dim As Vector2 poly1(4)
poly1(0).x = 0: poly1(0).y = 0
poly1(1).x = 0: poly1(1).y = 2
poly1(2).x = 1: poly1(2).y = 4
poly1(3).x = 2: poly1(3).y = 2
poly1(4).x = 2: poly1(4).y = 0
Dim As Vector2 poly2(4)
poly2(0).x = 4: poly2(0).y = 0
poly2(1).x = 4: poly2(1).y = 2
poly2(2).x = 5: poly2(2).y = 4
poly2(3).x = 6: poly2(3).y = 2
poly2(4).x = 6: poly2(4).y = 0
Dim As Vector2 poly3(4)
poly3(0).x = 1: poly3(0).y = 0
poly3(1).x = 1: poly3(1).y = 2
poly3(2).x = 5: poly3(2).y = 4
poly3(3).x = 9: poly3(3).y = 2
poly3(4).x = 9: poly3(4).y = 0

Print "poly1 = ";
printPoly(poly1())
Print "poly2 = ";
printPoly(poly2())
Print "poly3 = ";
printPoly(poly3())
Print
Print "poly1 and poly2 overlap? "; Iif(polygonsOverlap(poly1(), poly2()), "true", "false")
Print "poly1 and poly3 overlap? "; Iif(polygonsOverlap(poly1(), poly3()), "true", "false")
Print "poly2 and poly3 overlap? "; Iif(polygonsOverlap(poly2(), poly3()), "true", "false")

Sleep
Output:
poly1 = {{0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0}}
poly2 = {{4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0}}
poly3 = {{1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0}}

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true
Translation of: Wren
package main

import "fmt"

type Vector2 struct {
    x, y float64
}

type Projection struct {
    min, max float64
}

func (v Vector2) dot(other Vector2) float64 {
    return v.x*other.x + v.y*other.y
}

/* In the following a polygon is represented as a slice of vertices
   and a vertex by a pair of x, y coordinates in the plane. */

func getAxes(poly [][2]float64) []Vector2 {
    axes := make([]Vector2, len(poly))
    for i := 0; i < len(poly); i++ {
        vertex1 := poly[i]
        j := i + 1
        if i+1 == len(poly) {
            j = 0
        }
        vertex2 := poly[j]
        vector1 := Vector2{vertex1[0], vertex1[1]}
        vector2 := Vector2{vertex2[0], vertex2[1]}
        edge := Vector2{vector1.x - vector2.x, vector1.y - vector2.y}
        axes[i] = Vector2{-edge.y, edge.x}
    }
    return axes
}

func projectOntoAxis(poly [][2]float64, axis Vector2) Projection {
    vertex0 := poly[0]
    vector0 := Vector2{vertex0[0], vertex0[1]}
    min := axis.dot(vector0)
    max := min
    for i := 1; i < len(poly); i++ {
        vertex := poly[i]
        vector := Vector2{vertex[0], vertex[1]}
        p := axis.dot(vector)
        if p < min {
            min = p
        } else if p > max {
            max = p
        }
    }
    return Projection{min, max}
}

func projectionsOverlap(proj1, proj2 Projection) bool {
    if proj1.max < proj2.min {
        return false
    }
    if proj2.max < proj1.min {
        return false
    }
    return true
}

func polygonsOverlap(poly1, poly2 [][2]float64) bool {
    axes1 := getAxes(poly1)
    axes2 := getAxes(poly2)
    for _, axes := range [][]Vector2{axes1, axes2} {
        for _, axis := range axes {
            proj1 := projectOntoAxis(poly1, axis)
            proj2 := projectOntoAxis(poly2, axis)
            if !projectionsOverlap(proj1, proj2) {
                return false
            }
        }
    }
    return true
}

func main() {
    poly1 := [][2]float64{{0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0}}
    poly2 := [][2]float64{{4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0}}
    poly3 := [][2]float64{{1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0}}
    fmt.Println("poly1 = ", poly1)
    fmt.Println("poly2 = ", poly2)
    fmt.Println("poly3 = ", poly3)
    fmt.Println()
    fmt.Println("poly1 and poly2 overlap?", polygonsOverlap(poly1, poly2))
    fmt.Println("poly1 and poly3 overlap?", polygonsOverlap(poly1, poly3))
    fmt.Println("poly2 and poly3 overlap?", polygonsOverlap(poly2, poly3))
}
Output:
poly1 =  [[0 0] [0 2] [1 4] [2 2] [2 0]]
poly2 =  [[4 0] [4 2] [5 4] [6 2] [6 0]]
poly3 =  [[1 0] [1 2] [5 4] [9 2] [9 0]]

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true
An implementation of the Separating Axis Theorem algorithm for convex polygons.
import java.util.ArrayList;
import java.util.List;

public final class CheckIfTwoPolygonsOverlap {

	public static void main(String[] args) {
		Polygon polygon1 = new Polygon(List.of( new Point(0.0, 0.0), new Point(0.0, 2.0), new Point(1.0, 4.0),
				                                new Point(2.0, 2.0), new Point(2.0, 0.0) ));
				
		Polygon polygon2 = new Polygon(List.of( new Point(4.0, 0.0), new Point(4.0, 2.0), new Point(5.0, 4.0),
				                                new Point(6.0, 2.0), new Point(6.0, 0.0) ));
		
		Polygon polygon3 = new Polygon(List.of( new Point(1.0, 0.0), new Point(1.0, 2.0), new Point(5.0, 4.0),
				                                new Point(9.0, 2.0), new Point(9.0, 0.0) ));
		
		System.out.println("polygon1 = " + polygon1);
		System.out.println("polygon2 = " + polygon2);
		System.out.println("polygon3 = " + polygon3);
		System.out.println();
		System.out.println("polygon1 and polygon2 overlap? " + polygon1.overlaps(polygon2));
		System.out.println("polygon1 and polygon3 overlap? " + polygon1.overlaps(polygon3));
		System.out.println("polygon2 and polygon3 overlap? " + polygon2.overlaps(polygon3));
	}
	
	private static class Polygon {
		
		public Polygon(List<Point> points) {
			vertices = points.stream().map( point -> new Vector(point.x, point.y) ).toList();
			computeAxes();
		}
		
		public boolean overlaps(Polygon other) {
			List<Vector> allAxes = new ArrayList<Vector>(axes);
			allAxes.addAll(other.axes);
	
			for ( Vector axis : allAxes ) {
		        Projection projection1 = projectionOnAxis(axis); 
		        Projection projection2 = other.projectionOnAxis(axis);
		        if ( ! projection1.overlaps(projection2) ) {
		        	return false;
		        }
			}
			
			return true;
	    }
		
		public Projection projectionOnAxis(Vector axis) {
			double min = Double.POSITIVE_INFINITY;
			double max = Double.NEGATIVE_INFINITY;
		
			for ( Vector vertex : vertices ) {
			    double p = axis.scalarProduct(vertex);
			    if ( p < min ) {
			        min = p;
			    }
			    if ( p > max ) {
			      max = p;
			    }
			}
			
			return new Projection(min, max);
	    }
		
		public String toString() {
			StringBuilder result = new StringBuilder("[ ");
			for ( Vector vertex : vertices ) {
				result.append(vertex);
			}
			
			result.append("]");
			return result.toString();			
		}
		
		private void computeAxes() {
	        axes = new ArrayList<Vector>();
            for ( int i = 0; i < vertices.size(); i++ ) {
                Vector vertex1 = vertices.get(i);
                Vector vertex2 = vertices.get(( i + 1 ) % vertices.size());
                Vector edge = vertex1.edgeWith(vertex2);
                axes.addLast(edge.perpendicular());
            }
        }
		
		private List<Vector> vertices;
	    private List<Vector> axes;
		
	}
	
	final record Vector(double x, double y) {			
		
	    public double scalarProduct(Vector other) {
	    	return x * other.x + y * other.y;
	    }
	    
	    public Vector edgeWith(Vector other) {
	    	return new Vector(x - other.x, y - other.y);
	    }
	    
	    public Vector perpendicular() {
	    	return new Vector(-y, x);
	    }
	    
	    public String toString() {
			return "(" + x + ", " + y + ") ";
		}
	    		
	}

	final record Projection(double min, double max) {
		
		public boolean overlaps(Projection other) {
			return ! ( max < other.min || other.max < min );
		}

	}

	final record Point(double x, double y) { }

}
Output:
polygon1 = [ (0.0, 0.0) (0.0, 2.0) (1.0, 4.0) (2.0, 2.0) (2.0, 0.0) ]
polygon2 = [ (4.0, 0.0) (4.0, 2.0) (5.0, 4.0) (6.0, 2.0) (6.0, 0.0) ]
polygon3 = [ (1.0, 0.0) (1.0, 2.0) (5.0, 4.0) (9.0, 2.0) (9.0, 0.0) ]

polygon1 and polygon2 overlap? false
polygon1 and polygon3 overlap? true
polygon2 and polygon3 overlap? true
Works with: NodeJS 16.14.2
Translation of: Java
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

class Vector {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    scalarProduct(other) {
        return this.x * other.x + this.y * other.y;
    }

    edgeWith(other) {
        return new Vector(this.x - other.x, this.y - other.y);
    }

    perpendicular() {
        return new Vector(-this.y, this.x);
    }

    toString() {
        return `(${this.x}, ${this.y}) `;
    }
}

class Projection {
    constructor(min, max) {
        this.min = min;
        this.max = max;
    }

    overlaps(other) {
        return !(this.max < other.min || other.max < this.min);
    }
}

class Polygon {
    constructor(points) {
        this.vertices = points.map(point => new Vector(point.x, point.y));
        this.axes = [];
        this.computeAxes();
    }

    overlaps(other) {
        const allAxes = [...this.axes, ...other.axes];

        for (const axis of allAxes) {
            const projection1 = this.projectionOnAxis(axis);
            const projection2 = other.projectionOnAxis(axis);
            if (!projection1.overlaps(projection2)) {
                return false;
            }
        }

        return true;
    }

    projectionOnAxis(axis) {
        let min = Infinity;
        let max = -Infinity;

        for (const vertex of this.vertices) {
            const p = axis.scalarProduct(vertex);
            if (p < min) {
                min = p;
            }
            if (p > max) {
                max = p;
            }
        }

        return new Projection(min, max);
    }

    computeAxes() {
        this.axes = [];
        for (let i = 0; i < this.vertices.length; i++) {
            const vertex1 = this.vertices[i];
            const vertex2 = this.vertices[(i + 1) % this.vertices.length];
            const edge = vertex1.edgeWith(vertex2);
            this.axes.push(edge.perpendicular());
        }
    }

    toString() {
        let result = "[ ";
        for (const vertex of this.vertices) {
            result += vertex.toString();
        }
        result += "]";
        return result;
    }
}

// Example usage
const polygon1 = new Polygon([
    new Point(0.0, 0.0),
    new Point(0.0, 2.0),
    new Point(1.0, 4.0),
    new Point(2.0, 2.0),
    new Point(2.0, 0.0)
]);

const polygon2 = new Polygon([
    new Point(4.0, 0.0),
    new Point(4.0, 2.0),
    new Point(5.0, 4.0),
    new Point(6.0, 2.0),
    new Point(6.0, 0.0)
]);

const polygon3 = new Polygon([
    new Point(1.0, 0.0),
    new Point(1.0, 2.0),
    new Point(5.0, 4.0),
    new Point(9.0, 2.0),
    new Point(9.0, 0.0)
]);

console.log("polygon1 = " + polygon1);
console.log("polygon2 = " + polygon2);
console.log("polygon3 = " + polygon3);
console.log();

console.log("polygon1 and polygon2 overlap? " + polygon1.overlaps(polygon2));
console.log("polygon1 and polygon3 overlap? " + polygon1.overlaps(polygon3));
console.log("polygon2 and polygon3 overlap? " + polygon2.overlaps(polygon3));
Output:
polygon1 = [ (0, 0) (0, 2) (1, 4) (2, 2) (2, 0) ]
polygon2 = [ (4, 0) (4, 2) (5, 4) (6, 2) (6, 0) ]
polygon3 = [ (1, 0) (1, 2) (5, 4) (9, 2) (9, 0) ]

polygon1 and polygon2 overlap? false
polygon1 and polygon3 overlap? true
polygon2 and polygon3 overlap? true



Adapted from Wren (2D convex polygons)

Works with: jq

Also works with gojq, the Go implementation of jq

In the following:

  • a vertex is represented by a pair of x, y coordinates in the plane;
  • a polygon is represented as a list of vertices;
  • a projection is represented by a JSON object {min, max}
# Input: [$A, $B] where $A and $B are points
# Output: the vector $B - $A
def AB:
  . as [$A, $B]
  | [ $B[0] - $A[0],  $B[1] - $A[1]];

# Input: a vector
# Output: perpendicular
def perp: [- .[1], .[0]];

# dot product of this and $v, assumed to be of the same dimension
def dot($v):
  . as $this
  | reduce range(0; $this|length) as $i (0; . + ($this[$i] * $v[$i] ));
  
def getAxes:
  . as $poly
  | reduce range(0; $poly|length) as $i ([];
      $poly[$i] as $vertex1
      | $poly[if $i+1 == ($poly|length) then 0 else $i+1 end] as $vertex2
      | . + [ [$vertex1, $vertex2] | AB | perp] );

# emit {min, max}
def projectOntoAxis($axis):
  . as $poly
  | { max: - infinite, min: infinite }
  | reduce range(0; $poly|length) as $i (.;
       ($axis | dot( $poly[$i] )) as $p
       | if $p < .min then .min = $p else . end
       | if $p > .max then .max = $p else . end ) ;

def projectionsOverlap($proj1; $proj2):
  if   $proj1.max < $proj2.min then false
  elif $proj2.max < $proj1.min then false
  else true
  end;

# If there's an axis for which the projections do not overlap, then false; else true
def polygonsOverlap($poly1; $poly2):
  any( $poly1, $poly2 | getAxes[];
    . as $axis
    | ($poly1 | projectOntoAxis($axis)) as $proj1
    | ($poly2 | projectOntoAxis($axis)) as $proj2
    | projectionsOverlap($proj1; $proj2) | not)
  | not;    

def poly1: [[0, 0], [0, 2], [1, 4], [2, 2], [2, 0]];
def poly2: [[4, 0], [4, 2], [5, 4], [6, 2], [6, 0]];
def poly3: [[1, 0], [1, 2], [5, 4], [9, 2], [9, 0]];

def task:
  "poly1 = \(poly1)",
  "poly2 = \(poly2)",
  "poly3 = \(poly3)",
  "",
  "poly1 and poly2 overlap? \(polygonsOverlap(poly1; poly2))",
  "poly1 and poly3 overlap? \(polygonsOverlap(poly1; poly3))",
  "poly2 and poly3 overlap? \(polygonsOverlap(poly2; poly3))"
  ;

task
Output:

As for #Wren

using Plots
using Polyhedra
using GLPK

lib = DefaultLibrary{Float64}(GLPK.Optimizer)

const poly1 = polyhedron(vrep([
    0 0
    0 2
    1 4
    2 2
    2 0
]), lib)

const poly2 = polyhedron(vrep([
    4 0
    4 2
    5 4
    6 2
    6 0
]), lib)

const poly3 = polyhedron(vrep([
    1 0
    1 2
    5 4
    9 2
    9 0
]), lib)

println("Polygons poly1 and poly2 intersect at ", npoints(intersect(poly1, poly2)), " points.")
println("Polygons poly1 and poly3 intersect at ", npoints(intersect(poly1, poly3)), " points.")
println("Polygons poly2 and poly3 intersect at ", npoints(intersect(poly2, poly3)), " points.")
const P1 = polyhedron(vrep([
    -1.9 -1.7
    -1.8  0.5
     1.7  0.7
     1.9 -0.3
     0.9 -1.1
]), lib)

const P2 = polyhedron(vrep([
   -2.5 -1.1
   -0.8  0.8
    0.1  0.9
    1.8 -1.2
    1.3  0.1
]), lib)

Pint = intersect(P1, P2)
println("Polygons P1 and P2 intersect at ", npoints(Pint), " points.")

plot(P1, color="blue", alpha=0.2)      
plot!(P2, color="red", alpha=0.2)      
plot!(Pint, color="yellow", alpha=0.6)
Output:
Polygons poly1 and poly2 intersect at 0 points.
Polygons poly1 and poly3 intersect at 5 points.
Polygons poly2 and poly3 intersect at 5 points.
Polygons P1 and P2 intersect at 8 points.



Translation of: Python
import kotlin.math.*

data class Vector2(val x: Double, val y: Double) {
    fun dot(other: Vector2): Double = this.x * other.x + this.y * other.y
}

class Projection(var min: Double = Double.POSITIVE_INFINITY, var max: Double = Double.NEGATIVE_INFINITY) {
    fun overlaps(proj2: Projection): Boolean = !(this.max < proj2.min || proj2.max < this.min)
}

class Polygon(vertices: List<Pair<Double, Double>>) {
    private val vertices: List<Vector2> = vertices.map { Vector2(it.first, it.second) }
    private val axes: List<Vector2> = getAxes()

    public fun getVertices(): List<Vector2> { return vertices}
    

    private fun getAxes(): List<Vector2> {
        val axes = mutableListOf<Vector2>()
        for (i in vertices.indices) {
            val vertex1 = vertices[i]
            val vertex2 = if (i + 1 == vertices.size) vertices[0] else vertices[i + 1]
            val edge = Vector2(vertex1.x - vertex2.x, vertex1.y - vertex2.y)
            axes.add(Vector2(-edge.y, edge.x))
        }
        return axes
    }

    fun projectionOnAxis(axis: Vector2): Projection {
        return Projection().apply {
            vertices.forEach { vertex ->
                val p = axis.dot(vertex)
                if (p < min) min = p
                if (p > max) max = p
            }
        }
    }

    fun overlaps(other: Polygon): Boolean {
        (this.axes + other.axes).forEach { axis ->
            val proj1 = this.projectionOnAxis(axis)
            val proj2 = other.projectionOnAxis(axis)
            if (!proj1.overlaps(proj2)) return false
        }
        return true
    }
}

fun main() {
    val poly1 = Polygon(listOf(0.0 to 0.0, 0.0 to 2.0, 1.0 to 4.0, 2.0 to 2.0, 2.0 to 0.0))
    val poly2 = Polygon(listOf(4.0 to 0.0, 4.0 to 2.0, 5.0 to 4.0, 6.0 to 2.0, 6.0 to 0.0))
    val poly3 = Polygon(listOf(1.0 to 0.0, 1.0 to 2.0, 5.0 to 4.0, 9.0 to 2.0, 9.0 to 0.0))
    val polygons = listOf(poly1, poly2, poly3)

    polygons.forEachIndexed { index, polygon ->
        println("poly${index + 1} = ${polygon.getVertices()}")
    }
    println("poly1 and poly2 overlap? ${polygons[0].overlaps(polygons[1])}")
    println("poly1 and poly3 overlap? ${polygons[0].overlaps(polygons[2])}")
    println("poly2 and poly3 overlap? ${polygons[1].overlaps(polygons[2])}")
}
Output:
poly1 = [Vector2(x=0.0, y=0.0), Vector2(x=0.0, y=2.0), Vector2(x=1.0, y=4.0), Vector2(x=2.0, y=2.0), Vector2(x=2.0, y=0.0)]
poly2 = [Vector2(x=4.0, y=0.0), Vector2(x=4.0, y=2.0), Vector2(x=5.0, y=4.0), Vector2(x=6.0, y=2.0), Vector2(x=6.0, y=0.0)]
poly3 = [Vector2(x=1.0, y=0.0), Vector2(x=1.0, y=2.0), Vector2(x=5.0, y=4.0), Vector2(x=9.0, y=2.0), Vector2(x=9.0, y=0.0)]
poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true

Since the method to do the calculation is actually built-in to Mathematica, I've chosen to use this example to showcase Mathematica's output capabilities. The output shown here isn't intended to be some shining example of good design, just a basic illustration of how to produce a graphical table in Mathematica.

polygons = Polygon /@ {
    	{{0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0}},
                  {{4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0}},
                  {{1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0}}
    };

overlappingPolygonsQ[polys___] := ! RegionDisjoint[polys];

imageOptions = {ImageSize -> Tiny, AlignmentPoint -> Center, 
   AspectRatio -> 3/4};

Grid[
 {
  Style[#, Large] & /@ {"Plot", "Description", "Overlapping?"},
  {Graphics[{{Red,                  
        First@#}, {Blue,                    
        Last@#}} &  [{polygons[[1]], polygons[[2]]}], imageOptions], 
   Style["Polygon 1 (red) and 2 (blue)"], 
   overlappingPolygonsQ[polygons[[1]], polygons[[2]]]},   
  
  {Graphics[{{Darker[Magenta],           First@#}, {Darker@Green, 
        Last@#}} &  [{polygons[[3]], polygons[[1]]}], imageOptions], 
   Style["Polygon 1 (dark magenta) and 3 (green)"], 
   overlappingPolygonsQ[polygons[[1]], polygons[[3]]]},
  
  {Graphics[{{Darker@Red, First@#}, {Lighter@Blue, 
        Last@#}} &  [{polygons[[3]], polygons[[2]]}], imageOptions], 
   Style["Polygon 2 (dark red) and 3 (lighter blue)"],
   overlappingPolygonsQ[polygons[[3]], polygons[[2]]]}
  },
 Frame -> All, Spacings -> {2, 1.5, 2}, 
 Alignment -> {Center, Center},
 ItemStyle -> {FontFamily -> "CMU Concrete", FontSize -> Medium},
 Background -> {{}, {None, {LightGray, LightBlue}}}]
Output:

Translation of: Wren
Translation of: Go
type
  Vector2 = (float, float)
  Projection = tuple[min, max: float]
  Polygon = seq[Vector2]

func dot(v1, v2: Vector2): float =
  v1[0] * v2[0] + v1[1] * v2[1]

func axes(poly: Polygon): seq[Vector2] =
  result.setLen(poly.len)
  for i, vertex1 in poly:
    let vertex2 = poly[if i + 1 == poly.len: 0 else: i + 1]
    let edge = (vertex1[0] - vertex2[0], vertex1[1] - vertex2[1])
    result[i] = (-edge[1], edge[0])

func projectionOnAxis(poly: Polygon; axis: Vector2): Projection =
  result.min = Inf
  result.max = -Inf
  for vertex in poly:
    let p = axis.dot(vertex)
    if p < result.min:
      result.min = p
    if p > result.max:
      result.max = p

func projectionOverlaps(proj1, proj2: Projection): bool =
  if proj1.max < proj2.min: return false
  if proj2.max < proj1.min: return false
  result = true

func polygonsOverlap(poly1, poly2: Polygon): bool =
  for axes in [poly1.axes, poly2.axes]:
    for axis in axes:
      let proj1 = poly1.projectionOnAxis(axis)
      let proj2 = poly2.projectionOnAxis(axis)
      if not projectionOverlaps(proj1, proj2):
        return false
  result = true

let poly1 = @[(0.0, 0.0), (0.0, 2.0), (1.0, 4.0), (2.0, 2.0), (2.0, 0.0)]
let poly2 = @[(4.0, 0.0), (4.0, 2.0), (5.0, 4.0), (6.0, 2.0), (6.0, 0.0)]
let poly3 = @[(1.0, 0.0), (1.0, 2.0), (5.0, 4.0), (9.0, 2.0), (9.0, 0.0)]

echo "poly1 = ", poly1
echo "poly2 = ", poly2
echo "poly3 = ", poly3
echo()
echo "poly1 and poly2 overlap? ", polygonsOverlap(poly1, poly2)
echo "poly1 and poly3 overlap? ", polygonsOverlap(poly1, poly3)
echo "poly2 and poly3 overlap? ", polygonsOverlap(poly2, poly3)
Output:
poly1 = @[(0.0, 0.0), (0.0, 2.0), (1.0, 4.0), (2.0, 2.0), (2.0, 0.0)]
poly2 = @[(4.0, 0.0), (4.0, 2.0), (5.0, 4.0), (6.0, 2.0), (6.0, 0.0)]
poly3 = @[(1.0, 0.0), (1.0, 2.0), (5.0, 4.0), (9.0, 2.0), (9.0, 0.0)]

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true
Translation of: Raku
# 20240930 Perl programming solution

use strict;
use warnings;

package Vector2 {
   sub new {
      my ($class, %args) = @_;
      return bless \%args, $class;
   }

   sub dot {
      my ($self, $other) = @_;
      return $self->{x} * $other->{x} + $self->{y} * $other->{y};
   }
}

package Projection {
   sub new {
      my ($class, %args) = @_;
      return bless \%args, $class;
   }
}

sub get_axes {
   my ($poly) = @_;
   my @axes;

   push @$poly, $poly->[0];  
   for my $i (0 .. @$poly - 2) {
      my $vector1 = Vector2->new(x => $poly->[$i][0], y => $poly->[$i][1]);
      my $vector2 = Vector2->new(x => $poly->[$i + 1][0], y => $poly->[$i + 1][1]);
      my $edge = Vector2->new( 
         x => $vector1->{x} - $vector2->{x},
         y => $vector1->{y} - $vector2->{y}
      );
      push @axes, Vector2->new(x => -$edge->{y}, y => $edge->{x});
   }
   return @axes;
}

sub project_onto_axis {
   my ($poly, $axis) = @_;
    
   my $vertex0 = $poly->[0];
   my $vector0 = Vector2->new(x => $vertex0->[0], y => $vertex0->[1]);
   my ($min, $max) = ($axis->dot($vector0), $axis->dot($vector0));

   foreach my $vertex (@$poly) {
      my $vector = Vector2->new(x => $vertex->[0], y => $vertex->[1]);
      my $projection = $axis->dot($vector);
        
      if ($projection < $min) { $min = $projection }
      if ($projection > $max) { $max = $projection }
   }
   return Projection->new(min => $min, max => $max);
}

sub projections_overlap {
   my ($proj1, $proj2) = @_;
   return !($proj1->{max} < $proj2->{min} || $proj2->{max} < $proj1->{min});
}

sub polygons_overlap {
   my ($poly1, $poly2) = @_;
   my @axes1 = get_axes($poly1);
   my @axes2 = get_axes($poly2);
   my @all_axes = (@axes1, @axes2);

   foreach my $axis (@all_axes) {
      my ($proj1, $proj2) = (project_onto_axis($poly1, $axis), project_onto_axis($poly2, $axis));
      return 0 unless projections_overlap($proj1, $proj2);
   }
   return 1;
}

my @poly1 = ([0, 0], [0, 2], [1, 4], [2, 2], [2, 0]);
my @poly2 = ([4, 0], [4, 2], [5, 4], [6, 2], [6, 0]);
my @poly3 = ([1, 0], [1, 2], [5, 4], [9, 2], [9, 0]);

print "poly1 = ", join(' ', map { "($_->[0] $_->[1])" } @poly1), "\n";
print "poly2 = ", join(' ', map { "($_->[0] $_->[1])" } @poly2), "\n";
print "poly3 = ", join(' ', map { "($_->[0] $_->[1])" } @poly3), "\n\n";

print "poly1 and poly2 overlap? ", polygons_overlap(\@poly1, \@poly2) ? "True" : "False", "\n";
print "poly1 and poly3 overlap? ", polygons_overlap(\@poly1, \@poly3) ? "True" : "False", "\n";
print "poly2 and poly3 overlap? ", polygons_overlap(\@poly2, \@poly3) ? "True" : "False", "\n";

You may Attempt This Online!

Translation of: Wren
-- demo\rosetta\Polygons_overlap.exw
with javascript_semantics
function getAxes(sequence poly)
    integer l = length(poly)
    sequence axes = repeat(0,l)
    for i=1 to l do
        sequence p = poly[i], n = poly[iff(i=l?1:i+1)]
        axes[i] = {n[2]-p[2],p[1]-n[1]} -- ie {-y,x}
    end for
    return axes
end function

function projectOntoAxis(sequence poly,axis)
    atom {ax,ay} = axis
    sequence p = repeat(0,length(poly))
    for i,pi in poly do
        atom {px,py} = pi
        p[i] = ax*px+ay*py
    end for
    return {min(p),max(p)}
end function

function projectionsOverlap(sequence proj1, proj2)
    atom {min1,max1} = proj1, 
         {min2,max2} = proj2
    return max1>=min2 and max2>=min1
end function

function polygonsOverlap(sequence poly1, poly2)
    sequence axes1 = getAxes(poly1),
             axes2 = getAxes(poly2)
    for axes in {axes1, axes2} do
        for axis in axes do
            sequence proj1 = projectOntoAxis(poly1, axis),
                     proj2 = projectOntoAxis(poly2, axis)
            if not projectionsOverlap(proj1, proj2) then return false end if
        end for
    end for
    return true
end function

constant poly1 = {{0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0}},
         poly2 = {{4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0}},
         poly3 = {{1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0}},
fmt = """
poly1 = %v
poly2 = %v
poly3 = %v

poly1 and poly2 overlap? %t
poly1 and poly3 overlap? %t
poly2 and poly3 overlap? %t
"""
printf(1,fmt,{poly1,poly2,poly3,polygonsOverlap(poly1, poly2),
                                polygonsOverlap(poly1, poly3),
                                polygonsOverlap(poly2, poly3)})
Output:
poly1 = {{0,0},{0,2},{1,4},{2,2},{2,0}}
poly2 = {{4,0},{4,2},{5,4},{6,2},{6,0}}
poly3 = {{1,0},{1,2},{5,4},{9,2},{9,0}}

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true
Library: Pluto-bitmap
Library: Pluto-fmt
require "bitmap"
local fmt = require "fmt"

local polys = {
    { {0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0} },
    { {4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0} },
    { {1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0} }
}

for i = 1, 3 do print($"poly{i} = {fmt.swrite(polys[i])}") end
print()

print($"poly1 and poly2 overlap? {bitmap.polygonsOverlap(polys[1], polys[2])}")
print($"poly1 and poly3 overlap? {bitmap.polygonsOverlap(polys[1], polys[3])}")
print($"poly2 and poly3 overlap? {bitmap.polygonsOverlap(polys[2], polys[3])}")
Output:
poly1 = {{0, 0}, {0, 2}, {1, 4}, {2, 2}, {2, 0}}
poly2 = {{4, 0}, {4, 2}, {5, 4}, {6, 2}, {6, 0}}
poly3 = {{1, 0}, {1, 2}, {5, 4}, {9, 2}, {9, 0}}

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true
Translation of: Nim
# overlaying_polygons.py by xing216
from math import inf
class Vector2:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y
    def dot(self, other: 'Vector2') -> float:
        return self.x * other.x + self.y * other.y
    def __repr__(self) -> str:
        return f'({self.x}, {self.y})'
class Projection:
    min: float
    max: float
    def overlaps(self, proj2: 'Projection') -> bool:
        if self.max < proj2.min or proj2.max < self.min: return False
        return True
class Polygon:
    def __init__(self, vertices: list[tuple[float, float]]) -> None:
        self.vertices = [Vector2(*vertex) for vertex in vertices]
        self.axes = self.get_axes()
    def get_axes(self) -> list[Vector2]:
        axes = []
        for i, vertex1 in enumerate(self.vertices):
            if i + 1 == len(self.vertices): vertex2 = self.vertices[0]
            else: vertex2 = self.vertices[i + 1]
            edge = (vertex1.x - vertex2.x, vertex1.y - vertex2.y)
            axes.append(Vector2(-edge[1], edge[0]))
        return axes
    def projection_on_axis(self, axis: Vector2) -> Projection:
        projection = Projection()
        projection.min = inf
        projection.max = -inf
        for vertex in self.vertices:
            p = axis.dot(vertex)
            if p < projection.min:
                projection.min = p
            if p > projection.max:
                projection.max = p
        return projection
    def overlaps(self, other: 'Polygon') -> bool:
        for axes in [self.axes, other.axes]:
            for axis in axes:
                proj1 = self.projection_on_axis(axis)
                proj2 = other.projection_on_axis(axis)
                if not proj1.overlaps(proj2): return False
        return True
    
poly1 = Polygon([(0.0, 0.0), (0.0, 2.0), (1.0, 4.0), (2.0, 2.0), (2.0, 0.0)])
poly2 = Polygon([(4.0, 0.0), (4.0, 2.0), (5.0, 4.0), (6.0, 2.0), (6.0, 0.0)])
poly3 = Polygon([(1.0, 0.0), (1.0, 2.0), (5.0, 4.0), (9.0, 2.0), (9.0, 0.0)])
polygons = (poly1, poly2, poly3)
for i, polygon in enumerate(polygons):
    print(f'poly{i+1} = {polygon.vertices}')
print(f'poly1 and poly2 overlap? {polygons[0].overlaps(polygons[1])}')
print(f'poly1 and poly3 overlap? {polygons[0].overlaps(polygons[2])}')
print(f'poly2 and poly3 overlap? {polygons[1].overlaps(polygons[2])}')
Output:
poly1 = [(0.0, 0.0), (0.0, 2.0), (1.0, 4.0), (2.0, 2.0), (2.0, 0.0)]
poly2 = [(4.0, 0.0), (4.0, 2.0), (5.0, 4.0), (6.0, 2.0), (6.0, 0.0)]
poly3 = [(1.0, 0.0), (1.0, 2.0), (5.0, 4.0), (9.0, 2.0), (9.0, 0.0)]
poly1 and poly2 overlap? False
poly1 and poly3 overlap? True
poly2 and poly3 overlap? True
Translation of: Go
# 20230810 Raku programming solution

class Vector2 { has ( $.x, $.y );
   method dot ( \other ) { self.x * other.x + self.y * other.y }
}; 

class Projection { has ( $.min, $.max ) };

sub getAxes ( \poly ) {
   return poly.append(poly[0]).rotor(2=>-1).map: -> (\vertex1,\vertex2) {
      my \vector1 = Vector2.new: x => vertex1[0], y => vertex1[1];
      my \vector2 = Vector2.new: x => vertex2[0], y => vertex2[1];
      my \edge    = Vector2.new: x => vector1.x - vector2.x, 
                                 y => vector1.y - vector2.y;
      $_ = Vector2.new: x => -edge.y, y => edge.x
   }
}

sub projectOntoAxis ( \poly, \axis ) {
   my \vertex0 = poly[0];
   my \vector0 = Vector2.new: x => vertex0[0], y => vertex0[1];
   my $max     = my $min = axis.dot: vector0;
   for poly -> \vertex {
      my \vector = Vector2.new: x => vertex[0], y => vertex[1];
      given axis.dot: vector { when $_ < $min { $min = $_ } 
                               when $_ > $max { $max = $_ } }
   }
   return Projection.new: min => $min, max => $max
}

sub projectionsOverlap ( \proj1, \proj2 ) {
   return ! ( proj1.max < proj2.min or proj2.max < proj1.min )
}

sub polygonsOverlap( \poly1, \poly2 ) {
   my (\axes1,\axes2) := (poly1,poly2).map: { getAxes $_ };
   for (axes1, axes2) -> \axes {
     for axes -> \axis {
         my (\proj1,\proj2) := (poly1,poly2).map: { projectOntoAxis $_, axis }
         return False unless projectionsOverlap(proj1, proj2) 
      }
   }
   return True
}

my \poly1 = [ <0 0>, <0 2>, <1 4>, <2 2>, <2 0> ];
my \poly2 = [ <4 0>, <4 2>, <5 4>, <6 2>, <6 0> ];
my \poly3 = [ <1 0>, <1 2>, <5 4>, <9 2>, <9 0> ];

say "poly1 = ", poly1;
say "poly2 = ", poly2;
say "poly3 = ", poly3;
say();
say "poly1 and poly2 overlap? ", polygonsOverlap(poly1, poly2);
say "poly1 and poly3 overlap? ", polygonsOverlap(poly1, poly3);
say "poly2 and poly3 overlap? ", polygonsOverlap(poly2, poly3);

You may Attempt This Online!


Translation of: C++
use std::fmt;

// Replaces C++ Point class
#[derive(Debug, Copy, Clone, PartialEq)] // Add common traits
struct Point {
    pub x: f32,
    pub y: f32,
}

// Replaces C++ Vector class
#[derive(Debug, Copy, Clone, PartialEq)] // Add common traits
struct Vector {
    pub x: f32,
    pub y: f32,
}

impl Vector {
    // Renamed from scalarProduct to snake_case
    // Takes immutable references (&) as it doesn't modify self or other
    pub fn scalar_product(&self, other: &Vector) -> f32 {
        self.x * other.x + self.y * other.y
    }

    // Renamed from edgeWith to snake_case
    // Takes immutable references, returns a new Vector
    pub fn edge_with(&self, other: &Vector) -> Vector {
        Vector {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }

    // Takes an immutable reference, returns a new Vector
    pub fn perpendicular(&self) -> Vector {
        Vector {
            x: -self.y,
            y: self.x,
        }
    }
}

// Implement Display trait for nice printing (replaces C++ to_string)
impl fmt::Display for Vector {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

// Replaces C++ Projection class
#[derive(Debug, Copy, Clone, PartialEq)] // Add common traits
struct Projection {
    pub min: f32,
    pub max: f32,
}

impl Projection {
    // Takes immutable references (&)
    pub fn overlaps(&self, other: &Projection) -> bool {
        !(self.max < other.min || other.max < self.min)
    }
}

// Replaces C++ Polygon class
#[derive(Debug, Clone)] // Cannot be Copy because Vec is not Copy
struct Polygon {
    // Fields are private by default unless marked `pub`
    vertices: Vec<Vector>,
    axes: Vec<Vector>,
}

impl Polygon {
    // Associated function acting as a constructor
    // Takes a slice of Points (&[Point]) to be more flexible (accepts Vec, array, etc.)
    pub fn new(points: &[Point]) -> Self {
        let vertices = Self::compute_vertices(points);
        let axes = Self::compute_axes(&vertices);
        Polygon { vertices, axes }
    }

    // Helper function, not public (private to the module)
    fn compute_vertices(points: &[Point]) -> Vec<Vector> {
        // Use iterators for a more idiomatic Rust approach
        points
            .iter()
            .map(|p| Vector { x: p.x, y: p.y })
            .collect()
    }

    // Helper function, not public (private to the module)
    fn compute_axes(vertices: &[Vector]) -> Vec<Vector> {
        let mut axes = Vec::new();
        if vertices.len() < 2 {
            return axes; // Handle cases with less than 2 vertices
        }

        for i in 0..vertices.len() {
            let p1 = vertices[i];
            // Use modulo (%) for wrap-around indexing
            let p2 = vertices[(i + 1) % vertices.len()];
            let edge = p1.edge_with(&p2);
            axes.push(edge.perpendicular());
        }
        axes
    }

    // Public method to check overlaps
    // Takes immutable references (&)
    pub fn overlaps(&self, other: &Polygon) -> bool {
        // Combine axes from both polygons using iterators and chain
        let all_axes = self.axes.iter().chain(other.axes.iter());

        for axis in all_axes {
            let projection1 = self.projection_on_axis(axis);
            let projection2 = other.projection_on_axis(axis);

            if !projection1.overlaps(&projection2) {
                return false; // Found a separating axis
            }
        }

        true // No separating axis found, polygons overlap
    }

    // Public method to project polygon onto an axis
    // Takes immutable references (&)
    pub fn projection_on_axis(&self, axis: &Vector) -> Projection {
        // Use Rust's float constants
        let mut min = f32::INFINITY;
        let mut max = f32::NEG_INFINITY;

        // Iterate over borrowed vertices
        for vertex in &self.vertices {
            let p = axis.scalar_product(vertex);
            // Use min/max functions for clarity
            min = min.min(p);
            max = max.max(p);
        }

        Projection { min, max }
    }
}

// Implement Display trait for nice printing (replaces C++ to_string)
impl fmt::Display for Polygon {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[ ")?;
        // Iterate and use the Display impl of Vector
        for vertex in &self.vertices {
            write!(f, "{} ", vertex)?;
        }
        write!(f, "]")
    }
}

fn main() {
    // Use vec! macro for easy vector creation
    let polygon1 = Polygon::new(&[
        Point { x: 0.0, y: 0.0 }, Point { x: 0.0, y: 2.0 }, Point { x: 1.0, y: 4.0 },
        Point { x: 2.0, y: 2.0 }, Point { x: 2.0, y: 0.0 }
    ]);

    let polygon2 = Polygon::new(&[
        Point { x: 4.0, y: 0.0 }, Point { x: 4.0, y: 2.0 }, Point { x: 5.0, y: 4.0 },
        Point { x: 6.0, y: 2.0 }, Point { x: 6.0, y: 0.0 }
    ]);

    let polygon3 = Polygon::new(&[
        Point { x: 1.0, y: 0.0 }, Point { x: 1.0, y: 2.0 }, Point { x: 5.0, y: 4.0 },
        Point { x: 9.0, y: 2.0 }, Point { x: 9.0, y: 0.0 }
    ]);

    // Use println! macro, which automatically uses the Display trait
    println!("polygon1: {}", polygon1);
    println!("polygon2: {}", polygon2);
    println!("polygon3: {}", polygon3);
    println!(); // Print an empty line

    // println! automatically formats bool as true/false (like C++ std::boolalpha)
    println!("polygon1 and polygon2 overlap? {}", polygon1.overlaps(&polygon2));
    println!("polygon1 and polygon3 overlap? {}", polygon1.overlaps(&polygon3));
    println!("polygon2 and polygon3 overlap? {}", polygon2.overlaps(&polygon3));
}
Output:
polygon1: [ (0, 0) (0, 2) (1, 4) (2, 2) (2, 0) ]
polygon2: [ (4, 0) (4, 2) (5, 4) (6, 2) (6, 0) ]
polygon3: [ (1, 0) (1, 2) (5, 4) (9, 2) (9, 0) ]

polygon1 and polygon2 overlap? false
polygon1 and polygon3 overlap? true
polygon2 and polygon3 overlap? true


Library: Wren-vector
Library: Wren-dynamic

The task description doesn't say whether the polygons are 2D or 3D and whether they're convex or not. So for now, I've assumed the simplest case of 2D convex polygons though a non-convex polygon can always be decomposed into a combination of convex polygons.

The approach here is based on the Separating Axis theorem ("SAT"). See here for a simple explanation of this with pseudo-code.

import "./vector" for Vector2
import "./dynamic" for Tuple

var Projection = Tuple.create("Projection", ["min", "max"])

/* In the following a polygon is represented as a list of vertices
   and a vertex by a pair of x, y coordinates in the plane. */

var getAxes = Fn.new { |poly|
    var axes = List.filled(poly.count, null)
    for (i in 0...poly.count) {
        var vertex1 = poly[i]
        var vertex2 = poly[(i+1 == poly.count) ? 0 : i+1]
        var vector1 = Vector2.new(vertex1[0], vertex1[1])
        var vector2 = Vector2.new(vertex2[0], vertex2[1])
        var edge = vector1 - vector2
        axes[i] = edge.perp
    }
    return axes
}

var projectOntoAxis = Fn.new { |poly, axis|
    var vertex0 = poly[0]
    var vector0 = Vector2.new(vertex0[0], vertex0[1])
    var min = axis.dot(vector0)
    var max = min
    for (i in 1...poly.count) {
        var vertex = poly[i]
        var vector = Vector2.new(vertex[0], vertex[1])
        var p = axis.dot(vector)
        if (p < min) {
            min = p
        } else if (p > max) {
            max = p
        }
    }
    return Projection.new(min, max)
}

var projectionsOverlap = Fn.new { |proj1, proj2|
    if (proj1.max < proj2.min) return false
    if (proj2.max < proj1.min) return false
    return true
}

var polygonsOverlap = Fn.new { |poly1, poly2|
    var axes1 = getAxes.call(poly1)
    var axes2 = getAxes.call(poly2)
    for (axes in [axes1, axes2]) {
        for (axis in axes) {
            var proj1 = projectOntoAxis.call(poly1, axis)
            var proj2 = projectOntoAxis.call(poly2, axis)
            if (!projectionsOverlap.call(proj1, proj2)) return false
        }
    }
    return true
}

var poly1 = [[0, 0], [0, 2], [1, 4], [2, 2], [2, 0]]
var poly2 = [[4, 0], [4, 2], [5, 4], [6, 2], [6, 0]]
var poly3 = [[1, 0], [1, 2], [5, 4], [9, 2], [9, 0]]
System.print("poly1 = %(poly1)")
System.print("poly2 = %(poly2)")
System.print("poly3 = %(poly3)")
System.print()
System.print("poly1 and poly2 overlap? %(polygonsOverlap.call(poly1, poly2))") 
System.print("poly1 and poly3 overlap? %(polygonsOverlap.call(poly1, poly3))")
System.print("poly2 and poly3 overlap? %(polygonsOverlap.call(poly2, poly3))")
Output:
poly1 = [[0, 0], [0, 2], [1, 4], [2, 2], [2, 0]]
poly2 = [[4, 0], [4, 2], [5, 4], [6, 2], [6, 0]]
poly3 = [[1, 0], [1, 2], [5, 4], [9, 2], [9, 0]]

poly1 and poly2 overlap? false
poly1 and poly3 overlap? true
poly2 and poly3 overlap? true


Works with: Zig version 0.14.0
Translation of: Rust
const std = @import("std");

// Replaces Rust Point struct
pub const Point = struct {
    x: f32,
    y: f32,
    
    pub fn init(x: f32, y: f32) Point {
        return .{ .x = x, .y = y };
    }
};

// Replaces Rust Vector struct
pub const Vector = struct {
    x: f32,
    y: f32,
    
    pub fn init(x: f32, y: f32) Vector {
        return .{ .x = x, .y = y };
    }
    
    // Renamed from scalar_product
    pub fn scalarProduct(self: *const Vector, other: *const Vector) f32 {
        return self.x * other.x + self.y * other.y;
    }
    
    // Renamed from edge_with
    pub fn edgeWith(self: *const Vector, other: *const Vector) Vector {
        return .{
            .x = self.x - other.x,
            .y = self.y - other.y,
        };
    }
    
    pub fn perpendicular(self: *const Vector) Vector {
        return .{
            .x = -self.y,
            .y = self.x,
        };
    }
    
    // Similar to Display implementation in Rust
    pub fn format(
        self: *const Vector,
        comptime fmt: []const u8,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) !void {
        _ = fmt;
        _ = options;
        try writer.print("({d}, {d})", .{ self.x, self.y });
    }
};

// Replaces Rust Projection struct
pub const Projection = struct {
    min: f32,
    max: f32,
    
    pub fn init(min: f32, max: f32) Projection {
        return .{ .min = min, .max = max };
    }
    
    pub fn overlaps(self: *const Projection, other: *const Projection) bool {
        return !(self.max < other.min or other.max < self.min);
    }
};

// Replaces Rust Polygon struct
pub const Polygon = struct {
    vertices: std.ArrayList(Vector),
    axes: std.ArrayList(Vector),
    
    pub fn init(allocator: std.mem.Allocator, points: []const Point) !Polygon {
        var vertices = try computeVertices(allocator, points);
        errdefer vertices.deinit();
        
        var axes = try computeAxes(allocator, vertices.items);
        errdefer axes.deinit();
        
        return .{ 
            .vertices = vertices, 
            .axes = axes,
        };
    }
    
    pub fn deinit(self: *Polygon) void {
        self.vertices.deinit();
        self.axes.deinit();
    }
    
    // Helper function
    fn computeVertices(allocator: std.mem.Allocator, points: []const Point) !std.ArrayList(Vector) {
        var vertices = std.ArrayList(Vector).init(allocator);
        errdefer vertices.deinit();
        
        for (points) |p| {
            try vertices.append(.{ .x = p.x, .y = p.y });
        }
        
        return vertices;
    }
    
    // Helper function
    fn computeAxes(allocator: std.mem.Allocator, vertices: []const Vector) !std.ArrayList(Vector) {
        var axes = std.ArrayList(Vector).init(allocator);
        errdefer axes.deinit();
        
        if (vertices.len < 2) {
            return axes; // Handle cases with less than 2 vertices
        }
        
        for (vertices, 0..) |p1, i| {
            // Use modulo for wrap-around indexing
            const p2 = vertices[(i + 1) % vertices.len];
            const edge = p1.edgeWith(&p2);
            try axes.append(edge.perpendicular());
        }
        
        return axes;
    }
    
    pub fn overlaps(self: *const Polygon, other: *const Polygon) bool {
        // Check axes from this polygon
        for (self.axes.items) |*axis| {
            const projection1 = self.projectionOnAxis(axis);
            const projection2 = other.projectionOnAxis(axis);
            
            if (!projection1.overlaps(&projection2)) {
                return false; // Found a separating axis
            }
        }
        
        // Check axes from the other polygon
        for (other.axes.items) |*axis| {
            const projection1 = self.projectionOnAxis(axis);
            const projection2 = other.projectionOnAxis(axis);
            
            if (!projection1.overlaps(&projection2)) {
                return false; // Found a separating axis
            }
        }
        
        return true; // No separating axis found, polygons overlap
    }
    
    pub fn projectionOnAxis(self: *const Polygon, axis: *const Vector) Projection {
        var min = std.math.inf(f32);
        var max = -std.math.inf(f32);
        
        for (self.vertices.items) |*vertex| {
            const p = axis.scalarProduct(vertex);
            min = @min(min, p);
            max = @max(max, p);
        }
        
        return .{ .min = min, .max = max };
    }
    
    // Similar to Display implementation in Rust
    pub fn format(
        self: *const Polygon,
        comptime fmt: []const u8,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) !void {
        _ = fmt;
        _ = options;
        try writer.writeAll("[ ");
        
        for (self.vertices.items) |*vertex| {
            try writer.print("{} ", .{vertex});
        }
        
        try writer.writeAll("]");
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const points1 = [_]Point{
        Point.init(0.0, 0.0), Point.init(0.0, 2.0), Point.init(1.0, 4.0),
        Point.init(2.0, 2.0), Point.init(2.0, 0.0),
    };
    
    const points2 = [_]Point{
        Point.init(4.0, 0.0), Point.init(4.0, 2.0), Point.init(5.0, 4.0),
        Point.init(6.0, 2.0), Point.init(6.0, 0.0),
    };
    
    const points3 = [_]Point{
        Point.init(1.0, 0.0), Point.init(1.0, 2.0), Point.init(5.0, 4.0),
        Point.init(9.0, 2.0), Point.init(9.0, 0.0),
    };
    
    var polygon1 = try Polygon.init(allocator, &points1);
    defer polygon1.deinit();
    
    var polygon2 = try Polygon.init(allocator, &points2);
    defer polygon2.deinit();
    
    var polygon3 = try Polygon.init(allocator, &points3);
    defer polygon3.deinit();
    
    const stdout = std.io.getStdOut().writer();
    
    try stdout.print("polygon1: {}\n", .{polygon1});
    try stdout.print("polygon2: {}\n", .{polygon2});
    try stdout.print("polygon3: {}\n", .{polygon3});
    try stdout.print("\n", .{});
    
    try stdout.print("polygon1 and polygon2 overlap? {}\n", .{polygon1.overlaps(&polygon2)});
    try stdout.print("polygon1 and polygon3 overlap? {}\n", .{polygon1.overlaps(&polygon3)});
    try stdout.print("polygon2 and polygon3 overlap? {}\n", .{polygon2.overlaps(&polygon3)});
}
Output:
polygon1: [ (0, 0) (0, 2) (1, 4) (2, 2) (2, 0) ]
polygon2: [ (4, 0) (4, 2) (5, 4) (6, 2) (6, 0) ]
polygon3: [ (1, 0) (1, 2) (5, 4) (9, 2) (9, 0) ]

polygon1 and polygon2 overlap? false
polygon1 and polygon3 overlap? true
polygon2 and polygon3 overlap? true
Cookies help us deliver our services. By using our services, you agree to our use of cookies.