delete duplicate utils submodule
This commit is contained in:
		
							parent
							
								
									6f696bfc71
								
							
						
					
					
						commit
						db9a6269a1
					
				
							
								
								
									
										165
									
								
								masque/utils.py
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								masque/utils.py
									
									
									
									
									
								
							@ -1,165 +0,0 @@
 | 
				
			|||||||
"""
 | 
					 | 
				
			||||||
Various helper functions
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
from typing import Any, Union, Tuple, Sequence, Dict, List
 | 
					 | 
				
			||||||
from abc import ABCMeta
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import numpy
 | 
					 | 
				
			||||||
from numpy.typing import NDArray, ArrayLike
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Type definitions
 | 
					 | 
				
			||||||
layer_t = Union[int, Tuple[int, int], str]
 | 
					 | 
				
			||||||
annotations_t = Dict[str, List[Union[int, float, str]]]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def is_scalar(var: Any) -> bool:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Alias for 'not hasattr(var, "__len__")'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        var: Checks if `var` has a length.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    return not hasattr(var, "__len__")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def get_bit(bit_string: Any, bit_id: int) -> bool:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Interprets bit number `bit_id` from the right (lsb) of `bit_string` as a boolean
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        bit_string: Bit string to test
 | 
					 | 
				
			||||||
        bit_id: Bit number, 0-indexed from the right (lsb)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        Boolean value of the requested bit
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    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 boolean `value`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        bit_string: Bit string to alter
 | 
					 | 
				
			||||||
        bit_id: Bit number, 0-indexed from right (lsb)
 | 
					 | 
				
			||||||
        value: Boolean value to set bit to
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        Altered `bit_string`
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    mask = (1 << bit_id)
 | 
					 | 
				
			||||||
    bit_string &= ~mask
 | 
					 | 
				
			||||||
    if value:
 | 
					 | 
				
			||||||
        bit_string |= mask
 | 
					 | 
				
			||||||
    return bit_string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def rotation_matrix_2d(theta: float) -> NDArray[numpy.float64]:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    2D rotation matrix for rotating counterclockwise around the origin.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        theta: Angle to rotate, in radians
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        rotation matrix
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    return numpy.array([[numpy.cos(theta), -numpy.sin(theta)],
 | 
					 | 
				
			||||||
                        [numpy.sin(theta), +numpy.cos(theta)]])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def normalize_mirror(mirrored: Sequence[bool]) -> Tuple[bool, float]:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Converts 0-2 mirror operations `(mirror_across_x_axis, mirror_across_y_axis)`
 | 
					 | 
				
			||||||
    into 0-1 mirror operations and a rotation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        mirrored: `(mirror_across_x_axis, mirror_across_y_axis)`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        `mirror_across_x_axis` (bool) and
 | 
					 | 
				
			||||||
        `angle_to_rotate` in radians
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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: ArrayLike, closed_path: bool = True) -> NDArray[numpy.float64]:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Given a list of vertices, remove any consecutive duplicates.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        vertices: `[[x0, y0], [x1, y1], ...]`
 | 
					 | 
				
			||||||
        closed_path: If True, `vertices` is interpreted as an implicity-closed path
 | 
					 | 
				
			||||||
            (i.e. the last vertex will be removed if it is the same as the first)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        `vertices` with no consecutive duplicates.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    vertices = numpy.array(vertices)
 | 
					 | 
				
			||||||
    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: ArrayLike, closed_path: bool = True) -> NDArray[numpy.float64]:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Given a list of vertices, remove any superflous vertices (i.e.
 | 
					 | 
				
			||||||
        those which lie along the line formed by their neighbors)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Args:
 | 
					 | 
				
			||||||
        vertices: Nx2 ndarray of vertices
 | 
					 | 
				
			||||||
        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`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Returns:
 | 
					 | 
				
			||||||
        `vertices` with colinear (superflous) vertices removed.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    vertices = remove_duplicate_vertices(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*dx0]]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AutoSlots(ABCMeta):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Metaclass for automatically generating __slots__ based on superclass type annotations.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Superclasses must set `__slots__ = ()` to make this work properly.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is a workaround for the fact that non-empty `__slots__` can't be used
 | 
					 | 
				
			||||||
    with multiple inheritance. Since we only use multiple inheritance with abstract
 | 
					 | 
				
			||||||
    classes, they can have empty `__slots__` and their attribute type annotations
 | 
					 | 
				
			||||||
    can be used to generate a full `__slots__` for the concrete class.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __new__(cls, name, bases, dctn):
 | 
					 | 
				
			||||||
        parents = set()
 | 
					 | 
				
			||||||
        for base in bases:
 | 
					 | 
				
			||||||
            parents |= set(base.mro())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        slots = tuple(dctn.get('__slots__', tuple()))
 | 
					 | 
				
			||||||
        for parent in parents:
 | 
					 | 
				
			||||||
            if not hasattr(parent, '__annotations__'):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            slots += tuple(getattr(parent, '__annotations__').keys())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dctn['__slots__'] = slots
 | 
					 | 
				
			||||||
        return super().__new__(cls, name, bases, dctn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user