def goal(ts, *xs): n3 = len(xs) n = n3 // 3 control_points = np.array(xs).reshape((n, 3)) curve = SvBezierCurve(control_points) pts = curve.evaluate_array(ts) return np.ravel(pts)
def test_third_derivs_equal(self): ts = np.linspace(0.0, 1.0, num=10) points = cubic_control_points() cubic = SvCubicBezierCurve(*points) generic = SvBezierCurve(points) cubic_points = cubic.third_derivative_array(ts) generic_points = generic.third_derivative_array(ts) self.assert_numpy_arrays_equal(cubic_points, generic_points, precision=6)
def test_cubic_equals_generic(self): ts = np.linspace(0.0, 1.0, num=10) points = cubic_control_points() cubic = SvCubicBezierCurve(*points) generic = SvBezierCurve(points) cubic_points = cubic.evaluate_array(ts) generic_points = generic.evaluate_array(ts) self.assert_numpy_arrays_equal(cubic_points, generic_points, precision=6)
def process(self): if not any(socket.is_linked for socket in self.outputs): return vertices_s = self.inputs['Vertices'].sv_get() degree_s = self.inputs['Degree'].sv_get() vertices_s = ensure_nesting_level(vertices_s, 3) degree_s = ensure_nesting_level(degree_s, 2) curve_out = [] points_out = [] for vertices, degree in zip_long_repeat(vertices_s, degree_s): if isinstance(degree, (tuple, list)): degree = degree[0] n = len(vertices) npoints = degree + 1 vertices = np.array(vertices) #xdata = np.linspace(0, 1, num=n) xdata = Spline.create_knots(vertices, metric=self.metric) ydata = np.ravel(vertices) p0 = init_guess(vertices, npoints) popt, pcov = curve_fit(goal, xdata, ydata, p0) control_points = popt.reshape((npoints, 3)) curve = SvBezierCurve(control_points) curve_out.append(curve) points_out.append(control_points.tolist()) self.outputs['Curve'].sv_set(curve_out) self.outputs['ControlPoints'].sv_set(points_out)
def test_blend_third(self): p0 = np.array([0, 0, 0]) p1 = np.array([3, 0, 0]) v0 = np.array([3, 3, 0]) v1 = np.array([3, 3, 0]) a0 = np.array([0, 0, 1]) a1 = np.array([0, 0, 1]) k0 = np.array([1, 1, 0]) k1 = np.array([1, 1, 0]) curve = SvBezierCurve.blend_third_derivatives(p0, v0, a0, k0, p1, v1, a1, k1) v0r = curve.tangent(0) v1r = curve.tangent(1) a0r = curve.second_derivative(0) a1r = curve.second_derivative(1) k0r = curve.third_derivative_array(np.array([0]))[0] k1r = curve.third_derivative_array(np.array([1]))[0] self.assert_numpy_arrays_equal(v0r, v0, precision=6) self.assert_numpy_arrays_equal(v1r, v1, precision=6) self.assert_numpy_arrays_equal(a0r, a0, precision=6) self.assert_numpy_arrays_equal(a1r, a1, precision=6) self.assert_numpy_arrays_equal(k0r, k0, precision=6) self.assert_numpy_arrays_equal(k1r, k1, precision=6)
def test_blend_tangent(self): p0 = np.array([0, 0, 0]) p1 = np.array([3, 0, 0]) v0 = np.array([3, 3, 0]) v1 = np.array([3, 3, 0]) curve = SvBezierCurve.from_points_and_tangents(p0, v0, v1, p1) v0r = curve.tangent(0) v1r = curve.tangent(1) self.assert_numpy_arrays_equal(v0r, v0) self.assert_numpy_arrays_equal(v1r, v1)
def interpret(self, interpreter, variables): vec = lambda v: Vector((v[0], v[1], 0)) interpreter.assert_not_closed() interpreter.start_new_segment() v0 = interpreter.position if interpreter.has_last_vertex: v0_index = interpreter.get_last_vertex() else: v0_index = interpreter.new_vertex(*v0) knot1 = None for i, segment in enumerate(self.segments): # For first segment, knot1 is initial pen position; # for the following, knot1 is knot2 of previous segment. if knot1 is None: knot1 = interpreter.position else: knot1 = knot2 handle = interpreter.calc_vertex(self.is_abs, segment.control[0], segment.control[1], variables) knot2 = interpreter.calc_vertex(self.is_abs, segment.knot2[0], segment.knot2[1], variables) interpreter.position = knot2 if self.num_segments is not None: r = interpreter.eval_(self.num_segments, variables) else: r = interpreter.dflt_num_verts curve = SvBezierCurve([vec(knot1), vec(handle), vec(knot2)]) interpreter.curves.append(curve) points = interpolate_quadratic_bezier(vec(knot1), vec(handle), vec(knot2), r) interpreter.new_knot("Q#.{}.h".format(i), *handle) interpreter.new_knot("Q#.{}.k".format(i), *knot2) interpreter.prev_quad_bezier_knot = handle for point in points[1:]: v1_index = interpreter.new_vertex(point.x, point.y) interpreter.new_edge(v0_index, v1_index) v0_index = v1_index if self.close: interpreter.close_segment(v1_index) interpreter.has_last_vertex = True
def test_blend_second(self): p0 = np.array([0, 0, 0]) p1 = np.array([3, 0, 0]) v0 = np.array([3, 3, 0]) v1 = np.array([3, 3, 0]) a0 = np.array([0, 0, 1]) a1 = np.array([0, 0, 1]) curve = SvBezierCurve.blend_second_derivatives(p0, v0, a0, p1, v1, a1) v0r = curve.tangent(0) v1r = curve.tangent(1) a0r = curve.second_derivative(0) a1r = curve.second_derivative(1) self.assert_numpy_arrays_equal(v0r, v0) self.assert_numpy_arrays_equal(v1r, v1) self.assert_numpy_arrays_equal(a0r, a0) self.assert_numpy_arrays_equal(a1r, a1, precision=8)
def process(self): if not any(socket.is_linked for socket in self.outputs): return points_s = self.inputs['Points'].sv_get() tangents_s = self.inputs['Tangents'].sv_get() points_s = ensure_nesting_level(points_s, 3) tangents_s = ensure_nesting_level(tangents_s, 3) curve_out = [] controls_out = [] for points, tangents in zip_long_repeat(points_s, tangents_s): new_controls, new_curves = SvBezierCurve.build_tangent_curve( points, tangents, cyclic=self.cyclic, concat=self.concat) curve_out.append(new_curves) controls_out.append(new_controls) self.outputs['Curve'].sv_set(curve_out) self.outputs['ControlPoints'].sv_set(controls_out)
def process(self): if not any(socket.is_linked for socket in self.outputs): return start_s = self.inputs['Start'].sv_get() end_s = self.inputs['End'].sv_get() knot1_s = self.inputs[CONTROL1_SOCKET].sv_get() knot2_s = self.inputs[CONTROL2_SOCKET].sv_get() controls_s = self.inputs['ControlPoints'].sv_get(default=[[[[]]]]) start_s = ensure_nesting_level(start_s, 3) end_s = ensure_nesting_level(end_s, 3) knot1_s = ensure_nesting_level(knot1_s, 3) knot2_s = ensure_nesting_level(knot2_s, 3) controls_s = ensure_nesting_level(controls_s, 4) curves_out = [] controls_out = [] for starts, ends, knot1s, knot2s, controls_i in zip_long_repeat( start_s, end_s, knot1_s, knot2_s, controls_s): new_curves = [] new_controls = [] for start, end, knot1, knot2, controls in zip_long_repeat( starts, ends, knot1s, knot2s, controls_i): start, end = np.array(start), np.array(end) knot1, knot2 = np.array(knot1), np.array(knot2) if self.mode == CUBIC: curve = SvCubicBezierCurve(start, knot1, knot2, end) curve_controls = [ start.tolist(), knot1.tolist(), knot2.tolist(), end.tolist() ] elif self.mode == CUBIC_TANGENT: curve = SvBezierCurve.from_points_and_tangents( start, knot1, knot2, end) curve_controls = [ curve.p0.tolist(), curve.p1.tolist(), curve.p2.tolist(), curve.p3.tolist() ] elif self.mode == CUBIC_4PT: curve = SvCubicBezierCurve.from_four_points( start, knot1, knot2, end) curve_controls = [ curve.p0.tolist(), curve.p1.tolist(), curve.p2.tolist(), curve.p3.tolist() ] elif self.mode == QUADRATIC: curve = SvBezierCurve([start, knot1, end]) curve_controls = [p.tolist() for p in curve.points] else: # GENERIC if not controls: raise SvNoDataError( socket=self.inputs['ControlPoints'], node=self) if len(controls) < 2: raise Exception( "At least two control points are required to build a Bezier spline!" ) if self.is_cyclic: controls = controls + [controls[0]] curve = SvBezierCurve(controls) curve_controls = controls new_curves.append(curve) new_controls.extend(curve_controls) curves_out.append(new_curves) controls_out.append(new_controls) self.outputs['Curve'].sv_set(curves_out) self.outputs['ControlPoints'].sv_set(controls_out)
def interpret(self, interpreter, variables): vec = lambda v: Vector((v[0], v[1], 0)) interpreter.assert_not_closed() interpreter.start_new_segment() v0 = interpreter.position if interpreter.has_last_vertex: v0_index = interpreter.get_last_vertex() else: v0_index = interpreter.new_vertex(*v0) knot1 = None for i, segment in enumerate(self.segments): # For first segment, knot1 is initial pen position; # for the following, knot1 is knot2 of previous segment. if knot1 is None: knot1 = interpreter.position else: knot1 = knot2 if interpreter.prev_quad_bezier_knot is None: # If there is no previous command or if the previous command was # not a Q, q, T or t, assume the control point is coincident with # the current point. handle = knot1 else: # The first control point is assumed to be the reflection of the # second control point on the previous command relative to the # current point. prev_knot_x, prev_knot_y = interpreter.prev_quad_bezier_knot x0, y0 = knot1 dx, dy = x0 - prev_knot_x, y0 - prev_knot_y handle = x0 + dx, y0 + dy knot2 = interpreter.calc_vertex(self.is_abs, segment.knot2[0], segment.knot2[1], variables) interpreter.position = knot2 if self.num_segments is not None: r = interpreter.eval_(self.num_segments, variables) else: r = interpreter.dflt_num_verts curve = SvBezierCurve([vec(knot1), vec(handle), vec(knot2)]) interpreter.new_curve(curve, self) points = interpolate_quadratic_bezier(vec(knot1), vec(handle), vec(knot2), r) interpreter.new_knot("T#.{}.h".format(i), *handle) interpreter.new_knot("T#.{}.k".format(i), *knot2) interpreter.prev_quad_bezier_knot = handle for point in points[1:]: v1_index = interpreter.new_vertex(point.x, point.y) interpreter.new_edge(v0_index, v1_index) v0_index = v1_index if self.close: interpreter.close_segment(v1_index) interpreter.has_last_vertex = True
def process(self): if not any(socket.is_linked for socket in self.outputs): return output_src = self.output_src or self.concat curves_out = [] controls_out = [] is_first = True for curve1, curve2, factor1, factor2 in self.get_inputs(): _, t_max_1 = curve1.get_u_bounds() t_min_2, _ = curve2.get_u_bounds() curve1_end = curve1.evaluate(t_max_1) curve2_begin = curve2.evaluate(t_min_2) smooth = int(self.smooth_mode) if smooth == 0: new_curve = SvLine.from_two_points(curve1_end, curve2_begin) new_controls = [curve1_end, curve2_begin] elif smooth == 1: tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) tangent1 = factor1 * tangent_1_end tangent2 = factor2 * tangent_2_begin new_curve = SvCubicBezierCurve( curve1_end, curve1_end + tangent1 / 3.0, curve2_begin - tangent2 / 3.0, curve2_begin ) new_controls = [new_curve.p0.tolist(), new_curve.p1.tolist(), new_curve.p2.tolist(), new_curve.p3.tolist()] elif smooth == 2: tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) second_1_end = curve1.second_derivative(t_max_1) second_2_begin = curve2.second_derivative(t_min_2) new_curve = SvBezierCurve.blend_second_derivatives( curve1_end, tangent_1_end, second_1_end, curve2_begin, tangent_2_begin, second_2_begin) new_controls = [p.tolist() for p in new_curve.points] elif smooth == 3: tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) second_1_end = curve1.second_derivative(t_max_1) second_2_begin = curve2.second_derivative(t_min_2) third_1_end = curve1.third_derivative_array(np.array([t_max_1]))[0] third_2_begin = curve2.third_derivative_array(np.array([t_min_2]))[0] new_curve = SvBezierCurve.blend_third_derivatives( curve1_end, tangent_1_end, second_1_end, third_1_end, curve2_begin, tangent_2_begin, second_2_begin, third_2_begin) new_controls = [p.tolist() for p in new_curve.points] else: raise Exception("Unsupported smooth level") if self.mode == 'N' and not self.cyclic and output_src and is_first: curves_out.append(curve1) curves_out.append(new_curve) if self.mode == 'N' and output_src: curves_out.append(curve2) controls_out.append(new_controls) is_first = False if self.concat: curves_out = [SvConcatCurve(curves_out)] self.outputs['Curve'].sv_set(curves_out) self.outputs['ControlPoints'].sv_set(controls_out)
def process(self): if not any(socket.is_linked for socket in self.outputs): return output_src = self.output_src or self.concat curves_out = [] controls_out = [] is_first = True for params in self.get_inputs(): new_curves = [] new_controls = [] for curve1, curve2, factor1, factor2, parameter in params: _, t_max_1 = curve1.get_u_bounds() t_min_2, _ = curve2.get_u_bounds() curve1_end = curve1.evaluate(t_max_1) curve2_begin = curve2.evaluate(t_min_2) smooth = self.smooth_mode if smooth == '0': new_curve = SvLine.from_two_points(curve1_end, curve2_begin) controls = [curve1_end, curve2_begin] elif smooth == '1': tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) tangent1 = factor1 * tangent_1_end tangent2 = factor2 * tangent_2_begin new_curve = SvCubicBezierCurve( curve1_end, curve1_end + tangent1 / 3.0, curve2_begin - tangent2 / 3.0, curve2_begin) controls = [ new_curve.p0.tolist(), new_curve.p1.tolist(), new_curve.p2.tolist(), new_curve.p3.tolist() ] elif smooth == '1b': tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) new_curve = SvBiArc.calc( curve1_end, curve2_begin, tangent_1_end, tangent_2_begin, parameter, planar_tolerance=self.planar_tolerance) controls = [new_curve.junction.tolist()] elif smooth == '2': tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) second_1_end = curve1.second_derivative(t_max_1) second_2_begin = curve2.second_derivative(t_min_2) new_curve = SvBezierCurve.blend_second_derivatives( curve1_end, tangent_1_end, second_1_end, curve2_begin, tangent_2_begin, second_2_begin) controls = [p.tolist() for p in new_curve.points] elif smooth == '3': tangent_1_end = curve1.tangent(t_max_1) tangent_2_begin = curve2.tangent(t_min_2) second_1_end = curve1.second_derivative(t_max_1) second_2_begin = curve2.second_derivative(t_min_2) third_1_end = curve1.third_derivative_array( np.array([t_max_1]))[0] third_2_begin = curve2.third_derivative_array( np.array([t_min_2]))[0] new_curve = SvBezierCurve.blend_third_derivatives( curve1_end, tangent_1_end, second_1_end, third_1_end, curve2_begin, tangent_2_begin, second_2_begin, third_2_begin) controls = [p.tolist() for p in new_curve.points] else: raise Exception("Unsupported smooth level") if self.mode == 'N' and not self.cyclic and output_src and is_first: new_curves.append(curve1) new_curves.append(new_curve) if self.mode == 'N' and output_src: new_curves.append(curve2) new_controls.append(controls) is_first = False if self.concat: new_curves = [concatenate_curves(new_curves)] if self.join: curves_out.extend(new_curves) controls_out.extend(new_controls) else: curves_out.append(new_curves) controls_out.append(new_controls) self.outputs['Curve'].sv_set(curves_out) self.outputs['ControlPoints'].sv_set(controls_out)