src.acoustools.Paths.Interpolate
1import torch 2from torch import Tensor 3import itertools, math 4 5from acoustools.Paths.Distances import total_distance 6from acoustools.Utilities.Setup import device, DTYPE 7from acoustools.Utilities.Points import create_points 8 9def interpolate_path(path: list[Tensor], n:int, return_distance:bool = False) -> list[Tensor]: 10 ''' 11 Calls `interpolate_points on all adjacent pairs of points in path`\n 12 :param n: TOTAL number of points to interpolate (will be split between pairs) 13 :param return_distance: if `True` will also return total distance 14 :return: Path and optionally total distance 15 ''' 16 points = [] 17 total_dist, distances = total_distance(path) 18 19 20 for i,(p1, p2) in enumerate(itertools.pairwise(path)): 21 d = distances[i] 22 num = round((n * (d / total_dist )).item()) 23 points += interpolate_points(p1, p2, num) 24 25 if return_distance: 26 return points, total_dist 27 return points 28 29def interpolate_points(p1:Tensor, p2:Tensor, n:int)-> list[Tensor]: 30 ''' 31 Interpolates `n` points between `p1` and `p2`\n 32 :param p1: First point 33 :param p2: Second point 34 :param n: number of points to interpolate 35 :return: Path 36 ''' 37 if n > 0: 38 vec = (p2 - p1) / n 39 points = [] 40 for i in range(n): 41 points.append(p1 + i * vec) 42 else: 43 return p1 44 45 return points 46 47 48 49def interpolate_path_to_distance(path: list[Tensor], max_diatance:float=0.001) -> list[Tensor]: 50 ''' 51 Calls `interpolate_points on all adjacent pairs of points in path to make distance < max_distance` \n 52 :param max_diatance: max_distance between adjacent points 53 :return: Path and optionally total distance 54 ''' 55 points = [] 56 57 for i,(p1, p2) in enumerate(itertools.pairwise(path)): 58 total_dist, distances = total_distance([p1,p2]) 59 n = math.ceil(total_dist / max_diatance) 60 points += interpolate_points(p1, p2, n) 61 62 return points 63 64 65def interpolate_arc(start:Tensor, end:Tensor|None=None, origin:Tensor=None, n:int=100, 66 up:Tensor=torch.tensor([0,0,1.0]), anticlockwise:bool=False) -> list[Tensor]: 67 68 ''' 69 Creates an arc between start and end with origin at origin\n 70 :param start: Point defining start of the arc 71 :param end: Point defining the end of the arc (this is not checked to lie on the arc - the arc will end at the same angle as arc) 72 :param origin: Point defining origin of the arc 73 :param n: number of points to interpolate along the arc. Default 100 74 :param up: vector defining which way is 'up'. Default to positive z 75 :param anticlickwise: If true will create anticlockwise arc. Otherwise will create clockwise arc 76 :returns Points: List of points 77 78 ''' 79 80 if origin is None: 81 raise ValueError('Need to pass a value for origin') 82 83 radius = torch.sqrt(torch.sum((start - origin)**2)) 84 85 start_vec = (start-origin) 86 87 up = up.to(device).to(float) 88 89 if end is not None: 90 end_vec = (end-origin) 91 cos = torch.dot(start_vec.squeeze(),end_vec.squeeze()) / (torch.linalg.vector_norm(start_vec.squeeze()) * torch.linalg.vector_norm(end_vec.squeeze())) 92 angle = torch.acos(cos) 93 else: 94 end = start.clone() + 1e-10 95 end_vec = (end-origin) 96 angle = torch.tensor([3.14159 * 2]).to(device) 97 98 w = torch.cross(start_vec,end_vec,dim=1).to(float) 99 clockwise = torch.dot(w.squeeze(),up.squeeze())<0 100 101 u = start_vec.to(float) 102 u/= torch.linalg.vector_norm(start_vec.squeeze()) 103 if (w == 0).all(): 104 w += torch.ones_like(w) * 1e-10 105 106 v = torch.cross(w,u,dim=1) 107 v /= torch.linalg.vector_norm(v.squeeze()) 108 109 if clockwise == anticlockwise: #Should be false 110 angle = 2*3.14159 - angle 111 direction= -1 112 else: 113 direction = 1 114 115 points = [] 116 for i in range(n): 117 t = direction * ((angle) / n) * i 118 p = radius * (torch.cos(t)*u + torch.sin(t)*v) + origin 119 points.append(p) 120 121 return points 122 123 124def interpolate_circle(origin:Tensor, radius:float=1.0, plane='xy', n:int=100) -> list[Tensor]: 125 points = [] 126 for i in range(n): 127 a = radius * math.sin((2*math.pi*i) / n) 128 b = radius * math.cos((2*math.pi*i) / n) 129 if plane == 'xy': 130 p = create_points(1,1,a,b,0) 131 elif plane == 'xz': 132 p = create_points(1,1,a,0,b) 133 elif plane == 'yz': 134 p = create_points(1,1,0,a,b) 135 points.append(p) 136 return points
def
interpolate_path( path: list[torch.Tensor], n: int, return_distance: bool = False) -> list[torch.Tensor]:
10def interpolate_path(path: list[Tensor], n:int, return_distance:bool = False) -> list[Tensor]: 11 ''' 12 Calls `interpolate_points on all adjacent pairs of points in path`\n 13 :param n: TOTAL number of points to interpolate (will be split between pairs) 14 :param return_distance: if `True` will also return total distance 15 :return: Path and optionally total distance 16 ''' 17 points = [] 18 total_dist, distances = total_distance(path) 19 20 21 for i,(p1, p2) in enumerate(itertools.pairwise(path)): 22 d = distances[i] 23 num = round((n * (d / total_dist )).item()) 24 points += interpolate_points(p1, p2, num) 25 26 if return_distance: 27 return points, total_dist 28 return points
Calls interpolate_points on all adjacent pairs of points in path
Parameters
- n: TOTAL number of points to interpolate (will be split between pairs)
- return_distance: if
Truewill also return total distance
Returns
Path and optionally total distance
def
interpolate_points(p1: torch.Tensor, p2: torch.Tensor, n: int) -> list[torch.Tensor]:
30def interpolate_points(p1:Tensor, p2:Tensor, n:int)-> list[Tensor]: 31 ''' 32 Interpolates `n` points between `p1` and `p2`\n 33 :param p1: First point 34 :param p2: Second point 35 :param n: number of points to interpolate 36 :return: Path 37 ''' 38 if n > 0: 39 vec = (p2 - p1) / n 40 points = [] 41 for i in range(n): 42 points.append(p1 + i * vec) 43 else: 44 return p1 45 46 return points
Interpolates n points between p1 and p2
Parameters
- p1: First point
- p2: Second point
- n: number of points to interpolate
Returns
Path
def
interpolate_path_to_distance( path: list[torch.Tensor], max_diatance: float = 0.001) -> list[torch.Tensor]:
50def interpolate_path_to_distance(path: list[Tensor], max_diatance:float=0.001) -> list[Tensor]: 51 ''' 52 Calls `interpolate_points on all adjacent pairs of points in path to make distance < max_distance` \n 53 :param max_diatance: max_distance between adjacent points 54 :return: Path and optionally total distance 55 ''' 56 points = [] 57 58 for i,(p1, p2) in enumerate(itertools.pairwise(path)): 59 total_dist, distances = total_distance([p1,p2]) 60 n = math.ceil(total_dist / max_diatance) 61 points += interpolate_points(p1, p2, n) 62 63 return points
Calls interpolate_points on all adjacent pairs of points in path to make distance < max_distance
Parameters
- max_diatance: max_distance between adjacent points
Returns
Path and optionally total distance
def
interpolate_arc( start: torch.Tensor, end: torch.Tensor | None = None, origin: torch.Tensor = None, n: int = 100, up: torch.Tensor = tensor([0., 0., 1.]), anticlockwise: bool = False) -> list[torch.Tensor]:
66def interpolate_arc(start:Tensor, end:Tensor|None=None, origin:Tensor=None, n:int=100, 67 up:Tensor=torch.tensor([0,0,1.0]), anticlockwise:bool=False) -> list[Tensor]: 68 69 ''' 70 Creates an arc between start and end with origin at origin\n 71 :param start: Point defining start of the arc 72 :param end: Point defining the end of the arc (this is not checked to lie on the arc - the arc will end at the same angle as arc) 73 :param origin: Point defining origin of the arc 74 :param n: number of points to interpolate along the arc. Default 100 75 :param up: vector defining which way is 'up'. Default to positive z 76 :param anticlickwise: If true will create anticlockwise arc. Otherwise will create clockwise arc 77 :returns Points: List of points 78 79 ''' 80 81 if origin is None: 82 raise ValueError('Need to pass a value for origin') 83 84 radius = torch.sqrt(torch.sum((start - origin)**2)) 85 86 start_vec = (start-origin) 87 88 up = up.to(device).to(float) 89 90 if end is not None: 91 end_vec = (end-origin) 92 cos = torch.dot(start_vec.squeeze(),end_vec.squeeze()) / (torch.linalg.vector_norm(start_vec.squeeze()) * torch.linalg.vector_norm(end_vec.squeeze())) 93 angle = torch.acos(cos) 94 else: 95 end = start.clone() + 1e-10 96 end_vec = (end-origin) 97 angle = torch.tensor([3.14159 * 2]).to(device) 98 99 w = torch.cross(start_vec,end_vec,dim=1).to(float) 100 clockwise = torch.dot(w.squeeze(),up.squeeze())<0 101 102 u = start_vec.to(float) 103 u/= torch.linalg.vector_norm(start_vec.squeeze()) 104 if (w == 0).all(): 105 w += torch.ones_like(w) * 1e-10 106 107 v = torch.cross(w,u,dim=1) 108 v /= torch.linalg.vector_norm(v.squeeze()) 109 110 if clockwise == anticlockwise: #Should be false 111 angle = 2*3.14159 - angle 112 direction= -1 113 else: 114 direction = 1 115 116 points = [] 117 for i in range(n): 118 t = direction * ((angle) / n) * i 119 p = radius * (torch.cos(t)*u + torch.sin(t)*v) + origin 120 points.append(p) 121 122 return points
Creates an arc between start and end with origin at origin
Parameters
- start: Point defining start of the arc
- end: Point defining the end of the arc (this is not checked to lie on the arc - the arc will end at the same angle as arc)
- origin: Point defining origin of the arc
- n: number of points to interpolate along the arc. Default 100
- up: vector defining which way is 'up'. Default to positive z
- anticlickwise: If true will create anticlockwise arc. Otherwise will create clockwise arc :returns Points: List of points
def
interpolate_circle( origin: torch.Tensor, radius: float = 1.0, plane='xy', n: int = 100) -> list[torch.Tensor]:
125def interpolate_circle(origin:Tensor, radius:float=1.0, plane='xy', n:int=100) -> list[Tensor]: 126 points = [] 127 for i in range(n): 128 a = radius * math.sin((2*math.pi*i) / n) 129 b = radius * math.cos((2*math.pi*i) / n) 130 if plane == 'xy': 131 p = create_points(1,1,a,b,0) 132 elif plane == 'xz': 133 p = create_points(1,1,a,0,b) 134 elif plane == 'yz': 135 p = create_points(1,1,0,a,b) 136 points.append(p) 137 return points