1""" 2.. module:: _operations 3 :platform: Unix, Windows 4 :synopsis: Helper functions for operations module 5 6.. moduleauthor:: Onur Rauf Bingol <orbingol@gmail.com> 7 8""" 9 10from . import linalg, helpers 11from .exceptions import GeomdlException 12 13 14# Initialize an empty __all__ for controlling imports 15__all__ = [] 16 17 18def tangent_curve_single(obj, u, normalize): 19 """ Evaluates the curve tangent vector at the given parameter value. 20 21 The output returns a list containing the starting point (i.e. origin) of the vector and the vector itself. 22 23 :param obj: input curve 24 :type obj: abstract.Curve 25 :param u: parameter 26 :type u: float 27 :param normalize: if True, the returned vector is converted to a unit vector 28 :type normalize: bool 29 :return: a list containing "point" and "vector" pairs 30 :rtype: tuple 31 """ 32 # 1st derivative of the curve gives the tangent 33 ders = obj.derivatives(u, 1) 34 35 point = ders[0] 36 vector = linalg.vector_normalize(ders[1]) if normalize else ders[1] 37 38 return tuple(point), tuple(vector) 39 40 41def tangent_curve_single_list(obj, param_list, normalize): 42 """ Evaluates the curve tangent vectors at the given list of parameter values. 43 44 :param obj: input curve 45 :type obj: abstract.Curve 46 :param param_list: parameter list 47 :type param_list: list or tuple 48 :param normalize: if True, the returned vector is converted to a unit vector 49 :type normalize: bool 50 :return: a list containing "point" and "vector" pairs 51 :rtype: tuple 52 """ 53 ret_vector = [] 54 for param in param_list: 55 temp = tangent_curve_single(obj, param, normalize) 56 ret_vector.append(temp) 57 return tuple(ret_vector) 58 59 60def normal_curve_single(obj, u, normalize): 61 """ Evaluates the vector normal to the tangent vector at the input parameter, u. 62 63 The output returns a list containing the starting point (i.e. origin) of the vector and the vector itself. 64 65 :param obj: input curve 66 :type obj: abstract.Curve 67 :param u: parameter 68 :type u: float 69 :param normalize: if True, the returned vector is converted to a unit vector 70 :type normalize: bool 71 :return: a list containing "point" and "vector" pairs 72 :rtype: tuple 73 """ 74 # Find 1st derivative 75 ders = obj.derivatives(u, 1) 76 77 # Apply fix on https://github.com/orbingol/NURBS-Python/pull/50#issuecomment-499354073 78 t = ders[1] 79 tn = [0.0, 0.0, 1.0] 80 n = linalg.vector_cross(t, tn) 81 82 # Normalize the vector component 83 vector = linalg.vector_normalize(n) if normalize else n 84 point = ders[0] 85 86 return tuple(point), tuple(vector) 87 88 89def normal_curve_single_list(obj, param_list, normalize): 90 """ Evaluates the curve normal vectors at the given list of parameter values. 91 92 :param obj: input curve 93 :type obj: abstract.Curve 94 :param param_list: parameter list 95 :type param_list: list or tuple 96 :param normalize: if True, the returned vector is converted to a unit vector 97 :type normalize: bool 98 :return: a list containing "point" and "vector" pairs 99 :rtype: tuple 100 """ 101 ret_vector = [] 102 for param in param_list: 103 temp = normal_curve_single(obj, param, normalize) 104 ret_vector.append(temp) 105 return tuple(ret_vector) 106 107 108def binormal_curve_single(obj, u, normalize): 109 """ Evaluates the curve binormal vector at the given u parameter. 110 111 Curve binormal is the cross product of the normal and the tangent vectors. 112 The output returns a list containing the starting point (i.e. origin) of the vector and the vector itself. 113 114 :param obj: input curve 115 :type obj: abstract.Curve 116 :param u: parameter 117 :type u: float 118 :param normalize: if True, the returned vector is converted to a unit vector 119 :type normalize: bool 120 :return: a list containing "point" and "vector" pairs 121 :rtype: tuple 122 """ 123 # Cross product of tangent and normal vectors gives binormal vector 124 tan_vector = tangent_curve_single(obj, u, normalize) 125 norm_vector = normal_curve_single(obj, u, normalize) 126 127 point = tan_vector[0] 128 vector = linalg.vector_cross(tan_vector[1], norm_vector[1]) 129 vector = linalg.vector_normalize(vector) if normalize else vector 130 131 return tuple(point), tuple(vector) 132 133 134def binormal_curve_single_list(obj, param_list, normalize): 135 """ Evaluates the curve binormal vectors at the given list of parameter values. 136 137 :param obj: input curve 138 :type obj: abstract.Curve 139 :param param_list: parameter list 140 :type param_list: list or tuple 141 :param normalize: if True, the returned vector is converted to a unit vector 142 :type normalize: bool 143 :return: a list containing "point" and "vector" pairs 144 :rtype: tuple 145 """ 146 ret_vector = [] 147 for param in param_list: 148 temp = binormal_curve_single(obj, param, normalize) 149 ret_vector.append(temp) 150 return tuple(ret_vector) 151 152 153def tangent_surface_single(obj, uv, normalize): 154 """ Evaluates the surface tangent vector at the given (u,v) parameter pair. 155 156 The output returns a list containing the starting point (i.e., origin) of the vector and the vectors themselves. 157 158 :param obj: input surface 159 :type obj: abstract.Surface 160 :param uv: (u,v) parameter pair 161 :type uv: list or tuple 162 :param normalize: if True, the returned tangent vector is converted to a unit vector 163 :type normalize: bool 164 :return: A list in the order of "surface point", "derivative w.r.t. u" and "derivative w.r.t. v" 165 :rtype: list 166 """ 167 # Tangent is the 1st derivative of the surface 168 skl = obj.derivatives(uv[0], uv[1], 1) 169 170 point = skl[0][0] 171 vector_u = linalg.vector_normalize(skl[1][0]) if normalize else skl[1][0] 172 vector_v = linalg.vector_normalize(skl[0][1]) if normalize else skl[0][1] 173 174 return tuple(point), tuple(vector_u), tuple(vector_v) 175 176 177def tangent_surface_single_list(obj, param_list, normalize): 178 """ Evaluates the surface tangent vectors at the given list of parameter values. 179 180 :param obj: input surface 181 :type obj: abstract.Surface 182 :param param_list: parameter list 183 :type param_list: list or tuple 184 :param normalize: if True, the returned vector is converted to a unit vector 185 :type normalize: bool 186 :return: a list containing "point" and "vector" pairs 187 :rtype: tuple 188 """ 189 ret_vector = [] 190 for param in param_list: 191 temp = tangent_surface_single(obj, param, normalize) 192 ret_vector.append(temp) 193 return tuple(ret_vector) 194 195 196def normal_surface_single(obj, uv, normalize): 197 """ Evaluates the surface normal vector at the given (u, v) parameter pair. 198 199 The output returns a list containing the starting point (i.e. origin) of the vector and the vector itself. 200 201 :param obj: input surface 202 :type obj: abstract.Surface 203 :param uv: (u,v) parameter pair 204 :type uv: list or tuple 205 :param normalize: if True, the returned normal vector is converted to a unit vector 206 :type normalize: bool 207 :return: a list in the order of "surface point" and "normal vector" 208 :rtype: list 209 """ 210 # Take the 1st derivative of the surface 211 skl = obj.derivatives(uv[0], uv[1], 1) 212 213 point = skl[0][0] 214 vector = linalg.vector_cross(skl[1][0], skl[0][1]) 215 vector = linalg.vector_normalize(vector) if normalize else vector 216 217 return tuple(point), tuple(vector) 218 219 220def normal_surface_single_list(obj, param_list, normalize): 221 """ Evaluates the surface normal vectors at the given list of parameter values. 222 223 :param obj: input surface 224 :type obj: abstract.Surface 225 :param param_list: parameter list 226 :type param_list: list or tuple 227 :param normalize: if True, the returned vector is converted to a unit vector 228 :type normalize: bool 229 :return: a list containing "point" and "vector" pairs 230 :rtype: tuple 231 """ 232 ret_vector = [] 233 for param in param_list: 234 temp = normal_surface_single(obj, param, normalize) 235 ret_vector.append(temp) 236 return tuple(ret_vector) 237 238 239def find_ctrlpts_curve(t, curve, **kwargs): 240 """ Finds the control points involved in the evaluation of the curve point defined by the input parameter. 241 242 This function uses a modified version of the algorithm *A3.1 CurvePoint* from The NURBS Book by Piegl & Tiller. 243 244 :param t: parameter 245 :type t: float 246 :param curve: input curve object 247 :type curve: abstract.Curve 248 :return: 1-dimensional control points array 249 :rtype: list 250 """ 251 # Get keyword arguments 252 span_func = kwargs.get('find_span_func', helpers.find_span_linear) 253 254 # Find spans and the constant index 255 span = span_func(curve.degree, curve.knotvector, len(curve.ctrlpts), t) 256 idx = span - curve.degree 257 258 # Find control points involved in evaluation of the curve point at the input parameter 259 curve_ctrlpts = [() for _ in range(curve.degree + 1)] 260 for i in range(0, curve.degree + 1): 261 curve_ctrlpts[i] = curve.ctrlpts[idx + i] 262 263 # Return control points array 264 return curve_ctrlpts 265 266 267def find_ctrlpts_surface(t_u, t_v, surf, **kwargs): 268 """ Finds the control points involved in the evaluation of the surface point defined by the input parameter pair. 269 270 This function uses a modified version of the algorithm *A3.5 SurfacePoint* from The NURBS Book by Piegl & Tiller. 271 272 :param t_u: parameter on the u-direction 273 :type t_u: float 274 :param t_v: parameter on the v-direction 275 :type t_v: float 276 :param surf: input surface 277 :type surf: abstract.Surface 278 :return: 2-dimensional control points array 279 :rtype: list 280 """ 281 # Get keyword arguments 282 span_func = kwargs.get('find_span_func', helpers.find_span_linear) 283 284 # Find spans 285 span_u = span_func(surf.degree_u, surf.knotvector_u, surf.ctrlpts_size_u, t_u) 286 span_v = span_func(surf.degree_v, surf.knotvector_v, surf.ctrlpts_size_v, t_v) 287 288 # Constant indices 289 idx_u = span_u - surf.degree_u 290 idx_v = span_v - surf.degree_v 291 292 # Find control points involved in evaluation of the surface point at the input parameter pair (u, v) 293 surf_ctrlpts = [[] for _ in range(surf.degree_u + 1)] 294 for k in range(surf.degree_u + 1): 295 temp = [() for _ in range(surf.degree_v + 1)] 296 for l in range(surf.degree_v + 1): 297 temp[l] = surf.ctrlpts2d[idx_u + k][idx_v + l] 298 surf_ctrlpts[k] = temp 299 300 # Return 2-dimensional control points array 301 return surf_ctrlpts 302 303 304def link_curves(*args, **kwargs): 305 """ Links the input curves together. 306 307 The end control point of the curve k has to be the same with the start control point of the curve k + 1. 308 309 Keyword Arguments: 310 * ``tol``: tolerance value for checking equality. *Default: 10e-8* 311 * ``validate``: flag to enable input validation. *Default: False* 312 313 :return: a tuple containing knot vector, control points, weights vector and knots 314 """ 315 # Get keyword arguments 316 tol = kwargs.get('tol', 10e-8) 317 validate = kwargs.get('validate', False) 318 319 # Validate input 320 if validate: 321 for idx in range(len(args) - 1): 322 if linalg.point_distance(args[idx].ctrlpts[-1], args[idx + 1].ctrlpts[0]) > tol: 323 raise GeomdlException("Curve #" + str(idx) + " and Curve #" + str(idx + 1) + " don't touch each other") 324 325 kv = [] # new knot vector 326 cpts = [] # new control points array 327 wgts = [] # new weights array 328 kv_connected = [] # superfluous knots to be removed 329 pdomain_end = 0 330 331 # Loop though the curves 332 for arg in args: 333 # Process knot vectors 334 if not kv: 335 kv += list(arg.knotvector[:-(arg.degree + 1)]) # get rid of the last superfluous knot to maintain split curve notation 336 cpts += list(arg.ctrlpts) 337 # Process control points 338 if arg.rational: 339 wgts += list(arg.weights) 340 else: 341 tmp_w = [1.0 for _ in range(arg.ctrlpts_size)] 342 wgts += tmp_w 343 else: 344 tmp_kv = [pdomain_end + k for k in arg.knotvector[1:-(arg.degree + 1)]] 345 kv += tmp_kv 346 cpts += list(arg.ctrlpts[1:]) 347 # Process control points 348 if arg.rational: 349 wgts += list(arg.weights[1:]) 350 else: 351 tmp_w = [1.0 for _ in range(arg.ctrlpts_size - 1)] 352 wgts += tmp_w 353 354 pdomain_end += arg.knotvector[-1] 355 kv_connected.append(pdomain_end) 356 357 # Fix curve by appending the last knot to the end 358 kv += [pdomain_end for _ in range(arg.degree + 1)] 359 # Remove the last knot from knot insertion list 360 kv_connected.pop() 361 362 return kv, cpts, wgts, kv_connected 363