48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
"""Shared numeric tolerances for builder geometry and parameter comparisons."""
|
|
from math import isclose, remainder, tau
|
|
from typing import Any
|
|
|
|
import numpy
|
|
|
|
|
|
GEOMETRY_RTOL = 1e-5
|
|
GEOMETRY_ATOL = 1e-8
|
|
DOMAIN_RTOL = 1e-9
|
|
DOMAIN_ATOL = 1e-12
|
|
MANHATTAN_ANGLE_RTOL = 1e-9
|
|
MANHATTAN_ANGLE_ATOL = 1e-9
|
|
|
|
|
|
def scalar_close(a: float, b: float) -> bool:
|
|
"""Match the solver's existing scalar-comparison behavior."""
|
|
return isclose(float(a), float(b), rel_tol=GEOMETRY_RTOL, abs_tol=GEOMETRY_ATOL)
|
|
|
|
|
|
def array_close(a: Any, b: Any) -> bool:
|
|
"""Match NumPy's historical builder geometry-comparison behavior."""
|
|
return bool(numpy.allclose(a, b, rtol=GEOMETRY_RTOL, atol=GEOMETRY_ATOL))
|
|
|
|
|
|
def angles_equal(a: float, b: float) -> bool:
|
|
"""Return true when two rotations are equal modulo one full turn."""
|
|
delta = remainder(float(a) - float(b), tau)
|
|
return isclose(delta, 0.0, rel_tol=GEOMETRY_RTOL, abs_tol=GEOMETRY_ATOL)
|
|
|
|
|
|
def manhattan_axis(rotation: float) -> int | None:
|
|
"""Return 0 for horizontal, 1 for vertical, or None for a non-cardinal angle."""
|
|
angle = float(rotation) % (numpy.pi / 2)
|
|
if isclose(
|
|
angle,
|
|
0.0,
|
|
rel_tol=MANHATTAN_ANGLE_RTOL,
|
|
abs_tol=MANHATTAN_ANGLE_ATOL,
|
|
) or isclose(
|
|
angle,
|
|
numpy.pi / 2,
|
|
rel_tol=MANHATTAN_ANGLE_RTOL,
|
|
abs_tol=MANHATTAN_ANGLE_ATOL,
|
|
):
|
|
quarter_turn = round(float(rotation) / (numpy.pi / 2))
|
|
return quarter_turn % 2
|
|
return None
|