From 00264bc0ee01dbc8524e75a82061853590b765d4 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 24 Dec 2022 21:46:25 +0500 Subject: [PATCH 1/4] "Tangent Curve": add curve type option. --- nodes/curve/tangent_curve.py | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/nodes/curve/tangent_curve.py b/nodes/curve/tangent_curve.py index e2edcbd1f3..8e6653178e 100644 --- a/nodes/curve/tangent_curve.py +++ b/nodes/curve/tangent_curve.py @@ -6,7 +6,9 @@ from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level +from sverchok.utils.math import supported_metrics from sverchok.utils.curve import SvCurve, SvBezierCurve, SvConcatCurve, SvCubicBezierCurve +from sverchok.utils.curve.nurbs_solver_applications import interpolate_nurbs_curve_with_tangents class SvTangentsCurveNode(SverchCustomTreeNode, bpy.types.Node): """ @@ -36,12 +38,34 @@ class SvTangentsCurveNode(SverchCustomTreeNode, bpy.types.Node): default = False, update = updateNode) + curve_modes = [ + ('HERMITE', "Hermite", "Use Bezier curve representation of Hermite spline", 0), + ('NURBS', "NURBS", "Use NURBS curve interpolation", 1) + ] + + curve_mode : EnumProperty( + name = "Curve type", + description = "Type of algorithm to be used and type of generated curve", + items = curve_modes, + default = 'HERMITE', + update = updateNode) + + metric: EnumProperty(name='Metric', + description = "Knot mode", + default="DISTANCE", items=supported_metrics, + update=updateNode) + def draw_buttons(self, context, layout): - layout.prop(self, 'cyclic', toggle=True) - layout.prop(self, 'concat', toggle=True) + layout.prop(self, 'curve_mode') + layout.prop(self, 'cyclic') + if self.curve_mode == 'HERMITE': + layout.prop(self, 'concat') + elif self.curve_mode == 'NURBS': + layout.prop(self, 'metric') def draw_buttons_ext(self, context, layout): - layout.prop(self, 'make_nurbs', toggle=True) + if self.curve_mode == 'HERMITE': + layout.prop(self, 'make_nurbs', toggle=True) def sv_init(self, context): self.inputs.new('SvVerticesSocket', "Points") @@ -67,9 +91,17 @@ def process(self): curves_i = [] controls_i = [] for points, tangents in zip_long_repeat(*params): - new_controls, new_curve = SvBezierCurve.build_tangent_curve(points, tangents, - cyclic = self.cyclic, concat = self.concat, - as_nurbs = self.make_nurbs) + if self.curve_mode == 'HERMITE': + new_controls, new_curve = SvBezierCurve.build_tangent_curve(points, tangents, + cyclic = self.cyclic, concat = self.concat, + as_nurbs = self.make_nurbs) + else: + new_curve = interpolate_nurbs_curve_with_tangents(3, + points, tangents, + metric = self.metric, + cyclic = self.cyclic, + logger = self.get_logger()) + new_controls = new_curve.get_control_points().tolist() curves_i.append(new_curve) controls_i.append(new_controls) if output_nested: From 2cbf84969376934bcf69e4adba339e27ff54a7b7 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 24 Dec 2022 22:05:46 +0500 Subject: [PATCH 2/4] Update documentation. --- docs/nodes/curve/tangent_curve.rst | 47 ++++++++++++++++++++---------- utils/surface/nurbs.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/docs/nodes/curve/tangent_curve.rst b/docs/nodes/curve/tangent_curve.rst index c5036433c4..a1f32bfe60 100644 --- a/docs/nodes/curve/tangent_curve.rst +++ b/docs/nodes/curve/tangent_curve.rst @@ -13,21 +13,28 @@ the curve must pass, and tangent vectors of the curve at those points. .. image:: https://user-images.githubusercontent.com/14288520/205862183-f8eb5c09-2ba5-4315-8a68-1dab9c102fca.png :target: https://user-images.githubusercontent.com/14288520/205862183-f8eb5c09-2ba5-4315-8a68-1dab9c102fca.png -The curve is generated as a series of cubic Bezier curves. Control points of -Bezier curves are defined as follows: +The node can use one of two algorithms: -* For each segment defined by a pair of input points, one Bezier curve is - generated - for example, one curve for first and second point, one curve for - second and third point, and so on. -* For each segment, the first point is the starting point of Bezier curve, and - the second point is the end point of Bezier curve. -* Provided tangent vectors are placed so that the middle point of each vector - is at corresponding input point - middle of the first tangent vector at the - first input point, and so on. Then end points of these vectors will define - additional control points for Bezier curves. +* Hermite_ spline. The curve is generated as a series of cubic Bezier curves. + Control points of Bezier curves are defined as follows: -Generated curves may be optionally concatenated into one Curve object. + * For each segment defined by a pair of input points, one Bezier curve is + generated - for example, one curve for first and second point, one curve for + second and third point, and so on. + * For each segment, the first point is the starting point of Bezier curve, and + the second point is the end point of Bezier curve. + * Provided tangent vectors are placed so that the middle point of each vector + is at corresponding input point - middle of the first tangent vector at the + first input point, and so on. Then end points of these vectors will define + additional control points for Bezier curves. + Generated curves may be optionally concatenated into one Curve object. + +* NURBS curve. The node creates interpolating NURBS curve (of 3rd degree) + through the specified points, with additional condition that it should have + specified tangents at those points. + +.. _Hermite: https://en.wikipedia.org/wiki/Cubic_Hermite_spline Inputs ------ @@ -44,15 +51,23 @@ Parameters This node has the following parameters: +* **Curve type**. This defines the algorithm to be used by the node, and the + type of resulting curve. The available options are: + + * **Hermite**. Use Hermite_ spline algorithm, and generate either a list of + Bezier curves, or concatenated NURBS curve. + * **NURBS**. Use NURBS interpolation algotithm. + * **Cyclic**. If checked, then the node will generate additional Bezier curve segment to connect the last point with the first one. Unchecked by default. .. image:: https://user-images.githubusercontent.com/14288520/205867628-5f3de9ae-ab0a-435b-9ebb-3dc409ff4e51.png :target: https://user-images.githubusercontent.com/14288520/205867628-5f3de9ae-ab0a-435b-9ebb-3dc409ff4e51.png -* **Concatenate**. If checked, then the node will concatenate all generated - Bezier curve segments into one Curve object. Otherwise, it will output each - segment as a separate Curve object. Checked by default. +* **Concatenate**. This parameter is available only when **Curve type** + parameter is set to **Hermite**. If checked, then the node will concatenate + all generated Bezier curve segments into one Curve object. Otherwise, it will + output each segment as a separate Curve object. Checked by default. .. image:: https://user-images.githubusercontent.com/14288520/205943520-e9ec6cd6-54de-483f-8af9-753d4720d27e.png :target: https://user-images.githubusercontent.com/14288520/205943520-e9ec6cd6-54de-483f-8af9-753d4720d27e.png @@ -101,4 +116,4 @@ More complex example: draw a smooth curve so that it would touch three circles i * List->List Struct-> :doc:`List Slice ` * List->List Struct-> :doc:`List Split ` * Viz-> :doc:`Viewer Draw Curve ` -* Viz-> :doc:`Viewer Draw ` \ No newline at end of file +* Viz-> :doc:`Viewer Draw ` diff --git a/utils/surface/nurbs.py b/utils/surface/nurbs.py index 0d4694568f..e9f93778bd 100644 --- a/utils/surface/nurbs.py +++ b/utils/surface/nurbs.py @@ -1299,6 +1299,53 @@ def loft_by_binormals(curves, degree_v = 3, control_points, weights) return surface +def loft_with_tangents(curves, tangent_fields, degree_v = 3, + metric = 'DISTANCE', tknots=None, + knotvector_accuracy = 6, + implementation = SvNurbsMaths.NATIVE, + logger = None): + + if logger is None: + logger = getLogger() + + n_curves = len(curves) + curves = unify_curves_degree(curves) + curves = unify_curves(curves, accuracy=knotvector_accuracy) + degree_u = curves[0].get_degree() + + src_points = [curve.get_homogenous_control_points() for curve in curves] + src_points = np.array(src_points) + src_points = np.transpose(src_points, axes=(1,0,2)) + + tangents = [field.evaluate_array(curve.get_control_points()) for curve, field in zip(curves, tangent_fields)] + tangents = np.array(tangents) + tangents = np.transpose(tangents, axes=(2,0,1)) + + n,m,ndim = tangents.shape + tangents = np.concatenate((tangents, np.zeros((n,m,1))), axis=2) + + tknots_vs = [Spline.create_knots(src_points[i,:], metric=metric) for i in range(n_curves)] + tknots_vs = np.array(tknots_vs) + tknots_v = np.mean(tknots_vs, axis=0) + + v_curves = [interpolate_nurbs_curve_with_tangents(degree_v, points, tangents, tknots=tknots_v, implementation=implementation, logger=logger) for points, tangents in zip(src_points, tangents)] + control_points = [curve.get_homogenous_control_points() for curve in v_curves] + control_points = np.array(control_points) + 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)) + + knotvector_u = curves[0].get_knotvector() + knotvector_v = v_curves[0].get_knotvector() + + surface = SvNurbsSurface.build(SvNurbsSurface.NATIVE, + degree_u, degree_v, + knotvector_u, knotvector_v, + control_points, weights) + return surface + def interpolate_nurbs_curves(curves, base_vs, target_vs, degree_v = None, knots_u = 'UNIFY', implementation = SvNurbsSurface.NATIVE): From 3f98389418d52edfa0acc294d2300048b501a36a Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 25 Dec 2022 12:27:44 +0500 Subject: [PATCH 3/4] Generalize API a bit. --- utils/curve/bezier.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/utils/curve/bezier.py b/utils/curve/bezier.py index 82c8cb6a8f..b4cbd6915d 100644 --- a/utils/curve/bezier.py +++ b/utils/curve/bezier.py @@ -134,7 +134,7 @@ def blend_third_derivatives(cls, p0, v0, a0, k0, p7, v7, a7, k7): return SvBezierCurve([p0, p1, p2, p3, p4, p5, p6, p7]) @classmethod - def build_tangent_curve(cls, points, tangents, cyclic=False, concat=False, as_nurbs=False): + def build_tangent_curve(cls, points, tangents, hermite=True, cyclic=False, concat=False, as_nurbs=False): """ Build cubic Bezier curve spline, which goes through specified `points', having specified `tangents' at these points. @@ -143,6 +143,8 @@ def build_tangent_curve(cls, points, tangents, cyclic=False, concat=False, as_nu * points, tangents: lists of 3-tuples * cyclic: whether the curve should be closed (cyclic) * concat: whether to concatenate all curve segments into single Curve object + * hermite: if true, use Hermite spline - divide tangent vector by 3 to + obtain middle control points; otherwise, divide by 2. outputs: tuple: * list of curve control points - list of lists of 3-tuples @@ -155,13 +157,17 @@ def build_tangent_curve(cls, points, tangents, cyclic=False, concat=False, as_nu segments = list(zip(pairs, pairs[1:])) if cyclic: segments.append((pairs[-1], pairs[0])) + if hermite: + d = 3.0 + else: + d = 2.0 for pair1, pair2 in segments: point1, tangent1 = pair1 point2, tangent2 = pair2 point1, tangent1 = np.array(point1), np.array(tangent1) point2, tangent2 = np.array(point2), np.array(tangent2) - tangent1, tangent2 = tangent1/3.0, tangent2/3.0 + tangent1, tangent2 = tangent1/d, tangent2/d curve = SvCubicBezierCurve( point1, point1 + tangent1, From a9b85d9fbacc5537b768e528553e40b0f0a68fbc Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 25 Dec 2022 13:38:42 +0500 Subject: [PATCH 4/4] Add example to documentation. --- docs/nodes/curve/tangent_curve.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/nodes/curve/tangent_curve.rst b/docs/nodes/curve/tangent_curve.rst index a1f32bfe60..05f1550a5d 100644 --- a/docs/nodes/curve/tangent_curve.rst +++ b/docs/nodes/curve/tangent_curve.rst @@ -117,3 +117,11 @@ More complex example: draw a smooth curve so that it would touch three circles i * List->List Struct-> :doc:`List Split ` * Viz-> :doc:`Viewer Draw Curve ` * Viz-> :doc:`Viewer Draw ` + +--------- + +Example of NURBS mode usage: + +.. image:: https://user-images.githubusercontent.com/284644/209460542-535e4c0a-9c0f-44e4-a127-46fa104cd335.png + :target: https://user-images.githubusercontent.com/284644/209460542-535e4c0a-9c0f-44e4-a127-46fa104cd335.png +