Skip to content

geometry

stable

2D geometry primitives for games and graphics. Compute distances, angles, intersections, polygon areas, convex hulls, and collision tests between points, rectangles, circles, and polygons.

use plugin geometry::{distance, angle, midpoint, …}
26 functions Math
/ filter jk navigate Esc clear
Functions (26)
  1. distance Euclidean distance between two points
  2. angle Angle in radians from point A to point B
  3. midpoint Midpoint between two points
  4. lerp Linearly interpolate between two points
  5. normalize Normalize a 2D vector to unit length
  6. dot Dot product of two 2D vectors
  7. cross2d 2D cross product (scalar)
  8. vector_length Length of a 2D vector
  9. rotate_point Rotate a point around a centre
  10. reflect_point Reflect a point through a centre
  11. point_in_rect Test if point is inside a rectangle
  12. rects_overlap Test if two axis-aligned rectangles overlap
  13. circle_contains Test if circle contains a point
  14. circles_overlap Test if two circles overlap
  15. point_in_polygon Ray-cast point-in-polygon test
  16. line_intersection Intersection of two line segments
  17. polygon_area Area of a polygon (shoelace formula)
  18. polygon_centroid Centroid of a polygon
  19. polygon_perimeter Perimeter of a polygon
  20. convex_hull Convex hull of a point set (Graham scan)
  21. bounding_box Axis-aligned bounding box of a point set
  22. triangle_area Area of a triangle from three points
  23. rect_area Area of a rectangle from width and height
  24. rect_perimeter Perimeter of a rectangle
  25. circle_area Area of a circle from radius
  26. circle_circumference Circumference of a circle from radius

Overview

geometry is a stateless toolkit of 2D geometry primitives aimed at games, simulations, and graphics. There are no handles or objects to manage: every function takes plain numbers (coordinates, widths, radii, angles in radians) and returns either a number, a boolean, or a small {x, y} table — so a point is just an ordinary map you can build, inspect, and pass around freely. Polygon and point-set functions accept an array of {x, y} tables and read both "x"/"y" keys and positional 1/2 entries.

Use it for the building blocks of 2D math: distances and angles, vector operations (dot, cross, normalize, length), point transforms (rotate, reflect, lerp, midpoint), area and perimeter formulas, and the collision and containment tests that drive hit detection — point-in-rect, point-in-polygon, circle and rectangle overlap, line intersection, convex hull, and bounding box.

Common patterns

Detect whether a moving entity collides with a circular hazard, using a vector toward it to decide a knockback direction:

use plugin geometry::{circle_contains, angle, normalize}

let hazard_x = 100.0
let hazard_y = 100.0
let radius = 20.0

if circle_contains(hazard_x, hazard_y, radius, 110.0, 105.0) {
  let dir = angle(hazard_x, hazard_y, 110.0, 105.0)
  let push = normalize(110.0 - hazard_x, 105.0 - hazard_y)
  print("hit at angle {dir}, push ({push["x"]}, {push["y"]})")
}

Build a polygon, then measure its area, perimeter, and centroid together:

use plugin geometry::{polygon_area, polygon_perimeter, polygon_centroid}

let room = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 8.0, "y": 0.0},
  #{"x": 8.0, "y": 6.0},
  #{"x": 0.0, "y": 6.0}
]
let c = polygon_centroid(room)
print("area {polygon_area(room)}, perimeter {polygon_perimeter(room)}")
print("centre at ({c["x"]}, {c["y"]})")

Reduce a scattered point cloud to its convex hull and frame it with a bounding box:

use plugin geometry::{convex_hull, bounding_box}

let cloud = [
  #{"x": 1.0, "y": 1.0},
  #{"x": 9.0, "y": 2.0},
  #{"x": 5.0, "y": 5.0},
  #{"x": 8.0, "y": 9.0},
  #{"x": 2.0, "y": 8.0}
]
let hull = convex_hull(cloud)
let bb = bounding_box(hull)
print("from ({bb["min_x"]},{bb["min_y"]}) to ({bb["max_x"]},{bb["max_y"]})")

Euclidean distance between two points

Returns the straight-line distance between two points.

use plugin geometry::{distance}

print(distance(0.0, 0.0, 3.0, 4.0))  // 5.0

Use it to range-check one entity against another before reacting:

use plugin geometry::{distance}

let d = distance(2.0, 2.0, 10.0, 8.0)
if d < 5.0 {
  print("in range ({d})")
} else {
  print("too far ({d})")
}

Angle in radians from point A to point B

