def area(self): points = [v._position for v in self._vertices] first_tri = points[:3] area_first = geometry.area_tri(*first_tri) # For quads if len(first_tri) <= 3: return area_first second_tri = points[1:] return area_first + geometry.area_tri(*second_tri)
def triangle_random_points(num_points, loop_triangles): """ Generates a list of random points over mesh loop triangles. :arg num_points: the number of random points to generate on each triangle. :type int: :arg loop_triangles: list of the triangles to generate points on. :type loop_triangles: :class:`bpy.types.MeshLoopTriangle`, sequence :return: list of random points over all triangles. :rtype: list """ from random import random from mathutils.geometry import area_tri # For each triangle, generate the required number of random points sampled_points = [None] * (num_points * len(loop_triangles)) for i, lt in enumerate(loop_triangles): # Get triangle vertex coordinates verts = lt.id_data.vertices ltv = lt.vertices[:] tv = (verts[ltv[0]].co, verts[ltv[1]].co, verts[ltv[2]].co) for k in range(num_points): # If this is a quad, we need to weight its 2 tris by their area if len(tv) != 1: area1 = area_tri(*tv[0]) area2 = area_tri(*tv[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tv[0 if (random() < area1) else 1] else: vecs = tv[0] u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[num_points * i + k] = p return sampled_points
def distribute_points_accurate(bl_verts, tri_faces, number, tri_weights, proportional = True): if proportional: # returns list of numbers which mean how many points should be created on face according its area weighed_face_areas = [area_tri(*[bl_verts[i] for i in f]) * w for f, w in zip(tri_faces, tri_weights)] else: weighed_face_areas = tri_weights face_numbers = [0 for _ in tri_faces] chosen_faces = random.choices(range(len(tri_faces)), weighed_face_areas, k=number) for i in chosen_faces: face_numbers[i] += 1 return face_numbers
def distribute_points_fast(bl_verts, tri_faces, number): # returns list of numbers which mean how many points should be created on face according its area # actually this function has much better performance but for whole algorithm it does not matter # leave this function unused face_areas = [area_tri(*[bl_verts[i] for i in f]) for f in tri_faces] total_area = sum(face_areas) face_numbers = [int(area * number / total_area) for area in face_areas] chosen_faces = random.choices(range(len(tri_faces)), face_areas, k=number-sum(face_numbers)) for i in chosen_faces: face_numbers[i] += 1 return face_numbers
def tri_face_areas(self): if not self._tri_face_areas: if isinstance(self._verts, np.ndarray): self._tri_face_areas = np_calc_tris_areas(self._verts[np.array( self._tri_faces)]) else: self._tri_face_areas = [ area_tri(*[self._verts[i] for i in f]) for f in self._tri_faces ] return self._tri_face_areas
def center_of_gravity(obj): """ Return a Vector with the coordinates of the center of gravity of the object, relatively to the object location. It assumes a uniform distribution of mass. It can only work with triangular and quad meshes. """ x = y = z = 0 orig = Vector((0, 0, 0)) for face in obj.data.polygons: if face.normal.length == 0.0: continue distance = distance_point_to_plane(orig, face.center, face.normal) nb_edges = len(face.vertices) v = [obj.data.vertices[f].co for f in face.vertices] if nb_edges == 3: face_vol = - face.area * distance / 3 x += face_vol * (v[0][0] + v[1][0] + v[2][0]) / 4 y += face_vol * (v[0][1] + v[1][1] + v[2][1]) / 4 z += face_vol * (v[0][2] + v[1][2] + v[2][2]) / 4 elif nb_edges == 4: # let's cut the quad in triangles! # triangle 1: vertices 0, 1, 2 area1 = area_tri(v[0], v[1], v[2]) face_vol1 = - area1 * distance / 3 x += face_vol1 * (v[0][0] + v[1][0] + v[2][0]) / 4 y += face_vol1 * (v[0][1] + v[1][1] + v[2][1]) / 4 z += face_vol1 * (v[0][2] + v[1][2] + v[2][2]) / 4 # triangle 2: vertices 2, 3, 0 area2 = area_tri(v[2], v[3], v[0]) face_vol2 = - area2 * distance / 3 x += face_vol2 * (v[0][0] + v[2][0] + v[3][0]) / 4 y += face_vol2 * (v[0][1] + v[2][1] + v[3][1]) / 4 z += face_vol2 * (v[0][2] + v[2][2] + v[3][2]) / 4 else: raise OrientationException return Vector((x, y, z))
def outline_normal(self, edge, face, center_to_edge=None): # outlineとedgeが平面ならcenter_to_edgeを返す vec1, vec2 = edge.vertices[0].co, edge.vertices[1].co if center_to_edge is None: # 外部でまだ計算していない場合 v1c = face.center - vec1 center_to_edge = v1c.project(vec2 - vec1) - v1c center_to_edge.normalize() # == normal # normal長を調整 cvs = [None, None] for i, v in enumerate(edge.vertices): for e in (e for e in v.edges if e != edge): if (e.vertices[0].co - e.vertices[1].co).length > MIN_NUMBER: if not self.usehidden and e.hide: if len(e.faces) == 1: cv = e.vert_another(v) cvs[i] = cv.co break if cvs[0] is None and cvs[1] is None: # 隣接無し。普通は有り得ない return center_to_edge elif cvs[0] and cvs[1]: # 2 if geo.area_tri(vec1, vec2, cvs[1]) >= MIN_NUMBER or \ geo.area_tri(vec2, cvs[1], cvs[0]) >= MIN_NUMBER: edge_normal = geo.normal(vec1, vec2, cvs[1], cvs[0]) else: return center_to_edge else: # 1 if cvs[0] and geo.area_tri(cvs[0], vec1, vec2): edge_normal = geo.normal(cvs[0], vec1, vec2) elif cvs[1] and geo.area_tri(vec1, vec2, cvs[1]): edge_normal = geo.normal(vec1, vec2, cvs[1]) else: return center_to_edge if edge_normal.dot(center_to_edge) < 0.0: edge_normal.negate() angle = center_to_edge.angle(edge_normal) if math.pi / 2 - angle >= self.threthold: # 平行でない center_to_edge /= math.cos(angle) return edge_normal
def calcArea(): global totalArea totalArea = 0 for poly in bm_tess.polygons: uno = bm_tess.uv_layers.active.data[poly.loop_indices[0]].uv dos = bm_tess.uv_layers.active.data[poly.loop_indices[1]].uv tres = bm_tess.uv_layers.active.data[poly.loop_indices[2]].uv area = area_tri(uno, dos, tres) totalArea += area bpy.data.meshes.remove( bm_tess, do_unlink=True, do_id_user=True, do_ui_user=True)
def calculateTriangleArea(mesh, vertices, matrix): vcs = [matrix*mesh.vertices[i].co for i in vertices] return area_tri(*vcs)
def correct_tessellate(cls, polyline, tri_indices, sort=False): """不正な面の修正。 v4----v v4-------v / \ / \ / \ v1----v2 v3 v1---v2---v3 \ / v---v v1-v2-3がほぼ直線の場合でもv1-v2-v3に面が貼られる場合が有るので、 v1-v2-v4, v2-v3-v4で貼り直す。 :param polyline: 1つの面を構成するBMLoopかVectorのリスト :type polyline: list[BMLoop | Vector] | tuple[BMLoop | Vector] :param tri_indices: polylineにおいて三角形を構成するインデックスの 二次元リスト。 :type tri_indices: list[(int, int, int)] | tuple[(int, int, int)] :param sort: triの先頭要素でソートする :type sort: bool :return: インデックスの二次元リスト。 :rtype: list[(int, int, int)] """ n = len(polyline) if n <= 3: return [(0, 1, 2)] is_loop = isinstance(polyline[0], bmesh.types.BMLoop) if is_loop: vectors = [loop.vert.co for loop in polyline] else: vectors = polyline new_tris = [] removed_tris = set() corrected_faces_num = 0 for tri in tri_indices: v1, v2, v3 = tri if tri in removed_tris: continue if is_loop: area = geom.area_tri(polyline[v1].vert.co, polyline[v2].vert.co, polyline[v3].vert.co) else: area = geom.area_tri(polyline[v1], polyline[v2], polyline[v3]) if area > cls.AREA_THRESHOLD: continue # a-bが連続しているように並び替え if (v1 + 1) % n == v2: if (v1 - 1) % n == v3: v1, v2, v3 = v3, v1, v2 elif (v2 + 1) % n == v3: v1, v2, v3 = v2, v3, v1 elif (v3 + 1) % n == v1: if (v3 - 1) % n == v2: v1, v2, v3 = v2, v3, v1 else: v1, v2, v3 = v3, v1, v2 else: # 修正が必要な構造ではない continue vec1 = vectors[v1] vec2 = vectors[v2] vec3 = vectors[v3] if (v2 + 1) % n == v3: # v----v---------v # \ / # v1 -- v2 -- v3 v5, v6 = v1, v3 fill_143 = False else: if (vec2 - vec1).dot(vec3 - vec1) < 0: # v--------v # / \ # v3 v1---v2 # \ / # v---v v5, v6 = v3, v2 fill_143 = True else: # v---------v # / \ # v1---v2 v3 # \ / # v---v v5, v6 = v1, v3 fill_143 = False for link_tri in tri_indices: if link_tri != tri: if v5 in link_tri and v6 in link_tri: break else: link_tri = None if link_tri: v4 = link_tri[link_tri.index(v5) - 1] tri1 = (v1, v2, v4) if fill_143: tri2 = (v1, v4, v3) else: tri2 = (v2, v3, v4) new_tris.append(tri1) new_tris.append(tri2) removed_tris.add(tri) removed_tris.add(link_tri) corrected_faces_num += 1 r_tri_indices = [tuple(tri) for tri in tri_indices] for tri in removed_tris: r_tri_indices.remove(tri) for tri in new_tris: r_tri_indices.append(tri) if sort: for tri in r_tri_indices: tri.sort() r_tri_indices.sort(key=lambda tri: tri[0]) return r_tri_indices
def calculateTriangleArea(mesh, vertices, matrix): vcs = [matrix * mesh.vertices[i].co for i in vertices] return area_tri(*vcs)
def tri_face_areas(self): if not self._tri_face_areas: self._tri_face_areas = [ area_tri(*[self._verts[i] for i in f]) for f in self._tri_faces ] return self._tri_face_areas
def calc_area(self): """:rtype: float""" return geom.area_tri(*self.coords)
def face_random_points_ngons(num_points, tessfaces): from random import random import mathutils from mathutils.geometry import area_tri, tessellate_polygon # Split all quads into 2 tris, tris remain unchanged tri_faces = [] for f in tessfaces: tris = [] verts = f.id_data.vertices fv = f.vertices[:] if len(fv) == 3: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) elif len(fv) == 4: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) tris.append((verts[fv[0]].co, verts[fv[3]].co, verts[fv[2]].co)) else: fvngon = [v.co for v in verts] tris.extend([[fvngon[i] for i in tess] for tess in tessellate_polygon([fvngon])]) tri_faces.append(tris) # For each face, generate the required number of random points sampled_points = [None] * (num_points * len(tessfaces)) # sampled points need to be vectorized as npo below for i, tf, npo in enumerate(zip(tri_faces, num_points)): for k in range(npo): # If this is a quad, we need to weight its 2 tris by their area if len(tf) == 2: area1 = area_tri(*tf[0]) area2 = area_tri(*tf[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tf[0 if (random() < area1) else 1] elif len(tf) == 1: vecs = tf[0] else: areas = [area_tri(*tface) for tface in tf] area_tot = sum(areas) areas = [(area / area_tot) for area in areas] # vecs = tf[0 if (random() < area1) else 1] ??? u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[npo * i + k] = p return sampled_points
def face_random_points(num_points, tessfaces): """ Generates a list of random points over mesh tessfaces. :arg num_points: the number of random points to generate on each face. :type int: :arg tessfaces: list of the faces to generate points on. :type tessfaces: :class:`bpy.types.MeshTessFace`, sequence :return: list of random points over all faces. :rtype: list """ from random import random from mathutils.geometry import area_tri # Split all quads into 2 tris, tris remain unchanged tri_faces = [] for f in tessfaces: tris = [] verts = f.id_data.vertices fv = f.vertices[:] tris.append(( verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co, )) if len(fv) == 4: tris.append(( verts[fv[0]].co, verts[fv[3]].co, verts[fv[2]].co, )) tri_faces.append(tris) # For each face, generate the required number of random points sampled_points = [None] * (num_points * len(tessfaces)) for i, tf in enumerate(tri_faces): for k in range(num_points): # If this is a quad, we need to weight its 2 tris by their area if len(tf) != 1: area1 = area_tri(*tf[0]) area2 = area_tri(*tf[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tf[0 if (random() < area1) else 1] else: vecs = tf[0] u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[num_points * i + k] = p return sampled_points
def face_random_points(num_points, tessfaces): """ Generates a list of random points over mesh tessfaces. :arg num_points: the number of random points to generate on each face. :type int: :arg tessfaces: list of the faces to generate points on. :type tessfaces: :class:`bpy.types.MeshTessFace`, sequence :return: list of random points over all faces. :rtype: list """ from random import random from mathutils.geometry import area_tri # Split all quads into 2 tris, tris remain unchanged tri_faces = [] for f in tessfaces: tris = [] verts = f.id_data.vertices fv = f.vertices[:] tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co, )) if len(fv) == 4: tris.append((verts[fv[0]].co, verts[fv[3]].co, verts[fv[2]].co, )) tri_faces.append(tris) # For each face, generate the required number of random points sampled_points = [None] * (num_points * len(tessfaces)) for i, tf in enumerate(tri_faces): for k in range(num_points): # If this is a quad, we need to weight its 2 tris by their area if len(tf) != 1: area1 = area_tri(*tf[0]) area2 = area_tri(*tf[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tf[0 if (random() < area1) else 1] else: vecs = tf[0] u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[num_points * i + k] = p return sampled_points
def calc_area(self): return geom.area_tri(*self.coords)
#print(" reverting verti ces") v1 = e.verts[1] v2 = e.verts[0] lfverts[0]=Vector((v1.co)) lfverts[1]=Vector((v2.co)) nonCommonVIndex = list(set([v.index for v in lf.verts]) ^ set([v1.index,v2.index]))[0] lfverts[2]=Vector((bm.verts[nonCommonVIndex].co)) areaOld = lf.calc_area() flatFace(lfverts) vectorAlF = vertices[vertsDictionary[v1.index]]-vertices[vertsDictionary[v2.index]] vectorTF = lfverts[0]-lfverts[1] q = vectorTF.rotation_difference(vectorAlF) for v in lfverts: v.rotate(q) faceTranslate(vertices[vertsDictionary[v1.index]]-lfverts[0],lfverts) areaNew = mg.area_tri(lfverts[0], lfverts[1], lfverts[2]) addFace(vertices, faces, lfverts, lf.index, [v1.index,v2.index,nonCommonVIndex]) flatted.append(lf.index) #print(" flatted %d, old area=%2.2g new area=%2.2g"%(lf.index,areaOld,areaNew)) refFace.append(lf) managedEdge = True #print("Finished managing edges") if not managedEdge: #print("+-- no adjacent to flat, remove from list bfaces") if f in bfaces: bfaces.remove(f) #print("List faces still to manage:%d"%(len(refFace))) #for i,face in enumerate(refFace): #print(" --+[%d] %d"%(i,face.index))
def face_random_points_ngons(num_points, tessfaces): from random import random import mathutils from mathutils.geometry import area_tri, tessellate_polygon # Split all quads into 2 tris, tris remain unchanged tri_faces = [] for f in tessfaces: tris = [] verts = f.id_data.vertices fv = f.vertices[:] if len(fv) == 3: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) elif len(fv) == 4: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) tris.append((verts[fv[0]].co, verts[fv[3]].co, verts[fv[2]].co)) else: fvngon = [v.co for v in verts] tris.extend([[fvngon[i] for i in tess] for tess in tessellate_polygon([fvngon])]) tri_faces.append(tris) # For each face, generate the required number of random points sampled_points = [None] * (num_points * len(tessfaces)) # sampled points need to be vectorized as npo below for i, tf, npo in enumerate(zip(tri_faces,num_points)): for k in range(npo): # If this is a quad, we need to weight its 2 tris by their area if len(tf) == 2: area1 = area_tri(*tf[0]) area2 = area_tri(*tf[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tf[0 if (random() < area1) else 1] elif len(tf) == 1: vecs = tf[0] else: areas = [area_tri(*tface) for tface in tf] area_tot = sum(areas) areas = [(area / area_tot) for area in areas] # vecs = tf[0 if (random() < area1) else 1] ??? u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[npo * i + k] = p return sampled_points