nodeslicer/core/geometry.py
2026-08-26 12:02:01 +03:00

62 lines
No EOL
2.3 KiB
Python

import numpy as np
import zlib
import struct
from typing import Any, Callable
def crc32_array(a: np.ndarray) -> int:
if not a.size: return 0
ac = np.ascontiguousarray(a)
mv = memoryview(ac.data).cast("B")
return zlib.adler32(mv)
def crc32_list(l: list[np.ndarray]) -> int:
if not l: return 0
h = 0
for a in l:
h = zlib.adler32(struct.pack(">I", crc32_array(a)), h)
return h
def euler_rotation_matrix(rx: float, ry: float, rz: float) -> np.ndarray:
"""Computes a 3x3 rotation matrix (Rz @ Ry @ Rx) from Euler angles in radians."""
cx, sx = np.cos(rx), np.sin(rx)
cy, sy = np.cos(ry), np.sin(ry)
cz, sz = np.cos(rz), np.sin(rz)
Rx = np.array([[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]], dtype=np.float64)
Ry = np.array([[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]], dtype=np.float64)
Rz = np.array([[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]], dtype=np.float64)
return Rz @ (Ry @ Rx)
from collections import OrderedDict
def ndarray_lru_cache(maxsize: int = 128) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
cache: OrderedDict[tuple[Any, ...], Any] = OrderedDict()
def wrapper(*args, **kwargs):
key_parts = []
for arg in args:
if isinstance(arg, np.ndarray):
key_parts.append(crc32_array(arg))
elif isinstance(arg, list) and arg and isinstance(arg[0], np.ndarray):
key_parts.append(crc32_list(arg))
else:
key_parts.append(arg)
for k, v in sorted(kwargs.items()):
if isinstance(v, np.ndarray):
key_parts.append((k, crc32_array(v)))
elif isinstance(v, list) and v and isinstance(v[0], np.ndarray):
key_parts.append((k, crc32_list(v)))
else:
key_parts.append((k, v))
key = tuple(key_parts)
if key in cache:
cache.move_to_end(key)
return cache[key]
result = func(*args, **kwargs)
cache[key] = result
if len(cache) > maxsize:
cache.popitem(last=False)
return result
return wrapper
return decorator