def concatenate_nurbs_curves(curves): if not curves: raise Exception("List of curves must be not empty") curves = unify_curves_degree(curves) result = curves[0] for curve in curves[1:]: result = result.concatenate(curve) return result
def concatenate_nurbs_curves(curves): if not curves: raise Exception("List of curves must be not empty") curves = unify_curves_degree(curves) result = curves[0] for i, curve in enumerate(curves[1:]): try: result = result.concatenate(curve) except Exception as e: raise Exception(f"Can't append curve #{i+1}: {e}") return result
def make_ruled_surface(self, curve2, vmin, vmax): curve = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") curve, curve2 = unify_curves_degree([curve, curve2]) if curve.get_degree() != curve2.get_degree(): raise UnsupportedCurveTypeException( f"curves have different degrees: {curve.get_degree()} != {curve2.get_degree()}" ) #print(f"kv1: {curve.get_knotvector().shape}, kv2: {curve2.get_knotvector().shape}") kv1, kv2 = curve.get_knotvector(), curve2.get_knotvector() if kv1.shape != kv2.shape or (kv1 != kv2).any(): curve, curve2 = unify_two_curves(curve, curve2) #raise UnsupportedCurveTypeException("curves have different knot vectors") my_control_points = curve.get_control_points() other_control_points = curve2.get_control_points() if len(my_control_points) != len(other_control_points): raise UnsupportedCurveTypeException( "curves have different number of control points") if vmin != 0: my_control_points = ( 1 - vmin) * my_control_points + vmin * other_control_points if vmax != 0: other_control_points = ( 1 - vmax) * my_control_points + vmax * other_control_points control_points = np.stack((my_control_points, other_control_points)) control_points = np.transpose(control_points, axes=(1, 0, 2)) weights = np.stack((curve.get_weights(), curve2.get_weights())).T knotvector_v = sv_knotvector.generate(1, 2, clamped=True) surface = SvNurbsMaths.build_surface( self.get_nurbs_implementation(), degree_u=curve.get_degree(), degree_v=1, knotvector_u=curve.get_knotvector(), knotvector_v=knotvector_v, control_points=control_points, weights=weights) return surface
def lerp_to(self, curve2, coefficient): curve1 = self curve2 = SvNurbsCurve.to_nurbs(curve2) if curve2 is None: raise UnsupportedCurveTypeException("second curve is not NURBS") curve1, curve2 = unify_curves_degree([curve1, curve2]) curve1, curve2 = unify_two_curves(curve1, curve2) #c1cp = curve1.get_homogenous_control_points() #c2cp = curve2.get_homogenous_control_points() c1cp = curve1.get_control_points() c2cp = curve2.get_control_points() ws1 = curve1.get_weights() ws2 = curve2.get_weights() points = c1cp * (1 - coefficient) + coefficient * c2cp weights = ws1 * (1 - coefficient) + coefficient * ws2 return SvNurbsCurve.build(curve1.get_nurbs_implementation(), curve1.get_degree(), curve1.get_knotvector(), points, weights)
def process(self): # upgrades older versions of ProfileMK3 to the version that has self.file_pointer if self.filename and not self.file_pointer: text = self.get_bpy_data_from_name(self.filename, bpy.data.texts) if text: self.file_pointer = text if not any(o.is_linked for o in self.outputs): return sync_pointer_and_stored_name(self, "file_pointer", "filename") profile = self.load_profile() optional_inputs = self.get_optional_inputs(profile) var_names = self.get_variables() self.debug("Var_names: %s; optional: %s", var_names, optional_inputs) inputs = self.get_input() result_vertices = [] result_edges = [] result_knots = [] result_names = [] result_curves = [] if var_names: input_values = [] for name in var_names: try: input_values.append(inputs[name]) except KeyError as e: name = e.args[0] if name not in optional_inputs: if name in self.inputs: raise SvNoDataError(self.inputs[name]) else: self.adjust_sockets() raise SvNoDataError(self.inputs[name]) else: input_values.append([None]) parameters = match_long_repeat(input_values) else: parameters = [[[]]] input_names = [ socket.name for socket in self.inputs if socket.is_linked ] for values in zip(*parameters): variables = dict(zip(var_names, values)) curves_form = Interpreter.NURBS if self.nurbs_out else None interpreter = Interpreter(self, input_names, curves_form=curves_form, z_axis=self.selected_axis) interpreter.interpret(profile, variables) verts = self.extend_out_verts(interpreter.vertices) result_vertices.append(verts) result_edges.append(interpreter.edges) knots = self.extend_out_verts(interpreter.knots) result_knots.append(knots) result_names.append([[name] for name in interpreter.knotnames]) all_curves = interpreter.curves if self.concat_curves: new_curves = [] for curves in self.group_curves(all_curves): if self.nurbs_out: curves = unify_curves_degree(curves) curve = concatenate_curves(curves) new_curves.append(curve) result_curves.append(new_curves) else: result_curves.append(all_curves) self.outputs['Vertices'].sv_set(result_vertices) self.outputs['Edges'].sv_set(result_edges) self.outputs['Knots'].sv_set(result_knots) self.outputs['KnotNames'].sv_set(result_names) if 'Curve' in self.outputs: self.outputs['Curve'].sv_set(result_curves)
def gordon_surface(u_curves, v_curves, intersections, metric='POINTS', u_knots=None, v_knots=None, knotvector_accuracy=6, reparametrize_tolerance=1e-2): """ Generate a NURBS surface from a net of NURBS curves, by use of Gordon's algorithm. :param u_curves - list of NURBS curves along U direciton (length N) :param v_curves - list of NURBS curves along V direcion (length M) :param intersections - np.array of shape (N, M, 3): points of curves intersection :param metric - metric function that can be used to calculate T values of curves intersections from their positions. :param u_knots - np.array, T values of curves intersection for each curve from u_curves :param v_knots - np.array, T values of curves intersection for each curve from v_curves return value: a NURBS surface. See also: The NURBS Book, 2nd ed., p.10.5. """ if not u_curves or not v_curves: raise Exception("U or V curves are not provided") if (u_knots is None) != (v_knots is None): raise Exception( "u_knots and v_knots must be either both provided or both omited") if any(c.is_rational() for c in u_curves): raise Exception( "Some of U-curves are rational. Rational curves are not supported for Gordon surface." ) if any(c.is_rational() for c in v_curves): raise Exception( "Some of V-curves are rational. Rational curves are not supported for Gordon surface." ) intersections = np.array(intersections) if u_knots is not None: loft_u_kwargs = loft_v_kwargs = interpolate_kwargs = { 'metric': 'POINTS' } u_curves = [ reparametrize_by_segments(c, knots, reparametrize_tolerance) for c, knots in zip(u_curves, u_knots) ] v_curves = [ reparametrize_by_segments(c, knots, reparametrize_tolerance) for c, knots in zip(v_curves, v_knots) ] #print("U", u_curves) #print("V", v_curves) else: loft_u_kwargs = loft_v_kwargs = interpolate_kwargs = {'metric': metric} u_curves = unify_curves_degree(u_curves) u_curves = unify_curves(u_curves, accuracy=knotvector_accuracy) #, method='AVERAGE') v_curves = unify_curves_degree(v_curves) v_curves = unify_curves(v_curves, accuracy=knotvector_accuracy) #, method='AVERAGE') u_curves_degree = u_curves[0].get_degree() v_curves_degree = v_curves[0].get_degree() n = len(intersections) m = len(intersections[0]) loft_v_degree = min(len(u_curves) - 1, v_curves_degree) loft_u_degree = min(len(v_curves) - 1, u_curves_degree) _, _, lofted_v = simple_loft(u_curves, degree_v=loft_v_degree, knotvector_accuracy=knotvector_accuracy, **loft_v_kwargs) _, _, lofted_u = simple_loft(v_curves, degree_v=loft_u_degree, knotvector_accuracy=knotvector_accuracy, **loft_u_kwargs) lofted_u = lofted_u.swap_uv() int_degree_u = min(m - 1, u_curves_degree) int_degree_v = min(n - 1, v_curves_degree) interpolated = interpolate_nurbs_surface(int_degree_u, int_degree_v, intersections, **interpolate_kwargs) interpolated = interpolated.swap_uv() #print(f"Loft.U: {lofted_u}") #print(f"Loft.V: {lofted_v}") #print(f"Interp: {interpolated}") #print(f" {interpolated.get_knotvector_u()}") #print(f" {interpolated.get_knotvector_v()}") lofted_u, lofted_v, interpolated = unify_nurbs_surfaces( [lofted_u, lofted_v, interpolated], knotvector_accuracy=knotvector_accuracy) control_points = lofted_u.get_control_points() + \ lofted_v.get_control_points() - \ interpolated.get_control_points() surface = SvNurbsSurface.build(SvNurbsSurface.NATIVE, interpolated.get_degree_u(), interpolated.get_degree_v(), interpolated.get_knotvector_u(), interpolated.get_knotvector_v(), control_points, weights=None) #print(f"Result: {surface}") return lofted_u, lofted_v, interpolated, surface
def simple_loft(curves, degree_v=None, knots_u='UNIFY', metric='DISTANCE', tknots=None, implementation=SvNurbsSurface.NATIVE): """ Loft between given NURBS curves (a.k.a skinning). inputs: * degree_v - degree of resulting surface along V parameter; by default - use the same degree as provided curves * knots_u - one of: - 'UNIFY' - unify knotvectors of given curves by inserting additional knots - 'AVERAGE' - average knotvectors of given curves; this will work only if all curves have the same number of control points * metric - metric for interpolation; most useful are 'DISTANCE' and 'CENTRIPETAL' * implementation - NURBS maths implementation output: tuple: * list of curves - input curves after unification * list of NURBS curves along V direction * generated NURBS surface. """ if knots_u not in {'UNIFY', 'AVERAGE'}: raise Exception(f"Unsupported knots_u option: {knots_u}") curve_class = type(curves[0]) curves = unify_curves_degree(curves) if knots_u == 'UNIFY': curves = unify_curves(curves) else: kvs = [len(curve.get_control_points()) for curve in curves] max_kv, min_kv = max(kvs), min(kvs) if max_kv != min_kv: raise Exception( f"U knotvector averaging is not applicable: Curves have different number of control points: {kvs}" ) degree_u = curves[0].get_degree() if degree_v is None: degree_v = degree_u if degree_v > len(curves): raise Exception( f"V degree ({degree_v}) must be not greater than number of curves ({len(curves)}) minus 1" ) src_points = [curve.get_homogenous_control_points() for curve in curves] # lens = [len(pts) for pts in src_points] # max_len, min_len = max(lens), min(lens) # if max_len != min_len: # raise Exception(f"Unify error: curves have different number of control points: {lens}") src_points = np.array(src_points) #print("Src:", src_points) src_points = np.transpose(src_points, axes=(1, 0, 2)) v_curves = [ interpolate_nurbs_curve(curve_class, degree_v, points, metric=metric, tknots=tknots) for points in src_points ] control_points = [ curve.get_homogenous_control_points() for curve in v_curves ] control_points = np.array(control_points) #weights = [curve.get_weights() for curve in v_curves] #weights = np.array([curve.get_weights() for curve in curves]).T n, m, ndim = control_points.shape control_points = control_points.reshape((n * m, ndim)) control_points, weights = from_homogenous(control_points) control_points = control_points.reshape((n, m, 3)) weights = weights.reshape((n, m)) mean_v_vector = control_points.mean(axis=0) tknots_v = Spline.create_knots(mean_v_vector, metric=metric) knotvector_v = sv_knotvector.from_tknots(degree_v, tknots_v) if knots_u == 'UNIFY': knotvector_u = curves[0].get_knotvector() else: knotvectors = np.array([curve.get_knotvector() for curve in curves]) knotvector_u = knotvectors.mean(axis=0) surface = SvNurbsSurface.build(implementation, degree_u, degree_v, knotvector_u, knotvector_v, control_points, weights) surface.u_bounds = curves[0].get_u_bounds() return curves, v_curves, surface