Source code for simvx.core.navigation3d.mesh

"""NavigationMesh3D: triangle-based navmesh with A* pathfinding."""

import heapq
import logging
import math
import random

import numpy as np

from ..math.types import Vec3
from ._helpers import (
    _closest_point_on_triangle_np,
    _interpolate_height,
    _point_in_polygon_xz,
    _point_in_triangle_xz,
    _vec3_dist,
)

log = logging.getLogger(__name__)


def _segments_cross_xz(
    ax: float,
    az: float,
    bx: float,
    bz: float,
    cx: float,
    cz: float,
    dx: float,
    dz: float,
) -> bool:
    """Test whether segments a-b and c-d properly cross in the XZ plane.

    "Properly" means each segment has one endpoint strictly to either side of
    the other's line. Segments that merely touch at an endpoint, or that lie
    along the same line, are not crossings: they enclose no area, so they cannot
    on their own make two polygons overlap.
    """

    def side(px: float, pz: float, qx: float, qz: float, rx: float, rz: float) -> float:
        return (qx - px) * (rz - pz) - (qz - pz) * (rx - px)

    d1 = side(cx, cz, dx, dz, ax, az)
    d2 = side(cx, cz, dx, dz, bx, bz)
    if not ((d1 > 0.0 > d2) or (d2 > 0.0 > d1)):
        return False
    d3 = side(ax, az, bx, bz, cx, cz)
    d4 = side(ax, az, bx, bz, dx, dz)
    return (d3 > 0.0 > d4) or (d4 > 0.0 > d3)


def _dist_xyz(a: tuple[float, float, float], b: tuple[float, float, float]) -> float:
    """Euclidean distance between two positions held as plain float triples."""
    dx, dy, dz = a[0] - b[0], a[1] - b[1], a[2] - b[2]
    return math.sqrt(dx * dx + dy * dy + dz * dz)


def _closest_point_on_segment_xz(
    px: float,
    pz: float,
    ax: float,
    az: float,
    bx: float,
    bz: float,
) -> tuple[float, float]:
    """Closest point to (px, pz) on the segment a-b, in the XZ plane."""
    dx, dz = bx - ax, bz - az
    length_sq = dx * dx + dz * dz
    if length_sq < 1e-18:
        return ax, az
    t = ((px - ax) * dx + (pz - az) * dz) / length_sq
    t = 0.0 if t < 0.0 else (1.0 if t > 1.0 else t)
    return ax + t * dx, az + t * dz