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 True will 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