Skip to content

Commit

Permalink
Implemented calculation for nearest integer-valued point in a given n…
Browse files Browse the repository at this point in the history
…ormal direction relative to a control point
  • Loading branch information
timbernat committed Apr 25, 2024
1 parent 5945f3c commit ce8aaf6
Showing 1 changed file with 34 additions and 2 deletions.
36 changes: 34 additions & 2 deletions polymerist/maths/lattices/coordops.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
from itertools import product as cartesian_product

from ...genutils.typetools.numpytypes import Shape, N, M, DType
from ...genutils.typetools.categorical import Numeric


# COORDINATE-TO-POINT DISTANCES
def mean_coord(coords : np.ndarray[Shape[M, N], DType], weights : Optional[np.ndarray[Shape[M, 1], DType]]=None) -> np.ndarray[Shape[N], DType]:
'''The average (center-of-mass) coordinate of a vector of coordinates, with optional array of weightsfor each coordinates'''
if weights is None:
Expand All @@ -27,7 +29,7 @@ def dists_to_centroid(coords : np.ndarray[Shape[M, N], DType], norm_order : Opti
'''The distance of each coordinate in an array of coordinates to the coordinates' centroid'''
return dists_to_point(coords, point=mean_coord(coords), norm_order=norm_order)


# BOUNDING BOXES
def bounding_box_dims(coords : np.ndarray[Shape[M, N], DType]) -> np.ndarray[Shape[N], DType]:
'''The N-dimensional tight bounding box side lengths around an array of coordinates'''
return coords.ptp(axis=0)
Expand All @@ -47,4 +49,34 @@ def coords_inside_bbox(coords : np.ndarray[Shape[M, N], DType], lower : np.ndarr
'''Boolean mask of whether coordinates are within the boundaries of some bounding box
With strict=True, points on the boundary are not considered inside; with strict=False, points on the boundary are considered insde as well'''
less_funct = np.less if strict else np.less_equal # set "<" vs "<=" check by strict flag
return less_funct(lower, coords) & less_funct(coords, upper)
return less_funct(lower, coords) & less_funct(coords, upper)

# NORMAL VECTORS
def nearest_int_coord_along_normal(point : np.ndarray[Shape[N], Numeric], normal : np.ndarray[Shape[N], Numeric]) -> np.ndarray[Shape[N], int]:
'''
Takes an N-dimensional control point and an N-dimensional normal vector
Returns the integer-valued point nearest to the control point which lies in
the normal direction relative to the control point
'''
# Check that inputs have vector-like shapes and compatible dimensions
assert(point.ndim == 1)
assert(normal.ndim == 1)
assert(point.size == normal.size)

min_int_bound_point = np.floor(point)
max_int_bound_point = np.ceil( point)
if np.isclose(min_int_bound_point, max_int_bound_point).all(): # edge case: if already on an integer-valued point, the fllor and ceiling will be identical
int_point = point
else:
int_bound_extrema = np.vstack([min_int_bound_point, max_int_bound_point]) # package extremal pointsinto single array
int_bound_points = bounding_box_points(int_bound_extrema) # expand extremal points into complete N-dimensional integer box around the control point
dots = np.inner((int_bound_points - point), normal) # take dot product between the normal and the direction vectors from the control point to the integer bounding points
i = dots.argmax() # position of integer point in most similar direction to normal

if dots[i] > 0:
int_point = int_bound_points[i]
else:
raise ValueError(f'Could not locate valid integral point in normal direction (closest found was {int_bound_points[i]} with dot product {dots[i]})')
pass

return int_point.astype(int)

0 comments on commit ce8aaf6

Please sign in to comment.