-
Notifications
You must be signed in to change notification settings - Fork 0
Home
geom2d is a Go library for performing computational geometry operations. It provides robust and efficient primitives and algorithms for 2D geometric shapes, including:
- Points
- Line Segments
- Circles
- Rectangles
- Polygons (via PolyTree)
The library is designed for use in applications such as graphics, CAD, GIS, and game development.
-
Utilises Generics: Supports multiple numeric types (
int
,float32
,float64
), providing flexibility - Precision and Robustness: Built to handle computational geometry challenges with precision.
- Boolean Operations: Supports union, intersection, and subtraction for polygons.
- Transformations: Offers methods to scale, rotate, and reflect geometric shapes.
- Hierarchy: Manage complex polygons with holes and islands using a tree-based structure.
Start by installing the library in your Go project:
go get github.com/mikenye/geom2d
Explore the detailed documentation for each type and operation:
- Point
- LineSegment
- Circle
- Rectangle
- PolyTree
Learn how to use geom2d through practical examples:
- Basic Usage
- Boolean Polygon Operations
- Transformations
This library leverages Go's generics (introduced in Go 1.18) to support multiple numeric types, providing flexibility while maintaining type safety and performance. Generics allow functions, methods, and types to operate on different data types while using a single, reusable implementation.
In this library, T
is a type parameter that represents any signed numeric type. The constraint for T
ensures that only signed numeric types (like int
, float64
, etc.) are allowed, making it easier to use the library in a variety of applications.
For example, you can use this package with both integer and floating-point numbers:
p1 := NewPoint(3, 4) // p1 will be Point[int] type
p2 := NewPoint(3.5, 4.5) // p2 will be Point[float64] type
In both cases, T
resolves to the respective numeric type (int
or float64
).
Furthermore, the type can be specified on the function:
p3 := NewPoint[float64](3, 4) // p2 will be Point[float64] type, even though integer values given
Generics allow you to:
- Avoid type conversions (e.g., between
int
andfloat64
). - Prevent type-related runtime errors by enforcing type safety at compile time.
- Reuse code for different numeric types without duplication.
You’ll often see methods and functions returning or accepting T
. For example:
func (p Point[T]) X() T
Here, X()
returns the x-coordinate of the point, with the type of T
depending on how the Point
was created (e.g., int
, float64
, etc.). This means the method will work seamlessly whether the Point
is defined with integer or floating-point types.
While this library supports any signed numeric type for input (via T
), some functions return results as float64
by design. This is particularly relevant for functions that involve calculations which are inherently decimal, such as distances or geometric intersections.
For example, the Point[T].ProjectOntoLineSegment
function calculates the orthogonal projection of a Point
onto a LineSegment
. The result of this operation is returned as a Point[float64]
:
func (p Point[T]) DistanceToLineSegment(l LineSegment[T]) float64
Even if the input types are integers, the projection’s coordinates are likely to include decimal components. Returning a Point[float64]
ensures precision and avoids truncation during such calculations.
-
Precision: Calculations like distance or intersection often require fractional precision to be meaningful. Using
float64
avoids rounding errors or truncation. -
Simplicity: Returning
float64
avoids forcing users to handle type conversions for commonly expected results, especially in mathematical contexts.
If you need the result in integer form, the library provides helper functions for type conversion:
-
type
.AsInt()
: Truncates the decimal component of each coordinate. -
type
.AsIntRounded()
: Rounds each coordinate to the nearest integer.
p := geom2d.NewPoint[int](3, 4)
l := geom2d.NewLineSegment[int](geom2d.NewPoint(0, 0), geom2d.NewPoint(10, 0))
projection := p.ProjectOntoLineSegment(l) // Returns a Point[float64]
fmt.Println("Projection as Point[float64]:", projection)
projectionAsInt := projection.AsInt() // Truncated
projectionAsIntRounded := projection.AsIntRounded() // Rounded
fmt.Println("Projection as Point[int] (truncated):", projectionAsInt)
fmt.Println("Projection as Point[int] (rounded):", projectionAsIntRounded)
Projection as Point[float64]: Point[(3, 0)]
Projection as Point[int] (truncated): Point[(3, 0)]
Projection as Point[int] (rounded): Point[(3, 0)]
This design is applied consistently across functions that return results with inherent decimal precision.
By defaulting to float64
, the library ensures precision is preserved while leaving you in control of how to handle the result in your application.