hub/venv/lib/python3.7/site-packages/trimesh/interval.py

126 lines
3.4 KiB
Python

"""
interval.py
--------------
Deal with 1D intervals which are defined by:
[start position, end position]
"""
import numpy as np
def check(a, b, digits):
"""
Check input ranges, convert them to vector form,
and get a fixed precision integer version of them.
Parameters
--------------
a : (2, ) or (2, n) float
Start and end of a 1D interval
b : (2, ) or (2, n) float
Start and end of a 1D interval
digits : int
How many digits to consider
Returns
--------------
a : (2, n) float
Ranges as vector
b : (2, n) float
Ranges as vector
a_int : (2, n) int64
Ranges rounded to digits, as vector
b_int : (2, n) int64
Ranges rounded to digits, as vector
is_1D : bool
If True, input was single pair of ranges
"""
a = np.array(a, dtype=np.float64)
b = np.array(b, dtype=np.float64)
if a.shape != b.shape or a.shape[-1] != 2:
raise ValueError('ranges must be identical and (2,)!')
# if input was single interval reshape it here
is_1D = False
if len(a.shape) == 1:
a = a.reshape((-1, 2))
b = b.reshape((-1, 2))
is_1D = True
# make sure ranges are sorted
a.sort(axis=1)
b.sort(axis=1)
# compare in fixed point as integers
a_int = (a * 10**digits).round().astype(np.int64)
b_int = (b * 10**digits).round().astype(np.int64)
return a, b, a_int, b_int, is_1D
def intersection(a, b, digits=8):
"""
Given a pair of ranges, merge them in to
one range if they overlap at all
Parameters
--------------
a : (2, ) float
Start and end of a 1D interval
b : (2, ) float
Start and end of a 1D interval
digits : int
How many digits to consider
Returns
--------------
intersects : bool or (n,) bool
Indicates if the ranges overlap at all
new_range : (2, ) or (2, 2) float
The unioned range from the two inputs,
or both of the original ranges if not overlapping
"""
# check shape and convert
a, b, a_int, b_int, is_1D = check(a, b, digits)
# what are the starting and ending points of the overlap
overlap = np.zeros(a.shape, dtype=np.float64)
# A fully overlaps B
current = np.logical_and(a_int[:, 0] <= b_int[:, 0],
a_int[:, 1] >= b_int[:, 1])
overlap[current] = b[current]
# B fully overlaps A
current = np.logical_and(a_int[:, 0] >= b_int[:, 0],
a_int[:, 1] <= b_int[:, 1])
overlap[current] = a[current]
# A starts B ends
# A:, 0 B:, 0 A:, 1 B:, 1
current = np.logical_and(
np.logical_and(a_int[:, 0] <= b_int[:, 0],
b_int[:, 0] < a_int[:, 1]),
a_int[:, 1] < b_int[:, 1])
overlap[current] = np.column_stack([b[current][:, 0],
a[current][:, 1]])
# B starts A ends
# B:, 0 A:, 0 B:, 1 A:, 1
current = np.logical_and(
np.logical_and(b_int[:, 0] <= a_int[:, 0],
a_int[:, 0] < b_int[:, 1]),
b_int[:, 1] < a_int[:, 1])
overlap[current] = np.column_stack([a[current][:, 0],
b[current][:, 1]])
# is range overlapping at all
intersects = overlap.ptp(axis=1) > 10**-digits
if is_1D:
return intersects[0], overlap[0]
return intersects, overlap