From ee61b016b19f153641e38242424792cf67911153 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 00:02:29 +0500 Subject: [PATCH 1/9] NURBS Curve Degree Elevate & Reduce nodes. --- index.md | 3 + nodes/curve/elevate_degree.py | 93 +++++++++++++++++++++++++++ nodes/curve/reduce_degree.py | 118 ++++++++++++++++++++++++++++++++++ utils/curve/nurbs.py | 1 + 4 files changed, 215 insertions(+) create mode 100644 nodes/curve/elevate_degree.py create mode 100644 nodes/curve/reduce_degree.py diff --git a/index.md b/index.md index d9b3704ab3..3dfab19ff8 100644 --- a/index.md +++ b/index.md @@ -77,6 +77,9 @@ SvRefineNurbsCurveNode SvCurveRemoveExcessiveKnotsNode --- + SvCurveElevateDegreeNode + SvCurveReduceDegreeNode + --- SvAdaptivePlotNurbsCurveNode ## Curves @ Bezier diff --git a/nodes/curve/elevate_degree.py b/nodes/curve/elevate_degree.py new file mode 100644 index 0000000000..1265e651e8 --- /dev/null +++ b/nodes/curve/elevate_degree.py @@ -0,0 +1,93 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length +from sverchok.utils.curve import SvCurve +from sverchok.utils.curve.nurbs import SvNurbsCurve + +class SvCurveElevateDegreeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Elevate Degree + Tooltip: Elevate degree of a NURBS curve + """ + bl_idname = 'SvCurveElevateDegreeNode' + bl_label = 'Elevate Degree (NURBS Curve)' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_CURVE_INSERT_KNOT' + + modes = [ + ('DELTA', "Elevate by", "Specify difference between current degree and target degree", 0), + ('TARGET', "Set degree", "Specify target degree", 1) + ] + + def update_sockets(self, context): + self.inputs['Degree'].label = "Delta" if self.mode == 'DELTA' else "Degree" + updateNode(self, context) + + mode : EnumProperty( + name = "Mode", + description = "How new curve degree is specified", + items = modes, + update = update_sockets) + + degree : IntProperty( + name = "Degree", + description = "Target degree or degree delta", + min = 0, + default = 1, + update = updateNode) + + def draw_buttons(self, context, layout): + layout.prop(self, 'mode', text='') + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' + self.outputs.new('SvCurveSocket', 'Curve') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + curve_s = self.inputs['Curve'].sv_get() + degree_s = self.inputs['Degree'].sv_get() + + input_level = get_data_nesting_level(curve_s, data_types=(SvCurve,)) + flat_output = input_level < 2 + curve_s = ensure_nesting_level(curve_s, 2, data_types=(SvCurve,)) + degree_s = ensure_nesting_level(degree_s, 2) + + curves_out = [] + for params in zip_long_repeat(curve_s, degree_s): + new_curves = [] + for curve, degree in zip_long_repeat(*params): + curve = SvNurbsCurve.to_nurbs(curve) + if curve is None: + raise Exception("One of curves is not NURBS") + if self.mode == 'DELTA': + kwargs = dict(delta = degree) + else: + kwargs = dict(target = degree) + curve = curve.elevate_degree(**kwargs) + new_curves.append(curve) + if flat_output: + curves_out.extend(new_curves) + else: + curves_out.append(new_curves) + + self.outputs['Curve'].sv_set(curves_out) + +def register(): + bpy.utils.register_class(SvCurveElevateDegreeNode) + +def unregister(): + bpy.utils.unregister_class(SvCurveElevateDegreeNode) + diff --git a/nodes/curve/reduce_degree.py b/nodes/curve/reduce_degree.py new file mode 100644 index 0000000000..4bfe7b9f5a --- /dev/null +++ b/nodes/curve/reduce_degree.py @@ -0,0 +1,118 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length +from sverchok.utils.curve import SvCurve +from sverchok.utils.curve.nurbs import SvNurbsCurve + +class SvCurveReduceDegreeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Reduce Degree + Tooltip: Reduce degree of a NURBS curve + """ + bl_idname = 'SvCurveReduceDegreeNode' + bl_label = 'Reduce Degree (NURBS Curve)' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_CURVE_INSERT_KNOT' + + modes = [ + ('DELTA', "Reduce by", "Specify difference between current degree and target degree", 0), + ('TARGET', "Set degree", "Specify target degree", 1) + ] + + def update_sockets(self, context): + self.inputs['Degree'].label = "Delta" if self.mode == 'DELTA' else "Degree" + updateNode(self, context) + + mode : EnumProperty( + name = "Mode", + description = "How new curve degree is specified", + items = modes, + update = update_sockets) + + degree : IntProperty( + name = "Degree", + description = "Target degree or degree delta", + min = 0, + default = 1, + update = updateNode) + + tolerance : FloatProperty( + name = "Tolerance", + description = "Error tolerance", + min = 0.0, + default = 1e-4, + precision = 8, + update = updateNode) + + if_possible : BoolProperty( + name = "If possible", + description = "Don't fail when trying to reduce the curve's degree too many times, just reduce it as much as possible", + default = False, + update = updateNode) + + def draw_buttons(self, context, layout): + layout.prop(self, 'mode', text='') + layout.prop(self, 'if_possible') + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' + self.inputs.new('SvStringsSocket', 'Tolerance').prop_name = 'tolerance' + self.outputs.new('SvCurveSocket', 'Curve') + self.outputs.new('SvStringsSocket', 'Error') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + curve_s = self.inputs['Curve'].sv_get() + degree_s = self.inputs['Degree'].sv_get() + tolerance_s = self.inputs['Tolerance'].sv_get() + + input_level = get_data_nesting_level(curve_s, data_types=(SvCurve,)) + flat_output = input_level < 2 + curve_s = ensure_nesting_level(curve_s, 2, data_types=(SvCurve,)) + degree_s = ensure_nesting_level(degree_s, 2) + tolerance_s = ensure_nesting_level(tolerance_s, 2) + + curves_out = [] + error_out = [] + for params in zip_long_repeat(curve_s, degree_s, tolerance_s): + new_curves = [] + new_errors = [] + for curve, degree, tolerance in zip_long_repeat(*params): + curve = SvNurbsCurve.to_nurbs(curve) + if curve is None: + raise Exception("One of curves is not NURBS") + if self.mode == 'DELTA': + kwargs = dict(delta = degree) + else: + kwargs = dict(target = degree) + curve, error = curve.reduce_degree(tolerance=tolerance, return_error=True, if_possible = self.if_possible, **kwargs) + new_curves.append(curve) + new_errors.append(error) + if flat_output: + curves_out.extend(new_curves) + error_out.extend(new_errors) + else: + curves_out.append(new_curves) + error_out.append(new_errors) + + self.outputs['Curve'].sv_set(curves_out) + self.outputs['Error'].sv_set(error_out) + +def register(): + bpy.utils.register_class(SvCurveReduceDegreeNode) + +def unregister(): + bpy.utils.unregister_class(SvCurveReduceDegreeNode) + diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 883ccd6f0a..0c455825d6 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -420,6 +420,7 @@ def reduce_degree_once(curve): result = reduced_segments[0] for segment in reduced_segments[1:]: result = result.concatenate(segment, remove_knots=True, tolerance=tolerance) + max_error = max(max_error, tolerance) result = result.reparametrize(src_t_min, src_t_max) return result, max_error, True From 3738f6650852321553489dbfdce5a05c0db32452 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 01:00:56 +0500 Subject: [PATCH 2/9] NURBS Surface degree elevation & reduction nodes. --- index.md | 3 + nodes/curve/elevate_degree.py | 1 + nodes/curve/reduce_degree.py | 1 + nodes/surface/elevate_degree.py | 107 +++++++++++++++++++++++++++ nodes/surface/reduce_degree.py | 125 ++++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 nodes/surface/elevate_degree.py create mode 100644 nodes/surface/reduce_degree.py diff --git a/index.md b/index.md index 3dfab19ff8..4120d9ae2a 100644 --- a/index.md +++ b/index.md @@ -154,6 +154,9 @@ SvSurfaceInsertKnotNode SvSurfaceRemoveKnotNode SvSurfaceRemoveExcessiveKnotsNode + --- + SvSurfaceElevateDegreeNode + SvSurfaceReduceDegreeNode ## Surfaces SvExPlaneSurfaceNode diff --git a/nodes/curve/elevate_degree.py b/nodes/curve/elevate_degree.py index 1265e651e8..835941ffd2 100644 --- a/nodes/curve/elevate_degree.py +++ b/nodes/curve/elevate_degree.py @@ -52,6 +52,7 @@ def sv_init(self, context): self.inputs.new('SvCurveSocket', "Curve") self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' self.outputs.new('SvCurveSocket', 'Curve') + self.update_sockets(context) def process(self): if not any(socket.is_linked for socket in self.outputs): diff --git a/nodes/curve/reduce_degree.py b/nodes/curve/reduce_degree.py index 4bfe7b9f5a..6a71810165 100644 --- a/nodes/curve/reduce_degree.py +++ b/nodes/curve/reduce_degree.py @@ -69,6 +69,7 @@ def sv_init(self, context): self.inputs.new('SvStringsSocket', 'Tolerance').prop_name = 'tolerance' self.outputs.new('SvCurveSocket', 'Curve') self.outputs.new('SvStringsSocket', 'Error') + self.update_sockets(context) def process(self): if not any(socket.is_linked for socket in self.outputs): diff --git a/nodes/surface/elevate_degree.py b/nodes/surface/elevate_degree.py new file mode 100644 index 0000000000..3be5361b5a --- /dev/null +++ b/nodes/surface/elevate_degree.py @@ -0,0 +1,107 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length +from sverchok.utils.surface import SvSurface +from sverchok.utils.surface.nurbs import SvNurbsSurface + +class SvSurfaceElevateDegreeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Elevate Surface Degree + Tooltip: Elevate degree of a NURBS surface + """ + bl_idname = 'SvSurfaceElevateDegreeNode' + bl_label = 'Elevate Degree (NURBS Surface)' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_SURFACE_INSERT_KNOT' + + directions = [ + ('U', "U", "U direction", 0), + ('V', "V", "V direction", 1) + ] + + direction : EnumProperty( + name = "Parameter", + description = "By which parameter to elevate degree", + items = directions, + default = 'U', + update = updateNode) + + modes = [ + ('DELTA', "Elevate by", "Specify difference between current degree and target degree", 0), + ('TARGET', "Set degree", "Specify target degree", 1) + ] + + def update_sockets(self, context): + self.inputs['Degree'].label = "Delta" if self.mode == 'DELTA' else "Degree" + updateNode(self, context) + + mode : EnumProperty( + name = "Mode", + description = "How new curve degree is specified", + items = modes, + update = update_sockets) + + degree : IntProperty( + name = "Degree", + description = "Target degree or degree delta", + min = 0, + default = 1, + update = updateNode) + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + layout.prop(self, 'mode', text='') + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' + self.outputs.new('SvSurfaceSocket', "Surface") + self.update_sockets(context) + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + degree_s = self.inputs['Degree'].sv_get() + + input_level = get_data_nesting_level(surface_s, data_types=(SvSurface,)) + flat_output = input_level < 2 + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + degree_s = ensure_nesting_level(degree_s, 2) + + surfaces_out = [] + for params in zip_long_repeat(surface_s, degree_s): + new_surfaces = [] + for surface, degree in zip_long_repeat(*params): + surface = SvNurbsSurface.get(surface) + if surface is None: + raise Exception("One of surfaces is not NURBS") + if self.mode == 'DELTA': + kwargs = dict(delta = degree) + else: + kwargs = dict(target = degree) + surface = surface.elevate_degree(self.direction, **kwargs) + new_surfaces.append(surface) + if flat_output: + surfaces_out.extend(new_surfaces) + else: + surfaces_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surfaces_out) + +def register(): + bpy.utils.register_class(SvSurfaceElevateDegreeNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceElevateDegreeNode) + diff --git a/nodes/surface/reduce_degree.py b/nodes/surface/reduce_degree.py new file mode 100644 index 0000000000..2693af6766 --- /dev/null +++ b/nodes/surface/reduce_degree.py @@ -0,0 +1,125 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length +from sverchok.utils.surface import SvSurface +from sverchok.utils.surface.nurbs import SvNurbsSurface + +class SvSurfaceReduceDegreeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Reduce Surface Degree + Tooltip: Reduce the degree of a NURBS surface + """ + bl_idname = 'SvSurfaceReduceDegreeNode' + bl_label = 'Reduce Degree (NURBS Surface)' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_SURFACE_INSERT_KNOT' + + directions = [ + ('U', "U", "U direction", 0), + ('V', "V", "V direction", 1) + ] + + direction : EnumProperty( + name = "Parameter", + description = "By which parameter to elevate degree", + items = directions, + default = 'U', + update = updateNode) + + modes = [ + ('DELTA', "Reduce by", "Specify difference between current degree and target degree", 0), + ('TARGET', "Set degree", "Specify target degree", 1) + ] + + def update_sockets(self, context): + self.inputs['Degree'].label = "Delta" if self.mode == 'DELTA' else "Degree" + updateNode(self, context) + + mode : EnumProperty( + name = "Mode", + description = "How new curve degree is specified", + items = modes, + update = update_sockets) + + degree : IntProperty( + name = "Degree", + description = "Target degree or degree delta", + min = 0, + default = 1, + update = updateNode) + + tolerance : FloatProperty( + name = "Tolerance", + description = "Error tolerance", + min = 0.0, + default = 1e-4, + precision = 8, + update = updateNode) + + if_possible : BoolProperty( + name = "If possible", + description = "Don't fail when trying to reduce the surface's degree too many times, just reduce it as much as possible", + default = False, + update = updateNode) + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + layout.prop(self, 'mode', text='') + layout.prop(self, 'if_possible') + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' + self.inputs.new('SvStringsSocket', 'Tolerance').prop_name = 'tolerance' + self.outputs.new('SvSurfaceSocket', "Surface") + self.update_sockets(context) + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + degree_s = self.inputs['Degree'].sv_get() + tolerance_s = self.inputs['Tolerance'].sv_get() + + input_level = get_data_nesting_level(surface_s, data_types=(SvSurface,)) + flat_output = input_level < 2 + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + degree_s = ensure_nesting_level(degree_s, 2) + tolerance_s = ensure_nesting_level(tolerance_s, 2) + + surfaces_out = [] + for params in zip_long_repeat(surface_s, degree_s, tolerance_s): + new_surfaces = [] + for surface, degree, tolerance in zip_long_repeat(*params): + surface = SvNurbsSurface.get(surface) + if surface is None: + raise Exception("One of surfaces is not NURBS") + if self.mode == 'DELTA': + kwargs = dict(delta = degree) + else: + kwargs = dict(target = degree) + surface = surface.reduce_degree(self.direction, tolerance=tolerance, **kwargs) + new_surfaces.append(surface) + if flat_output: + surfaces_out.extend(new_surfaces) + else: + surfaces_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surfaces_out) + +def register(): + bpy.utils.register_class(SvSurfaceReduceDegreeNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceReduceDegreeNode) + From 3594be4f558ed5855af54efc75f1fbf798f224fd Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 01:19:12 +0500 Subject: [PATCH 3/9] On degree reduction tolerance. --- utils/curve/nurbs.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 0c455825d6..0cb8dc034c 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -391,7 +391,7 @@ def reduce_degree(self, delta=None, target=None, tolerance=1e-6, return_error=Fa if delta == 0: return self - def reduce_degree_once(curve): + def reduce_degree_once(curve, tolerance): if curve.is_bezier(): old_control_points = curve.get_homogenous_control_points() control_points, error = reduce_bezier_degree(curve.get_degree(), old_control_points, 1) @@ -412,7 +412,8 @@ def reduce_degree_once(curve): max_error = 0.0 for i, segment in enumerate(segments): try: - s, error, ok = reduce_degree_once(segment) + s, error, ok = reduce_degree_once(segment, tolerance) + print(f"Curve segment #{i}: error = {error}") except CantReduceDegreeException as e: raise CantReduceDegreeException(f"At segment #{i}: {e}") from e max_error = max(max_error, error) @@ -420,20 +421,23 @@ def reduce_degree_once(curve): result = reduced_segments[0] for segment in reduced_segments[1:]: result = result.concatenate(segment, remove_knots=True, tolerance=tolerance) - max_error = max(max_error, tolerance) + #max_error = max(max_error, tolerance) result = result.reparametrize(src_t_min, src_t_max) return result, max_error, True total_error = 0.0 + remaining_tolerance = tolerance result = self for i in range(delta): try: - result, error, ok = reduce_degree_once(result) + result, error, ok = reduce_degree_once(result, remaining_tolerance) except CantReduceDegreeException as e: raise CantReduceDegreeException(f"At iteration #{i}: {e}") from e if not ok: # if if_possible would be false, we would get an exception break + print(f"Iteration #{i}, error = {error}") total_error += error + remaining_tolerance -= error if total_error > tolerance: if if_possible: if return_error: From f00c1515db852ab739331f871fcaa51dbccf00be Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 09:33:27 +0500 Subject: [PATCH 4/9] "remove excessive knots": move tolerance to the main node UI. --- docs/nodes/curve/remove_excessive_knots.rst | 5 ++--- docs/nodes/surface/remove_excessive_knots.rst | 5 ++--- nodes/curve/remove_excessive_knots.py | 2 +- nodes/surface/remove_excessive_knots.py | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/nodes/curve/remove_excessive_knots.rst b/docs/nodes/curve/remove_excessive_knots.rst index fe296645ba..efc7c5004f 100644 --- a/docs/nodes/curve/remove_excessive_knots.rst +++ b/docs/nodes/curve/remove_excessive_knots.rst @@ -23,9 +23,8 @@ Parameters This node has the following parameter: -* **Tolerance**. This parameter is available in the N panel only. This defines - how much is it allowed to change the shape of the curve by knot removal - procedure. The default value is ``10^-6``. +* **Tolerance**. This defines how much is it allowed to change the shape of the + curve by knot removal procedure. The default value is ``10^-6``. Outputs ------- diff --git a/docs/nodes/surface/remove_excessive_knots.rst b/docs/nodes/surface/remove_excessive_knots.rst index e126c7ad5e..07b491f218 100644 --- a/docs/nodes/surface/remove_excessive_knots.rst +++ b/docs/nodes/surface/remove_excessive_knots.rst @@ -26,9 +26,8 @@ This node has the following parameters: * **Direction**. This defines, in which parametric directions should the knot removal procedure be performed. The available options are **U+V** (both directions), **U** and **V**. The default value is **U+V**. -* **Tolerance**. This parameter is available in the N panel only. This defines - how much is it allowed to change the shape of the surface by knot removal - procedure. The default value is ``10^-6``. +* **Tolerance**. This defines how much is it allowed to change the shape of the + surface by knot removal procedure. The default value is ``10^-6``. Outputs ------- diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py index d3ee061b72..f4ba5aee97 100644 --- a/nodes/curve/remove_excessive_knots.py +++ b/nodes/curve/remove_excessive_knots.py @@ -35,7 +35,7 @@ def sv_init(self, context): self.inputs.new('SvCurveSocket', "Curve") self.outputs.new('SvCurveSocket', "Curve") - def draw_buttons_ext(self, context, layout): + def draw_buttons(self, context, layout): layout.prop(self, 'tolerance') def process(self): diff --git a/nodes/surface/remove_excessive_knots.py b/nodes/surface/remove_excessive_knots.py index f0281bd152..f743cccb83 100644 --- a/nodes/surface/remove_excessive_knots.py +++ b/nodes/surface/remove_excessive_knots.py @@ -51,7 +51,7 @@ def sv_init(self, context): def draw_buttons(self, context, layout): layout.prop(self, 'direction', expand=True) - def draw_buttons_ext(self, context, layout): + def draw_buttons(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, 'tolerance') From d8dcfd1a0b75f3019400187a20f566bd68ec0f3f Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 09:57:44 +0500 Subject: [PATCH 5/9] Documentation for elevate & reduce curve degree nodes. --- docs/nodes/curve/curve_index.rst | 2 + docs/nodes/curve/elevate_degree.rst | 53 ++++++++++++++++++++++ docs/nodes/curve/reduce_degree.rst | 68 +++++++++++++++++++++++++++++ nodes/curve/reduce_degree.py | 12 ++--- 4 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 docs/nodes/curve/elevate_degree.rst create mode 100644 docs/nodes/curve/reduce_degree.rst diff --git a/docs/nodes/curve/curve_index.rst b/docs/nodes/curve/curve_index.rst index f3f4177fb9..20562b8823 100644 --- a/docs/nodes/curve/curve_index.rst +++ b/docs/nodes/curve/curve_index.rst @@ -57,6 +57,8 @@ Curves remove_knot refine_nurbs_curve remove_excessive_knots + elevate_degree + reduce_degree adaptive_plot_nurbs catenary_curve bezier_fit diff --git a/docs/nodes/curve/elevate_degree.rst b/docs/nodes/curve/elevate_degree.rst new file mode 100644 index 0000000000..c492806786 --- /dev/null +++ b/docs/nodes/curve/elevate_degree.rst @@ -0,0 +1,53 @@ +Elevate Degree (NURBS Curve) +============================ + +Functionality +------------- + +This node elevates (increases) the degree of a NURBS curve. + +The opposite action can be performed with "Reduce Degree (NURBS Curve)" node. + +Curves of higher degrees have more control points, and so, with higher degree, +one control point controls smaller segment of the curve. + +This node can work only with NURBS and NURBS-like curves. + +Inputs +------ + +This node has the following inputs: + +* **Curve**. The curve to perform operation on. This input is mandatory. +* **Degree**. In **Elevate by** mode, this is the delta to be added to curve's + degree. In **Set degree** mode, this is new curve degree to be set. New + degree can not be less than current degree of the curve. The default value is + 1. + +Parameters +---------- + +This node has the following parameter: + +* **Mode**. This defines how the new degree of the curve is specified: + + * In **Elevate by** mode, **Degree** input specifies the number to be added + to current degree of the curve. + * In **Set degree** mode, **Degree** input specifies the new degree of the + curve. + +Outputs +------- + +This node has the following output: + +* **Curve**. The curve of elevated degree. + +Example of Usage +---------------- + +Here orange is the original curve of 3rd degree, and green is it's control +polygon. Red is the control polygon of curve with elevated (4th) degree. + +.. image:: https://user-images.githubusercontent.com/284644/189500544-0b0d70b0-f312-43b6-ac5a-b726bce6ccbd.png + diff --git a/docs/nodes/curve/reduce_degree.rst b/docs/nodes/curve/reduce_degree.rst new file mode 100644 index 0000000000..a042afbbb3 --- /dev/null +++ b/docs/nodes/curve/reduce_degree.rst @@ -0,0 +1,68 @@ +Reduce Degree (NURBS Curve) +=========================== + +Functionality +------------- + +This node reduces (decreases) the degree of a NURBS curve. + +The action of this node is the opposite to the action of "Elevate Degree (NURBS Curve)" node. + +Degree reduction is the process which can not be always performed exactly. Some +curves can not be degree reduced without visible deviation of curve's geometry. +For other, degree reduction produces only small error. + +This node can work only with NURBS and NURBS-like curves. + +Inputs +------ + +This node has the following inputs: + +* **Curve**. The curve object to perform operation on. This input is mandatory. +* **Degree**. In **Reduce by** mode, this is the delta to be substracted from + curve's degree. In **Set degree** mode, this is new curve degree to be set. + New degree can not be greater than current degree of the curve. The default + value is 1. +* **Tolerance**. Maximum tolerable deviation of new curve from original. If + degree reduction process will have error estimation more than this tolerance, + then, depending on **Only if possible** parameter, the node will either fail or + return the curve untouched (or degree reduced by less than wanted value). The + default value is ``0.0001``. + +Parameters +---------- + +This node has the following parameters: + +* **Mode**. This defines how the new degree of the curve is specified: + + * In **Reduce by** mode, **Degree** input specifies the number to be substracted + from current degree of the curve. + * In **Set degree** mode, **Degree** input specifies the new degree of the + curve. + +* **Only if possible**. If this flag is checked, the node will try to degree + reduce the curve by requested amount; it it is not possible to do so without + deviation greater than the specified tolerance, the node will just degree + reduce the curve the number of times it can. If not checked, then the node + will fail (become red) in such a situation, and the processint will stop. + Unchecked by default. + +Outputs +------- + +This node has the following output: + +* **Curve**. The resulting curve. + +Example of Usage +---------------- + +Here, the initial curve of 3rd degree is reduced to degree of 2. Orange is the +original curve, blue is it's control polygon; green is the resulting curve, and +red is it's control polygon. As you can see, in this case the operation could +not be performed without visible deviation. + +.. image:: https://user-images.githubusercontent.com/284644/189500535-3a126895-7a36-453d-af8a-ff4b0615836e.png + diff --git a/nodes/curve/reduce_degree.py b/nodes/curve/reduce_degree.py index 6a71810165..e420f9d8e9 100644 --- a/nodes/curve/reduce_degree.py +++ b/nodes/curve/reduce_degree.py @@ -54,7 +54,7 @@ def update_sockets(self, context): update = updateNode) if_possible : BoolProperty( - name = "If possible", + name = "Only if possible", description = "Don't fail when trying to reduce the curve's degree too many times, just reduce it as much as possible", default = False, update = updateNode) @@ -68,7 +68,7 @@ def sv_init(self, context): self.inputs.new('SvStringsSocket', 'Degree').prop_name = 'degree' self.inputs.new('SvStringsSocket', 'Tolerance').prop_name = 'tolerance' self.outputs.new('SvCurveSocket', 'Curve') - self.outputs.new('SvStringsSocket', 'Error') + #self.outputs.new('SvStringsSocket', 'Error') self.update_sockets(context) def process(self): @@ -86,10 +86,8 @@ def process(self): tolerance_s = ensure_nesting_level(tolerance_s, 2) curves_out = [] - error_out = [] for params in zip_long_repeat(curve_s, degree_s, tolerance_s): new_curves = [] - new_errors = [] for curve, degree, tolerance in zip_long_repeat(*params): curve = SvNurbsCurve.to_nurbs(curve) if curve is None: @@ -98,18 +96,14 @@ def process(self): kwargs = dict(delta = degree) else: kwargs = dict(target = degree) - curve, error = curve.reduce_degree(tolerance=tolerance, return_error=True, if_possible = self.if_possible, **kwargs) + curve = curve.reduce_degree(tolerance=tolerance, if_possible = self.if_possible, **kwargs) new_curves.append(curve) - new_errors.append(error) if flat_output: curves_out.extend(new_curves) - error_out.extend(new_errors) else: curves_out.append(new_curves) - error_out.append(new_errors) self.outputs['Curve'].sv_set(curves_out) - self.outputs['Error'].sv_set(error_out) def register(): bpy.utils.register_class(SvCurveReduceDegreeNode) From 716c744e39b3fbed25b41e8344e7b1dde683100e Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 10:09:43 +0500 Subject: [PATCH 6/9] Docs for Elevate & Reduce surface degree nodes. --- docs/nodes/surface/elevate_degree.rst | 61 ++++++++++++++++++++++++ docs/nodes/surface/reduce_degree.rst | 67 +++++++++++++++++++++++++++ docs/nodes/surface/surface_index.rst | 2 + 3 files changed, 130 insertions(+) create mode 100644 docs/nodes/surface/elevate_degree.rst create mode 100644 docs/nodes/surface/reduce_degree.rst diff --git a/docs/nodes/surface/elevate_degree.rst b/docs/nodes/surface/elevate_degree.rst new file mode 100644 index 0000000000..f992fbc763 --- /dev/null +++ b/docs/nodes/surface/elevate_degree.rst @@ -0,0 +1,61 @@ +Elevate Degree (NURBS Surface) +============================== + +Functionality +------------- + +This node elevates (increases) the degree of a NURBS surface. + +The opposite action can be performed with "Reduce Degree (NURBS Surface)" node. + +Surfaces of higher degrees have more control points, and so, with higher degree, +one control point controls smaller segment of the surface. + +Remember that NURBS surface has two degrees, one along U parameter and one +along V. This node can elevate any of them. + +This node can work only with NURBS and NURBS-like surfaces. + +Inputs +------ + +This node has the following inputs: + +* **Surface**. The surface to perform operation on. This input is mandatory. +* **Degree**. In **Elevate by** mode, this is the delta to be added to surface's + degree. In **Set degree** mode, this is new surface degree to be set. New + degree can not be less than current degree of the surface. The default value is + 1. + +Parameters +---------- + +This node has the following parameters: + +* **Direction**. This specifies the parameter direction, degree along which is + to be increased. The available options are **U** and **V**. The default + option is **U**. +* **Mode**. This defines how the new degree of the surface is specified: + + * In **Elevate by** mode, **Degree** input specifies the number to be added + to current degree of the surface. + * In **Set degree** mode, **Degree** input specifies the new degree of the + surface. + +Outputs +------- + +This node has the following output: + +* **Surface**. The surface of elevated degree. + +Example of Usage +---------------- + +Here the degree of the surface is increased by 1 along U and then reduced by 1 +also along U, just to illustrate that degree elevation and reduction are the +opposite processes. Red is the control net of the original and the resulting +surfaces. Blue is the control polygon of degree elevated surface. + +.. image:: https://user-images.githubusercontent.com/284644/189500531-e4354c9d-0dd4-4bc5-861d-43d9229c28e6.png + diff --git a/docs/nodes/surface/reduce_degree.rst b/docs/nodes/surface/reduce_degree.rst new file mode 100644 index 0000000000..e391f7eb27 --- /dev/null +++ b/docs/nodes/surface/reduce_degree.rst @@ -0,0 +1,67 @@ +Reduce Degree (NURBS Surface) +============================= + +Functionality +------------- + +This node reduces (decreases) the degree of a NURBS surface. + +The action of this node is the opposite to the action of "Elevate Degree (NURBS Surface)" node. + +Degree reduction is the process which can not be always performed exactly. Some +surfaces can not be degree reduced without visible deviation of surface's geometry. +For other, degree reduction produces only small error. + +Remember that NURBS surface has two degrees, one along U parameter and one +along V. This node can reduce any of them. + +This node can work only with NURBS and NURBS-like surfaces. + +Inputs +------ + +This node has the following inputs: + +* **Surface**. The surface to perform operation on. This input is mandatory. +* **Degree**. In **Reduce by** mode, this is the delta to be substracted from + surface's degree. In **Set degree** mode, this is new surface degree to be + set. New degree can not be greater than current degree of the surface. The + default value is 1. +* **Tolerance**. Maximum tolerable deviation of new surface from original. If + degree reduction process will have error estimation more than this tolerance, + then, depending on **Only if possible** parameter, the node will either fail or + return the surface untouched (or degree reduced by less than wanted value). The + default value is ``0.0001``. + +Parameters +---------- + +This node has the following parameters: + +* **Direction**. This specifies the parameter direction, degree along which is + to be decreased. The available options are **U** and **V**. The default + option is **U**. +* **Mode**. This defines how the new degree of the surface is specified: + + * In **Reduce by** mode, **Degree** input specifies the number to be + substracted from current degree of the surface. + * In **Set degree** mode, **Degree** input specifies the new degree of the + surface. + +Outputs +------- + +This node has the following output: + +* **Surface**. The surface of a reduced degree. + +Example of Usage +---------------- + +Here the degree of the surface is increased by 1 along U and then reduced by 1 +also along U, just to illustrate that degree elevation and reduction are the +opposite processes. Red is the control net of the original and the resulting +surfaces. Blue is the control polygon of degree elevated surface. + +.. image:: https://user-images.githubusercontent.com/284644/189500531-e4354c9d-0dd4-4bc5-861d-43d9229c28e6.png + diff --git a/docs/nodes/surface/surface_index.rst b/docs/nodes/surface/surface_index.rst index a7eb3bf62f..eaac565284 100644 --- a/docs/nodes/surface/surface_index.rst +++ b/docs/nodes/surface/surface_index.rst @@ -42,6 +42,8 @@ Surface insert_knot remove_knot remove_excessive_knots + elevate_degree + reduce_degree intersect_curve_surface nearest_point ortho_project From f9322e4af040243cbc6977ce0dde3a355cafe75b Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 10:28:34 +0500 Subject: [PATCH 7/9] Add node icons. --- nodes/curve/elevate_degree.py | 2 +- nodes/curve/reduce_degree.py | 2 +- nodes/surface/elevate_degree.py | 2 +- nodes/surface/reduce_degree.py | 2 +- ui/icons/sv_elevate_curve_degree.png | Bin 0 -> 1838 bytes ui/icons/sv_elevate_surface_degree.png | Bin 0 -> 3080 bytes ui/icons/sv_reduce_curve_degree.png | Bin 0 -> 1591 bytes ui/icons/sv_reduce_surface_degree.png | Bin 0 -> 2696 bytes ui/icons/svg/sv_elevate_curve_degree.svg | 195 +++++++++++++++++++++ ui/icons/svg/sv_elevate_surface_degree.svg | 88 ++++++++++ ui/icons/svg/sv_reduce_curve_degree.svg | 195 +++++++++++++++++++++ ui/icons/svg/sv_reduce_surface_degree.svg | 88 ++++++++++ 12 files changed, 570 insertions(+), 4 deletions(-) create mode 100644 ui/icons/sv_elevate_curve_degree.png create mode 100644 ui/icons/sv_elevate_surface_degree.png create mode 100644 ui/icons/sv_reduce_curve_degree.png create mode 100644 ui/icons/sv_reduce_surface_degree.png create mode 100644 ui/icons/svg/sv_elevate_curve_degree.svg create mode 100644 ui/icons/svg/sv_elevate_surface_degree.svg create mode 100644 ui/icons/svg/sv_reduce_curve_degree.svg create mode 100644 ui/icons/svg/sv_reduce_surface_degree.svg diff --git a/nodes/curve/elevate_degree.py b/nodes/curve/elevate_degree.py index 835941ffd2..243561fc58 100644 --- a/nodes/curve/elevate_degree.py +++ b/nodes/curve/elevate_degree.py @@ -21,7 +21,7 @@ class SvCurveElevateDegreeNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCurveElevateDegreeNode' bl_label = 'Elevate Degree (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_CURVE_INSERT_KNOT' + sv_icon = 'SV_ELEVATE_CURVE_DEGREE' modes = [ ('DELTA', "Elevate by", "Specify difference between current degree and target degree", 0), diff --git a/nodes/curve/reduce_degree.py b/nodes/curve/reduce_degree.py index e420f9d8e9..35242134c6 100644 --- a/nodes/curve/reduce_degree.py +++ b/nodes/curve/reduce_degree.py @@ -21,7 +21,7 @@ class SvCurveReduceDegreeNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCurveReduceDegreeNode' bl_label = 'Reduce Degree (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_CURVE_INSERT_KNOT' + sv_icon = 'SV_REDUCE_CURVE_DEGREE' modes = [ ('DELTA', "Reduce by", "Specify difference between current degree and target degree", 0), diff --git a/nodes/surface/elevate_degree.py b/nodes/surface/elevate_degree.py index 3be5361b5a..c7877cf378 100644 --- a/nodes/surface/elevate_degree.py +++ b/nodes/surface/elevate_degree.py @@ -21,7 +21,7 @@ class SvSurfaceElevateDegreeNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvSurfaceElevateDegreeNode' bl_label = 'Elevate Degree (NURBS Surface)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_SURFACE_INSERT_KNOT' + sv_icon = 'SV_ELEVATE_SURFACE_DEGREE' directions = [ ('U', "U", "U direction", 0), diff --git a/nodes/surface/reduce_degree.py b/nodes/surface/reduce_degree.py index 2693af6766..4666616181 100644 --- a/nodes/surface/reduce_degree.py +++ b/nodes/surface/reduce_degree.py @@ -21,7 +21,7 @@ class SvSurfaceReduceDegreeNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvSurfaceReduceDegreeNode' bl_label = 'Reduce Degree (NURBS Surface)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_SURFACE_INSERT_KNOT' + sv_icon = 'SV_REDUCE_SURFACE_DEGREE' directions = [ ('U', "U", "U direction", 0), diff --git a/ui/icons/sv_elevate_curve_degree.png b/ui/icons/sv_elevate_curve_degree.png new file mode 100644 index 0000000000000000000000000000000000000000..06059003627a430587fde923addd2a317b5e0cc6 GIT binary patch literal 1838 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 literal 0 HcmV?d00001 diff --git a/ui/icons/sv_reduce_surface_degree.png b/ui/icons/sv_reduce_surface_degree.png new file mode 100644 index 0000000000000000000000000000000000000000..a4d03138ee7c2e0c84b6ace2b54c5a1a656b56fc GIT binary patch literal 2696 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/ui/icons/svg/sv_elevate_surface_degree.svg b/ui/icons/svg/sv_elevate_surface_degree.svg new file mode 100644 index 0000000000..5bd7601dc7 --- /dev/null +++ b/ui/icons/svg/sv_elevate_surface_degree.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/ui/icons/svg/sv_reduce_curve_degree.svg b/ui/icons/svg/sv_reduce_curve_degree.svg new file mode 100644 index 0000000000..3099fef319 --- /dev/null +++ b/ui/icons/svg/sv_reduce_curve_degree.svg @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/ui/icons/svg/sv_reduce_surface_degree.svg b/ui/icons/svg/sv_reduce_surface_degree.svg new file mode 100644 index 0000000000..4fd6b8ffcf --- /dev/null +++ b/ui/icons/svg/sv_reduce_surface_degree.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + From 144fe2604fe11aea5f37fc619253d1ab31c4b5c5 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 10:44:14 +0500 Subject: [PATCH 8/9] Fix logging. --- nodes/curve/reduce_degree.py | 5 ++++- nodes/surface/reduce_degree.py | 5 ++++- utils/curve/nurbs.py | 14 +++++++++----- utils/surface/nurbs.py | 15 +++++++++------ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/nodes/curve/reduce_degree.py b/nodes/curve/reduce_degree.py index 35242134c6..2a4176013f 100644 --- a/nodes/curve/reduce_degree.py +++ b/nodes/curve/reduce_degree.py @@ -96,7 +96,10 @@ def process(self): kwargs = dict(delta = degree) else: kwargs = dict(target = degree) - curve = curve.reduce_degree(tolerance=tolerance, if_possible = self.if_possible, **kwargs) + curve = curve.reduce_degree(tolerance=tolerance, + if_possible = self.if_possible, + logger = self.get_logger(), + **kwargs) new_curves.append(curve) if flat_output: curves_out.extend(new_curves) diff --git a/nodes/surface/reduce_degree.py b/nodes/surface/reduce_degree.py index 4666616181..611a8a6c9d 100644 --- a/nodes/surface/reduce_degree.py +++ b/nodes/surface/reduce_degree.py @@ -108,7 +108,10 @@ def process(self): kwargs = dict(delta = degree) else: kwargs = dict(target = degree) - surface = surface.reduce_degree(self.direction, tolerance=tolerance, **kwargs) + surface = surface.reduce_degree(self.direction, + tolerance = tolerance, + logger = self.get_logger(), + **kwargs) new_surfaces.append(surface) if flat_output: surfaces_out.extend(new_surfaces) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 0cb8dc034c..1ce55339b1 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -377,7 +377,7 @@ def elevate_degree(self, delta=None, target=None): return result #raise UnsupportedCurveTypeException("Degree elevation is not implemented for non-bezier curves yet") - def reduce_degree(self, delta=None, target=None, tolerance=1e-6, return_error=False, if_possible=False): + def reduce_degree(self, delta=None, target=None, tolerance=1e-6, return_error=False, if_possible=False, logger=None): orig_delta, orig_target = delta, target if delta is None and target is None: delta = 1 @@ -391,6 +391,9 @@ def reduce_degree(self, delta=None, target=None, tolerance=1e-6, return_error=Fa if delta == 0: return self + if logger is None: + logger = getLogger() + def reduce_degree_once(curve, tolerance): if curve.is_bezier(): old_control_points = curve.get_homogenous_control_points() @@ -413,7 +416,7 @@ def reduce_degree_once(curve, tolerance): for i, segment in enumerate(segments): try: s, error, ok = reduce_degree_once(segment, tolerance) - print(f"Curve segment #{i}: error = {error}") + logger.debug(f"Curve segment #{i}: error = {error}") except CantReduceDegreeException as e: raise CantReduceDegreeException(f"At segment #{i}: {e}") from e max_error = max(max_error, error) @@ -435,7 +438,7 @@ def reduce_degree_once(curve, tolerance): raise CantReduceDegreeException(f"At iteration #{i}: {e}") from e if not ok: # if if_possible would be false, we would get an exception break - print(f"Iteration #{i}, error = {error}") + logger.debug(f"Iteration #{i}, error = {error}") total_error += error remaining_tolerance -= error if total_error > tolerance: @@ -446,7 +449,7 @@ def reduce_degree_once(curve, tolerance): return result else: raise CantReduceDegreeException(f"Tolerance exceeded at iteration #{i}, error is {total_error}") - print(f"Curve degree reduction error: {total_error}") + logger.debug(f"Curve degree reduction error: {total_error}") if return_error: return result, total_error else: @@ -1365,7 +1368,8 @@ def remove_one_knot(curve): if not if_possible and (removed_count < count): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") - #print(f"Removed knot t={u} for {removed_count} times") + if logger is not None: + logger.debug(f"Removed knot t={u} for {removed_count} times") return curve diff --git a/utils/surface/nurbs.py b/utils/surface/nurbs.py index 36b1c0e1c9..86afedab8a 100644 --- a/utils/surface/nurbs.py +++ b/utils/surface/nurbs.py @@ -13,7 +13,7 @@ from sverchok.utils.curve.algorithms import unify_curves_degree, SvCurveFrameCalculator from sverchok.utils.surface.core import UnsupportedSurfaceTypeException from sverchok.utils.surface import SvSurface, SurfaceCurvatureCalculator, SurfaceDerivativesData -from sverchok.utils.logging import info +from sverchok.utils.logging import info, getLogger from sverchok.data_structure import repeat_last_for_length from sverchok.dependencies import geomdl @@ -249,7 +249,7 @@ def elevate_degree(self, direction, delta=None, target=None): self.get_knotvector_u(), fixed_u_knotvector, new_points, new_weights) - def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): + def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6, logger=None): if delta is None and target is None: delta = 1 if delta is not None and target is not None: @@ -265,6 +265,9 @@ def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): if delta == 0: return self + if logger is None: + logger = getLogger() + implementation = self.get_nurbs_implementation() if direction == SvNurbsSurface.U: @@ -280,7 +283,7 @@ def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): self.get_degree_u(), self.get_knotvector_u(), fixed_v_points, fixed_v_weights) try: - fixed_v_curve, error = fixed_v_curve.reduce_degree(delta=delta, tolerance=remaining_tolerance, return_error=True) + fixed_v_curve, error = fixed_v_curve.reduce_degree(delta=delta, tolerance=remaining_tolerance, return_error=True, logger=logger) except CantReduceDegreeException as e: raise CantReduceDegreeException(f"At parallel #{i}: {e}") from e max_error = max(max_error, error) @@ -294,7 +297,7 @@ def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): new_points = np.transpose(np.array(new_points), axes=(1,0,2)) new_weights = np.array(new_weights).T - print(f"Surface degree reduction error: {max_error}") + logger.debug(f"Surface degree reduction error: {max_error}") return SvNurbsSurface.build(self.get_nurbs_implementation(), new_u_degree, self.get_degree_v(), @@ -314,7 +317,7 @@ def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): self.get_degree_v(), self.get_knotvector_v(), fixed_u_points, fixed_u_weights) try: - fixed_u_curve, error = fixed_u_curve.reduce_degree(delta=delta, tolerance=remaining_tolerance, return_error=True) + fixed_u_curve, error = fixed_u_curve.reduce_degree(delta=delta, tolerance=remaining_tolerance, return_error=True, logger=logger) except CantReduceDegreeException as e: raise CantReduceDegreeException(f"At parallel #{i}: {e}") from e max_error = max(max_error, error) @@ -328,7 +331,7 @@ def reduce_degree(self, direction, delta=None, target=None, tolerance=1e-6): new_points = np.array(new_points) new_weights = np.array(new_weights) - print(f"Surface degree reduction error: {max_error}") + logger.debug(f"Surface degree reduction error: {max_error}") return SvNurbsSurface.build(implementation, self.get_degree_u(), new_v_degree, From e3be1c89ab6e3c531ba0f8dcab46e1a1f7179d86 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 11 Sep 2022 10:52:54 +0500 Subject: [PATCH 9/9] Add tests. --- tests/nurbs_tests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/nurbs_tests.py b/tests/nurbs_tests.py index 0450cd8173..436f711466 100644 --- a/tests/nurbs_tests.py +++ b/tests/nurbs_tests.py @@ -898,3 +898,26 @@ def test_outside_sphere_3(self): expected_result = True self.assertEquals(result, expected_result) +class CurveDegreeTests(SverchokTestCase): + def test_elevate_degree(self): + """Elevate NURBS curve degree 2 -> 3""" + cpts = np.array([[-3,0.0,0], [0, 3, 0], [3,0,0]]) + degree = 2 + knotvector = sv_knotvector.generate(degree, len(cpts)) + curve = SvNurbsCurve.build(SvNurbsCurve.NATIVE, degree, knotvector, cpts) + new_curve = curve.elevate_degree(delta = 1) + result = new_curve.get_control_points() + expected = np.array([[-3,0,0], [-1,2,0], [1,2,0], [3,0,0]]) + self.assert_numpy_arrays_equal(result, expected, precision=8) + + def test_reduce_degree(self): + """Reduce NURBS curve degree 3 -> 2""" + cpts = np.array([[-3,0,0], [-1,2,0], [1,2,0], [3,0,0]]) + degree = 3 + knotvector = sv_knotvector.generate(degree, len(cpts)) + curve = SvNurbsCurve.build(SvNurbsCurve.NATIVE, degree, knotvector, cpts) + new_curve = curve.reduce_degree(delta = 1) + result = new_curve.get_control_points() + expected = np.array([[-3,0.0,0], [0, 3, 0], [3,0,0]]) + self.assert_numpy_arrays_equal(result, expected, precision=8) +