//! 2-D geometric primitives operating in `glam::DVec2` (f64) coords.
//!
//! All coordinates are in meters in a flat Euclidean plane. See spec
//! §2 (invariants) for the numerical tolerances and §3.1 (D1) for the
//! rationale behind double precision.

pub mod offset;
pub mod polygon;

pub use offset::offset_inward;
pub use polygon::{Polygon, PolygonError};

use glam::DVec2;

/// Signed area of a closed ring. Positive for CCW, negative for CW.
/// Used by both polygon validation and DCEL face classification.
#[must_use]
pub fn signed_area(verts: &[DVec2]) -> f64 {
    let n = verts.len();
    if n < 3 {
        return 0.0;
    }
    let mut a = 0.0;
    for i in 0..n {
        let p = verts[i];
        let q = verts[(i + 1) % n];
        a += p.x * q.y - q.x * p.y;
    }
    0.5 * a
}

/// Proper segment-segment intersection: true iff the two segments
/// cross strictly in their interiors. Used both by polygon
/// self-intersection checks and planar-graph validation.
#[must_use]
pub fn segments_properly_intersect(p1: DVec2, p2: DVec2, p3: DVec2, p4: DVec2) -> bool {
    let d1 = cross_sign(p3, p4, p1);
    let d2 = cross_sign(p3, p4, p2);
    let d3 = cross_sign(p1, p2, p3);
    let d4 = cross_sign(p1, p2, p4);
    (d1 * d2 < 0.0) && (d3 * d4 < 0.0)
}

fn cross_sign(a: DVec2, b: DVec2, c: DVec2) -> f64 {
    let v = (b - a).perp_dot(c - a);
    if v.abs() < EPS_GEOM {
        0.0
    } else {
        v.signum()
    }
}

/// Geometric tolerance, meters. Used for "two points are the same" and
/// "edge has zero length" tests.
pub const EPS_GEOM: f64 = 1.0e-6;

/// Area tolerance, m². Used for "polygon has zero area" and "two
/// parcels overlap" tests.
pub const EPS_AREA: f64 = 1.0e-9;

/// Angular tolerance, radians. Used for "three points are collinear"
/// tests.
pub const EPS_ANGLE: f64 = 1.0e-4;
