def process(self): if not any((s.is_linked for s in self.outputs)): return need_eval = self.outputs['Vertices'].is_linked curves_s = self.inputs['Curve'].sv_get() resolution_s = self.inputs['Resolution'].sv_get() length_s = self.inputs['Length'].sv_get() samples_s = self.inputs['Samples'].sv_get(default=[[]]) length_s = ensure_nesting_level(length_s, 3) resolution_s = ensure_nesting_level(resolution_s, 2) samples_s = ensure_nesting_level(samples_s, 2) curves_s = ensure_nesting_level(curves_s, 2, data_types=(SvCurve,)) ts_out = [] verts_out = [] for curves, resolutions, input_lengths_i, samples_i in zip_long_repeat(curves_s, resolution_s, length_s, samples_s): for curve, resolution, input_lengths, samples in zip_long_repeat(curves, resolutions, input_lengths_i, samples_i): mode = self.mode accuracy = self.accuracy if self.id_data.sv_draft: mode = 'LIN' accuracy = self.accuracy_draft if self.specify_accuracy: tolerance = 10 ** (-accuracy) else: tolerance = None solver = SvCurveLengthSolver(curve) solver.prepare(mode, resolution, tolerance=tolerance) if self.eval_mode == 'AUTO': total_length = solver.get_total_length() input_lengths = np.linspace(0.0, total_length, num = samples) else: input_lengths = np.array(input_lengths) ts = solver.solve(input_lengths) ts_out.append(ts.tolist()) if need_eval: verts = curve.evaluate_array(ts).tolist() verts_out.append(verts) self.outputs['T'].sv_set(ts_out) self.outputs['Vertices'].sv_set(verts_out)
def process(self): if not any((s.is_linked for s in self.outputs)): return need_eval = self.outputs['Vertices'].is_linked curves = self.inputs['Curve'].sv_get() resolution_s = self.inputs['Resolution'].sv_get() length_s = self.inputs['Length'].sv_get() samples_s = self.inputs['Samples'].sv_get(default=[[]]) length_s = ensure_nesting_level(length_s, 2) ts_out = [] verts_out = [] for curve, resolution, input_lengths, samples, in zip_long_repeat( curves, resolution_s, length_s, samples_s): if self.eval_mode == 'AUTO': if isinstance(samples, (list, tuple)): samples = samples[0] if isinstance(resolution, (list, tuple)): resolution = resolution[0] mode = self.mode if self.id_data.sv_draft: mode = 'LIN' solver = SvCurveLengthSolver(curve) solver.prepare(mode, resolution) if self.eval_mode == 'AUTO': total_length = solver.get_total_length() input_lengths = np.linspace(0.0, total_length, num=samples) else: input_lengths = np.array(input_lengths) ts = solver.solve(input_lengths) ts_out.append(ts.tolist()) if need_eval: verts = curve.evaluate_array(ts).tolist() verts_out.append(verts) self.outputs['T'].sv_set(ts_out) self.outputs['Vertices'].sv_set(verts_out)
class SvBendAlongCurveField(SvVectorField): ZERO = 'ZERO' FRENET = 'FRENET' HOUSEHOLDER = 'householder' TRACK = 'track' DIFF = 'diff' TRACK_NORMAL = 'track_normal' def __init__(self, curve, algorithm, scale_all, axis, t_min, t_max, up_axis=None, resolution=50, length_mode='T'): self.curve = curve self.axis = axis self.t_min = t_min self.t_max = t_max self.algorithm = algorithm self.scale_all = scale_all self.up_axis = up_axis self.length_mode = length_mode if algorithm == SvBendAlongCurveField.ZERO: self.curve.pre_calc_torsion_integral(resolution) elif algorithm == SvBendAlongCurveField.TRACK_NORMAL: self.normal_tracker = SvNormalTrack(curve, resolution) if length_mode == 'L': self.length_solver = SvCurveLengthSolver(curve) self.length_solver.prepare('SPL', resolution) self.__description__ = "Bend along {}".format(curve) def get_matrix(self, tangent, scale): return MathutilsRotationCalculator.get_matrix(tangent, scale, self.axis, self.algorithm, self.scale_all, self.up_axis) def get_matrices(self, ts, scale): n = len(ts) if self.scale_all: scale_matrix = np.array([ [scale, 0, 0], [0, scale, 0], [0, 0, 1/scale] ]) else: scale_matrix = np.array([ [1, 0, 0], [0, 1, 0], [0, 0, 1/scale] ]) if self.algorithm == SvBendAlongCurveField.FRENET: frenet, _ , _ = self.curve.frame_array(ts) return frenet @ scale_matrix elif self.algorithm == SvBendAlongCurveField.ZERO: frenet, _ , _ = self.curve.frame_array(ts) angles = - self.curve.torsion_integral(ts) zeros = np.zeros((n,)) ones = np.ones((n,)) row1 = np.stack((np.cos(angles), np.sin(angles), zeros)).T # (n, 3) row2 = np.stack((-np.sin(angles), np.cos(angles), zeros)).T # (n, 3) row3 = np.stack((zeros, zeros, ones)).T # (n, 3) rotation_matrices = np.dstack((row1, row2, row3)) return frenet @ rotation_matrices @ scale_matrix elif self.algorithm == SvBendAlongCurveField.TRACK_NORMAL: matrices = self.normal_tracker.evaluate_array(ts) return matrices @ scale_matrix else: raise Exception("Unsupported algorithm") def get_t_value(self, x, y, z): curve_t_min, curve_t_max = self.curve.get_u_bounds() t = [x, y, z][self.axis] if self.length_mode == 'T': t = (curve_t_max - curve_t_min) * (t - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: t = (t - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 t = t * self.length_solver.get_total_length() t = self.length_solver.solve(np.array([t]))[0] return t def get_t_values(self, xs, ys, zs): curve_t_min, curve_t_max = self.curve.get_u_bounds() ts = [xs, ys, zs][self.axis] if self.length_mode == 'T': ts = (curve_t_max - curve_t_min) * (ts - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: ts = (ts - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 ts = ts * self.length_solver.get_total_length() ts = self.length_solver.solve(ts) return ts def get_scale(self): if self.length_mode == 'T': curve_t_min, curve_t_max = self.curve.get_u_bounds() t_range = curve_t_max - curve_t_min else: t_range = self.length_solver.get_total_length() return (self.t_max - self.t_min) / t_range def evaluate(self, x, y, z): t = self.get_t_value(x, y, z) spline_tangent = self.curve.tangent(t) spline_vertex = self.curve.evaluate(t) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrix = self.get_matrices(np.array([t]), scale) else: matrix = self.get_matrix(spline_tangent, scale) src_vector_projection = np.array([x, y, z]) src_vector_projection[self.axis] = 0 new_vertex = np.matmul(matrix, src_vector_projection) + spline_vertex vector = new_vertex - np.array([x, y, z]) return vector def evaluate_grid(self, xs, ys, zs): def multiply(matrices, vectors): vectors = vectors[np.newaxis] vectors = np.transpose(vectors, axes=(1,2,0)) r = matrices @ vectors return r[:,:,0] ts = self.get_t_values(xs, ys, zs).flatten() spline_tangents = self.curve.tangent_array(ts) spline_vertices = self.curve.evaluate_array(ts) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrices = self.get_matrices(ts, scale) else: matrices = np.vectorize(lambda t : self.get_matrix(t, scale), signature='(3)->(3,3)')(spline_tangents) src_vectors = np.stack((xs, ys, zs)).T src_vector_projections = src_vectors.copy() src_vector_projections[:,self.axis] = 0 #multiply = np.vectorize(lambda m, v: m @ v, signature='(3,3),(3)->(3)') new_vertices = multiply(matrices, src_vector_projections) + spline_vertices R = (new_vertices - src_vectors).T return R[0], R[1], R[2]
class SvBendAlongCurveField(SvVectorField): def __init__(self, curve, algorithm, scale_all, axis, t_min, t_max, up_axis=None, resolution=50, length_mode='T'): self.curve = curve self.axis = axis self.t_min = t_min self.t_max = t_max self.algorithm = algorithm self.scale_all = scale_all self.up_axis = up_axis self.length_mode = length_mode if algorithm == 'ZERO': self.curve.pre_calc_torsion_integral(resolution) if length_mode == 'L': self.length_solver = SvCurveLengthSolver(curve) self.length_solver.prepare('SPL', resolution) self.__description__ = "Bend along {}".format(curve) def get_matrix(self, tangent, scale): x = Vector((1.0, 0.0, 0.0)) y = Vector((0.0, 1.0, 0.0)) z = Vector((0.0, 0.0, 1.0)) if self.axis == 0: ax1, ax2, ax3 = x, y, z elif self.axis == 1: ax1, ax2, ax3 = y, x, z else: ax1, ax2, ax3 = z, x, y if self.scale_all: scale_matrix = Matrix.Scale(1 / scale, 4, ax1) @ Matrix.Scale( scale, 4, ax2) @ Matrix.Scale(scale, 4, ax3) else: scale_matrix = Matrix.Scale(1 / scale, 4, ax1) scale_matrix = np.array(scale_matrix.to_3x3()) tangent = Vector(tangent) if self.algorithm == 'householder': rot = autorotate_householder(ax1, tangent).inverted() elif self.algorithm == 'track': axis = "XYZ"[self.axis] rot = autorotate_track(axis, tangent, self.up_axis) elif self.algorithm == 'diff': rot = autorotate_diff(tangent, ax1) else: raise Exception("Unsupported algorithm") rot = np.array(rot.to_3x3()) return np.matmul(rot, scale_matrix) def get_matrices(self, ts, scale): frenet, _, _ = self.curve.frame_array(ts) if self.scale_all: scale_matrix = np.array([[scale, 0, 0], [0, scale, 0], [0, 0, 1 / scale]]) else: scale_matrix = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1 / scale]]) n = len(ts) if self.algorithm == 'FRENET': return frenet @ scale_matrix elif self.algorithm == 'ZERO': angles = -self.curve.torsion_integral(ts) zeros = np.zeros((n, )) ones = np.ones((n, )) row1 = np.stack( (np.cos(angles), np.sin(angles), zeros)).T # (n, 3) row2 = np.stack( (-np.sin(angles), np.cos(angles), zeros)).T # (n, 3) row3 = np.stack((zeros, zeros, ones)).T # (n, 3) rotation_matrices = np.dstack((row1, row2, row3)) return frenet @ rotation_matrices @ scale_matrix else: raise Exception("Unsupported algorithm") def get_t_value(self, x, y, z): curve_t_min, curve_t_max = self.curve.get_u_bounds() t = [x, y, z][self.axis] if self.length_mode == 'T': t = (curve_t_max - curve_t_min) * (t - self.t_min) / ( self.t_max - self.t_min) + curve_t_min else: t = (t - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 t = t * self.length_solver.get_total_length() t = self.length_solver.solve(np.array([t]))[0] return t def get_t_values(self, xs, ys, zs): curve_t_min, curve_t_max = self.curve.get_u_bounds() ts = [xs, ys, zs][self.axis] if self.length_mode == 'T': ts = (curve_t_max - curve_t_min) * (ts - self.t_min) / ( self.t_max - self.t_min) + curve_t_min else: ts = (ts - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 ts = ts * self.length_solver.get_total_length() ts = self.length_solver.solve(ts) return ts def get_scale(self): if self.length_mode == 'T': curve_t_min, curve_t_max = self.curve.get_u_bounds() t_range = curve_t_max - curve_t_min else: t_range = self.length_solver.get_total_length() return (self.t_max - self.t_min) / t_range def evaluate(self, x, y, z): t = self.get_t_value(x, y, z) spline_tangent = self.curve.tangent(t) spline_vertex = self.curve.evaluate(t) scale = self.get_scale() if self.algorithm in {'ZERO', 'FRENET'}: matrix = self.get_matrices(np.array([t]), scale) else: matrix = self.get_matrix(spline_tangent, scale) src_vector_projection = np.array([x, y, z]) src_vector_projection[self.axis] = 0 new_vertex = np.matmul(matrix, src_vector_projection) + spline_vertex vector = new_vertex - np.array([x, y, z]) return vector def evaluate_grid(self, xs, ys, zs): ts = self.get_t_values(xs, ys, zs).flatten() spline_tangents = self.curve.tangent_array(ts) spline_vertices = self.curve.evaluate_array(ts) scale = self.get_scale() if self.algorithm in {'ZERO', 'FRENET'}: matrices = self.get_matrices(ts, scale) else: matrices = np.vectorize(lambda t: self.get_matrix(t, scale), signature='(3)->(3,3)')(spline_tangents) src_vectors = np.stack((xs, ys, zs)).T src_vector_projections = src_vectors.copy() src_vector_projections[:, self.axis] = 0 #matrices = matrices[np.newaxis][np.newaxis] multiply = np.vectorize(lambda m, v: m @ v, signature='(3,3),(3)->(3)') new_vertices = multiply(matrices, src_vector_projections) + spline_vertices R = (new_vertices - src_vectors).T return R[0], R[1], R[2]