Odin bindings for libtess2, a port of the GLU tessellator: a scanline tessellator useful for polygon boolean operations and offsetting.
Setup
Clone this repository into your Odin project, then run the included build script from the source/ directory:
cdsource
./build_libtess2.sh # macOS / Linux
build_libtess2.bat # Windows (Developer Command Prompt)
This will pull in the libtess2 source, patch it to enable double precision, and compile it to bin/.
Usage
Convenience API
The simplest way to use the library. Each call creates and destroys a tessellator internally.
import tess "path/to/libtess2"
polygon := [][2]f64{
{0, 0}, {100, 0}, {100, 100}, {0, 100},
}
// Boolean operations — pass a slice of polygonsunion := tess.union_polygons({polygon_a, polygon_b})
defer tess.delete_contours(union)
diff := tess.difference_polygons({polygon_a, polygon_b})
defer tess.delete_contours(diff)
isect := tess.intersect_polygons({polygon_a, polygon_b})
defer tess.delete_contours(isect)
xor := tess.xor_polygons({polygon_a, polygon_b})
defer tess.delete_contours(xor)
// Uniform offset — join type baked into the call name
inset_round := tess.offset_polygon_round(polygon, {-10.0}, arc_resolution = 0.5)
defer tess.delete_contours(inset_round)
inset_miter := tess.offset_polygon_miter(polygon, {-10.0}, miter_limit = 2.0)
defer tess.delete_contours(inset_miter)
inset_bevel := tess.offset_polygon_bevel(polygon, {-10.0})
defer tess.delete_contours(inset_bevel)
// Per-edge offset — one delta per edge, last value is reused for remaining edges
deltas := []f64{-5, -20, -5, -5}
inset2 := tess.offset_polygon_round(polygon, deltas, arc_resolution = 0.5)
defer tess.delete_contours(inset2)
// Results are [][][2]f64 — a slice of contours, each a slice of verticesfor contour in inset_round {
for vertex in contour {
fmt.println(vertex)
}
}
Join Types
Function
Description
Extra parameter
offset_polygon_round
Arc join, chord-deviation controlled
arc_resolution: f64
offset_polygon_miter
Sharp point join, with spike limit
miter_limit: f64
offset_polygon_bevel
Straight cut join
—
All three accept either a single uniform delta {-10.0} or a per-edge delta slice. Negative delta shrinks, positive expands.
arc_resolution is a chord deviation tolerance in the same units as your geometry. A value of 0.5 works well for geometry in the 50–150 unit range. Smaller values produce smoother arcs at the cost of more vertices.
miter_limit caps how far a miter spike can extend from the original vertex, expressed as a multiple of the offset delta. When exceeded, the join falls back to bevel. 2.0 is conservative (Clipper default), 4.0 is more permissive (SVG default).
Knobby API
For full control, use offset_polygon_edges directly. This is useful when you need per-edge deltas with heterogeneous signs and want to explicitly declare whether the contour is being inset or outset.
// All knobs exposed — most callers should prefer the named wrappers above
result := tess.offset_polygon_edges(
polygon,
deltas,
join_type = .Round,
arc_resolution = 0.5,
miter_limit = 2.0,
allocator = context.allocator,
)
defer tess.delete_contours(result)
Stack-based API
For performance-sensitive code, reuse a tessellator across multiple operations by pushing contours and tessellating in sequence.