src.acoustools.Paths.Bezier
1import torch 2from torch import Tensor 3 4import itertools, math 5 6try: 7 from svgpathtools import svg2paths, Line 8 from svgpathtools import CubicBezier as _CubicBezier 9 svg_warning = False 10except ImportError: 11 svg_warning = True 12 13from acoustools.Utilities.Points import create_points 14from acoustools.Paths.Interpolate import interpolate_path, interpolate_points 15from acoustools.Paths.Distances import distance 16 17from acoustools.Paths.Curves import CubicBezier, Spline 18 19def interpolate_bezier(bezier:CubicBezier, n:int=100) -> list[Tensor]: 20 21 ''' 22 Create cubic Bezier curve based on positions given \n 23 :param start: Start position 24 :param end: End position 25 :param offset_1: offset from start to first control point 26 :param offset_2: offset from start to second control point 27 :param n: number of samples 28 :returns points: 29 ''' 30 31 #Make even sample?= distance 32 33 start,end,offset_1,offset_2 = bezier.get_data() 34 35 36 if type(offset_1) == list: offset_1 = torch.tensor(offset_1).reshape((1,3,1)) 37 if type(offset_1) == int: offset_1 = torch.ones_like(start) * offset_1 38 39 if type(offset_2) == list: offset_2 = torch.tensor(offset_2).reshape((1,3,1)) 40 if type(offset_2) == int: offset_2 = torch.ones_like(start) * offset_2 41 42 43 P1 = start 44 P2 = start + offset_1 45 P3 = start + offset_2 46 P4 = end 47 48 points = [] 49 50 for i in range(n): 51 t = i/n 52 # THIS IS QUITE SLOW - REDUCE THIS TO ONE LINE - 53 P5 = (1-t)*P1 + t*P2 54 P6 = (1-t)*P2 + t*P3 55 P7 = (1-t)*P3 + t*P4 56 P8 = (1-t)*P5 + t*P6 57 P9 = (1-t)*P6 + t*P7 58 point = (1-t)*P8 + t*P9 59 60 points.append(point) 61 62 return points 63 64def interpolate_bezier_velocity(bezier:CubicBezier, n:int=100) -> list[Tensor]: 65 ''' 66 Gets the velocity of a cubic Bezier curve based on positions given \n 67 :param start: Start position 68 :param end: End position 69 :param offset_1: offset from start to first control point 70 :param offset_2: offset from start to second control point 71 :param n: number of samples 72 :returns points: 73 ''' 74 75 start,end,offset_1,offset_2 = bezier.get_data() 76 77 P0 = start 78 P1 = start + offset_1 79 P2 = start + offset_2 80 P3 = end 81 82 points = [] 83 for i in range(n): 84 t = i/n 85 p = 3*(1-t)**2 *(P1-P0) + 6*(1-t)*t*(P2-P1) + 3*t**2 * (P3-P2) 86 points.append(p) 87 88 return points 89 90 91def interpolate_bezier_acceleration(bezier:CubicBezier, n:int=100) -> list[Tensor]: 92 ''' 93 Gets the acceleration of a cubic Bezier curve based on positions given \n 94 :param start: Start position 95 :param end: End position 96 :param offset_1: offset from start to first control point 97 :param offset_2: offset from start to second control point 98 :param n: number of samples 99 :returns points: 100 ''' 101 102 start,end,offset_1,offset_2 = bezier.get_data() 103 104 P0 = start 105 P1 = start + offset_1 106 P2 = start + offset_2 107 P3 = end 108 109 points = [] 110 for i in range(n): 111 t = i/n 112 p = 6*(1-t)*(P2-2*P1+P0) + 6*t*(P3-2*P2+P1) 113 points.append(p) 114 115 return points 116 117 118def svg_to_beziers(pth:str, flip_y:bool= False, n:int=20, dx:float=0, dy:float=0, scale_x:float = 1/10, scale_y:float = 1/10) -> tuple[list[Tensor], Spline]: 119 ''' 120 Converts a .SVG file containing bezier curves to a set of AcousTools bezier curves \n 121 :param pth: String path to .svg file 122 :param flip_y: If true flip the y axis 123 :param n: Number of samples along bezier to return 124 :param dx: change in x direction to apply 125 :param dy: change in y direction to apply 126 :param scale_x: scale in x direction to apply 127 :param scale_y: scale in y direction to apply 128 :returns (points, bezier): Points and the bezier curve as list of tuples. Bezier defined as (start, end, offset1, offset2) where offsets are from start 129 ''' 130 if svg_warning: 131 raise ImportError('Requires svgpathtools module `pip install svgpathtools`') 132 133 134 paths, _ = svg2paths(pth) 135 136 137 def ReIm_to_AcousTools_point(point, flip_y, dx, dy, scale_x, scale_y): 138 if flip_y: 139 y_mul = -1 140 else: 141 y_mul = 1 142 143 point_AT = create_points(1,x=(point.real*scale_x) + dx, y=(y_mul*point.imag*scale_y)-dy,z=0) 144 return point_AT 145 146 points = [] 147 control_points = [] 148 i = -1 149 150 for pth in paths: 151 for bez in pth: 152 if type(bez) == _CubicBezier: 153 i += 1 154 155 start_RI = bez.start 156 control_1_RI = bez.control1 157 control_2_RI = bez.control2 158 end_RI = bez.end 159 160 start = ReIm_to_AcousTools_point(start_RI, flip_y, dx, dy, scale_x , scale_y) 161 control1 = ReIm_to_AcousTools_point(control_1_RI, flip_y,dx, dy, scale_x , scale_y) 162 control2 = ReIm_to_AcousTools_point(control_2_RI, flip_y,dx, dy, scale_x , scale_y) 163 end = ReIm_to_AcousTools_point(end_RI, flip_y,dx, dy, scale_x , scale_y) 164 165 b = CubicBezier(start, end, control1-start, control2-start) 166 control_points.append(b) 167 168 points += interpolate_bezier(b, n=n) 169 170 elif type(bez) == Line: 171 start_RI = bez.start 172 end_RI = bez.end 173 174 start = ReIm_to_AcousTools_point(start_RI, flip_y) 175 end = ReIm_to_AcousTools_point(end_RI, flip_y) 176 points += interpolate_path([start, end],n=n) 177 178 179 # xs = [p[:,0] for p in points] 180 # max_x = max(xs).clone() 181 # min_x = min(xs).clone() 182 183 # ys = [p[:,1] for p in points] 184 # max_y = max(ys).clone() 185 # min_y = min(ys).clone() 186 187 188 return points, Spline(control_points) 189 190def bezier_to_C1(spline:Spline, check_C0:bool=True, n:int=20, get_points=True) -> tuple[list[Tensor]]: 191 ''' 192 Converts a spline of beziers to be C1 continuous (https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve#Smooth_joining) 193 :param spline: spline of curves to convert 194 :param check_C0: If True will encure C0 continuity as well. Raises an error if violated 195 :param n: number of samples 196 :returns points,new_bezier: Points and new C1 spline curve 197 ''' 198 # new_bezier = CubicBezier(None,None,None,None) 199 # new_bezier.start = bezier[0] 200 # new_bezier.append(bezier[0]) 201 202 new_spline = Spline() 203 new_spline.add_curve(spline[0]) 204 205 for i,(b1,b2) in enumerate(itertools.pairwise(spline)): 206 P0, P3, c11, c12 = b1.get_data() 207 start_2,P6, c21, c22 = b2.get_data() 208 P1 = P0 + c11 209 P2 = P0 + c12 210 P5 = P3 + c22 211 212 # if check_C0: assert (P3 == start_2).all() #Assert we have C0 continuity 213 214 P4_offset = (P3 - P2) 215 216 new_spline.add_curve(CubicBezier(P3, P6, P4_offset, c22)) 217 218 if (new_spline[0][0] == new_spline[-1][1]).all(): #C0 continuous at the last point -> Path is a loop 219 [P0, P3, c11, c12] = new_spline[-1].get_data() 220 [start_2,P6, c21, c22 ] = new_spline[0].get_data() 221 222 P1 = P0 + c11 223 P2 = P0 + c12 224 P5 = P3 + c22 225 226 # if check_C0: assert (P3 == start_2).all() #Assert we have C0 continuity 227 228 P4_offset = (P3 - P2) 229 230 new_spline[0] = CubicBezier(P3, P6, P4_offset, c22) 231 232 233 if get_points: 234 points =[] 235 for bez in new_spline: 236 points += interpolate_bezier(bez, n) 237 238 239 return points,new_spline 240 241 return new_spline 242 243def close_bezier(spline:Spline, n:int=20) -> tuple[list[Tensor]]: 244 ''' 245 Links the last point in a bezier to the start of it with a new bezier \n 246 :param bezier: Bezier spline to close as list of (start, end, offset1, offset2) where offsets are from start 247 :param n: number of points to sample 248 :returns points,bezier: points,bezier 249 ''' 250 251 start = spline[0] 252 end = spline[-1] 253 254 new_b = [end[1], start[0],torch.zeros_like(start[0]),torch.zeros_like(start[0])] 255 spline.add_curve(CubicBezier(*new_b)) 256 257 if n != 0: 258 points =[] 259 for bez in spline: 260 points += interpolate_bezier(bez, n) 261 return points,spline 262 return spline 263 264def bezier_to_distance(bezier:CubicBezier, max_distance:float=0.001, start_n=20): 265 ''' 266 Samples bezier to have at most max_distance between points. \n 267 :param bezier:`acoustools.Paths.Curves.Bezier` object 268 :param max_distance: maximum straight line distance between points 269 :param start_n: number of points to start 270 :returns points: 271 ''' 272 bezier_points = interpolate_bezier(bezier,n=start_n) 273 274 points = [] 275 276 for i,(p1,p2) in enumerate(itertools.pairwise(bezier_points)): 277 278 n = int(torch.ceil(torch.max(distance(p1, p2) / max_distance)).item()) 279 points += interpolate_points(p1,p2, n) 280 281 return points 282 283def create_bezier_circle(N=10, origin=(0,0,0), radius=0.01, plane='xy'): 284 angle = 3.14*2 / N 285 origin = create_points(1,1,origin[0], origin[1],origin[2]) 286 beziers = [] 287 for i in range(N): 288 pos_1 = radius * math.sin(angle*i) 289 pos_2 = radius * math.cos(angle*i) 290 291 if plane == 'xy': 292 start = create_points(1,1,pos_1,pos_2,0) + origin 293 elif plane == 'xz': 294 start = create_points(1,1,pos_1,0,pos_2) + origin 295 elif plane == 'yz': 296 start = create_points(1,1,0,pos_1,pos_2) + origin 297 else: 298 raise ValueError("Plane not valid. Must be xy, xz or yz") 299 300 pos_3 = radius * math.sin(angle*(i+1)) 301 pos_4 = radius * math.cos(angle*(i+1)) 302 303 if plane == 'xy': 304 end = create_points(1,1,pos_3,pos_4,0) + origin 305 elif plane == 'xz': 306 end = create_points(1,1,pos_3,0,pos_4) + origin 307 elif plane == 'yz': 308 end = create_points(1,1,0,pos_3,pos_4) + origin 309 else: 310 raise ValueError("Plane not valid. Must be xy, xz or yz") 311 312 offset_1 = create_points(1,1,0,0,0) 313 314 bez = CubicBezier(start,end,offset_1, offset_1.clone()) 315 beziers.append(bez) 316 spline = Spline(beziers) 317 318 return spline 319 320 321def connect_ends(spline:Spline): 322 ''' 323 Sets the end of the last curve in spline to be the start of the first 324 ''' 325 start = spline[0] 326 end = spline[-1] 327 328 end.end = start.start
def
interpolate_bezier( bezier: acoustools.Paths.Curves.CubicBezier, n: int = 100) -> list[torch.Tensor]:
20def interpolate_bezier(bezier:CubicBezier, n:int=100) -> list[Tensor]: 21 22 ''' 23 Create cubic Bezier curve based on positions given \n 24 :param start: Start position 25 :param end: End position 26 :param offset_1: offset from start to first control point 27 :param offset_2: offset from start to second control point 28 :param n: number of samples 29 :returns points: 30 ''' 31 32 #Make even sample?= distance 33 34 start,end,offset_1,offset_2 = bezier.get_data() 35 36 37 if type(offset_1) == list: offset_1 = torch.tensor(offset_1).reshape((1,3,1)) 38 if type(offset_1) == int: offset_1 = torch.ones_like(start) * offset_1 39 40 if type(offset_2) == list: offset_2 = torch.tensor(offset_2).reshape((1,3,1)) 41 if type(offset_2) == int: offset_2 = torch.ones_like(start) * offset_2 42 43 44 P1 = start 45 P2 = start + offset_1 46 P3 = start + offset_2 47 P4 = end 48 49 points = [] 50 51 for i in range(n): 52 t = i/n 53 # THIS IS QUITE SLOW - REDUCE THIS TO ONE LINE - 54 P5 = (1-t)*P1 + t*P2 55 P6 = (1-t)*P2 + t*P3 56 P7 = (1-t)*P3 + t*P4 57 P8 = (1-t)*P5 + t*P6 58 P9 = (1-t)*P6 + t*P7 59 point = (1-t)*P8 + t*P9 60 61 points.append(point) 62 63 return points
Create cubic Bezier curve based on positions given
Parameters
- start: Start position
- end: End position
- offset_1: offset from start to first control point
- offset_2: offset from start to second control point
- n: number of samples :returns points:
def
interpolate_bezier_velocity( bezier: acoustools.Paths.Curves.CubicBezier, n: int = 100) -> list[torch.Tensor]:
65def interpolate_bezier_velocity(bezier:CubicBezier, n:int=100) -> list[Tensor]: 66 ''' 67 Gets the velocity of a cubic Bezier curve based on positions given \n 68 :param start: Start position 69 :param end: End position 70 :param offset_1: offset from start to first control point 71 :param offset_2: offset from start to second control point 72 :param n: number of samples 73 :returns points: 74 ''' 75 76 start,end,offset_1,offset_2 = bezier.get_data() 77 78 P0 = start 79 P1 = start + offset_1 80 P2 = start + offset_2 81 P3 = end 82 83 points = [] 84 for i in range(n): 85 t = i/n 86 p = 3*(1-t)**2 *(P1-P0) + 6*(1-t)*t*(P2-P1) + 3*t**2 * (P3-P2) 87 points.append(p) 88 89 return points
Gets the velocity of a cubic Bezier curve based on positions given
Parameters
- start: Start position
- end: End position
- offset_1: offset from start to first control point
- offset_2: offset from start to second control point
- n: number of samples :returns points:
def
interpolate_bezier_acceleration( bezier: acoustools.Paths.Curves.CubicBezier, n: int = 100) -> list[torch.Tensor]:
92def interpolate_bezier_acceleration(bezier:CubicBezier, n:int=100) -> list[Tensor]: 93 ''' 94 Gets the acceleration of a cubic Bezier curve based on positions given \n 95 :param start: Start position 96 :param end: End position 97 :param offset_1: offset from start to first control point 98 :param offset_2: offset from start to second control point 99 :param n: number of samples 100 :returns points: 101 ''' 102 103 start,end,offset_1,offset_2 = bezier.get_data() 104 105 P0 = start 106 P1 = start + offset_1 107 P2 = start + offset_2 108 P3 = end 109 110 points = [] 111 for i in range(n): 112 t = i/n 113 p = 6*(1-t)*(P2-2*P1+P0) + 6*t*(P3-2*P2+P1) 114 points.append(p) 115 116 return points
Gets the acceleration of a cubic Bezier curve based on positions given
Parameters
- start: Start position
- end: End position
- offset_1: offset from start to first control point
- offset_2: offset from start to second control point
- n: number of samples :returns points:
def
svg_to_beziers( pth: str, flip_y: bool = False, n: int = 20, dx: float = 0, dy: float = 0, scale_x: float = 0.1, scale_y: float = 0.1) -> tuple[list[torch.Tensor], acoustools.Paths.Curves.Spline]:
119def svg_to_beziers(pth:str, flip_y:bool= False, n:int=20, dx:float=0, dy:float=0, scale_x:float = 1/10, scale_y:float = 1/10) -> tuple[list[Tensor], Spline]: 120 ''' 121 Converts a .SVG file containing bezier curves to a set of AcousTools bezier curves \n 122 :param pth: String path to .svg file 123 :param flip_y: If true flip the y axis 124 :param n: Number of samples along bezier to return 125 :param dx: change in x direction to apply 126 :param dy: change in y direction to apply 127 :param scale_x: scale in x direction to apply 128 :param scale_y: scale in y direction to apply 129 :returns (points, bezier): Points and the bezier curve as list of tuples. Bezier defined as (start, end, offset1, offset2) where offsets are from start 130 ''' 131 if svg_warning: 132 raise ImportError('Requires svgpathtools module `pip install svgpathtools`') 133 134 135 paths, _ = svg2paths(pth) 136 137 138 def ReIm_to_AcousTools_point(point, flip_y, dx, dy, scale_x, scale_y): 139 if flip_y: 140 y_mul = -1 141 else: 142 y_mul = 1 143 144 point_AT = create_points(1,x=(point.real*scale_x) + dx, y=(y_mul*point.imag*scale_y)-dy,z=0) 145 return point_AT 146 147 points = [] 148 control_points = [] 149 i = -1 150 151 for pth in paths: 152 for bez in pth: 153 if type(bez) == _CubicBezier: 154 i += 1 155 156 start_RI = bez.start 157 control_1_RI = bez.control1 158 control_2_RI = bez.control2 159 end_RI = bez.end 160 161 start = ReIm_to_AcousTools_point(start_RI, flip_y, dx, dy, scale_x , scale_y) 162 control1 = ReIm_to_AcousTools_point(control_1_RI, flip_y,dx, dy, scale_x , scale_y) 163 control2 = ReIm_to_AcousTools_point(control_2_RI, flip_y,dx, dy, scale_x , scale_y) 164 end = ReIm_to_AcousTools_point(end_RI, flip_y,dx, dy, scale_x , scale_y) 165 166 b = CubicBezier(start, end, control1-start, control2-start) 167 control_points.append(b) 168 169 points += interpolate_bezier(b, n=n) 170 171 elif type(bez) == Line: 172 start_RI = bez.start 173 end_RI = bez.end 174 175 start = ReIm_to_AcousTools_point(start_RI, flip_y) 176 end = ReIm_to_AcousTools_point(end_RI, flip_y) 177 points += interpolate_path([start, end],n=n) 178 179 180 # xs = [p[:,0] for p in points] 181 # max_x = max(xs).clone() 182 # min_x = min(xs).clone() 183 184 # ys = [p[:,1] for p in points] 185 # max_y = max(ys).clone() 186 # min_y = min(ys).clone() 187 188 189 return points, Spline(control_points)
Converts a .SVG file containing bezier curves to a set of AcousTools bezier curves
Parameters
- pth: String path to .svg file
- flip_y: If true flip the y axis
- n: Number of samples along bezier to return
- dx: change in x direction to apply
- dy: change in y direction to apply
- scale_x: scale in x direction to apply
- scale_y: scale in y direction to apply :returns (points, bezier): Points and the bezier curve as list of tuples. Bezier defined as (start, end, offset1, offset2) where offsets are from start
def
bezier_to_C1( spline: acoustools.Paths.Curves.Spline, check_C0: bool = True, n: int = 20, get_points=True) -> tuple[list[torch.Tensor]]:
191def bezier_to_C1(spline:Spline, check_C0:bool=True, n:int=20, get_points=True) -> tuple[list[Tensor]]: 192 ''' 193 Converts a spline of beziers to be C1 continuous (https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve#Smooth_joining) 194 :param spline: spline of curves to convert 195 :param check_C0: If True will encure C0 continuity as well. Raises an error if violated 196 :param n: number of samples 197 :returns points,new_bezier: Points and new C1 spline curve 198 ''' 199 # new_bezier = CubicBezier(None,None,None,None) 200 # new_bezier.start = bezier[0] 201 # new_bezier.append(bezier[0]) 202 203 new_spline = Spline() 204 new_spline.add_curve(spline[0]) 205 206 for i,(b1,b2) in enumerate(itertools.pairwise(spline)): 207 P0, P3, c11, c12 = b1.get_data() 208 start_2,P6, c21, c22 = b2.get_data() 209 P1 = P0 + c11 210 P2 = P0 + c12 211 P5 = P3 + c22 212 213 # if check_C0: assert (P3 == start_2).all() #Assert we have C0 continuity 214 215 P4_offset = (P3 - P2) 216 217 new_spline.add_curve(CubicBezier(P3, P6, P4_offset, c22)) 218 219 if (new_spline[0][0] == new_spline[-1][1]).all(): #C0 continuous at the last point -> Path is a loop 220 [P0, P3, c11, c12] = new_spline[-1].get_data() 221 [start_2,P6, c21, c22 ] = new_spline[0].get_data() 222 223 P1 = P0 + c11 224 P2 = P0 + c12 225 P5 = P3 + c22 226 227 # if check_C0: assert (P3 == start_2).all() #Assert we have C0 continuity 228 229 P4_offset = (P3 - P2) 230 231 new_spline[0] = CubicBezier(P3, P6, P4_offset, c22) 232 233 234 if get_points: 235 points =[] 236 for bez in new_spline: 237 points += interpolate_bezier(bez, n) 238 239 240 return points,new_spline 241 242 return new_spline
Converts a spline of beziers to be C1 continuous (https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve#Smooth_joining)
Parameters
- spline: spline of curves to convert
- check_C0: If True will encure C0 continuity as well. Raises an error if violated
- n: number of samples :returns points,new_bezier: Points and new C1 spline curve
def
close_bezier( spline: acoustools.Paths.Curves.Spline, n: int = 20) -> tuple[list[torch.Tensor]]:
244def close_bezier(spline:Spline, n:int=20) -> tuple[list[Tensor]]: 245 ''' 246 Links the last point in a bezier to the start of it with a new bezier \n 247 :param bezier: Bezier spline to close as list of (start, end, offset1, offset2) where offsets are from start 248 :param n: number of points to sample 249 :returns points,bezier: points,bezier 250 ''' 251 252 start = spline[0] 253 end = spline[-1] 254 255 new_b = [end[1], start[0],torch.zeros_like(start[0]),torch.zeros_like(start[0])] 256 spline.add_curve(CubicBezier(*new_b)) 257 258 if n != 0: 259 points =[] 260 for bez in spline: 261 points += interpolate_bezier(bez, n) 262 return points,spline 263 return spline
Links the last point in a bezier to the start of it with a new bezier
Parameters
- bezier: Bezier spline to close as list of (start, end, offset1, offset2) where offsets are from start
- n: number of points to sample :returns points,bezier: points,bezier
def
bezier_to_distance( bezier: acoustools.Paths.Curves.CubicBezier, max_distance: float = 0.001, start_n=20):
265def bezier_to_distance(bezier:CubicBezier, max_distance:float=0.001, start_n=20): 266 ''' 267 Samples bezier to have at most max_distance between points. \n 268 :param bezier:`acoustools.Paths.Curves.Bezier` object 269 :param max_distance: maximum straight line distance between points 270 :param start_n: number of points to start 271 :returns points: 272 ''' 273 bezier_points = interpolate_bezier(bezier,n=start_n) 274 275 points = [] 276 277 for i,(p1,p2) in enumerate(itertools.pairwise(bezier_points)): 278 279 n = int(torch.ceil(torch.max(distance(p1, p2) / max_distance)).item()) 280 points += interpolate_points(p1,p2, n) 281 282 return points
Samples bezier to have at most max_distance between points.
Parameters
- bezier:
acoustools.Paths.Curves.Bezierobject - max_distance: maximum straight line distance between points
- start_n: number of points to start :returns points:
def
create_bezier_circle(N=10, origin=(0, 0, 0), radius=0.01, plane='xy'):
284def create_bezier_circle(N=10, origin=(0,0,0), radius=0.01, plane='xy'): 285 angle = 3.14*2 / N 286 origin = create_points(1,1,origin[0], origin[1],origin[2]) 287 beziers = [] 288 for i in range(N): 289 pos_1 = radius * math.sin(angle*i) 290 pos_2 = radius * math.cos(angle*i) 291 292 if plane == 'xy': 293 start = create_points(1,1,pos_1,pos_2,0) + origin 294 elif plane == 'xz': 295 start = create_points(1,1,pos_1,0,pos_2) + origin 296 elif plane == 'yz': 297 start = create_points(1,1,0,pos_1,pos_2) + origin 298 else: 299 raise ValueError("Plane not valid. Must be xy, xz or yz") 300 301 pos_3 = radius * math.sin(angle*(i+1)) 302 pos_4 = radius * math.cos(angle*(i+1)) 303 304 if plane == 'xy': 305 end = create_points(1,1,pos_3,pos_4,0) + origin 306 elif plane == 'xz': 307 end = create_points(1,1,pos_3,0,pos_4) + origin 308 elif plane == 'yz': 309 end = create_points(1,1,0,pos_3,pos_4) + origin 310 else: 311 raise ValueError("Plane not valid. Must be xy, xz or yz") 312 313 offset_1 = create_points(1,1,0,0,0) 314 315 bez = CubicBezier(start,end,offset_1, offset_1.clone()) 316 beziers.append(bez) 317 spline = Spline(beziers) 318 319 return spline
def
connect_ends(spline: acoustools.Paths.Curves.Spline):
322def connect_ends(spline:Spline): 323 ''' 324 Sets the end of the last curve in spline to be the start of the first 325 ''' 326 start = spline[0] 327 end = spline[-1] 328 329 end.end = start.start
Sets the end of the last curve in spline to be the start of the first