From f4a53fda77cc52889180fd520322bcb3e85aee1d Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Mon, 4 May 2020 01:13:11 +0500 Subject: [PATCH 1/5] Curvature lines node. ??? --- index.md | 2 + nodes/surface/curvature_lines.py | 99 ++++++++++++++++++++++++++++++++ nodes/surface/curvatures.py | 7 ++- utils/surface.py | 30 +++++++--- 4 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 nodes/surface/curvature_lines.py diff --git a/index.md b/index.md index 4937452d5..5b5fc91d7 100644 --- a/index.md +++ b/index.md @@ -97,6 +97,7 @@ SvExEvalCurveNode ## Surfaces +<<<<<<< HEAD SvExPlaneSurfaceNode SvExSphereNode SvExSurfaceFormulaNode @@ -120,6 +121,7 @@ SvSurfaceNormalsNode SvSurfaceGaussCurvatureNode SvSurfaceCurvaturesNode + SvSurfaceCurvatureLinesNode --- SvExTessellateTrimSurfaceNode SvAdaptiveTessellateNode diff --git a/nodes/surface/curvature_lines.py b/nodes/surface/curvature_lines.py new file mode 100644 index 000000000..2283efd8c --- /dev/null +++ b/nodes/surface/curvature_lines.py @@ -0,0 +1,99 @@ + +import numpy as np + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +import sverchok +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length +from sverchok.utils.logging import info, exception +from sverchok.utils.surface import SvSurface + +class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Surface Curvature Lines + Tooltip: Generate surface principal curvature lines + """ + bl_idname = 'SvSurfaceCurvatureLinesNode' + bl_label = 'Surface Curvature Lines' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_EVAL_SURFACE' + + directions = [ + ('MIN', "Minimum", "Minimum principal curvature direction", 0), + ('MAX', "Maximum", "Maximum principal curvature direction", 1) + ] + + direction : EnumProperty( + name = "Direction", + items = directions, + default = 'MIN', + update = updateNode) + + step : FloatProperty( + name = "Step", + min = 0, default = 0.1, + update = updateNode) + + iterations : IntProperty( + name = "Iterations", + min = 1, default = 10, + update = updateNode) + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + p = self.inputs.new('SvVerticesSocket', "UVPoints") + p.use_prop = True + p.prop = (0.5, 0.5, 0.0) + self.inputs.new('SvStringsSocket', 'Step').prop_name = 'step' + self.inputs.new('SvStringsSocket', 'Iterations').prop_name = 'iterations' + self.outputs.new('SvVerticesSocket', "Vertices") + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surfaces_s = self.inputs['Surface'].sv_get() + surfaces_s = ensure_nesting_level(surfaces_s, 2, data_types=(SvSurface,)) + src_point_s = self.inputs['UVPoints'].sv_get() + src_point_s = ensure_nesting_level(src_point_s, 4) + step_s = self.inputs['Step'].sv_get() + iterations_s = self.inputs['Iterations'].sv_get() + + verts_out = [] + inputs = zip_long_repeat(surfaces_s, src_point_s, step_s, iterations_s) + for surfaces, src_point_i, step_i, iterations_i in inputs: + for surface, src_points, step, iterations in zip_long_repeat(surfaces, src_point_i, step_i, iterations_i): + for src_point in src_points: + new_verts = [] + u,v,_ = src_point + for i in range(iterations): + vertex = surface.evaluate(u, v).tolist() + new_verts.append(vertex) + + calculator = surface.curvature_calculator(np.array([u]), np.array([v]), order=True) + data = calculator.calc(need_uv_directions = True, need_matrix=False) + if self.direction == 'MAX': + direction = data.principal_direction_2_uv[0] + else: + direction = data.principal_direction_1_uv[0] + direction = direction / np.linalg.norm(direction) + direction = direction * step + #print(direction) + u += direction[0] + v += direction[1] + #print(u,v) + verts_out.append(new_verts) + + self.outputs['Vertices'].sv_set(verts_out) + +def register(): + bpy.utils.register_class(SvSurfaceCurvatureLinesNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceCurvatureLinesNode) + diff --git a/nodes/surface/curvatures.py b/nodes/surface/curvatures.py index 852812fd0..ca5714bec 100644 --- a/nodes/surface/curvatures.py +++ b/nodes/surface/curvatures.py @@ -168,7 +168,12 @@ class SvSurfaceCurvaturesNode(bpy.types.Node, SverchCustomTreeNode): src_us = repeat_last_for_length(src_us, maxlen) src_vs = repeat_last_for_length(src_vs, maxlen) us, vs = np.array(src_us), np.array(src_vs) - data = surface.curvature_calculator(us, vs, order=self.order).calc(need_values, need_directions, need_gauss, need_mean, need_matrix) + calculator = surface.curvature_calculator(us, vs, order=self.order) + data = calculator.calc(need_values = need_values, + need_directions = need_directions, + need_gauss = need_gauss, + need_mean = need_mean, + need_matrix = need_matrix) if need_values: c1_out.append(data.principal_value_1.tolist()) c2_out.append(data.principal_value_2.tolist()) diff --git a/utils/surface.py b/utils/surface.py index d254a3c4a..3db3706c2 100644 --- a/utils/surface.py +++ b/utils/surface.py @@ -55,6 +55,7 @@ class SurfaceCurvatureData(object): def __init__(self): self.principal_value_1 = self.principal_value_2 = None self.principal_direction_1 = self.principal_direction_2 = None + self.principal_direction_1_uv = self.principal_direction_2_uv = None self.mean = self.gauss = None self.matrix = None @@ -196,19 +197,30 @@ class SurfaceCurvatureCalculator(object): dir_2 = dir_2 / np.linalg.norm(dir_2, axis=1, keepdims=True) if self.order: - c1mask = c1mask[np.newaxis].T - c2mask = c2mask[np.newaxis].T - dir_1_r = np.where(c1mask, dir_1, -dir_2) - dir_2_r = np.where(c2mask, dir_1, dir_2) + c1maskT = c1mask[np.newaxis].T + c2maskT = c2mask[np.newaxis].T + dir_1_r = np.where(c1maskT, dir_1, -dir_2) + dir_2_r = np.where(c2maskT, dir_1, dir_2) else: dir_1_r = dir_1 dir_2_r = dir_2 #r = (np.cross(dir_1_r, dir_2_r) * self.normals).sum(axis=1) #print(r) - return c1_r, c2_r, dir_1_r, dir_2_r + dir1_uv = eigvecs[:,:,0] + dir2_uv = eigvecs[:,:,1] + if self.order: + c1maskT = c1mask[np.newaxis].T + c2maskT = c2mask[np.newaxis].T + dir1_uv_r = np.where(c1maskT, dir1_uv, -dir2_uv) + dir2_uv_r = np.where(c2maskT, dir1_uv, dir2_uv) + else: + dir1_uv_r = dir1_uv + dir2_uv_r = dir2_uv + + return c1_r, c2_r, dir1_uv_r, dir2_uv_r, dir_1_r, dir_2_r - def calc(self, need_values=True, need_directions=True, need_gauss=True, need_mean=True, need_matrix = True): + def calc(self, need_values=True, need_directions=True, need_uv_directions = False, need_gauss=True, need_mean=True, need_matrix = True): """ Calculate curvature information. Return value: SurfaceCurvatureData instance. @@ -219,12 +231,16 @@ class SurfaceCurvatureCalculator(object): data = SurfaceCurvatureData() if need_matrix: need_directions = True + if need_uv_directions: + need_directions = True if need_directions: # If we need principal curvature directions, then the method # being used will calculate us curvature values for free. - c1, c2, dir1, dir2 = self.values_and_directions() + c1, c2, dir1_uv, dir2_uv, dir1, dir2 = self.values_and_directions() data.principal_value_1, data.principal_value_2 = c1, c2 data.principal_direction_1, data.principal_direction_2 = dir1, dir2 + data.principal_direction_1_uv = dir1_uv + data.principal_direction_2_uv = dir2_uv if need_gauss: data.gauss = c1 * c2 if need_mean: -- GitLab From d5b6b5ff7889fe843d580dd6c80c97e144c11a4b Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 17 May 2020 11:05:56 +0500 Subject: [PATCH 2/5] Runge-Kutta method. --- nodes/surface/curvature_lines.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/nodes/surface/curvature_lines.py b/nodes/surface/curvature_lines.py index 2283efd8c..8a90eae08 100644 --- a/nodes/surface/curvature_lines.py +++ b/nodes/surface/curvature_lines.py @@ -64,6 +64,25 @@ class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): step_s = self.inputs['Step'].sv_get() iterations_s = self.inputs['Iterations'].sv_get() + def get_direction(surface, u, v): + calculator = surface.curvature_calculator(np.array([u]), np.array([v]), order=True) + data = calculator.calc(need_uv_directions = True, need_matrix=False) + if self.direction == 'MAX': + return data.principal_direction_2_uv[0] + else: + return data.principal_direction_1_uv[0] + + def runge_kutta(surface, u, v, step): + u_k1, v_k1 = get_direction(surface, u, v) * step + #u_k1 *= step + #v_k1 *= step + u_k2, v_k2 = get_direction(surface, u + u_k1/2.0, v + v_k1/2.0) * step + u_k3, v_k3 = get_direction(surface, u + u_k2/2.0, v + v_k2/2.0) * step + u_k4, v_k4 = get_direction(surface, u + u_k3, v + v_k3) * step + du = (u_k1 + 2*u_k2 + 2*u_k3 + k4)/6.0 + dv = (v_k1 + 2*v_k2 + 2*v_k3 + k4)/6.0 + return np.array([du, dv]) + verts_out = [] inputs = zip_long_repeat(surfaces_s, src_point_s, step_s, iterations_s) for surfaces, src_point_i, step_i, iterations_i in inputs: @@ -74,13 +93,7 @@ class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): for i in range(iterations): vertex = surface.evaluate(u, v).tolist() new_verts.append(vertex) - - calculator = surface.curvature_calculator(np.array([u]), np.array([v]), order=True) - data = calculator.calc(need_uv_directions = True, need_matrix=False) - if self.direction == 'MAX': - direction = data.principal_direction_2_uv[0] - else: - direction = data.principal_direction_1_uv[0] + direction = runge_kutta(surface, u, v, step) direction = direction / np.linalg.norm(direction) direction = direction * step #print(direction) -- GitLab From 7ebc5dee0bb9e4464e81f21a91934e9dbee196c3 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 17 May 2020 11:08:46 +0500 Subject: [PATCH 3/5] Rebase fix. --- index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/index.md b/index.md index 5b5fc91d7..43462605b 100644 --- a/index.md +++ b/index.md @@ -97,7 +97,6 @@ SvExEvalCurveNode ## Surfaces -<<<<<<< HEAD SvExPlaneSurfaceNode SvExSphereNode SvExSurfaceFormulaNode -- GitLab From a19db0e69cee48429c1883f745fadc8ed1de5bff Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 17 May 2020 11:29:58 +0500 Subject: [PATCH 4/5] Option to go to the opposite direction. --- nodes/surface/curvature_lines.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/nodes/surface/curvature_lines.py b/nodes/surface/curvature_lines.py index 8a90eae08..3783bb891 100644 --- a/nodes/surface/curvature_lines.py +++ b/nodes/surface/curvature_lines.py @@ -41,8 +41,15 @@ class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): min = 1, default = 10, update = updateNode) + negate : BoolProperty( + name = "Negate", + description = "Go to the opposite direction", + default = False, + update = updateNode) + def draw_buttons(self, context, layout): layout.prop(self, 'direction', expand=True) + layout.prop(self, 'negate', toggle=True) def sv_init(self, context): self.inputs.new('SvSurfaceSocket', "Surface") @@ -68,9 +75,12 @@ class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): calculator = surface.curvature_calculator(np.array([u]), np.array([v]), order=True) data = calculator.calc(need_uv_directions = True, need_matrix=False) if self.direction == 'MAX': - return data.principal_direction_2_uv[0] + direction = data.principal_direction_2_uv[0] else: - return data.principal_direction_1_uv[0] + direction = data.principal_direction_1_uv[0] + if self.negate: + direction = - direction + return direction def runge_kutta(surface, u, v, step): u_k1, v_k1 = get_direction(surface, u, v) * step @@ -79,8 +89,8 @@ class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): u_k2, v_k2 = get_direction(surface, u + u_k1/2.0, v + v_k1/2.0) * step u_k3, v_k3 = get_direction(surface, u + u_k2/2.0, v + v_k2/2.0) * step u_k4, v_k4 = get_direction(surface, u + u_k3, v + v_k3) * step - du = (u_k1 + 2*u_k2 + 2*u_k3 + k4)/6.0 - dv = (v_k1 + 2*v_k2 + 2*v_k3 + k4)/6.0 + du = (u_k1 + 2*u_k2 + 2*u_k3 + u_k4)/6.0 + dv = (v_k1 + 2*v_k2 + 2*v_k3 + v_k4)/6.0 return np.array([du, dv]) verts_out = [] -- GitLab From 998aae7b947ab3214417d97620c51b1a5573214d Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Fri, 19 Jun 2020 21:50:19 +0500 Subject: [PATCH 5/5] Move to Sverchok-Extra. --- index.md | 1 - nodes/surface/curvature_lines.py | 122 ------------------------------- 2 files changed, 123 deletions(-) delete mode 100644 nodes/surface/curvature_lines.py diff --git a/index.md b/index.md index 43462605b..4937452d5 100644 --- a/index.md +++ b/index.md @@ -120,7 +120,6 @@ SvSurfaceNormalsNode SvSurfaceGaussCurvatureNode SvSurfaceCurvaturesNode - SvSurfaceCurvatureLinesNode --- SvExTessellateTrimSurfaceNode SvAdaptiveTessellateNode diff --git a/nodes/surface/curvature_lines.py b/nodes/surface/curvature_lines.py deleted file mode 100644 index 3783bb891..000000000 --- a/nodes/surface/curvature_lines.py +++ /dev/null @@ -1,122 +0,0 @@ - -import numpy as np - -import bpy -from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty - -import sverchok -from sverchok.node_tree import SverchCustomTreeNode, throttled -from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level, repeat_last_for_length -from sverchok.utils.logging import info, exception -from sverchok.utils.surface import SvSurface - -class SvSurfaceCurvatureLinesNode(bpy.types.Node, SverchCustomTreeNode): - """ - Triggers: Surface Curvature Lines - Tooltip: Generate surface principal curvature lines - """ - bl_idname = 'SvSurfaceCurvatureLinesNode' - bl_label = 'Surface Curvature Lines' - bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_EVAL_SURFACE' - - directions = [ - ('MIN', "Minimum", "Minimum principal curvature direction", 0), - ('MAX', "Maximum", "Maximum principal curvature direction", 1) - ] - - direction : EnumProperty( - name = "Direction", - items = directions, - default = 'MIN', - update = updateNode) - - step : FloatProperty( - name = "Step", - min = 0, default = 0.1, - update = updateNode) - - iterations : IntProperty( - name = "Iterations", - min = 1, default = 10, - update = updateNode) - - negate : BoolProperty( - name = "Negate", - description = "Go to the opposite direction", - default = False, - update = updateNode) - - def draw_buttons(self, context, layout): - layout.prop(self, 'direction', expand=True) - layout.prop(self, 'negate', toggle=True) - - def sv_init(self, context): - self.inputs.new('SvSurfaceSocket', "Surface") - p = self.inputs.new('SvVerticesSocket', "UVPoints") - p.use_prop = True - p.prop = (0.5, 0.5, 0.0) - self.inputs.new('SvStringsSocket', 'Step').prop_name = 'step' - self.inputs.new('SvStringsSocket', 'Iterations').prop_name = 'iterations' - self.outputs.new('SvVerticesSocket', "Vertices") - - def process(self): - if not any(socket.is_linked for socket in self.outputs): - return - - surfaces_s = self.inputs['Surface'].sv_get() - surfaces_s = ensure_nesting_level(surfaces_s, 2, data_types=(SvSurface,)) - src_point_s = self.inputs['UVPoints'].sv_get() - src_point_s = ensure_nesting_level(src_point_s, 4) - step_s = self.inputs['Step'].sv_get() - iterations_s = self.inputs['Iterations'].sv_get() - - def get_direction(surface, u, v): - calculator = surface.curvature_calculator(np.array([u]), np.array([v]), order=True) - data = calculator.calc(need_uv_directions = True, need_matrix=False) - if self.direction == 'MAX': - direction = data.principal_direction_2_uv[0] - else: - direction = data.principal_direction_1_uv[0] - if self.negate: - direction = - direction - return direction - - def runge_kutta(surface, u, v, step): - u_k1, v_k1 = get_direction(surface, u, v) * step - #u_k1 *= step - #v_k1 *= step - u_k2, v_k2 = get_direction(surface, u + u_k1/2.0, v + v_k1/2.0) * step - u_k3, v_k3 = get_direction(surface, u + u_k2/2.0, v + v_k2/2.0) * step - u_k4, v_k4 = get_direction(surface, u + u_k3, v + v_k3) * step - du = (u_k1 + 2*u_k2 + 2*u_k3 + u_k4)/6.0 - dv = (v_k1 + 2*v_k2 + 2*v_k3 + v_k4)/6.0 - return np.array([du, dv]) - - verts_out = [] - inputs = zip_long_repeat(surfaces_s, src_point_s, step_s, iterations_s) - for surfaces, src_point_i, step_i, iterations_i in inputs: - for surface, src_points, step, iterations in zip_long_repeat(surfaces, src_point_i, step_i, iterations_i): - for src_point in src_points: - new_verts = [] - u,v,_ = src_point - for i in range(iterations): - vertex = surface.evaluate(u, v).tolist() - new_verts.append(vertex) - direction = runge_kutta(surface, u, v, step) - direction = direction / np.linalg.norm(direction) - direction = direction * step - #print(direction) - u += direction[0] - v += direction[1] - #print(u,v) - verts_out.append(new_verts) - - self.outputs['Vertices'].sv_set(verts_out) - -def register(): - bpy.utils.register_class(SvSurfaceCurvatureLinesNode) - -def unregister(): - bpy.utils.unregister_class(SvSurfaceCurvatureLinesNode) - -- GitLab