def mean_weighted_unequally(np_faces, v_pols, v_normals, non_planar, factor_mode): pol_sides = v_pols.shape[1] factor_func = VERTEX_NORMAL_FACTOR_METHODS[factor_mode] if non_planar: face_factor = np.zeros((len(v_pols), pol_sides), dtype=np.float64) f_normals = np.zeros((len(v_pols), 3), dtype=np.float64) for i in range(pol_sides - 1): side1 = v_pols[::, (1 + i) % pol_sides] - v_pols[::, i] side2 = v_pols[::, (i - 1) % pol_sides] - v_pols[::, i] cross = np.cross(side1, side2) face_factor[:, i] = factor_func(cross, side1, side2) f_normals += cross else: side1 = v_pols[::, 1] - v_pols[::, 0] side2 = v_pols[::, 2] - v_pols[::, 0] f_normals = np.cross(side1, side2) face_factor = factor_func(f_normals, side1, side2) if factor_mode in AREA_DEPENDENT_FACTORS: np_normalize_vectors(f_normals) for i in range(np_faces.shape[1]): np.add.at(v_normals, np_faces[:, i], f_normals * face_factor[:, i, np.newaxis]) if not factor_mode in AREA_DEPENDENT_FACTORS: np_normalize_vectors(f_normals) return f_normals, v_normals
def np_vertex_normals(vertices, faces, algorithm='MWE', output_numpy=False): if isinstance(vertices, np.ndarray): np_verts = vertices else: np_verts = np.array(vertices) if isinstance(faces, np.ndarray): np_faces = faces else: np_faces = np.array(faces) v_normals = np.zeros(np_verts.shape, dtype=np_verts.dtype) if np_faces.dtype == object: np_len = np.vectorize(len) lens = np_len(np_faces) pol_types = np.unique(lens) for pol_sides in pol_types: mask = lens == pol_sides np_faces_g = np.array(np_faces[mask].tolist()) v_pols = np_verts[np_faces_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] add_faces_normals(v_pols, np_faces, algorithm, pol_sides, v_normals) if output_numpy: return np_normalize_vectors(v_normals) return np_normalize_vectors(v_normals).tolist()
def np_faces_normals(v_pols): pol_sides = v_pols.shape[1] if pol_sides > 3: f_normals = np.zeros((len(v_pols), 3), dtype=np.float64) for i in range(pol_sides - 2): f_normals += np.cross(v_pols[::, (1+i)%pol_sides] - v_pols[::, 0], v_pols[::, (2+i)%pol_sides] - v_pols[::, 0]) else: f_normals = np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, 2] - v_pols[::, 0]) np_normalize_vectors(f_normals) return f_normals
def edges_direction(vertices, edges, out_numpy=False): ''' calculate edges direction vertices: list as [vertex, vertex, ...], being each vertex [float, float, float]. Also accepts numpy arrays with two axis edges: list as [edge, edge,..], being each edge [int, int]. Also accept numpy arrays with one axis. out_numpy: boolean to determine if outputtig np_array or regular python list returns edges direction as [vertex, vertex,...] or numpy array with two axis ''' v_edges = get_v_edges(vertices, edges) if out_numpy: return np_normalize_vectors(v_edges[:, 1, :] - v_edges[:, 0, :]) return np_normalize_vectors(v_edges[:, 1, :] - v_edges[:, 0, :]).tolist()
def calc_mesh_normals_np(vertices, faces, get_f_normals=True, get_v_normals=True, non_planar=True, v_normal_alg='MWE', output_numpy=True): if not has_element(faces): return [], vertices np_verts, np_faces = prepare_arrays(vertices, faces) if get_v_normals: v_normals = np.zeros(np_verts.shape, dtype=np_verts.dtype) if v_normal_alg in ('MWE', 'MWAT'): norm_func = mean_weighted_equally else: norm_func = mean_weighted_unequally if np_faces.dtype == object: np_len = np.vectorize(len) lens = np_len(np_faces) pol_types = np.unique(lens) f_normals = np.zeros((len(np_faces), 3), dtype=np.float64) for pol_sides in pol_types: mask = lens == pol_sides np_faces_g = np.array(np_faces[mask].tolist()) v_pols = np_verts[np_faces_g] if get_v_normals: f_normal_g, v_normals = norm_func(np_faces_g, v_pols, v_normals, non_planar, v_normal_alg) else: f_normals = np_faces_normals(v_pols) f_normals[mask, :] = f_normal_g else: pol_sides = np_faces.shape[1] v_pols = np_verts[np_faces] if get_v_normals: f_normals, v_normals = norm_func(np_faces, v_pols, v_normals, non_planar, v_normal_alg) else: f_normals = np_faces_normals(v_pols) if output_numpy: return (f_normals if get_f_normals else [], np_normalize_vectors(v_normals) if get_v_normals else []) return (f_normals.tolist() if get_f_normals else [], np_normalize_vectors(v_normals).tolist() if get_v_normals else [])
def mean_weighted_equally(np_faces, v_pols, v_normals, non_planar, algorithm): pol_sides = v_pols.shape[1] if non_planar: f_normals = np.zeros((len(v_pols), 3), dtype=np.float64) for i in range(pol_sides - 1): f_normals += np.cross(v_pols[::, (1+i)%pol_sides] - v_pols[::, i], v_pols[::, (i-1)%pol_sides] - v_pols[::, i]) else: f_normals = np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, 2] - v_pols[::, 0]) if algorithm == 'MWE': np_normalize_vectors(f_normals) for i in range(np_faces.shape[1]): np.add.at(v_normals, np_faces[:, i], f_normals) if algorithm == 'MWAT': np_normalize_vectors(f_normals) return f_normals, v_normals
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_tangent_center_orig(v_pols): return np_normalize_vectors(np_center_median(v_pols) - v_pols[:, 0, :])
def np_tangent_longest_edge(v_pols): edges_dir = v_pols - np.roll(v_pols, 1, axis=1) ed_length = np.linalg.norm(edges_dir, axis=2) ed_idx = np.argmax(ed_length, axis=1) return np_normalize_vectors(edges_dir[np.arange(len(v_pols)), ed_idx, :])