Open Durman opened 2 years ago
untested..
import numpy as np
from sverchok.utils.modules.polygon_utils import np_process_polygons, np_faces_normals
def flip_to_match_1st_np(geom, reverse):
"""
this mode expects all faces to be coplanar, else you need to manually generate a flip mask.
"""
verts, edges, faces = geom
normals = np_process_polygons(verts, faces, func=np_faces_normals, output_numpy=True) # already normalized
direction = normals[0]
matched = np.linalg.norm((normals - direction), axis=1) < 0.004
flips = np.invert(matched) if reverse else matched
b_faces = [poly if flip else poly[::-1] for flip, poly in zip(flips, faces)]
return verts, edges, b_faces
tested.. works for ngons, i'll assume tris and quads have been tested by the mere fact that inset specials has been used for 14 months now.
It gives me next warning
2022-07-29 08:39:19,258 [WARNING] py.warnings: \sverchok\utils\modules\polygon_utils.py:292: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray. np_faces = np.array(faces)
Also it improves performance only 2 times. I guess it should be better without converting data into bmesh.
Most of the time (or about a half) it spends in converting faces into numpy array now. =)
According to the description the node (in this mode) expects coplanar entire mesh. So why not to calculate n-gone normals by any of their 3 vertices. But if we want to save the previous behavior the normals should be calculated in the same way as in bmesh module.
This implementation can handle n-gons without warnings but it calculates normals using only 3 first points. It fits into logic description of the node but potentially can effect some layouts. Also there is no significant performance improvement compare to the previous solution. Most of the time it spends on converting first 3 indexes of faces into a numpy array and applying the mask to Python face list.
If we want significant performance improvements the format of faces should be changed somehow.
from sverchok.utils.math import np_normalize_vectors
def flip_to_match_1st_np(geom, reverse):
"""
this mode expects all faces to be coplanar, else you need to manually generate a flip mask.
"""
verts, edges, faces = geom
normals = face_normals_np(verts, faces)
direction = normals[0]
flips = np.isclose(np.dot(normals, direction), 1, atol=0.004)
if reverse:
flips = ~flips
b_faces = [poly if flip else poly[::-1] for flip, poly in zip(flips, faces)]
return verts, edges, b_faces
def face_normals_np(verts, faces):
def first_3_indexes():
for f in faces:
for i in f[:3]:
yield i
faces = np.fromiter(first_3_indexes(), dtype=int) # 2x faster than from list
faces.shape = (-1, 3)
v1 = verts[faces[:, 0]]
v2 = verts[faces[:, 1]]
v3 = verts[faces[:, 2]]
d1 = v1 - v2
d2 = v3 - v2
normals = np.cross(d1, d2)
np_normalize_vectors(normals)
return normals
(in this mode) expects coplanar entire mesh. So why not to calculate n-gone normals by any of their 3 vertices.
yes, seems sensible.
but, not sure that returns the desired results for mesh input that has concave polygons however, i suppose
def first_3_indexes():
for f in faces:
for i in f[:3]:
yield i
could easily return them in reverse if the first 3 indices form a concave section.
Completely forgot about this case. Also there is corner case when all 3 vertices are on one line. In this case the normal will be (0,0,0)
.
Problem statement
It looks there is no need to convert input data into bmesh because it's quite expensive.
https://github.com/nortikin/sverchok/blob/ff53b66cc349b74b0db67139e92947d0d0f51bd6/nodes/modifier_change/flip_normals.py#L44-L60