base-geom 0.0.3

A library for 2D geometry, providing robust geometric algorithms and data structures.
Documentation
base-geom-0.0.3 has been yanked.

BaseGeom

Build

Basic 2D geometric operations

The intention of this library is to provide a foundation for 2D geometric operations, including distance computations, intersection tests.

It is intended for use in my other projects, and may not implement all possible geometric operations.

Examples

Add to Cargo.toml

base-geom = "0.0.1"

Creating and working with points (vectors)

use base_geom::prelude::*;
// Create points using the constructor or convenience function
let p1 = Point::new(1.0, 2.0);
let p2 = point(3.0, 4.0);
// Points support arithmetic operations
let sum = p1 + p2;
assert_eq!(sum.x, 4.0);
assert_eq!(sum.y, 6.0);
// Calculate distance between points
let distance = (p2 - p1).norm();
assert!((distance - 2.828427124746190).abs() < 1e-10);

Working with geometric primitives

use base_geom::prelude::*;
// Create a circle and segment
let center = point(0.0, 0.0);
let c = circle(center, 5.0);
let seg = segment(point(-3.0, 0.0), point(3.0, 0.0));
assert_eq!(c.c, center);  // Circle center field is 'c'
assert_eq!(c.r, 5.0);     // Circle radius field is 'r'
assert_eq!(seg.a.x, -3.0);
assert_eq!(seg.b.x, 3.0);

Distance computations

use base_geom::prelude::*;
// Distance from point to circle returns (distance, closest_point, is_equidistant)
let p = point(10.0, 0.0);
let c = circle(point(0.0, 0.0), 5.0);
let (dist, closest, _is_equidistant) = dist_point_circle(&p, &c);
assert_eq!(dist, 5.0);
// Distance from point to segment returns (distance, closest_point)
let seg = segment(point(0.0, 0.0), point(5.0, 0.0));
let p = point(2.5, 3.0);
let (dist, _closest) = dist_point_segment(&p, &seg);
assert_eq!(dist, 3.0);

Intersection computations

use base_geom::prelude::*;
// Test intersection between two circles
let c1 = circle(point(0.0, 0.0), 3.0);
let c2 = circle(point(4.0, 0.0), 3.0);
let result = int_circle_circle(c1, c2);
// Two circles with overlapping areas should intersect at two points
match result {
    CircleCircleConfig::NoncocircularTwoPoints(_, _) => {
        // Two intersection points found
        assert!(true);
    },
    _ => {
        // No intersection or other cases
        panic!("Expected intersection points");
    }
}

Working with arcs

[!IMPORTANT] Arcs are always CCW (counter-clockwise) in this library.

use base_geom::prelude::*;
// Create an arc from three points and radius (start, end, center, radius)
let start = point(1.0, 0.0);
let end = point(0.0, 1.0);
let center = point(0.0, 0.0);
let a = arc(start, end, center, 1.0);
assert_eq!(a.a, start);   // Arc start point field is 'a'
assert_eq!(a.b, end);     // Arc end point field is 'b'
assert_eq!(a.c, center);  // Arc center field is 'c'
assert_eq!(a.r, 1.0);     // Arc radius field is 'r'

Working with lines

use base_geom::prelude::*;
// Create a line from a point and direction vector
let origin = point(0.0, 0.0);
let direction = point(1.0, 1.0);
let l = line(origin, direction);
assert_eq!(l.origin, origin);
assert_eq!(l.dir, direction);

Working with intervals

use base_geom::prelude::*;
// Create an interval (tuple struct with two f64 values)
let iv = interval(1.0, 5.0);
assert_eq!(iv.0, 1.0);  // First endpoint
assert_eq!(iv.1, 5.0);  // Second endpoint
// Test if a value is contained in the interval
assert!(iv.contains(3.0));
assert!(!iv.contains(6.0));

Working with polylines (PVertex)

use base_geom::prelude::*;
// Create vertices for a polyline
let p1 = pvertex(point(0.0, 0.0), 0.0);
let p2 = pvertex(point(1.0, 0.0), 0.0);
let p3 = pvertex(point(1.0, 1.0), 0.0);
let polyline = vec![p1, p2, p3];
// Translate the polyline (returns a new polyline)
let offset = point(2.0, 3.0);
let translated = polyline_translate(&polyline, offset);
assert_eq!(translated[0].p.x, 2.0);
assert_eq!(translated[0].p.y, 3.0);

Arc-arc distance computation

use base_geom::prelude::*;
// Create two separate arcs
let a1 = arc(point(1.0, 0.0), point(-1.0, 0.0), point(0.0, 0.0), 1.0);
let a2 = arc(point(4.0, 0.0), point(2.0, 0.0), point(3.0, 0.0), 1.0);
// Compute distance between arcs (returns just the distance as f64)
let dist = dist_arc_arc(&a1, &a2);
assert!(dist > 0.0); // Arcs should be separated

Line-circle intersection

use base_geom::prelude::*;
// Create a line and circle that intersect
let l = line(point(-3.0, 0.0), point(1.0, 0.0)); // Horizontal line through origin
let c = circle(point(0.0, 0.0), 2.0);
let result = int_line_circle(&l, &c);
match result {
    LineCircleConfig::TwoPoints(..) => {
        // Line intersects circle at two points
        assert!(true);
    },
    _ => panic!("Expected two intersection points"),
}

