Header menu logo Euclid

Logo

Euclid

Euclid on nuget.org Build Status Docs Build Status Test Status license code size

A comprehensive 2D and 3D geometry library in F# without dependencies, designed for precision engineering and computational design.
It runs on JavaScript too.

Features

While most 3D geometry libraries just use a generic type ( often called Vec3 or similar) containing three floats for both points and vectors,
Euclid makes a distinction to preserve semantic meaning and avoid accidental misuse.
A point represents a position in space, while a vector represents a direction and magnitude.
Even unitized vectors are a separate type. They are guaranteed to have a length of 1.0.
This helps to skip checks for zero length vectors in many operations.

🎯 Core Geometry Types - Points (Pt, Pnt), Vectors (Vc, Vec), Unit Vectors (UnitVc, UnitVec) - Lines, Planes, Boxes, Bounding Boxes, Polylines - Rotations, Quaternions, 4x4 and rigid orthonormal 4x3 matrices

Key Advantages - Zero dependencies - lightweight and self-contained - Double precision - engineered for CAD/manufacturing accuracy - Immutable types - functional programming friendly - Cross-platform - compiles to .NET, JavaScript, TypeScript, Rust, Python via Fable - Performant - All small types are structs, functions are often inline and try to minimize the allocation of intermediate objects.

🔧 Design & Manufacturing Focus - Optimized for design, construction, and manufacturing workflows - Integrates seamlessly with Rhino3D - Right-handed coordinate system (Z-up) matching industry standards

Installation

Add Euclid to your F# project via NuGet:

dotnet add package Euclid

Or in F# scripting:

#r "nuget: Euclid"

Quick Start

open Euclid

// Create 3D points and vectors
let point1 = Pnt(1.0, 2.0, 3.0)
let point2 = Pnt(4.0, 5.0, 6.0)
let vector = Vec(1.0, 1.0, 0.0)

// Calculate distance
let distance = Pnt.distance point1 point2

// Create and use unit vectors
let unitVec = vector.Unitized  // returns a UnitVec

// Transform with 4x4 matrix
let matrix =
    Matrix.createShear(3.0, 0, 0, 0, 0, 0)
    *** // Combine transformations
    Matrix.createRotationZ 45

point1
|> Pnt.translate vector
|> Pnt.scale 3.0
|> Pnt.transform matrix

Coordinate System

This library uses a right-handed coordinate system with the Z-axis pointing up.

Same as in: Rhino3D, Blender, SketchUp, Revit, AutoCAD ❌ Different from: Unity, Unreal Engine, Maya

This choice aligns with industry-standard CAD and architectural software.

Design Philosophy

Points vs Vectors

When a 4x4 transformation matrix is applied: - Points: Undergo full transformation (rotation, scaling, translation) - Vectors: Only rotate and scale (no translation)

This follows homogeneous coordinate conventions where vectors have w=0.

Naming Conventions

Type

2D

3D

Point

Pt

Pnt

Vector

Vc

Vec

Unit Vector

UnitVc

UnitVec

Function Patterns

Functions are available in multiple forms:

// Static module function (lowercase)
let normalized = Vec.unitized myVector

// Instance method/property (uppercase)
let normalized = myVector.Unitized

API Documentation

📚 Full API Reference: goswinr.github.io/Euclid

Platform Support

Thanks to Fable, Euclid can be used not only on .NET but also in JavaScript, TypeScript, Rust, and Python.

Development

Prerequisites

Building from Source

git clone https://github.com/goswinr/Euclid.git
cd Euclid
dotnet build

Testing

Tests run on both .NET and JavaScript with TypeScript build verification.

.NET Testing

dotnet run --project ./Tests/Euclid.Tests.fsproj

JavaScript Testing

npm run test --prefix ./Tests

The test suite ensures cross-platform compatibility and verifies TypeScript type definitions.

Contributing

Contributions are welcome!

Changelog

📋 See CHANGELOG.md for version history.

Related Projects

🦏 Euclid.Rhino - Rhino3D integration

License

MIT

namespace Euclid
val point1: Pnt
Multiple items
[<Struct>] type Pnt = new: x: float * y: float * z: float -> Pnt val X: float val Y: float val Z: float override ToString: unit -> string static member ( * ) : a: Pnt * f: float -> Pnt + 1 overload static member (+) : a: Pnt * b: Pnt -> Pnt + 2 overloads static member (-) : a: Pnt * b: Pnt -> Vec + 2 overloads static member (/) : p: Pnt * f: float -> Pnt static member DivideByInt: pt: Pnt * i: int -> Pnt ...
<summary> An immutable 3D point. Made up from 3 floats: X, Y, and Z. A 3D point represents a location in space, but not direction or an offset. (use Vec for that.) (2D Points are called 'Pt' ) </summary>

--------------------
Pnt ()
new: x: float * y: float * z: float -> Pnt
val point2: Pnt
val vector: Vec
Multiple items
[<Struct>] type Vec = new: x: float * y: float * z: float -> Vec val X: float val Y: float val Z: float override ToString: unit -> string static member ( * ) : a: Vec * f: float -> Vec + 1 overload static member ( *** ) : a: Vec * b: Vec -> float static member (+) : a: Vec * b: Vec -> Vec static member (-) : a: Vec * b: Vec -> Vec static member (/) : v: Vec * f: float -> Vec ...
<summary> An immutable 3D vector of any length. Made up from 3 floats: X, Y, and Z. A 3D vector represents a direction or an offset in space, but not a location. A 4x4 transformation matrix applied to a vector will only rotate and scale the vector but not translate it. (3D unit-vectors of length 1.0 are called 'UnitVec' ) (2D vectors are called 'Vc' ) </summary>

--------------------
Vec ()
new: x: float * y: float * z: float -> Vec
val distance: float
static member Pnt.distance: a: Pnt -> b: Pnt -> float
val unitVec: UnitVec
property Vec.Unitized: UnitVec with get
<summary> Returns the 3D vector unitized. Fails with EuclidDivByZeroException if the length of the vector is too small (1e-16) to unitize. </summary>
val matrix: Matrix
Multiple items
[<Struct>] type Matrix = new: m11: float * m21: float * m31: float * x41: float * m12: float * m22: float * m32: float * y42: float * m13: float * m23: float * m33: float * z43: float * m14: float * m24: float * m34: float * m44: float -> Matrix val M11: float val M21: float val M31: float val X41: float val M12: float val M22: float val M32: float val Y42: float val M13: float ...
<summary> An immutable 4x4 transformation matrix. The matrix is represented in the following column-vector syntax form: M11 M21 M31 X41 M12 M22 M32 Y42 M13 M23 M33 Z43 M14 M24 M34 M44 Where X41, Y42 and Z43 refer to the translation part of the matrix. Note: Never use the struct default constructor Matrix() as it will create an invalid zero Matrix. Use Matrix.create or Matrix.createUnchecked instead. </summary>

--------------------
Matrix ()
new: m11: float * m21: float * m31: float * x41: float * m12: float * m22: float * m32: float * y42: float * m13: float * m23: float * m33: float * z43: float * m14: float * m24: float * m34: float * m44: float -> Matrix
static member Matrix.createShear: xy: float * xz: float * yx: float * yz: float * zx: float * zy: float -> Matrix
static member Matrix.createRotationZ: angleDegrees: float -> Matrix
static member Pnt.translate: shift: Vec -> pt: Pnt -> Pnt
static member Pnt.scale: f: float -> pt: Pnt -> Pnt
static member Pnt.transform: m: Matrix -> p: Pnt -> Pnt
val normalized: obj

Type something to start searching.