You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
masque/masque/utils.py

99 lines
3.0 KiB
Python

"""
Various helper functions
"""
from typing import Any, Union, Tuple
import numpy
# Type definitions
vector2 = Union[numpy.ndarray, Tuple[float, float]]
def is_scalar(var: Any) -> bool:
"""
Alias for 'not hasattr(var, "__len__")'
:param var: Checks if var has a length.
"""
return not hasattr(var, "__len__")
def get_bit(bit_string: Any, bit_id: int) -> bool:
"""
Returns true iff bit number 'bit_id' from the right of 'bit_string' is 1
:param bit_string: Bit string to test
:param bit_id: Bit number, 0-indexed from the right (lsb)
:return: value of the requested bit (bool)
"""
return bit_string & (1 << bit_id) != 0
def set_bit(bit_string: Any, bit_id: int, value: bool) -> Any:
"""
Returns 'bit_string' with bit number 'bit_id' set to 'value'.
:param bit_string: Bit string to alter
:param bit_id: Bit number, 0-indexed from right (lsb)
:param value: Boolean value to set bit to
:return: Altered 'bit_string'
"""
mask = (1 << bit_id)
bit_string &= ~mask
if value:
bit_string |= mask
return bit_string
def rotation_matrix_2d(theta: float) -> numpy.ndarray:
"""
2D rotation matrix for rotating counterclockwise around the origin.
:param theta: Angle to rotate, in radians
:return: rotation matrix
"""
return numpy.array([[numpy.cos(theta), -numpy.sin(theta)],
[numpy.sin(theta), +numpy.cos(theta)]])
def normalize_mirror(mirrored: Tuple[bool, bool]) -> Tuple[bool, float]:
mirrored_x, mirrored_y = mirrored
mirror_x = (mirrored_x != mirrored_y) #XOR
angle = numpy.pi if mirrored_y else 0
return mirror_x, angle
def remove_duplicate_vertices(vertices: numpy.ndarray, closed_path: bool = True) -> numpy.ndarray:
duplicates = (vertices == numpy.roll(vertices, 1, axis=0)).all(axis=1)
if not closed_path:
duplicates[0] = False
return vertices[~duplicates]
def remove_colinear_vertices(vertices: numpy.ndarray, closed_path: bool = True) -> numpy.ndarray:
'''
Given a list of vertices, remove any superflous vertices (i.e.
those which lie along the line formed by their neighbors)
:param vertices: Nx2 ndarray of vertices
:param closed_path: If True, the vertices are assumed to represent an implicitly
closed path. If False, the path is assumed to be open. Default True.
:return:
'''
vertices = numpy.array(vertices)
# Check for dx0/dy0 == dx1/dy1
dv = numpy.roll(vertices, -1, axis=0) - vertices # [y1-y0, y2-y1, ...]
dxdy = dv * numpy.roll(dv, 1, axis=0)[:, ::-1] #[[dx0*(dy_-1), (dx_-1)*dy0], dx1*dy0, dy1*dy0]]
dxdy_diff = numpy.abs(numpy.diff(dxdy, axis=1))[:, 0]
err_mult = 2 * numpy.abs(dxdy).sum(axis=1) + 1e-40
slopes_equal = (dxdy_diff / err_mult) < 1e-15
if not closed_path:
slopes_equal[[0, -1]] = False
return vertices[~slopes_equal]