Segment-segment intersection

use base_geom::prelude::*;
// Create two intersecting segments
let seg1 = segment(point(0.0, 0.0), point(2.0, 2.0));
let seg2 = segment(point(0.0, 2.0), point(2.0, 0.0));
let result = int_segment_segment(&seg1, &seg2);
match result {
    SegmentSegmentConfig::OnePoint(pt, ..) => {
        // Segments intersect at one point (should be around (1,1))
        assert!(point(1.0, 1.0).close_enough(pt, 1e-10));
    },
    _ => panic!("Expected one intersection point"),
}

Utility functions

use base_geom::prelude::*;
// Test floating point equality with tolerance
assert!(close_enough(1.0, 1.0000001, 1e-5));
assert!(!close_enough(1.0, 1.1, 1e-5));
// Check if two floats are almost equal using integer comparison
assert!(almost_equal_as_int(1.0, 1.0, 0));
// Get next/previous indices in a cyclic array
assert_eq!(next(0, 5), 1);  // next index in array of size 5
assert_eq!(next(4, 5), 0);  // wraps around
assert_eq!(prev(0, 5), 4);  // previous index wraps around
assert_eq!(prev(1, 5), 0);  // previous index

Arc-arc intersection

use base_geom::prelude::*;
// Create two intersecting arcs
let a1 = arc(point(1.0, 0.0), point(0.0, 1.0), point(0.0, 0.0), 1.0);
    let a2 = arc(point(1.0, 1.0), point(0.0, 0.0), point(1.0, 0.0), 1.0);
    let result = int_arc_arc(&a1, &a2);
    match result {
        ArcArcConfig::NonCocircularOnePoint(pt) => {
            // Arcs intersect at one point
            assert_eq!(point(0.5, 0.8660254037844386), pt);
        },
        _ => {
            // Could be two points, no intersection, or other cases
            assert!(false); // Accept other valid intersection results
        }
    }

Distance computations

use base_geom::prelude::*;
    let l = line(point(0.0, 3.0), point(1.0, 0.0)); // Line with point and direction
    let c = circle(point(0.0, 0.0), 2.0);
    let result = dist_line_circle(&l, &c);
    match result {
        DistLineCircleConfig::OnePair(dist, _param, _line_pt, _circle_pt) => {
            assert_eq!(1.0, dist);
        }
        _ => assert!(false), // Accept other valid distance results
    }
    // Distance from point to arc
    let p = point(2.0, 0.0);
    let a = arc(point(0.0, 1.0), point(1.0, 0.0), point(0.0, 0.0), 1.0);
    match dist_point_arc(&p, &a) {
        DistPointArcConfig::OnePoint(dist, _) => {
            assert_eq!(1.0, dist);
        }
        _ => assert!(false), // Accept other valid distance results
    }
    // Distance from segment to arc
    let seg = segment(point(3.0, 0.0), point(4.0, 0.0));
    let a = arc(point(0.0, 1.0), point(1.0, 0.0), point(0.0, 0.0), 1.0);
    let dist = dist_segment_arc(&seg, &a);
    assert_eq!(2.0, dist);
use base_geom::prelude::*;
    // Distance from segment to circle
    let seg = segment(point(3.0, 0.0), point(4.0, 0.0));
    let c = circle(point(0.0, 0.0), 1.0);
    let result = dist_segment_circle(&seg, &c);
    // Function returns DistSegmentCircleConfig enum
    match result {
        DistSegmentCircleConfig::OnePoint(dist, closest) => {
            assert_eq!(2.0, dist); // Distance should be non-negative
        }
        _ => assert!(false), // Accept any valid distance result
    }
    // Distance between two segments
    let seg1 = segment(point(0.0, 0.0), point(1.0, 0.0));
    let seg2 = segment(point(0.0, 2.0), point(1.0, 2.0));
    let dist = dist_segment_segment(&seg1, &seg2);
    assert_eq!(dist, 2.0); // Parallel segments 2 units apart

Intersection computations

use base_geom::prelude::*;
// Interval-interval intersection
let iv1 = interval(1.0, 5.0);
let iv2 = interval(3.0, 7.0);
let result = int_interval_interval(iv1, iv2);
match result {
    IntervalConfig::Overlap(start, end) => {
        // Intervals overlap from 3.0 to 5.0
        assert_eq!(start, 3.0);
        assert_eq!(end, 5.0);
    },
    _ => assert!(false), // Accept other valid intersection results
}
// Line-line intersection
let l1 = line(point(0.0, 0.0), point(1.0, 0.0)); // Line with origin and direction
let l2 = line(point(0.0, 0.0), point(0.0, 1.0)); // Line with origin and direction
let result = int_line_line(&l1, &l2);
match result {
    LineLineConfig::OnePoint(pt, _param1, _param2) => {
        // Lines intersect at origin
        assert_eq!(point(0.0, 0.0), pt);
    },
    _ => assert!(false), // Accept other valid intersection results
}