Returns the angle in radians from point (x1, y1) toward point (x2, y2), measured from the positive X axis. Range is (-PI, PI].

use plugin geometry::{angle}

let a = angle(0.0, 0.0, 1.0, 0.0)  // 0.0 (east)
let b = angle(0.0, 0.0, 0.0, 1.0)  // ~1.57 (north)
print(a)
print(b)

Midpoint between two points

Returns the midpoint {x, y} between two points.

use plugin geometry::{midpoint}

let m = midpoint(0.0, 0.0, 10.0, 4.0)
print(m["x"])  // 5.0
print(m["y"])  // 2.0

Linearly interpolate between two points

Linearly interpolates between (x1,y1) and (x2,y2) by factor t. Returns {x, y}. t=0 gives the first point, t=1 gives the second.

use plugin geometry::{lerp}

let p = lerp(0.0, 0.0, 10.0, 10.0, 0.25)
print(p["x"])  // 2.5
print(p["y"])  // 2.5

Normalize a 2D vector to unit length

Returns a unit vector {x, y} pointing in the same direction. Returns {0, 0} for zero-length input.

use plugin geometry::{normalize, vector_length}

let v = normalize(3.0, 4.0)
print(vector_length(v["x"], v["y"]))  // 1.0

Dot product of two 2D vectors

Returns the dot product of two 2D vectors. A result of 0 means the vectors are perpendicular.

use plugin geometry::{dot}

print(dot(1.0, 0.0, 0.0, 1.0))  // 0.0
print(dot(1.0, 0.0, 1.0, 0.0))  // 1.0

2D cross product (scalar)

Returns the Z component of the 3D cross product of two 2D vectors. Positive means counter-clockwise turn, negative means clockwise.

use plugin geometry::{cross2d}

print(cross2d(1.0, 0.0, 0.0, 1.0))   // 1.0  (CCW)
print(cross2d(1.0, 0.0, 0.0, -1.0))  // -1.0 (CW)

Length of a 2D vector

Returns the Euclidean length of a 2D vector.

use plugin geometry::{vector_length}

print(vector_length(3.0, 4.0))  // 5.0

Rotate a point around a centre

Rotates point (px, py) around centre (cx, cy) by angle radians. Returns {x, y}.

use plugin geometry::{rotate_point}

let p = rotate_point(1.0, 0.0, 0.0, 0.0, 1.5708)  // ~90 degrees
print(p["x"])  // ~0.0
print(p["y"])  // ~1.0

Reflect a point through a centre

Reflects point (px, py) through centre (cx, cy). Returns {x, y}.

use plugin geometry::{reflect_point}

let r = reflect_point(1.0, 2.0, 0.0, 0.0)
print(r["x"])  // -1.0
print(r["y"])  // -2.0

Test if point is inside a rectangle

Returns true if point (px, py) is inside the axis-aligned rectangle at (rx, ry) with width rw and height rh (inclusive bounds).

use plugin geometry::{point_in_rect}

print(point_in_rect(5.0, 5.0, 0.0, 0.0, 10.0, 10.0))   // true
print(point_in_rect(15.0, 5.0, 0.0, 0.0, 10.0, 10.0))  // false

Test if two axis-aligned rectangles overlap

Returns true if two axis-aligned rectangles overlap. Touching edges count as overlapping.

use plugin geometry::{rects_overlap}

print(rects_overlap(0.0, 0.0, 5.0, 5.0, 3.0, 3.0, 5.0, 5.0))  // true
print(rects_overlap(0.0, 0.0, 2.0, 2.0, 5.0, 5.0, 2.0, 2.0))  // false

Test if circle contains a point

Returns true if point (px, py) lies inside or on the circle centred at (cx, cy) with radius r.

use plugin geometry::{circle_contains}

print(circle_contains(0.0, 0.0, 5.0, 3.0, 4.0))  // true  (dist=5)
print(circle_contains(0.0, 0.0, 5.0, 4.0, 4.0))  // false (dist>5)

Test if two circles overlap

Returns true if two circles overlap or touch.

use plugin geometry::{circles_overlap}

print(circles_overlap(0.0, 0.0, 3.0, 5.0, 0.0, 3.0))  // true  (dist=5, sum=6)
print(circles_overlap(0.0, 0.0, 1.0, 5.0, 0.0, 1.0))  // false (dist=5, sum=2)

Ray-cast point-in-polygon test

Ray-casting algorithm: returns true if (px, py) is inside the polygon. polygon is a table of {x, y} point tables. Requires at least 3 vertices.

