Source code for pybosl2.vectors

# Copyright (c) 2026, pinkfish
#
# Licensed under the BSD 2-Clause License. See the LICENSE file in the project
# root for the full license text.
# SPDX-License-Identifier: BSD-2-Clause

# LibFile: pybosl2/vectors.py
#    Pure-Python port of the pieces of BOSL2's vectors.scad that pybosl2/paths.py
#    depends on.  All vector-valued parameters accept :class:`~pybosl2.points.Point`
#    or any :class:`~collections.abc.Sequence` of floats.
#
# FileSummary: Vector predicates and scalar-vector operations (BOSL2 vectors.scad).
# DocCategory: Math & geometry
# FileGroup: BOSL2

import math
from collections.abc import Sequence

import numpy as np

from pybosl2.math import EPSILON
from pybosl2.points import Point


[docs] def is_vector( v: Point | Sequence[float] | np.ndarray, length: int | None = None, zero: bool | None = None, eps: float = EPSILON, ) -> bool: """True if *v* is a list/tuple/ndarray of finite numbers (optionally of a given length and/or zero-ness). """ if isinstance(v, np.ndarray): if v.ndim != 1 or v.size == 0: return False elif not isinstance(v, (list, tuple)) or len(v) == 0: return False for x in v: if ( isinstance(x, bool) or not isinstance(x, (int, float, np.floating, np.integer)) or math.isinf(x) or math.isnan(x) ): return False if length is not None and len(v) != length: return False if zero is not None: is_zero = float(np.linalg.norm(np.asarray(v, dtype=float))) < eps if is_zero != zero: return False return True
[docs] def add_scalar(v: Point | Sequence[float] | np.ndarray, s: float) -> np.ndarray: """Return *v* with scalar *s* added to every entry.""" return np.asarray(v, dtype=float) + s
[docs] def unit( v: Point | Sequence[float] | np.ndarray, error: Point | Sequence[float] | np.ndarray | None = None, ) -> np.ndarray: """Normalize *v* to unit length. If *v* has (near) zero length, returns *error* if given, else raises ValueError (matching BOSL2's default assert-on-zero-vector behavior). """ arr = np.asarray(v, dtype=float) sides = float(np.linalg.norm(arr)) if sides < EPSILON: if error is not None: return np.asarray(error, dtype=float) raise ValueError("Cannot normalize a zero vector") return arr / sides