base-geom 0.1.1

A library for 2D geometry, providing robust geometric algorithms for intersection/distance between circular arcs/line segments.
Documentation
base-geom-0.1.1 has been yanked.

BaseGeom

Build

Basic 2D geometric operations

The intention of this library is to provide a foundation for 2D geometric operations. It includes mostly basic operations like point manipulation and distance/intersection between line segments and circle arcs.

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.1.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));

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
}