use plugin geometry::{point_in_polygon}

let poly = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 10.0, "y": 0.0},
  #{"x": 10.0, "y": 10.0},
  #{"x": 0.0, "y": 10.0}
]
print(point_in_polygon(5.0, 5.0, poly))   // true
print(point_in_polygon(15.0, 5.0, poly))  // false

Intersection of two line segments

Returns the intersection point {x, y} of segments (x1,y1)-(x2,y2) and (x3,y3)-(x4,y4), or nil if they do not intersect within their lengths.

use plugin geometry::{line_intersection}

let p = line_intersection(0.0, 0.0, 10.0, 10.0, 0.0, 10.0, 10.0, 0.0)
print(p["x"])  // 5.0
print(p["y"])  // 5.0

let none = line_intersection(0.0, 0.0, 1.0, 0.0, 5.0, 0.0, 6.0, 0.0)
print(none)  // nil (parallel, no overlap)

Because a miss returns nil, you can branch directly on the result:

use plugin geometry::{line_intersection}

let hit = line_intersection(0.0, 5.0, 10.0, 5.0, 4.0, 0.0, 4.0, 10.0)
if hit != nil {
  print("crosses at ({hit["x"]}, {hit["y"]})")  // (4.0, 5.0)
} else {
  print("no crossing")
}

Area of a polygon (shoelace formula)

Returns the area of a polygon using the shoelace formula. points is a table of {x, y} tables. Requires at least 3 vertices.

use plugin geometry::{polygon_area}

let square = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 4.0, "y": 0.0},
  #{"x": 4.0, "y": 4.0},
  #{"x": 0.0, "y": 4.0}
]
print(polygon_area(square))  // 16.0

Centroid of a polygon

Returns the centroid {x, y} of a polygon. Falls back to the arithmetic mean of vertices for degenerate (zero-area) polygons.

use plugin geometry::{polygon_centroid}

let tri = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 6.0, "y": 0.0},
  #{"x": 3.0, "y": 6.0}
]
let c = polygon_centroid(tri)
print(c["x"])  // 3.0

Perimeter of a polygon

Returns the total perimeter length of a closed polygon (last vertex connects back to first).

use plugin geometry::{polygon_perimeter}

let square = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 5.0, "y": 0.0},
  #{"x": 5.0, "y": 5.0},
  #{"x": 0.0, "y": 5.0}
]
print(polygon_perimeter(square))  // 20.0

Convex hull of a point set (Graham scan)

Returns the convex hull of a point set as an ordered table of {x, y} vertices using the Graham scan algorithm.

use plugin geometry::{convex_hull, polygon_area}

let pts = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 5.0, "y": 0.0},
  #{"x": 2.5, "y": 2.5},
  #{"x": 5.0, "y": 5.0},
  #{"x": 0.0, "y": 5.0}
]
let hull = convex_hull(pts)
print(polygon_area(hull))

Axis-aligned bounding box of a point set

Returns {min_x, min_y, max_x, max_y} for the axis-aligned bounding box of a point set.

use plugin geometry::{bounding_box}

let pts = [
  #{"x": 1.0, "y": 3.0},
  #{"x": 7.0, "y": 2.0},
  #{"x": 4.0, "y": 8.0}
]
let bb = bounding_box(pts)
print("{bb["min_x"]},{bb["min_y"]} to {bb["max_x"]},{bb["max_y"]}")

Area of a triangle from three points

Returns the area of a triangle from three coordinate pairs using the cross-product formula.

use plugin geometry::{triangle_area}

print(triangle_area(0.0, 0.0, 4.0, 0.0, 0.0, 3.0))  // 6.0

Area of a rectangle from width and height

Returns the absolute area |w * h| of a rectangle from its width and height.

use plugin geometry::{rect_area}

print(rect_area(6.0, 4.0))   // 24.0
print(rect_area(-3.0, 5.0))  // 15.0 (magnitudes)

Perimeter of a rectangle

Returns the perimeter 2 * (|w| + |h|) of a rectangle from its width and height.

use plugin geometry::{rect_perimeter}

print(rect_perimeter(6.0, 4.0))  // 20.0

Area of a circle from radius

Returns the area PI * r^2 of a circle from its radius.

use plugin geometry::{circle_area}

print(circle_area(5.0))  // ~78.54
print(circle_area(1.0))  // ~3.14159

Circumference of a circle from radius

Returns the circumference 2 * PI * r of a circle from its radius.

use plugin geometry::{circle_circumference}

print(circle_circumference(5.0))  // ~31.42
enespt-br