Source code for bosl2.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: bosl2/vectors.py
# Pure-Python port of the pieces of BOSL2's vectors.scad that bosl2/paths.py
# depends on. No osuse()/BOSL2 runtime dependency. Built on numpy: every
# function here accepts either a plain list/tuple or an ndarray and returns
# a real numpy ndarray (float64), rather than converting back to a plain
# list at the boundary -- callers that need a plain list for a native
# PythonSCAD call should do so explicitly with `.tolist()`.
#
# FileSummary: Vector predicates and scalar-vector operations (BOSL2 vectors.scad).
# FileGroup: BOSL2
import math
import numpy as np
from bosl2.math import EPSILON
[docs]
def is_vector(v, 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, s: float) -> np.ndarray:
"""Return *v* with scalar *s* added to every entry."""
return np.asarray(v, dtype=float) + s
[docs]
def unit(v, error=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