Module sverchok.utils.sv_mesh_utils
Expand source code
# ##### 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 itertools import chain
import numpy as np
from numpy.linalg import norm as np_margnitude
from sverchok.data_structure import invert_index_list, has_element
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata
from sverchok.utils.math import np_normalize_vectors
from sverchok.utils.modules.polygon_utils import np_faces_normals
from mathutils import Vector
def mesh_join(vertices_s, edges_s, faces_s):
'''Given list of meshes represented by lists of vertices, edges and faces,
produce one joined mesh.'''
offset = 0
result_vertices = []
result_edges = []
result_faces = []
if len(edges_s) == 0:
edges_s = [[]] * len(faces_s)
for vertices, edges, faces in zip(vertices_s, edges_s, faces_s):
result_vertices.extend(vertices)
new_edges = [tuple(i + offset for i in edge) for edge in edges]
new_faces = [[i + offset for i in face] for face in faces]
result_edges.extend(new_edges)
result_faces.extend(new_faces)
offset += len(vertices)
return result_vertices, result_edges, result_faces
def polygons_to_edges(obj, unique_edges=False):
out = []
for faces in obj:
out_edges = []
seen = set()
for face in faces:
for edge in zip(face, list(face[1:]) + list([face[0]])):
if unique_edges and tuple(sorted(edge)) in seen:
continue
if unique_edges:
seen.add(tuple(sorted(edge)))
out_edges.append(edge)
out.append(out_edges)
return out
def pols_to_edges_irregular_mesh(pols, unique_edges):
np_pols = np.array(pols)
np_len = np.vectorize(len)
lens = np_len(np_pols)
pol_types = np.unique(lens)
edges = []
for sides_number in pol_types:
mask = lens == sides_number
np_pols_g = np.array(np_pols[mask].tolist())
edges_g = np.empty(list(np_pols_g.shape)+[2], 'i')
edges_g[:, :, 0] = np_pols_g
edges_g[:, 1:, 1] = np_pols_g[:, :-1]
edges_g[:, 0, 1] = np_pols_g[:, -1]
edges_g = edges_g.reshape(-1, 2)
edges.append(edges_g)
if unique_edges:
return np.unique(np.sort(np.concatenate([eds for eds in edges])), axis=0)
return np.concatenate([eds for eds in edges])
def polygons_to_edges_np(obj, unique_edges=False, output_numpy=False):
result = []
for pols in obj:
if len(pols) == 0:
result.append([])
continue
regular_mesh = True
try:
np_pols = np.array(pols, dtype=np.int32)
except ValueError:
regular_mesh = False
if not regular_mesh:
if output_numpy:
result.append(pols_to_edges_irregular_mesh(pols, unique_edges))
else:
result.append(pols_to_edges_irregular_mesh(pols, unique_edges).tolist())
else:
edges = np.empty(list(np_pols.shape)+[2], 'i')
edges[:, :, 0] = np_pols
edges[:, 1:, 1] = np_pols[:, :-1]
edges[:, 0, 1] = np_pols[:, -1]
edges = edges.reshape(-1, 2)
if output_numpy:
if unique_edges:
result.append(np.unique(np.sort(edges), axis=0))
else:
result.append(edges)
else:
if unique_edges:
result.append(np.unique(np.sort(edges), axis=0).tolist())
else:
result.append(edges.tolist())
return result
def mask_vertices(verts, edges, faces, verts_mask):
if any(not m for m in verts_mask):
vert_indexes = [i for i, m in enumerate(verts_mask) if m]
index_set = set(vert_indexes)
vert_dict = {vert_idx: i for i, vert_idx in enumerate(vert_indexes)}
new_verts = [verts[i] for i in vert_indexes]
new_edges = [[vert_dict[n] for n in edge]
for edge in edges if index_set.issuperset(edge)]
new_faces = [[vert_dict[n] for n in face]
for face in faces if index_set.issuperset(face)]
return new_verts, new_edges, new_faces
return verts, edges, faces
# mesh cleaning functions
def get_unique_faces(faces):
return get_unique_topology(faces)[0]
def get_unique_topology(edg_pol):
'''
Removes doubled items
edg_pol: list of edges or polygons List[List[Int]]
returns Tuple (List of unique items, Boolean List with preserved items marked as True)
'''
uniq_edg_pols = []
unique_sets = []
preseved_mask = []
for e_p in edg_pol:
e_p_set = set(e_p)
if not e_p_set in unique_sets:
uniq_edg_pols.append(e_p)
unique_sets.append(e_p_set)
preseved_mask.append(True)
else:
preseved_mask.append(False)
return uniq_edg_pols, preseved_mask
def remove_unreferenced_verts(verts, edges, faces):
'''
Removes unreferenced vertices
verts: list of vertices List[List[float]]
edges: list of edges List[List[Int]]
faces: list of polygons List[List[Int]]
returns Tuple (List used verts,
List with updated edges,
List with updated polyogn
List with removed items marked as True)
'''
e_indx = set(chain.from_iterable(edges))
f_indx = set(chain.from_iterable(faces))
indx = set.union(e_indx, f_indx)
verts_out = [v for i, v in enumerate(verts) if i in indx]
v_index = {j: i for i, j in enumerate(sorted(indx))}
edges_out = [list(map(lambda n: v_index[n], e)) for e in edges]
faces_out = [list(map(lambda n: v_index[n], f)) for f in faces]
return verts_out, edges_out, faces_out, list(set(range(len(verts)))-indx)
def remove_unreferenced_topology(edge_pol, verts_length):
'''
Removes elements that point to unexisitng vertices
edg_pol: list of edges or polygons - List[List[Int]]
returns Tuple (referenced items - List[List[Int]],
Boolean List with preserved items marked as True - List[bool])
'''
edge_pol_out = []
preseved_mask = []
for ep in edge_pol:
if all([c < verts_length for c in ep]):
edge_pol_out.append(ep)
preseved_mask.append(True)
else:
preseved_mask.append(False)
return edge_pol_out, preseved_mask
def non_coincident_edges(edges):
'''
Removes edges with repeated indices
edges: list of edges - List[List[Int]] or Numpy array with shape (n,2)
returns Tuple (valid edges - List[List[Int]] or similar Numpy array,
Boolean List with preserved items marked as True - List[bool] or similar numpy array)
'''
if isinstance(edges, np.ndarray):
preseved_mask = edges[:, 0] != edges[:, 1]
edges_out = edges[preseved_mask]
else:
edges_out = []
preseved_mask = []
for e in edges:
if e[0] == e[1]:
preseved_mask.append(False)
else:
edges_out.append(e)
preseved_mask.append(True)
return edges_out, preseved_mask
def non_redundant_faces_indices(faces):
'''
Removes repeated indices from faces and removes faces with less than three indices
faces: list of faces - List[List[Int]]
returns Tuple (valid faces - List[List[Int]],
Boolean List with preserved items marked as True - List[bool])
'''
faces_out = []
preseved_mask = []
for f in faces:
new_face = []
for idx, c in enumerate(f):
if c != f[idx-1]:
new_face.append(c)
if len(new_face) > 2:
faces_out.append(new_face)
preseved_mask.append(True)
else:
preseved_mask.append(False)
return faces_out, preseved_mask
def clean_meshes(vertices, edges, faces,
remove_unreferenced_edges=False,
remove_unreferenced_faces=False,
remove_duplicated_edges=False,
remove_duplicated_faces=False,
remove_degenerated_edges=False,
remove_degenerated_faces=False,
remove_loose_verts=False,
calc_verts_idx=False,
calc_edges_idx=False,
calc_faces_idx=False):
'''
Cleans a group of meshes using different routines.
Returns cleaned meshes and removed items indexes
'''
verts_out, edges_out, faces_out = [], [], []
verts_removed_out, edges_removed_out, faces_removed_out = [], [], []
for verts_original, edges_original, faces_original in zip(vertices, edges, faces):
verts_changed, edges_changed, faces_changed = False, False, False
preserved_edges_idx = []
preserved_faces_idx = []
if remove_unreferenced_edges:
edges, preserved_edges_mask = remove_unreferenced_topology(edges_original, len(verts_original))
preserved_edges_idx = np.arange(len(edges_original))[preserved_edges_mask]
edges_changed = True
if remove_unreferenced_faces:
faces, preserved_faces_mask = remove_unreferenced_topology(faces_original, len(verts_original))
preserved_faces_idx = np.arange(len(faces_original))[preserved_faces_mask]
faces_changed = True
if remove_duplicated_edges:
if edges_changed:
edges, unique_edges_mask = get_unique_topology(edges)
preserved_edges_idx = preserved_edges_idx[unique_edges_mask]
else:
edges, unique_edges_mask = get_unique_topology(edges_original)
preserved_edges_idx = np.arange(len(edges_original))[unique_edges_mask]
edges_changed = True
if remove_duplicated_faces:
if faces_changed:
faces, unique_faces_mask = get_unique_topology(faces)
preserved_faces_idx = preserved_faces_idx[unique_faces_mask]
else:
faces, unique_faces_mask = get_unique_topology(faces_original)
preserved_faces_idx = np.arange(len(faces_original))[unique_faces_mask]
faces_changed = True
if remove_degenerated_edges:
if edges_changed:
edges, non_coincident_mask = non_coincident_edges(edges)
preserved_edges_idx = preserved_edges_idx[non_coincident_mask]
else:
edges, non_coincident_mask = non_coincident_edges(edges_original)
preserved_edges_idx = np.arange(len(edges_original))[non_coincident_mask]
edges_changed = True
if remove_degenerated_faces:
if faces_changed:
faces, non_redundant_mask = non_redundant_faces_indices(faces)
preserved_faces_idx = preserved_faces_idx[non_redundant_mask]
else:
faces, non_redundant_mask = non_redundant_faces_indices(faces_original)
preserved_faces_idx = np.arange(len(faces_original))[non_redundant_mask]
faces_changed = True
if remove_loose_verts:
verts, edges, faces, removed_verts_idx = remove_unreferenced_verts(
verts_original,
edges if edges_changed else edges_original,
faces if faces_changed else faces_original)
verts_changed = True
edges_changed = True
faces_changed = True
if verts_changed:
verts_out.append(verts)
if calc_verts_idx:
verts_removed_out.append(removed_verts_idx)
else:
verts_removed_out.append([])
else:
verts_out.append(verts_original)
verts_removed_out.append([])
if edges_changed:
edges_out.append(edges)
if calc_edges_idx and len(preserved_edges_idx) > 0:
edges_removed_out.append(invert_index_list(preserved_edges_idx, len(edges_original)).tolist())
else:
edges_removed_out.append([])
else:
edges_out.append(edges_original)
edges_removed_out.append([])
if faces_changed:
faces_out.append(faces)
if calc_faces_idx and len(preserved_faces_idx) > 0:
faces_removed_out.append(invert_index_list(preserved_faces_idx, len(faces_original)).tolist())
else:
faces_removed_out.append([])
else:
faces_out.append(faces_original)
faces_removed_out.append([])
return verts_out, edges_out, faces_out, verts_removed_out, edges_removed_out, faces_removed_out
def non_redundant_faces_indices_np(faces):
F = np.array(faces)
M = np.sort(F, axis=-1, order=None)
# from https://stackoverflow.com/a/16973510/1243487
K = np.ascontiguousarray(M).view(np.dtype((np.void, M.dtype.itemsize * M.shape[1])))
_, idx = np.unique(K, return_index=True)
return F[idx]
def point_inside_mesh(bvh, point):
point = Vector(point)
axis = Vector((1, 0, 0))
outside = False
count = 0
while True:
location, normal, index, distance = bvh.ray_cast(point, axis)
if index is None:
break
count += 1
point = location + axis * 0.00001
if count % 2 == 0:
outside = True
return not outside
################
# Mesh Normals #
################
def prepare_arrays(vertices, faces):
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)
return np_verts, np_faces
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 factor_mw_angle(cross, side1, side2):
return np.arcsin(np.clip(np_margnitude(cross, axis=1)/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)),-1,1))
def factor_mw_angle_area(cross, side1, side2):
area = np_margnitude(cross, axis=1)
return area*np.arcsin(np.clip(area/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)),-1,1))
def factor_mw_sine(cross, side1, side2):
return 1/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_sine_area(cross, side1, side2):
return np_margnitude(cross, axis=1)/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_sine_ed_length_r(cross, side1, side2):
return 1/((np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)**2))
def factor_mw_ed_length_r(cross, side1, side2):
return 1/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_ed_length(cross, side1, side2):
return (np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_root_ed_length_r(cross, side1, side2):
return 1/((np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))**0.5)
def factor_mw_area(cross, side1, side2):
return np_margnitude(cross, axis=1)
VERTEX_NORMAL_FACTOR_METHODS ={
'MWA': factor_mw_angle,
'MWS': factor_mw_sine,
'MWSELR': factor_mw_sine_ed_length_r,
'MWAT': factor_mw_area,
'MWAAT': factor_mw_angle_area,
'MWSAT': factor_mw_sine_area,
'MWEL':factor_mw_ed_length,
'MWELR':factor_mw_ed_length_r,
'MWRELR': factor_mw_root_ed_length_r,
}
AREA_DEPENDENT_FACTORS = ('MWAT', 'MWS', 'MWSELR')
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 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_normal_g = 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 calc_mesh_normals_bmesh(vertices, faces, get_f_normals=True, get_v_normals=True):
bm = bmesh_from_pydata(vertices, [], faces, normal_update=True)
vertex_normals = []
face_normals = []
if get_v_normals:
for vertex in bm.verts:
vertex_normals.append(tuple(vertex.normal))
if get_f_normals:
for face in bm.faces:
face_normals.append(tuple(face.normal.normalized()))
bm.free()
return face_normals, vertex_normals
Functions
def calc_mesh_normals_bmesh(vertices, faces, get_f_normals=True, get_v_normals=True)
-
Expand source code
def calc_mesh_normals_bmesh(vertices, faces, get_f_normals=True, get_v_normals=True): bm = bmesh_from_pydata(vertices, [], faces, normal_update=True) vertex_normals = [] face_normals = [] if get_v_normals: for vertex in bm.verts: vertex_normals.append(tuple(vertex.normal)) if get_f_normals: for face in bm.faces: face_normals.append(tuple(face.normal.normalized())) bm.free() return face_normals, vertex_normals
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)
-
Expand source code
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_normal_g = 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 clean_meshes(vertices, edges, faces, remove_unreferenced_edges=False, remove_unreferenced_faces=False, remove_duplicated_edges=False, remove_duplicated_faces=False, remove_degenerated_edges=False, remove_degenerated_faces=False, remove_loose_verts=False, calc_verts_idx=False, calc_edges_idx=False, calc_faces_idx=False)
-
Cleans a group of meshes using different routines. Returns cleaned meshes and removed items indexes
Expand source code
def clean_meshes(vertices, edges, faces, remove_unreferenced_edges=False, remove_unreferenced_faces=False, remove_duplicated_edges=False, remove_duplicated_faces=False, remove_degenerated_edges=False, remove_degenerated_faces=False, remove_loose_verts=False, calc_verts_idx=False, calc_edges_idx=False, calc_faces_idx=False): ''' Cleans a group of meshes using different routines. Returns cleaned meshes and removed items indexes ''' verts_out, edges_out, faces_out = [], [], [] verts_removed_out, edges_removed_out, faces_removed_out = [], [], [] for verts_original, edges_original, faces_original in zip(vertices, edges, faces): verts_changed, edges_changed, faces_changed = False, False, False preserved_edges_idx = [] preserved_faces_idx = [] if remove_unreferenced_edges: edges, preserved_edges_mask = remove_unreferenced_topology(edges_original, len(verts_original)) preserved_edges_idx = np.arange(len(edges_original))[preserved_edges_mask] edges_changed = True if remove_unreferenced_faces: faces, preserved_faces_mask = remove_unreferenced_topology(faces_original, len(verts_original)) preserved_faces_idx = np.arange(len(faces_original))[preserved_faces_mask] faces_changed = True if remove_duplicated_edges: if edges_changed: edges, unique_edges_mask = get_unique_topology(edges) preserved_edges_idx = preserved_edges_idx[unique_edges_mask] else: edges, unique_edges_mask = get_unique_topology(edges_original) preserved_edges_idx = np.arange(len(edges_original))[unique_edges_mask] edges_changed = True if remove_duplicated_faces: if faces_changed: faces, unique_faces_mask = get_unique_topology(faces) preserved_faces_idx = preserved_faces_idx[unique_faces_mask] else: faces, unique_faces_mask = get_unique_topology(faces_original) preserved_faces_idx = np.arange(len(faces_original))[unique_faces_mask] faces_changed = True if remove_degenerated_edges: if edges_changed: edges, non_coincident_mask = non_coincident_edges(edges) preserved_edges_idx = preserved_edges_idx[non_coincident_mask] else: edges, non_coincident_mask = non_coincident_edges(edges_original) preserved_edges_idx = np.arange(len(edges_original))[non_coincident_mask] edges_changed = True if remove_degenerated_faces: if faces_changed: faces, non_redundant_mask = non_redundant_faces_indices(faces) preserved_faces_idx = preserved_faces_idx[non_redundant_mask] else: faces, non_redundant_mask = non_redundant_faces_indices(faces_original) preserved_faces_idx = np.arange(len(faces_original))[non_redundant_mask] faces_changed = True if remove_loose_verts: verts, edges, faces, removed_verts_idx = remove_unreferenced_verts( verts_original, edges if edges_changed else edges_original, faces if faces_changed else faces_original) verts_changed = True edges_changed = True faces_changed = True if verts_changed: verts_out.append(verts) if calc_verts_idx: verts_removed_out.append(removed_verts_idx) else: verts_removed_out.append([]) else: verts_out.append(verts_original) verts_removed_out.append([]) if edges_changed: edges_out.append(edges) if calc_edges_idx and len(preserved_edges_idx) > 0: edges_removed_out.append(invert_index_list(preserved_edges_idx, len(edges_original)).tolist()) else: edges_removed_out.append([]) else: edges_out.append(edges_original) edges_removed_out.append([]) if faces_changed: faces_out.append(faces) if calc_faces_idx and len(preserved_faces_idx) > 0: faces_removed_out.append(invert_index_list(preserved_faces_idx, len(faces_original)).tolist()) else: faces_removed_out.append([]) else: faces_out.append(faces_original) faces_removed_out.append([]) return verts_out, edges_out, faces_out, verts_removed_out, edges_removed_out, faces_removed_out
def factor_mw_angle(cross, side1, side2)
-
Expand source code
def factor_mw_angle(cross, side1, side2): return np.arcsin(np.clip(np_margnitude(cross, axis=1)/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)),-1,1))
def factor_mw_angle_area(cross, side1, side2)
-
Expand source code
def factor_mw_angle_area(cross, side1, side2): area = np_margnitude(cross, axis=1) return area*np.arcsin(np.clip(area/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)),-1,1))
def factor_mw_area(cross, side1, side2)
-
Expand source code
def factor_mw_area(cross, side1, side2): return np_margnitude(cross, axis=1)
def factor_mw_ed_length(cross, side1, side2)
-
Expand source code
def factor_mw_ed_length(cross, side1, side2): return (np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_ed_length_r(cross, side1, side2)
-
Expand source code
def factor_mw_ed_length_r(cross, side1, side2): return 1/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_root_ed_length_r(cross, side1, side2)
-
Expand source code
def factor_mw_root_ed_length_r(cross, side1, side2): return 1/((np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))**0.5)
def factor_mw_sine(cross, side1, side2)
-
Expand source code
def factor_mw_sine(cross, side1, side2): return 1/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_sine_area(cross, side1, side2)
-
Expand source code
def factor_mw_sine_area(cross, side1, side2): return np_margnitude(cross, axis=1)/(np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1))
def factor_mw_sine_ed_length_r(cross, side1, side2)
-
Expand source code
def factor_mw_sine_ed_length_r(cross, side1, side2): return 1/((np_margnitude(side1, axis=1)*np_margnitude(side2, axis=1)**2))
def get_unique_faces(faces)
-
Expand source code
def get_unique_faces(faces): return get_unique_topology(faces)[0]
def get_unique_topology(edg_pol)
-
Removes doubled items edg_pol: list of edges or polygons List[List[Int]] returns Tuple (List of unique items, Boolean List with preserved items marked as True)
Expand source code
def get_unique_topology(edg_pol): ''' Removes doubled items edg_pol: list of edges or polygons List[List[Int]] returns Tuple (List of unique items, Boolean List with preserved items marked as True) ''' uniq_edg_pols = [] unique_sets = [] preseved_mask = [] for e_p in edg_pol: e_p_set = set(e_p) if not e_p_set in unique_sets: uniq_edg_pols.append(e_p) unique_sets.append(e_p_set) preseved_mask.append(True) else: preseved_mask.append(False) return uniq_edg_pols, preseved_mask
def mask_vertices(verts, edges, faces, verts_mask)
-
Expand source code
def mask_vertices(verts, edges, faces, verts_mask): if any(not m for m in verts_mask): vert_indexes = [i for i, m in enumerate(verts_mask) if m] index_set = set(vert_indexes) vert_dict = {vert_idx: i for i, vert_idx in enumerate(vert_indexes)} new_verts = [verts[i] for i in vert_indexes] new_edges = [[vert_dict[n] for n in edge] for edge in edges if index_set.issuperset(edge)] new_faces = [[vert_dict[n] for n in face] for face in faces if index_set.issuperset(face)] return new_verts, new_edges, new_faces return verts, edges, faces
def mean_weighted_equally(np_faces, v_pols, v_normals, non_planar, algorithm)
-
Expand source code
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 mean_weighted_unequally(np_faces, v_pols, v_normals, non_planar, factor_mode)
-
Expand source code
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 mesh_join(vertices_s, edges_s, faces_s)
-
Given list of meshes represented by lists of vertices, edges and faces, produce one joined mesh.
Expand source code
def mesh_join(vertices_s, edges_s, faces_s): '''Given list of meshes represented by lists of vertices, edges and faces, produce one joined mesh.''' offset = 0 result_vertices = [] result_edges = [] result_faces = [] if len(edges_s) == 0: edges_s = [[]] * len(faces_s) for vertices, edges, faces in zip(vertices_s, edges_s, faces_s): result_vertices.extend(vertices) new_edges = [tuple(i + offset for i in edge) for edge in edges] new_faces = [[i + offset for i in face] for face in faces] result_edges.extend(new_edges) result_faces.extend(new_faces) offset += len(vertices) return result_vertices, result_edges, result_faces
def non_coincident_edges(edges)
-
Removes edges with repeated indices edges: list of edges - List[List[Int]] or Numpy array with shape (n,2) returns Tuple (valid edges - List[List[Int]] or similar Numpy array, Boolean List with preserved items marked as True - List[bool] or similar numpy array)
Expand source code
def non_coincident_edges(edges): ''' Removes edges with repeated indices edges: list of edges - List[List[Int]] or Numpy array with shape (n,2) returns Tuple (valid edges - List[List[Int]] or similar Numpy array, Boolean List with preserved items marked as True - List[bool] or similar numpy array) ''' if isinstance(edges, np.ndarray): preseved_mask = edges[:, 0] != edges[:, 1] edges_out = edges[preseved_mask] else: edges_out = [] preseved_mask = [] for e in edges: if e[0] == e[1]: preseved_mask.append(False) else: edges_out.append(e) preseved_mask.append(True) return edges_out, preseved_mask
def non_redundant_faces_indices(faces)
-
Removes repeated indices from faces and removes faces with less than three indices faces: list of faces - List[List[Int]] returns Tuple (valid faces - List[List[Int]], Boolean List with preserved items marked as True - List[bool])
Expand source code
def non_redundant_faces_indices(faces): ''' Removes repeated indices from faces and removes faces with less than three indices faces: list of faces - List[List[Int]] returns Tuple (valid faces - List[List[Int]], Boolean List with preserved items marked as True - List[bool]) ''' faces_out = [] preseved_mask = [] for f in faces: new_face = [] for idx, c in enumerate(f): if c != f[idx-1]: new_face.append(c) if len(new_face) > 2: faces_out.append(new_face) preseved_mask.append(True) else: preseved_mask.append(False) return faces_out, preseved_mask
def non_redundant_faces_indices_np(faces)
-
Expand source code
def non_redundant_faces_indices_np(faces): F = np.array(faces) M = np.sort(F, axis=-1, order=None) # from https://stackoverflow.com/a/16973510/1243487 K = np.ascontiguousarray(M).view(np.dtype((np.void, M.dtype.itemsize * M.shape[1]))) _, idx = np.unique(K, return_index=True) return F[idx]
def point_inside_mesh(bvh, point)
-
Expand source code
def point_inside_mesh(bvh, point): point = Vector(point) axis = Vector((1, 0, 0)) outside = False count = 0 while True: location, normal, index, distance = bvh.ray_cast(point, axis) if index is None: break count += 1 point = location + axis * 0.00001 if count % 2 == 0: outside = True return not outside
def pols_to_edges_irregular_mesh(pols, unique_edges)
-
Expand source code
def pols_to_edges_irregular_mesh(pols, unique_edges): np_pols = np.array(pols) np_len = np.vectorize(len) lens = np_len(np_pols) pol_types = np.unique(lens) edges = [] for sides_number in pol_types: mask = lens == sides_number np_pols_g = np.array(np_pols[mask].tolist()) edges_g = np.empty(list(np_pols_g.shape)+[2], 'i') edges_g[:, :, 0] = np_pols_g edges_g[:, 1:, 1] = np_pols_g[:, :-1] edges_g[:, 0, 1] = np_pols_g[:, -1] edges_g = edges_g.reshape(-1, 2) edges.append(edges_g) if unique_edges: return np.unique(np.sort(np.concatenate([eds for eds in edges])), axis=0) return np.concatenate([eds for eds in edges])
def polygons_to_edges(obj, unique_edges=False)
-
Expand source code
def polygons_to_edges(obj, unique_edges=False): out = [] for faces in obj: out_edges = [] seen = set() for face in faces: for edge in zip(face, list(face[1:]) + list([face[0]])): if unique_edges and tuple(sorted(edge)) in seen: continue if unique_edges: seen.add(tuple(sorted(edge))) out_edges.append(edge) out.append(out_edges) return out
def polygons_to_edges_np(obj, unique_edges=False, output_numpy=False)
-
Expand source code
def polygons_to_edges_np(obj, unique_edges=False, output_numpy=False): result = [] for pols in obj: if len(pols) == 0: result.append([]) continue regular_mesh = True try: np_pols = np.array(pols, dtype=np.int32) except ValueError: regular_mesh = False if not regular_mesh: if output_numpy: result.append(pols_to_edges_irregular_mesh(pols, unique_edges)) else: result.append(pols_to_edges_irregular_mesh(pols, unique_edges).tolist()) else: edges = np.empty(list(np_pols.shape)+[2], 'i') edges[:, :, 0] = np_pols edges[:, 1:, 1] = np_pols[:, :-1] edges[:, 0, 1] = np_pols[:, -1] edges = edges.reshape(-1, 2) if output_numpy: if unique_edges: result.append(np.unique(np.sort(edges), axis=0)) else: result.append(edges) else: if unique_edges: result.append(np.unique(np.sort(edges), axis=0).tolist()) else: result.append(edges.tolist()) return result
def prepare_arrays(vertices, faces)
-
Expand source code
def prepare_arrays(vertices, faces): 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) return np_verts, np_faces
def remove_unreferenced_topology(edge_pol, verts_length)
-
Removes elements that point to unexisitng vertices edg_pol: list of edges or polygons - List[List[Int]] returns Tuple (referenced items - List[List[Int]], Boolean List with preserved items marked as True - List[bool])
Expand source code
def remove_unreferenced_topology(edge_pol, verts_length): ''' Removes elements that point to unexisitng vertices edg_pol: list of edges or polygons - List[List[Int]] returns Tuple (referenced items - List[List[Int]], Boolean List with preserved items marked as True - List[bool]) ''' edge_pol_out = [] preseved_mask = [] for ep in edge_pol: if all([c < verts_length for c in ep]): edge_pol_out.append(ep) preseved_mask.append(True) else: preseved_mask.append(False) return edge_pol_out, preseved_mask
def remove_unreferenced_verts(verts, edges, faces)
-
Removes unreferenced vertices verts: list of vertices List[List[float]] edges: list of edges List[List[Int]] faces: list of polygons List[List[Int]] returns Tuple (List used verts, List with updated edges, List with updated polyogn List with removed items marked as True)
Expand source code
def remove_unreferenced_verts(verts, edges, faces): ''' Removes unreferenced vertices verts: list of vertices List[List[float]] edges: list of edges List[List[Int]] faces: list of polygons List[List[Int]] returns Tuple (List used verts, List with updated edges, List with updated polyogn List with removed items marked as True) ''' e_indx = set(chain.from_iterable(edges)) f_indx = set(chain.from_iterable(faces)) indx = set.union(e_indx, f_indx) verts_out = [v for i, v in enumerate(verts) if i in indx] v_index = {j: i for i, j in enumerate(sorted(indx))} edges_out = [list(map(lambda n: v_index[n], e)) for e in edges] faces_out = [list(map(lambda n: v_index[n], f)) for f in faces] return verts_out, edges_out, faces_out, list(set(range(len(verts)))-indx)