Skip to content

Commit

Permalink
Component Analyzer Vertex normal modes (#4039)
Browse files Browse the repository at this point in the history
* vertex normals modes

* docs update

* referal to article
  • Loading branch information
vicdoval authored Apr 11, 2021
1 parent 97b32c9 commit 4572937
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 15 deletions.
4 changes: 3 additions & 1 deletion docs/nodes/analyzer/component_analyzer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ This node has the following parameters:

* For **Vertices** supported criteria are:

* **Normal**. Vertices normal vector.
* **Normal**. Vertices normal vector. Offers different calculation methods:
Bmesh (standard Blender),Mean Weighted Equally (Faster), Mean Weighted Based on Triangle Area
Mean Weighted Edge Length Reciprocal and Mean Weighted by Sine
* **Matrix ZY**: Matrix aligned with normal.
* **Sharpness**: Curvature of mesh in vertex.
* **Adjacent Edges**: Adjacent edges.
Expand Down
16 changes: 15 additions & 1 deletion nodes/analyzer/component_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
'n': ('matrix_normal', 'e'),
't': ('tangent_mode', 'e'),
'a': ('output_numpy', 'b'),
'v': ('vertex_normal_mode', 'e'),
}


Expand Down Expand Up @@ -99,7 +100,13 @@ class SvComponentAnalyzerNode(bpy.types.Node, SverchCustomTreeNode, SvRecursiveN
("X", "X", "Aligned with X", 0),
("Z", "Z", "Aligned with Z", 2),
]

vertex_normal_modes = [
('BMESH', 'Bmesh', 'Slower (Legacy)', 0),
('MWE', 'Mean Weighted Equally', 'Faster', 1),
('MWELR','Mean Weighted Edge Length Reciprocal', '', 2),
('MWAT', 'Mean Weighted Area Triangle', '', 3),
('MWS', 'Mean Weighted by Sine', '', 4)
]

@throttle_and_update_node
def update_mode(self, context):
Expand Down Expand Up @@ -194,6 +201,12 @@ def update_mode(self, context):
default="Median_Center",
update=update_mode)

vertex_normal_mode: EnumProperty(
name="Method",
items=vertex_normal_modes,
default="MWE",
update=update_mode)

matrix_track: EnumProperty(
name="Normal",
items=matrix_track_modes,
Expand Down Expand Up @@ -362,6 +375,7 @@ def process_data(self, params):
'u': self.matrix_normal_up,
'n': self.matrix_normal,
't': self.tangent_mode,
'v': self.vertex_normal_mode,
'a': self.output_numpy
}
special_op = []
Expand Down
41 changes: 28 additions & 13 deletions utils/modules/vertex_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@ def adjacent_edg_pol_idx(verts, edgs_pols):
faces: list as [polygon, polygon,..], being each polygon [int, int, ...].
returns value of each vertex as [value, value,...]
'''
def vertex_normal(vertices, edges, faces, output_numpy=True):
if len(faces) == 0:
bm = bmesh_from_pydata(vertices, edges, faces, normal_update=True)
def vertex_normal(vertices, faces, algorithm='MWE', output_numpy=True):
if len(faces) == 0 or algorithm == 'BMESH':
bm = bmesh_from_pydata(vertices, [], faces, normal_update=True)
vals = [tuple(v.normal) for v in bm.verts]
bm.free()
return vals

return np_vertex_normals(vertices, faces, output_numpy=output_numpy)
return np_vertex_normals(vertices, faces, algorithm, output_numpy=output_numpy)

def np_faces_normals(v_pols):
pol_sides = v_pols.shape[1]
Expand All @@ -108,8 +108,27 @@ def np_faces_normals(v_pols):
np_normalize_vectors(f_normals)

return f_normals
# based in this article https://www.researchgate.net/publication/220067106_A_comparison_of_algorithms_for_vertex_normal_computation
def add_faces_normals(v_pols, np_faces_g, algorithm, pol_sides, v_normals):
if algorithm in ('MWE', 'MWAT'):
if algorithm == 'MWE': #weighted equally
f_normal_g = np_normalize_vectors(np_faces_normals(v_pols))
else: #weighted by area of triangle
f_normal_g = np_faces_normals(v_pols)
for i in range(pol_sides):
np.add.at(v_normals, np_faces_g[:, i], f_normal_g)
else:
if algorithm == 'MWELR': #weighted edge length reciprocal
f_normal_g = np_normalize_vectors(np_faces_normals(v_pols))
else: # algorithm == 'MWS': #weighted by sine
f_normal_g = np_faces_normals(v_pols)
edges_length = np.linalg.norm(v_pols - np.roll(v_pols, 1, axis=1), axis=2)
factor = edges_length * np.roll(edges_length, -1, axis=1)

for i in range(pol_sides):
np.add.at(v_normals, np_faces_g[:, i], f_normal_g * factor[:, i, np.newaxis])

def np_vertex_normals(vertices, faces, output_numpy=False):
def np_vertex_normals(vertices, faces, algorithm='MWE', output_numpy=False):

if isinstance(vertices, np.ndarray):
np_verts = vertices
Expand All @@ -131,17 +150,13 @@ def np_vertex_normals(vertices, faces, output_numpy=False):
mask = lens == pol_sides
np_faces_g = np.array(np_faces[mask].tolist())
v_pols = np_verts[np_faces_g]
f_normal_g = np_normalize_vectors(np_faces_normals(v_pols))

for i in range(pol_sides):
np.add.at(v_normals, np_faces_g[:, i], f_normal_g)
add_faces_normals(v_pols, np_faces_g, algorithm, pol_sides, v_normals)

else:
pol_sides = np_faces.shape[1]
v_pols = np_verts[np_faces]
f_normal_g = np_normalize_vectors(np_faces_normals(v_pols))
for i in range(pol_sides):
np.add.at(v_normals, np_faces[:, i], f_normal_g)
add_faces_normals(v_pols, np_faces, algorithm, pol_sides, v_normals)


if output_numpy:
return np_normalize_vectors(v_normals)
Expand Down Expand Up @@ -205,7 +220,7 @@ def vertex_matrix(vertices, edges, faces, track, up):

# Name: (index, input_sockets, func_options, output_options, function, output_sockets, output_sockets_names, description)
vertex_modes_dict = {
'Normal': (0, 'vep', 'a', '', vertex_normal, 'v', 'Normal', 'Vertex normal'),
'Normal': (0, 'vp', 'va', '', vertex_normal, 'v', 'Normal', 'Vertex normal'),
'Matrix': (10, 'vep', 'mu', 'u', vertex_matrix, 'm', 'Matrix', 'Matrix aligned with normal'),
'Sharpness': (20, 'vep', '', '', vertex_shell_factor, 's', 'Sharpness', 'Curvature of mesh in vertex'),
'Adjacent edges': (30, 've', '', 'u', adjacent_edg_pol, 's', 'Edges', 'Adjacent edges'),
Expand Down

0 comments on commit 4572937

Please sign in to comment.