diff --git a/docs/nodes/curve/curve_index.rst b/docs/nodes/curve/curve_index.rst index 56799be654fababcb6dedb2114e39feec138f297..e06eb2cac1cdd5989762cd32bd6f8a6def4a3c67 100644 --- a/docs/nodes/curve/curve_index.rst +++ b/docs/nodes/curve/curve_index.rst @@ -39,6 +39,7 @@ Curves cast_curve curve_range flip_curve + reparametrize curve_segment split_curve eval_curve diff --git a/docs/nodes/curve/reparametrize.rst b/docs/nodes/curve/reparametrize.rst new file mode 100644 index 0000000000000000000000000000000000000000..ba679c7c2f55a4e064d03aa048d6adadcfb5cbe2 --- /dev/null +++ b/docs/nodes/curve/reparametrize.rst @@ -0,0 +1,30 @@ +Reparametrize Curve +=================== + +Functionality +------------- + +Given a Curve, this node generates another Curve object, which represents the +same curve with another parametrization. The parametrization of the curve is +changed so that the domain of the curve would be equal to specified ``[T_min; +T_max]`` interval. + +This node may be useful, for example, if you have a curve with domain ``[-1; +5]``, but another node expects a curve with domain ``[0; 1]``. + +Inputs +------ + +This node has the following inputs: + +* **Curve**. Original curve. This input is mandatory. +* **NewTMin**, **NewTMax**. Lower and upper bounds of the new curve domain. + Default values are 0 and 1. + +Outputs +------- + +This node has the following output: + +* **Curve**. Reparametrized curve. + diff --git a/docs/nodes/surface/reparametrize.rst b/docs/nodes/surface/reparametrize.rst new file mode 100644 index 0000000000000000000000000000000000000000..1b5c73f9a0819a69a3b3bdd066913a1f0f2d72d5 --- /dev/null +++ b/docs/nodes/surface/reparametrize.rst @@ -0,0 +1,32 @@ +Reparametrize Surface +===================== + +Functionality +------------- + +Given a Surface, this node generates another Surface object, which represents +the same surface with another parametrization. The parametrization is changed +so that the domain of the new surface would be equal to specified U and V +intervals. + +This node may be useful, for example, if you have a surface with domain ``[0; +2] x [0; 2*pi]``, but another node expects a surface with domain ``[0; 1] x [0; 1]``. + +Inputs +------ + +This node has the following inputs: + +* **Surface**. The original surface. This input is mandatory. +* **NewUMin**, **NewUMax**. Lower and upper bounds of the new surface domain in + U parameter. The default values are 0 and 1. +* **NewVMin**, **NewVMax**. Lower and upper bounds of the new surface domain in + V parameter. The default values are 0 and 1. + +Outputs +------- + +This node has the following output: + +* **Surface**. Reparametrized surface. + diff --git a/docs/nodes/surface/surface_index.rst b/docs/nodes/surface/surface_index.rst index e0be8e63e6124e107b72e26b34963edabdd44512..542513af0970453c39c07c9e8cccc75b16874e07 100644 --- a/docs/nodes/surface/surface_index.rst +++ b/docs/nodes/surface/surface_index.rst @@ -23,6 +23,7 @@ Surface subdomain flip swap + reparametrize normals curvatures gauss_curvature diff --git a/index.md b/index.md index c325fca31e35f74a4ca8f78cf1907c94bb172491..0c7e7925ce855b3e9eb95165410a61f6cfc7ff56 100644 --- a/index.md +++ b/index.md @@ -79,6 +79,7 @@ SvExConcatCurvesNode SvExBlendCurvesNode SvExFlipCurveNode + SvReparametrizeCurveNode SvExSurfaceBoundaryNode --- SvExCurveEndpointsNode @@ -117,6 +118,7 @@ SvExSurfaceSubdomainNode SvFlipSurfaceNode SvSwapSurfaceNode + SvReparametrizeSurfaceNode SvSurfaceNormalsNode SvSurfaceGaussCurvatureNode SvSurfaceCurvaturesNode diff --git a/nodes/curve/reparametrize.py b/nodes/curve/reparametrize.py new file mode 100644 index 0000000000000000000000000000000000000000..403b58c9beb47aeda81dd0f07ce314a70019b9ed --- /dev/null +++ b/nodes/curve/reparametrize.py @@ -0,0 +1,66 @@ + +import numpy as np + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level +from sverchok.utils.curve import SvCurve, SvReparametrizedCurve + +class SvReparametrizeCurveNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Reparametrize Curve + Tooltip: Change parameterization of the curve by linear mapping of T parameter to the new bounds + """ + bl_idname = 'SvReparametrizeCurveNode' + bl_label = 'Reparametrize Curve' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_REPARAM_CURVE' + + new_t_min : FloatProperty( + name = "New T Min", + description = "New lower bound of curve's T parameter", + default = 0.0, + update = updateNode) + + new_t_max : FloatProperty( + name = "New T Max", + description = "New upper bound of curve's T parameter", + default = 1.0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.inputs.new('SvStringsSocket', "NewTMin").prop_name = 'new_t_min' + self.inputs.new('SvStringsSocket', "NewTMax").prop_name = 'new_t_max' + 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() + tmin_s = self.inputs['NewTMin'].sv_get() + tmax_s = self.inputs['NewTMax'].sv_get() + + curve_s = ensure_nesting_level(curve_s, 2, data_types=(SvCurve,)) + tmin_s = ensure_nesting_level(tmin_s, 2) + tmax_s = ensure_nesting_level(tmax_s, 2) + + curve_out = [] + for curves, tmins, tmaxs in zip_long_repeat(curve_s, tmin_s, tmax_s): + new_curves = [] + for curve, t_min, t_max in zip_long_repeat(curves, tmins, tmaxs): + new_curve = SvReparametrizedCurve(curve, t_min, t_max) + new_curves.append(new_curve) + curve_out.append(new_curves) + + self.outputs['Curve'].sv_set(curve_out) + +def register(): + bpy.utils.register_class(SvReparametrizeCurveNode) + +def unregister(): + bpy.utils.unregister_class(SvReparametrizeCurveNode) + diff --git a/nodes/surface/reparametrize.py b/nodes/surface/reparametrize.py new file mode 100644 index 0000000000000000000000000000000000000000..53847b4b581942215574a7aec45487bb98419e07 --- /dev/null +++ b/nodes/surface/reparametrize.py @@ -0,0 +1,84 @@ +import numpy as np + +import bpy +from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level +from sverchok.utils.logging import info, exception +from sverchok.utils.surface import SvSurface, SvReparametrizedSurface + +class SvReparametrizeSurfaceNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Reparametrize Surface + Tooltip: Change parametrization of the surface by linear mapping of U, V parameters to the new bounds + """ + bl_idname = 'SvReparametrizeSurfaceNode' + bl_label = 'Reparametrize Surface' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_REPARAM_SURFACE' + + new_u_min : FloatProperty( + name = "New U Min", + description = "New lower bound of surface's U parameter", + default = 0.0, + update = updateNode) + + new_u_max : FloatProperty( + name = "New U Max", + description = "New upper bound of surface's U parameter", + default = 1.0, + update = updateNode) + + new_v_min : FloatProperty( + name = "New V Min", + description = "New lower bound of surface's V parameter", + default = 0.0, + update = updateNode) + + new_v_max : FloatProperty( + name = "New V Max", + description = "New upper bound of surface's V parameter", + default = 1.0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.inputs.new('SvStringsSocket', "NewUMin").prop_name = 'new_u_min' + self.inputs.new('SvStringsSocket', "NewUMax").prop_name = 'new_u_max' + self.inputs.new('SvStringsSocket', "NewVMin").prop_name = 'new_v_min' + self.inputs.new('SvStringsSocket', "NewVMax").prop_name = 'new_v_max' + self.outputs.new('SvSurfaceSocket', "Surface") + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + umin_s = self.inputs['NewUMin'].sv_get() + umax_s = self.inputs['NewUMax'].sv_get() + vmin_s = self.inputs['NewVMin'].sv_get() + vmax_s = self.inputs['NewVMax'].sv_get() + + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + umin_s = ensure_nesting_level(umin_s, 2) + umax_s = ensure_nesting_level(umax_s, 2) + vmin_s = ensure_nesting_level(vmin_s, 2) + vmax_s = ensure_nesting_level(vmax_s, 2) + + surface_out = [] + for surfaces, umins, umaxs, vmins, vmaxs in zip_long_repeat(surface_s, umin_s, umax_s, vmin_s, vmax_s): + new_surfaces = [] + for surface, u_min, u_max, v_min, v_max in zip_long_repeat(surfaces, umins, umaxs, vmins, vmaxs): + new_surface = SvReparametrizedSurface(surface, u_min, u_max, v_min, v_max) + new_surfaces.append(new_surface) + surface_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surface_out) + +def register(): + bpy.utils.register_class(SvReparametrizeSurfaceNode) + +def unregister(): + bpy.utils.unregister_class(SvReparametrizeSurfaceNode) + diff --git a/ui/icons/sv_flatten.png b/ui/icons/sv_flatten.png new file mode 100644 index 0000000000000000000000000000000000000000..2b1a684011616b5c86a1b1bb45bcb1e1a9205a31 Binary files /dev/null and b/ui/icons/sv_flatten.png differ diff --git a/ui/icons/sv_graft.png b/ui/icons/sv_graft.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1e7737169655281714ada086572540dd120176 Binary files /dev/null and b/ui/icons/sv_graft.png differ diff --git a/ui/icons/sv_reparam_curve.png b/ui/icons/sv_reparam_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..aa7431db957f44c5b027e46c0a5fe538a7e2faa4 Binary files /dev/null and b/ui/icons/sv_reparam_curve.png differ diff --git a/ui/icons/sv_reparam_surface.png b/ui/icons/sv_reparam_surface.png new file mode 100644 index 0000000000000000000000000000000000000000..28a00fc2ddaefe4a7ba6120d6e359420adfd6211 Binary files /dev/null and b/ui/icons/sv_reparam_surface.png differ diff --git a/ui/icons/sv_wrap.png b/ui/icons/sv_wrap.png new file mode 100644 index 0000000000000000000000000000000000000000..2642ab0c9ca6e3a4fda8db1dea0ddd41c0cedf9e Binary files /dev/null and b/ui/icons/sv_wrap.png differ diff --git a/ui/icons/svg/sv_flatten.svg b/ui/icons/svg/sv_flatten.svg new file mode 100644 index 0000000000000000000000000000000000000000..f2218e515689647e47f9c29e44efb344d56c36b5 --- /dev/null +++ b/ui/icons/svg/sv_flatten.svg @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_graft.svg b/ui/icons/svg/sv_graft.svg new file mode 100644 index 0000000000000000000000000000000000000000..29349ad9f539346533f805a9b1f088be4907a9e6 --- /dev/null +++ b/ui/icons/svg/sv_graft.svg @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_reparam_curve.svg b/ui/icons/svg/sv_reparam_curve.svg new file mode 100644 index 0000000000000000000000000000000000000000..5dab9b5dcb053ab369ca40f2e0f95c7c6319d1d9 --- /dev/null +++ b/ui/icons/svg/sv_reparam_curve.svg @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_reparam_surface.svg b/ui/icons/svg/sv_reparam_surface.svg new file mode 100644 index 0000000000000000000000000000000000000000..c7c637c75b489cdf50c4cbf3d2ae9d1c0e770e79 --- /dev/null +++ b/ui/icons/svg/sv_reparam_surface.svg @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_wrap.svg b/ui/icons/svg/sv_wrap.svg new file mode 100644 index 0000000000000000000000000000000000000000..0494dfe3124976d1ff90833cefc60dee1957ba59 --- /dev/null +++ b/ui/icons/svg/sv_wrap.svg @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + [] + + diff --git a/utils/curve.py b/utils/curve.py index 9b48ae43ea370f424e75c0b569cd0feb74bc3264..dc35ea76f2416d926c79b2f623bc63ae1fd35a82 100644 --- a/utils/curve.py +++ b/utils/curve.py @@ -685,6 +685,55 @@ class SvFlipCurve(SvCurve): sign = -sign return array +class SvReparametrizedCurve(SvCurve): + def __init__(self, curve, new_u_min, new_u_max): + self.curve = curve + self.new_u_min = new_u_min + self.new_u_max = new_u_max + if hasattr(curve, 'tangent_delta'): + self.tangent_delta = curve.tangent_delta + else: + self.tangent_delta = 0.001 + + def get_u_bounds(self): + return self.new_u_min, self.new_u_max + + @property + def scale(self): + u_min, u_max = self.curve.get_u_bounds() + return (u_max - u_min) / (self.new_u_max - self.new_u_min) + + def map_u(self, u): + u_min, u_max = self.curve.get_u_bounds() + return (u_max - u_min) * (u - self.new_u_min) / (self.new_u_max - self.new_u_min) + u_min + + def evaluate(self, t): + return self.curve.evaluate(self.map_u(t)) + + def evaluate_array(self, ts): + return self.curve.evaluate_array(self.map_u(ts)) + + def tangent(self, t): + return self.scale * self.curve.tangent(self.map_u(t)) + + def tangent_array(self, ts): + return self.scale * self.curve.tangent_array(self.map_u(ts)) + + def second_derivative_array(self, ts): + return self.scale**2 * self.curve.second_derivative_array(self.map_u(ts)) + + def third_derivative_array(self, ts): + return self.scale**3 * self.curve.third_derivative_array(self.map_u(ts)) + + def derivatives_array(self, n, ts): + derivs = self.curve.derivatives_array(n, ts) + k = self.scale + array = [] + for deriv in derivs: + array.append(k * deriv) + k = k * self.scale + return array + class SvCurveSegment(SvCurve): def __init__(self, curve, u_min, u_max, rescale=False): self.curve = curve diff --git a/utils/surface.py b/utils/surface.py index 418a8234f42bbffc626c91a8f7885b8036599075..c1cc315f4ac80a8c1c3914c3342612ebd067c704 100644 --- a/utils/surface.py +++ b/utils/surface.py @@ -581,6 +581,75 @@ class SvSwapSurface(SvSurface): def normal_array(self, us, vs): return self.surface.normal_array(vs, us) +class SvReparametrizedSurface(SvSurface): + def __init__(self, surface, new_u_min, new_u_max, new_v_min, new_v_max): + self.surface = surface + self.new_u_min = new_u_min + self.new_u_max = new_u_max + self.new_v_min = new_v_min + self.new_v_max = new_v_max + if hasattr(surface, "normal_delta"): + self.normal_delta = surface.normal_delta + else: + self.normal_delta = 0.001 + + def get_u_min(self): + return self.new_u_min + + def get_v_min(self): + return self.new_v_min + + def get_u_max(self): + return self.new_u_max + + def get_v_max(self): + return self.new_v_max + + def map_uv(self, u, v): + new_u_min, new_u_max = self.new_u_min, self.new_u_max + new_v_min, new_v_max = self.new_v_min, self.new_v_max + + u_min, u_max = self.surface.get_u_min(), self.surface.get_u_max() + v_min, v_max = self.surface.get_v_min(), self.surface.get_v_max() + + u = (u_max - u_min) * (u - new_u_min) / (new_u_max - new_u_min) + u_min + v = (v_max - v_min) * (v - new_v_min) / (new_v_max - new_v_min) + v_min + + return u, v + + def scale_u(self): + new_u_min, new_u_max = self.new_u_min, self.new_u_max + u_min, u_max = self.surface.get_u_min(), self.surface.get_u_max() + return (u_max - u_min) / (new_u_max - new_u_min) + + def scale_v(self): + new_v_min, new_v_max = self.new_v_min, self.new_v_max + v_min, v_max = self.surface.get_v_min(), self.surface.get_v_max() + return (v_max - v_min) / (new_v_max - new_v_min) + + def evaluate(self, u, v): + u, v = self.map_uv(u, v) + return self.surface.evaluate(u, v) + + def evaluate_array(self, us, vs): + us, vs = self.map_uv(us, vs) + return self.surface.evaluate_array(us, vs) + + def normal(self, u, v): + u, v = self.map_uv(u, v) + return self.surface.normal(u, v) + + def normal_array(self, us, vs): + us, vs = self.map_uv(us, vs) + return self.surface.normal_array(us, vs) + + def derivatives_data_array(self, us, vs): + us, vs = self.map_uv(us, vs) + data = self.surface.derivatives_data_array(us, vs) + data.du *= self.scale_u() + data.dv *= self.scale_v() + return data + class SvPlane(SvSurface): __description__ = "Plane"