From d83ff30e0f5c4a7411098b0a2ba8fd3e2900f8d6 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Sun, 26 Jan 2020 13:05:32 +0100 Subject: [PATCH 1/4] new Cast node --- index.md | 1 + nodes/transforms/spherize.py | 231 +++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 nodes/transforms/spherize.py diff --git a/index.md b/index.md index 2f8f5ec74..71a26d37b 100644 --- a/index.md +++ b/index.md @@ -103,6 +103,7 @@ SvDisplaceNode SvNoiseDisplaceNode SvRandomizeVerticesNode + SvSpherizeNode ## Modifier Change SvDeleteLooseNode diff --git a/nodes/transforms/spherize.py b/nodes/transforms/spherize.py new file mode 100644 index 000000000..2c718f6dd --- /dev/null +++ b/nodes/transforms/spherize.py @@ -0,0 +1,231 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### +from colorsys import rgb_to_hls +from itertools import repeat +import bpy +from bpy.props import EnumProperty, FloatProperty, FloatVectorProperty, StringProperty, BoolProperty +from mathutils import Vector, Matrix, Color +import numpy as np +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.core.socket_data import SvGetSocketInfo +from sverchok.data_structure import updateNode, list_match_func, numpy_list_match_modes, iter_list_match_func, numpy_list_match_func +from sverchok.utils.sv_itertools import recurse_f_level_control +from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata +from sverchok.utils.modules.color_utils import color_channels + + +def meshes_texture_diplace(params, constant, matching_f): + ''' + This function prepares the data to pass to the different displace functions. + + params are verts, pols, texture, scale_out, matrix, size, strength, axis + - verts, scale_out, and axis should be list as [[[float, float, float],],] (Level 3) + - pols should be list as [[[int, int, int, ...],],] (Level 3) + - texture can be [texture, texture] or [[texture, texture],[texture]] for per vertex texture + - matrix can be [matrix, matrix] or [[matrix, matrix],[texture]] for per vertex matrix, + in case of UV Coors in mapping_mode it should be [[[float, float, float],],] (Level 3) + size and strength should be list as [[float, float, ..], [float, ..], ..] (Level 2) + desired_levels = [3, 3, 2, 2, 2 or 3, 2, 2, 3] + constant are the function options (data that does not need to be matched) + matching_f stands for list matching formula to use + ''' + result = [] + center_mode, match_mode, size_mode = constant + params = matching_f(params) + local_match = numpy_list_match_func[match_mode] + + for props in zip(*params): + verts, sphere_scale, axis_scale, origin, size, strength = local_match([np.array(p) for p in props]) + if center_mode == 'AVERAGE': + origin = (np.sum(verts, axis=0)/verts.shape[0])[np.newaxis, :] + + np_verts_c = verts - origin + mag = np.linalg.norm(np_verts_c, axis=1) + + if size_mode == 'AVERAGE': + size = np.sum(mag)/mag.shape[0] + + else: + size = size[:, np.newaxis] + # sphere + verts_normalized = np_verts_c/mag[:, np.newaxis] * size * sphere_scale + origin + # cilinder + + ang = np.arctan2(np_verts_c[:,1],np_verts_c[:,0]) + x = np.cos(ang) + y = np.sin(ang) + z = np_verts_c[:,2] + verts_normalized = np.stack((x,y,z)).T * size * sphere_scale + origin + # cuboid + y= np.interp(ang,[-np.pi,-np.pi/2,0, np.pi/2, np.pi],[0, -1, 0, 1, 0]) + x= np.interp(ang,[-np.pi,-np.pi/2,0, np.pi/2, np.pi],[-1, 0, 1, 0, -1]) + z = np_verts_c[:,2] + verts_normalized = np.stack((x,y,z)).T * size * sphere_scale + origin + verts_out = verts + (verts_normalized - verts) * strength[:, np.newaxis] * axis_scale + + result.append(verts_out .tolist()) + + return result + + + + + + +class SvSpherizeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Add texture to verts + Tooltip: Affect input verts/mesh with a scene texture. Mimics Blender Displace modifier + + """ + + bl_idname = 'SvSpherizeNode' + bl_label = 'Spherize' + bl_icon = 'MOD_DISPLACE' + + origin_modes = [ + ('AVERAGE', 'Average', 'Texture displacement along Vertex Normal', '', 1), + ('EXTERNAL', 'External', 'Texture displacement along X axis', '', 2), + ] + + size_modes = [ + ('AVERAGE', 'Average', 'Input UV coordinates to evaluate texture', '', 1), + ('EXTERNAL', 'Defined', 'Matrix to apply to verts before evaluating texture', '', 2), + ] + @throttled + def handle_size_socket(self, context): + input = self.inputs['Size'] + if self.size_mode == 'AVERAGE': + if not input.hide_safe: + input.hide_safe = True + else: + if input.hide_safe: + input.hide_safe = False + + @throttled + def handle_origin_socket(self, context): + input = self.inputs['Origin'] + if self.origin_mode == 'AVERAGE': + if not input.hide_safe: + input.hide_safe = True + else: + if input.hide_safe: + input.hide_safe = False + + use_x: BoolProperty( + name="X", description="smooth vertices along X axis", + default=True, update=updateNode) + + use_y: BoolProperty( + name="Y", description="smooth vertices along Y axis", + default=True, update=updateNode) + + use_z: BoolProperty( + name="Z", description="smooth vertices along Z axis", + default=True, update=updateNode) + + origin_mode: EnumProperty( + name='Direction', + items=origin_modes, + default='AVERAGE', + description='Apply Mode', + update=handle_origin_socket) + + size_mode: EnumProperty( + name='Size_mode', + items=size_modes, + default='AVERAGE', + description="Mapping method", + update=handle_size_socket) + + origin: FloatVectorProperty( + name='Origin', description='Scale of the added vector', + size=3, default=(1, 1, 1), + update=updateNode) + + scale_out_v: FloatVectorProperty( + name='Axis Scale Out', description='Scale of the added vector', + size=3, default=(1, 1, 1), + update=updateNode) + + sphere_axis_scale: FloatVectorProperty( + name='Sphere Scale', description='Scale base sphere', + size=3, default=(1, 1, 1), + update=updateNode) + + strength: FloatProperty( + name='Strength', description='Stength of the effect', + default=1.0, update=updateNode) + + size: FloatProperty( + name='Size', description='Size the sphere', + default=1.0, update=updateNode) + + list_match: EnumProperty( + name="List Match", + description="Behavior on different list lengths", + items=numpy_list_match_modes, default="REPEAT", + update=updateNode) + + def sv_init(self, context): + + self.inputs.new('SvVerticesSocket', 'Vertices') + self.inputs.new('SvVerticesSocket', 'Sphere Scale').prop_name = 'sphere_axis_scale' + self.inputs.new('SvVerticesSocket', 'Axis Scale Out').prop_name = 'scale_out_v' + self.inputs.new('SvVerticesSocket', 'Origin').prop_name = 'origin' + self.inputs.new('SvStringsSocket', 'Size').prop_name = 'size' + self.inputs.new('SvStringsSocket', 'Strength').prop_name = 'strength' + self.inputs['Origin'].hide_safe = True + self.inputs['Size'].hide_safe = True + + self.outputs.new('SvVerticesSocket', 'Vertices') + + + def draw_buttons(self, context, layout): + layout.prop(self, 'origin_mode', expand=False, text='Origin') + layout.prop(self, 'size_mode', expand=False, text='Size') + + def draw_buttons_ext(self, context, layout): + '''draw buttons on the N-panel''' + self.draw_buttons(context, layout) + layout.prop(self, 'list_match', expand=False) + + def rclick_menu(self, context, layout): + layout.prop_menu_enum(self, "list_match", text="List Match") + + def process(self): + inputs, outputs = self.inputs, self.outputs + + if not outputs[0].is_linked: + return + + result = [] + + params = [si.sv_get(default=[[]], deepcopy=False) for si in inputs] + + + matching_f = list_match_func[self.list_match] + desired_levels = [3, 3, 3, 3, 2, 2] + ops = [self.origin_mode, self.list_match, self.size_mode] + + result = recurse_f_level_control(params, ops, meshes_texture_diplace, matching_f, desired_levels) + + self.outputs[0].sv_set(result) + +classes = [SvSpherizeNode] +register, unregister = bpy.utils.register_classes_factory(classes) -- GitLab From c3418ae1c68682d28faec0dbdbbc7e31a04f2747 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Thu, 30 Jan 2020 11:58:01 +0100 Subject: [PATCH 2/4] More Shapes and Docs --- docs/nodes/transforms/cast.rst | 92 ++++++++++ index.md | 2 +- nodes/transforms/cast.py | 296 +++++++++++++++++++++++++++++++++ nodes/transforms/spherize.py | 231 ------------------------- 4 files changed, 389 insertions(+), 232 deletions(-) create mode 100644 docs/nodes/transforms/cast.rst create mode 100644 nodes/transforms/cast.py delete mode 100644 nodes/transforms/spherize.py diff --git a/docs/nodes/transforms/cast.rst b/docs/nodes/transforms/cast.rst new file mode 100644 index 000000000..42cf1ce72 --- /dev/null +++ b/docs/nodes/transforms/cast.rst @@ -0,0 +1,92 @@ +Cast +==== + +The Cast modifier shifts the shape of a mesh towards a predefined shape (sphere, cylinder, prism, UV Sphere). + +Mimics and expands the Blender Cast Modifier (https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/cast.html). + +The node accepts regular Python lists and Numpy Arrays. (Flat Arrays for scalars and two-axis arrays for vectors). + +Inputs & Parameters +------------------- + ++----------------+-------------------------------------------------------------------------+ +| Parameters | Description | ++================+=========================================================================+ +| Shape | Sphere, Cylinder, Prism, UV Sphere | ++----------------+-------------------------------------------------------------------------+ +| Origin | Determine where the origin of the shape should be: | +| | | +| | - Average: The center of the mesh will be in the mean of the inputted | +| | vertices. | +| | - External: The center of the mesh will be taken from the origin input. | ++----------------+-------------------------------------------------------------------------+ +| Size | Determine where the origin of the shape should be: | +| | - Average: The size of the mesh will be in the mean of the inputted | +| | vertices. | +| | - External: The size of the mesh will be taken from the size input. | ++----------------+-------------------------------------------------------------------------+ + ++----------------+-------------------------------------------------------------------------+ +| Inputs | Description | ++================+=========================================================================+ +| Vertices | Vertices of the mesh to cast | ++----------------+-------------------------------------------------------------------------+ +| Shape Scale | Axis scaling of the base mesh | ++----------------+-------------------------------------------------------------------------+ +| Effect Scale | Axis scaling of the displacement vector | ++----------------+-------------------------------------------------------------------------+ +| Origin | Origin of the base mesh | ++----------------+-------------------------------------------------------------------------+ +| Size | Base size of the base mesh | ++----------------+-------------------------------------------------------------------------+ +| Strength | Scalar scaling of the displacement vector | ++----------------+-------------------------------------------------------------------------+ +| Meridians | Horizontal divisions. If less than 2 it will be interpreted as circular | ++----------------+-------------------------------------------------------------------------+ +| Parallels | Vertical divisions. If less than 1 it will be interpreted as circular | ++----------------+-------------------------------------------------------------------------+ + +Advanced Parameters +------------------- + +In the N-Panel (and on the right-click menu) you can find: + +**Output NumPy**: Output NumPy arrays in stead of regular lists (makes the node faster) + +**List Match**: Define how list with different lengths should be matched + +Examples +-------- + +Basic example + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_sphere_sverchok_blender_example_1.png + +Cast a box to a octahedron: + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_octahedron_sverchok_blender_example_1.png + +Cast a UV Sphere to a pentagonal prism: + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_prism_sverchok_blender_example_1.png + +Casting Suzzane to different height prisms + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_prism_sverchok_blender_example_shape_scale.png + +Casting a plane to a scaled sphere using external origin + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_sphere_sverchok_blender_example_external_origin_shape_scale.png + +Multiple concatenation of casts + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_sphere_sverchok_blender_example_external_origin_shape_scale_2.png + +All parameters are vectorized. In this example multiple origins are passed to the same shape + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_sphere_sverchok_blender_example_multiple_origin_shape_scale.png + +To get a tetrahedron Meridians:3 Parallels: 1.5 Shape scale: (1.08888, 1.08888, 0.8889) + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/transforms/cast/cast_to_tetrahedron_sverchok_blender_example_1.png diff --git a/index.md b/index.md index 71a26d37b..0fb2446ed 100644 --- a/index.md +++ b/index.md @@ -103,7 +103,7 @@ SvDisplaceNode SvNoiseDisplaceNode SvRandomizeVerticesNode - SvSpherizeNode + SvCastNode ## Modifier Change SvDeleteLooseNode diff --git a/nodes/transforms/cast.py b/nodes/transforms/cast.py new file mode 100644 index 000000000..9adf3fe82 --- /dev/null +++ b/nodes/transforms/cast.py @@ -0,0 +1,296 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### +import bpy +from bpy.props import EnumProperty, FloatProperty, FloatVectorProperty, BoolProperty +import numpy as np +from numpy import pi +from sverchok.node_tree import SverchCustomTreeNode, throttled +from sverchok.data_structure import updateNode, list_match_func, numpy_list_match_modes, numpy_list_match_func +from sverchok.utils.sv_itertools import recurse_f_level_control +from sverchok.utils.modules.vector_math_utils import angle_between + +def sphere_points(np_verts_c, mag): + return np_verts_c/mag[:, np.newaxis] + +def cylinder_points(np_verts_c, sides): + ang = np.arctan2(np_verts_c[:, 1], np_verts_c[:, 0]) + div = np.cos((ang + pi/sides) % (2 * pi / sides) - pi / sides) + div[sides < 2] = 1 + x_co = np.cos(ang) / div + y_co = np.sin(ang) / div + z_co = np_verts_c[:, 2] + return np.stack((x_co, y_co, z_co)).T + + +def prism_points(np_verts_c, sides): + ang = np.arctan2(np_verts_c[:, 1], np_verts_c[:, 0]) + ang2 = angle_between(np_verts_c, [[0, 0, 1]]) + div2 = np.cos((ang2 + pi/4) % (pi/2) - pi/4) + div = np.cos((ang + pi/sides) % (2*pi/sides) - pi/sides) *div2 + div[sides < 2] = 1 + x_co = np.cos(ang)*np.sin(ang2)/div + y_co = np.sin(ang)*np.sin(ang2)/div + z_co = np.cos(ang2)/div2 + return np.stack((x_co, y_co, z_co)).T + +def uv_sphere_points(np_verts_c, u_sides, v_sides): + ang = np.arctan2(np_verts_c[:, 1], np_verts_c[:, 0]) + ang2 = angle_between(np_verts_c, [[0, 0, 1]]) + div2 = np.cos((ang2) % (pi/v_sides) - pi/v_sides/2) / np.cos(pi/v_sides/2) + div2[v_sides < 1] = 1 + div = np.cos((ang)%(2*pi/u_sides)-pi/u_sides) *div2 / np.cos(pi/u_sides) + div[u_sides < 2] = 1 + x_co = np.cos(ang)*np.sin(ang2)/div + y_co = np.sin(ang)*np.sin(ang2)/div + z_co = np.cos(ang2)/div2 + return np.stack((x_co, y_co, z_co)).T + +def cast_meshes(params, constant, matching_f): + ''' + This function prepares the data to pass to the different cast functions. + + params are verts, base_scale, effect_scale, size, strength, size, u_sides, v_sides + - verts, base_scale, effect_scale and origin should be list as [[[float, float, float],],] (Level 3) + - size and strength should be list as [[float, float, ..], [float, ..], ..] (Level 2) + - u_sides and v_sides should be list as [[float, float, ..], [float, ..], ..] (Level 2) + desired_levels = [3, 3, 3, 3, 2, 2, 2, 2] + constant are the function options (data that does not need to be matched) + matching_f stands for list matching formula to use + ''' + result = [] + cast_shape, center_mode, match_mode, size_mode, output_numpy = constant + params = matching_f(params) + cast_func = CAST_FORMULAS[cast_shape] + local_match = numpy_list_match_func[match_mode] + + for props in zip(*params): + verts, base_scale, effect_scale, origin, size, strength, u_sides, v_sides = local_match([np.array(p) for p in props]) + if center_mode == 'AVERAGE': + origin = (np.sum(verts, axis=0)/verts.shape[0])[np.newaxis, :] + np_verts_c = verts - origin + mag = np.linalg.norm(np_verts_c, axis=1) + + if size_mode == 'AVERAGE': + size = np.sum(mag)/mag.shape[0] + else: + size = size[:, np.newaxis] + if cast_shape == 'UV Sphere': + sub_props = [np_verts_c, u_sides, v_sides] + elif cast_shape == 'Sphere': + sub_props = [np_verts_c, mag] + elif cast_shape == 'Prism': + sub_props = [np_verts_c, u_sides] + else: + sub_props = [np_verts_c, u_sides] + + + verts_normalized = cast_func(*sub_props) * size * base_scale + origin + verts_out = verts + (verts_normalized - verts) * strength[:, np.newaxis] * effect_scale + + result.append(verts_out if output_numpy else verts_out.tolist()) + + return result + + +CAST_FORMULAS = { + 'Sphere': sphere_points, + 'Cylinder': cylinder_points, + 'Prism': prism_points, + 'UV Sphere': uv_sphere_points + } + + + +class SvCastNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: To Sphere, Prism, Cylinder + Tooltip: Affect input verts/mesh with a scene texture. Mimics Blender Displace modifier + + """ + + bl_idname = 'SvCastNode' + bl_label = 'Cast' + bl_icon = 'MOD_DISPLACE' + + origin_modes = [ + ('AVERAGE', 'Average', 'Texture displacement along Vertex Normal', '', 1), + ('EXTERNAL', 'External', 'Texture displacement along X axis', '', 2), + ] + + size_modes = [ + ('AVERAGE', 'Average', 'Input UV coordinates to evaluate texture', '', 1), + ('EXTERNAL', 'Defined', 'Matrix to apply to verts before evaluating texture', '', 2), + ] + + cast_modes = [(t, t.title(), t.title(), '', id) for id, t in enumerate(CAST_FORMULAS)] + + @throttled + def handle_size_socket(self, context): + input_socket = self.inputs['Size'] + if self.size_mode == 'AVERAGE': + if not input_socket.hide_safe: + input_socket.hide_safe = True + else: + if input_socket.hide_safe: + input_socket.hide_safe = False + + @throttled + def handle_origin_socket(self, context): + input_socket = self.inputs['Origin'] + if self.origin_mode == 'AVERAGE': + if not input_socket.hide_safe: + input_socket.hide_safe = True + else: + if input_socket.hide_safe: + input_socket.hide_safe = False + + @throttled + def handle_uv_sockets(self, context): + u_socket = self.inputs['U'] + v_socket = self.inputs['V'] + if self.cast_mode == 'Sphere': + u_socket.hide_safe = True + v_socket.hide_safe = True + elif self.cast_mode in ['Cylinder', 'Prism']: + v_socket.hide_safe = True + if u_socket.hide_safe: + u_socket.hide_safe = False + else: + if u_socket.hide_safe: + u_socket.hide_safe = False + if v_socket.hide_safe: + v_socket.hide_safe = False + + cast_mode: EnumProperty( + name='Cast Type', + items=cast_modes, + default='Sphere', + description='Shape to cast', + update=handle_uv_sockets) + + origin_mode: EnumProperty( + name='Origin', + items=origin_modes, + default='AVERAGE', + description='Origin of base mesh', + update=handle_origin_socket) + + size_mode: EnumProperty( + name='Size_mode', + items=size_modes, + default='AVERAGE', + description="Size of base mesh", + update=handle_size_socket) + + origin: FloatVectorProperty( + name='Origin', description='Origin of the base mesh', + size=3, default=(0, 0, 0), + update=updateNode) + + scale_out_v: FloatVectorProperty( + name='Effect Scale', description='Scale of the added vector', + size=3, default=(1, 1, 1), + update=updateNode) + + sphere_axis_scale: FloatVectorProperty( + name='Shape Scale', description='Scale base shape', + size=3, default=(1, 1, 1), + update=updateNode) + + strength: FloatProperty( + name='Strength', description='Stength of the effect', + default=1.0, update=updateNode) + + size: FloatProperty( + name='Size', description='Size the sphere', + default=1.0, update=updateNode) + + U_div: FloatProperty( + name='Meridians', description='Meridians of the UV sphere. If less than 2 -> circular', + default=4.0, update=updateNode) + V_div: FloatProperty( + name='Parallels', description='Paralels of the UV sphere. If less than 2 -> circular', + default=4.0, update=updateNode) + + list_match: EnumProperty( + name="List Match", + description="Behavior on different list lengths", + items=numpy_list_match_modes, default="REPEAT", + update=updateNode) + + output_numpy: BoolProperty( + name='Output NumPy', + description='Output NumPy arrays', + default=False, update=updateNode) + + def sv_init(self, context): + + self.inputs.new('SvVerticesSocket', 'Vertices') + self.inputs.new('SvVerticesSocket', 'Shape Scale').prop_name = 'sphere_axis_scale' + self.inputs.new('SvVerticesSocket', 'Effect Scale').prop_name = 'scale_out_v' + self.inputs.new('SvVerticesSocket', 'Origin').prop_name = 'origin' + self.inputs.new('SvStringsSocket', 'Size').prop_name = 'size' + self.inputs.new('SvStringsSocket', 'Strength').prop_name = 'strength' + self.inputs.new('SvStringsSocket', 'U').prop_name = 'U_div' + self.inputs.new('SvStringsSocket', 'V').prop_name = 'V_div' + self.inputs['Origin'].hide_safe = True + self.inputs['Size'].hide_safe = True + self.inputs['U'].hide_safe = True + self.inputs['V'].hide_safe = True + + self.outputs.new('SvVerticesSocket', 'Vertices') + + + def draw_buttons(self, context, layout): + layout.prop(self, 'cast_mode', expand=False, text='To') + layout.prop(self, 'origin_mode', expand=False, text='Origin') + layout.prop(self, 'size_mode', expand=False, text='Size') + + def draw_buttons_ext(self, context, layout): + '''draw buttons on the N-panel''' + self.draw_buttons(context, layout) + layout.prop(self, 'output_numpy') + layout.prop(self, 'list_match', expand=False) + + def rclick_menu(self, context, layout): + layout.prop(self, 'output_numpy') + layout.prop_menu_enum(self, "list_match", text="List Match") + + def draw_label(self): + return self.label or self.name + ' to ' + self.cast_mode.title() + + def process(self): + inputs, outputs = self.inputs, self.outputs + + if not outputs[0].is_linked: + return + + result = [] + + params = [si.sv_get(default=[[]], deepcopy=False) for si in inputs] + + matching_f = list_match_func[self.list_match] + desired_levels = [3, 3, 3, 3, 2, 2, 2, 2] + ops = [self.cast_mode, self.origin_mode, self.list_match, self.size_mode, self.output_numpy] + + result = recurse_f_level_control(params, ops, cast_meshes, matching_f, desired_levels) + + self.outputs[0].sv_set(result) + +classes = [SvCastNode] +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/nodes/transforms/spherize.py b/nodes/transforms/spherize.py deleted file mode 100644 index 2c718f6dd..000000000 --- a/nodes/transforms/spherize.py +++ /dev/null @@ -1,231 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### -from colorsys import rgb_to_hls -from itertools import repeat -import bpy -from bpy.props import EnumProperty, FloatProperty, FloatVectorProperty, StringProperty, BoolProperty -from mathutils import Vector, Matrix, Color -import numpy as np -from sverchok.node_tree import SverchCustomTreeNode, throttled -from sverchok.core.socket_data import SvGetSocketInfo -from sverchok.data_structure import updateNode, list_match_func, numpy_list_match_modes, iter_list_match_func, numpy_list_match_func -from sverchok.utils.sv_itertools import recurse_f_level_control -from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata -from sverchok.utils.modules.color_utils import color_channels - - -def meshes_texture_diplace(params, constant, matching_f): - ''' - This function prepares the data to pass to the different displace functions. - - params are verts, pols, texture, scale_out, matrix, size, strength, axis - - verts, scale_out, and axis should be list as [[[float, float, float],],] (Level 3) - - pols should be list as [[[int, int, int, ...],],] (Level 3) - - texture can be [texture, texture] or [[texture, texture],[texture]] for per vertex texture - - matrix can be [matrix, matrix] or [[matrix, matrix],[texture]] for per vertex matrix, - in case of UV Coors in mapping_mode it should be [[[float, float, float],],] (Level 3) - size and strength should be list as [[float, float, ..], [float, ..], ..] (Level 2) - desired_levels = [3, 3, 2, 2, 2 or 3, 2, 2, 3] - constant are the function options (data that does not need to be matched) - matching_f stands for list matching formula to use - ''' - result = [] - center_mode, match_mode, size_mode = constant - params = matching_f(params) - local_match = numpy_list_match_func[match_mode] - - for props in zip(*params): - verts, sphere_scale, axis_scale, origin, size, strength = local_match([np.array(p) for p in props]) - if center_mode == 'AVERAGE': - origin = (np.sum(verts, axis=0)/verts.shape[0])[np.newaxis, :] - - np_verts_c = verts - origin - mag = np.linalg.norm(np_verts_c, axis=1) - - if size_mode == 'AVERAGE': - size = np.sum(mag)/mag.shape[0] - - else: - size = size[:, np.newaxis] - # sphere - verts_normalized = np_verts_c/mag[:, np.newaxis] * size * sphere_scale + origin - # cilinder - - ang = np.arctan2(np_verts_c[:,1],np_verts_c[:,0]) - x = np.cos(ang) - y = np.sin(ang) - z = np_verts_c[:,2] - verts_normalized = np.stack((x,y,z)).T * size * sphere_scale + origin - # cuboid - y= np.interp(ang,[-np.pi,-np.pi/2,0, np.pi/2, np.pi],[0, -1, 0, 1, 0]) - x= np.interp(ang,[-np.pi,-np.pi/2,0, np.pi/2, np.pi],[-1, 0, 1, 0, -1]) - z = np_verts_c[:,2] - verts_normalized = np.stack((x,y,z)).T * size * sphere_scale + origin - verts_out = verts + (verts_normalized - verts) * strength[:, np.newaxis] * axis_scale - - result.append(verts_out .tolist()) - - return result - - - - - - -class SvSpherizeNode(bpy.types.Node, SverchCustomTreeNode): - """ - Triggers: Add texture to verts - Tooltip: Affect input verts/mesh with a scene texture. Mimics Blender Displace modifier - - """ - - bl_idname = 'SvSpherizeNode' - bl_label = 'Spherize' - bl_icon = 'MOD_DISPLACE' - - origin_modes = [ - ('AVERAGE', 'Average', 'Texture displacement along Vertex Normal', '', 1), - ('EXTERNAL', 'External', 'Texture displacement along X axis', '', 2), - ] - - size_modes = [ - ('AVERAGE', 'Average', 'Input UV coordinates to evaluate texture', '', 1), - ('EXTERNAL', 'Defined', 'Matrix to apply to verts before evaluating texture', '', 2), - ] - @throttled - def handle_size_socket(self, context): - input = self.inputs['Size'] - if self.size_mode == 'AVERAGE': - if not input.hide_safe: - input.hide_safe = True - else: - if input.hide_safe: - input.hide_safe = False - - @throttled - def handle_origin_socket(self, context): - input = self.inputs['Origin'] - if self.origin_mode == 'AVERAGE': - if not input.hide_safe: - input.hide_safe = True - else: - if input.hide_safe: - input.hide_safe = False - - use_x: BoolProperty( - name="X", description="smooth vertices along X axis", - default=True, update=updateNode) - - use_y: BoolProperty( - name="Y", description="smooth vertices along Y axis", - default=True, update=updateNode) - - use_z: BoolProperty( - name="Z", description="smooth vertices along Z axis", - default=True, update=updateNode) - - origin_mode: EnumProperty( - name='Direction', - items=origin_modes, - default='AVERAGE', - description='Apply Mode', - update=handle_origin_socket) - - size_mode: EnumProperty( - name='Size_mode', - items=size_modes, - default='AVERAGE', - description="Mapping method", - update=handle_size_socket) - - origin: FloatVectorProperty( - name='Origin', description='Scale of the added vector', - size=3, default=(1, 1, 1), - update=updateNode) - - scale_out_v: FloatVectorProperty( - name='Axis Scale Out', description='Scale of the added vector', - size=3, default=(1, 1, 1), - update=updateNode) - - sphere_axis_scale: FloatVectorProperty( - name='Sphere Scale', description='Scale base sphere', - size=3, default=(1, 1, 1), - update=updateNode) - - strength: FloatProperty( - name='Strength', description='Stength of the effect', - default=1.0, update=updateNode) - - size: FloatProperty( - name='Size', description='Size the sphere', - default=1.0, update=updateNode) - - list_match: EnumProperty( - name="List Match", - description="Behavior on different list lengths", - items=numpy_list_match_modes, default="REPEAT", - update=updateNode) - - def sv_init(self, context): - - self.inputs.new('SvVerticesSocket', 'Vertices') - self.inputs.new('SvVerticesSocket', 'Sphere Scale').prop_name = 'sphere_axis_scale' - self.inputs.new('SvVerticesSocket', 'Axis Scale Out').prop_name = 'scale_out_v' - self.inputs.new('SvVerticesSocket', 'Origin').prop_name = 'origin' - self.inputs.new('SvStringsSocket', 'Size').prop_name = 'size' - self.inputs.new('SvStringsSocket', 'Strength').prop_name = 'strength' - self.inputs['Origin'].hide_safe = True - self.inputs['Size'].hide_safe = True - - self.outputs.new('SvVerticesSocket', 'Vertices') - - - def draw_buttons(self, context, layout): - layout.prop(self, 'origin_mode', expand=False, text='Origin') - layout.prop(self, 'size_mode', expand=False, text='Size') - - def draw_buttons_ext(self, context, layout): - '''draw buttons on the N-panel''' - self.draw_buttons(context, layout) - layout.prop(self, 'list_match', expand=False) - - def rclick_menu(self, context, layout): - layout.prop_menu_enum(self, "list_match", text="List Match") - - def process(self): - inputs, outputs = self.inputs, self.outputs - - if not outputs[0].is_linked: - return - - result = [] - - params = [si.sv_get(default=[[]], deepcopy=False) for si in inputs] - - - matching_f = list_match_func[self.list_match] - desired_levels = [3, 3, 3, 3, 2, 2] - ops = [self.origin_mode, self.list_match, self.size_mode] - - result = recurse_f_level_control(params, ops, meshes_texture_diplace, matching_f, desired_levels) - - self.outputs[0].sv_set(result) - -classes = [SvSpherizeNode] -register, unregister = bpy.utils.register_classes_factory(classes) -- GitLab From 30fb704830ee7fab8fb9c19e0e3c1e158c7da697 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Thu, 30 Jan 2020 12:17:37 +0100 Subject: [PATCH 3/4] added docs to index --- docs/nodes/transforms/transforms_index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/nodes/transforms/transforms_index.rst b/docs/nodes/transforms/transforms_index.rst index d05350dcb..8870810ca 100644 --- a/docs/nodes/transforms/transforms_index.rst +++ b/docs/nodes/transforms/transforms_index.rst @@ -12,6 +12,7 @@ Transforms barycentric_transform bend_along_path bend_along_surface + cast deform mirror_mk2 symmetrize -- GitLab From f5d702baefcc3171b9a5955387766567095a53c2 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Fri, 31 Jan 2020 17:50:23 +0100 Subject: [PATCH 4/4] corrected bl_icon --- nodes/transforms/cast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes/transforms/cast.py b/nodes/transforms/cast.py index 9adf3fe82..4893fe562 100644 --- a/nodes/transforms/cast.py +++ b/nodes/transforms/cast.py @@ -125,7 +125,7 @@ class SvCastNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCastNode' bl_label = 'Cast' - bl_icon = 'MOD_DISPLACE' + bl_icon = 'MOD_CAST' origin_modes = [ ('AVERAGE', 'Average', 'Texture displacement along Vertex Normal', '', 1), -- GitLab