geometry
stable2D 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, …} Functions (26)
- distance Euclidean distance between two points
- angle Angle in radians from point A to point B
- midpoint Midpoint between two points
- lerp Linearly interpolate between two points
- normalize Normalize a 2D vector to unit length
- dot Dot product of two 2D vectors
- cross2d 2D cross product (scalar)
- vector_length Length of a 2D vector
- rotate_point Rotate a point around a centre
- reflect_point Reflect a point through a centre
- point_in_rect Test if point is inside a rectangle
- rects_overlap Test if two axis-aligned rectangles overlap
- circle_contains Test if circle contains a point
- circles_overlap Test if two circles overlap
- point_in_polygon Ray-cast point-in-polygon test
- line_intersection Intersection of two line segments
- polygon_area Area of a polygon (shoelace formula)
- polygon_centroid Centroid of a polygon
- polygon_perimeter Perimeter of a polygon
- convex_hull Convex hull of a point set (Graham scan)
- bounding_box Axis-aligned bounding box of a point set
- triangle_area Area of a triangle from three points
- rect_area Area of a rectangle from width and height
- rect_perimeter Perimeter of a rectangle
- circle_area Area of a circle from radius
- 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