Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mesh_tissue/polyhedra.py')
-rw-r--r--mesh_tissue/polyhedra.py557
1 files changed, 557 insertions, 0 deletions
diff --git a/mesh_tissue/polyhedra.py b/mesh_tissue/polyhedra.py
new file mode 100644
index 00000000..394d605a
--- /dev/null
+++ b/mesh_tissue/polyhedra.py
@@ -0,0 +1,557 @@
+# ##### 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 #####
+
+# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
+# ------------------------------- version 0.84 ------------------------------- #
+# #
+# Creates duplicates of selected mesh to active morphing the shape according #
+# to target faces. #
+# #
+# (c) Alessandro Zomparelli #
+# (2017) #
+# #
+# http://www.co-de-it.com/ #
+# #
+# ############################################################################ #
+
+
+import bpy
+from bpy.types import (
+ Operator,
+ Panel,
+ PropertyGroup,
+ )
+from bpy.props import (
+ BoolProperty,
+ EnumProperty,
+ FloatProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty
+ )
+from mathutils import Vector, Quaternion, Matrix
+import numpy as np
+from math import *
+import random, time, copy
+import bmesh
+from .utils import *
+
+class polyhedra_wireframe(Operator):
+ bl_idname = "object.polyhedra_wireframe"
+ bl_label = "Tissue Polyhedra Wireframe"
+ bl_description = "Generate wireframes around the faces.\
+ \nDoesn't works with boundary edges.\
+ \n(Experimental)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ thickness : FloatProperty(
+ name="Thickness", default=0.1, min=0.001, soft_max=200,
+ description="Wireframe thickness"
+ )
+
+ subdivisions : IntProperty(
+ name="Segments", default=1, min=1, soft_max=10,
+ description="Max sumber of segments, used for the longest edge"
+ )
+
+ #regular_sections : BoolProperty(
+ # name="Regular Sections", default=False,
+ # description="Turn inner loops into polygons"
+ # )
+
+ dissolve_inners : BoolProperty(
+ name="Dissolve Inners", default=False,
+ description="Dissolve inner edges"
+ )
+
+ @classmethod
+ def poll(cls, context):
+ try:
+ #bool_tessellated = context.object.tissue_tessellate.generator != None
+ ob = context.object
+ return ob.type == 'MESH' and ob.mode == 'OBJECT'# and bool_tessellated
+ except:
+ return False
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_dialog(self)
+
+ def execute(self, context):
+
+ merge_dist = self.thickness*0.001
+
+ subs = self.subdivisions
+
+ start_time = time.time()
+ ob = context.object
+ me = simple_to_mesh(ob)
+ bm = bmesh.new()
+ bm.from_mesh(me)
+
+ bm.verts.ensure_lookup_table()
+ bm.edges.ensure_lookup_table()
+ bm.faces.ensure_lookup_table()
+
+ # Subdivide edges
+ proportional_subs = True
+ if subs > 1 and proportional_subs:
+ wire_length = [e.calc_length() for e in bm.edges]
+ all_edges = list(bm.edges)
+ max_segment = max(wire_length)/subs
+ split_edges = [[] for i in range(subs+1)]
+ for e, l in zip(all_edges, wire_length):
+ split_edges[int(l//max_segment)].append(e)
+ for i in range(2,subs):
+ perc = {}
+ for e in split_edges[i]:
+ perc[e]=0.1
+ bmesh.ops.bisect_edges(bm, edges=split_edges[i], cuts=i, edge_percents=perc)
+
+ ### Create double faces
+ double_faces = []
+ double_layer_edge = []
+ double_layer_piece = []
+ for f in bm.faces:
+ verts0 = [v.co for v in f.verts]
+ verts1 = [v.co for v in f.verts]
+ verts1.reverse()
+ double_faces.append(verts0)
+ double_faces.append(verts1)
+
+ # Create new bmesh object and data layers
+ bm1 = bmesh.new()
+
+ # Create faces and assign Edge Layers
+ for verts in double_faces:
+ new_verts = []
+ for v in verts:
+ vert = bm1.verts.new(v)
+ new_verts.append(vert)
+ bm1.faces.new(new_verts)
+
+ bm1.verts.ensure_lookup_table()
+ bm1.edges.ensure_lookup_table()
+ bm1.faces.ensure_lookup_table()
+
+ n_faces = len(bm.faces)
+ n_doubles = len(bm1.faces)
+
+ polyhedra = []
+
+ for e in bm.edges:
+ done = []
+
+ # ERROR: Naked edges
+ e_faces = len(e.link_faces)
+ if e_faces < 2:
+ bm.free()
+ bm1.free()
+ message = "Naked edges are not allowed"
+ self.report({'ERROR'}, message)
+ return {'CANCELLED'}
+
+ edge_vec = e.verts[1].co - e.verts[0].co
+
+ # run first face
+ for i1 in range(e_faces-1):
+ f1 = e.link_faces[i1]
+ #edge_verts1 = [v.index for v in f1.verts if v in e.verts]
+ verts1 = [v.index for v in f1.verts]
+ va1 = verts1.index(e.verts[0].index)
+ vb1 = verts1.index(e.verts[1].index)
+ # chech if order of the edge matches the order of the face
+ dir1 = va1 == (vb1+1)%len(verts1)
+ edge_vec1 = edge_vec if dir1 else -edge_vec
+
+ # run second face
+ faces2 = []
+ normals2 = []
+ for i2 in range(i1+1,e_faces):
+ #for i2 in range(n_faces):
+ if i1 == i2: continue
+ f2 = e.link_faces[i2]
+ f2.normal_update()
+ #edge_verts2 = [v.index for v in f2.verts if v in e.verts]
+ verts2 = [v.index for v in f2.verts]
+ va2 = verts2.index(e.verts[0].index)
+ vb2 = verts2.index(e.verts[1].index)
+ # chech if order of the edge matches the order of the face
+ dir2 = va2 == (vb2+1)%len(verts2)
+ # check for normal consistency
+ if dir1 != dir2:
+ # add face
+ faces2.append(f2.index+1)
+ normals2.append(f2.normal)
+ else:
+ # add flipped face
+ faces2.append(-(f2.index+1))
+ normals2.append(-f2.normal)
+
+
+
+ # find first polyhedra (positive)
+ plane_x = f1.normal # normal
+ plane_y = plane_x.cross(edge_vec1) # tangent face perp edge
+ id1 = (f1.index+1)
+
+ min_angle0 = 10000
+
+ # check consistent faces
+ if id1 not in done:
+ id2 = None
+ min_angle = min_angle0
+ for i2, n2 in zip(faces2,normals2):
+ v2 = flatten_vector(-n2, plane_x, plane_y)
+ angle = vector_rotation(v2)
+ if angle < min_angle:
+ id2 = i2
+ min_angle = angle
+ if id2: done.append(id2)
+ new_poly = True
+ # add to existing polyhedron
+ for p in polyhedra:
+ if id1 in p or id2 in p:
+ new_poly = False
+ if id2 not in p: p.append(id2)
+ if id1 not in p: p.append(id1)
+ break
+ # start new polyhedron
+ if new_poly: polyhedra.append([id1, id2])
+
+ # find second polyhedra (negative)
+ plane_x = -f1.normal # normal
+ plane_y = plane_x.cross(-edge_vec1) # tangent face perp edge
+ id1 = -(f1.index+1)
+
+ if id1 not in done:
+ id2 = None
+ min_angle = min_angle0
+ for i2, n2 in zip(faces2, normals2):
+ v2 = flatten_vector(n2, plane_x, plane_y)
+ angle = vector_rotation(v2)
+ if angle < min_angle:
+ id2 = -i2
+ min_angle = angle
+ done.append(id2)
+ add = True
+ for p in polyhedra:
+ if id1 in p or id2 in p:
+ add = False
+ if id2 not in p: p.append(id2)
+ if id1 not in p: p.append(id1)
+ break
+ if add: polyhedra.append([id1, id2])
+
+ for i in range(len(bm1.faces)):
+ for j in (False,True):
+ if j: id = i+1
+ else: id = -(i+1)
+ join = []
+ keep = []
+ for p in polyhedra:
+ if id in p: join += p
+ else: keep.append(p)
+ if len(join) > 0:
+ keep.append(list(dict.fromkeys(join)))
+ polyhedra = keep
+
+ for i, p in enumerate(polyhedra):
+ for j in p:
+ bm1.faces[j].material_index = i
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe, found {} polyhedra in {:.4f} sec'.format(len(polyhedra), end_time-start_time))
+
+
+ delete_faces = []
+ wireframe_faces = []
+ not_wireframe_faces = []
+ flat_faces = []
+
+ bm.free()
+
+ #bmesh.ops.bisect_edges(bm1, edges=bm1.edges, cuts=3)
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe, subdivide edges in {:.4f} sec'.format(end_time-start_time))
+
+ bm1.faces.index_update()
+ #merge_verts = []
+ for p in polyhedra:
+ delete_faces_poly = []
+ wireframe_faces_poly = []
+ faces_id = [(f-1)*2 if f > 0 else (-f-1)*2+1 for f in p]
+ faces_id_neg = [(-f-1)*2 if -f > 0 else (f-1)*2+1 for f in p]
+ merge_verts = []
+ faces = [bm1.faces[f_id] for f_id in faces_id]
+ for f in faces:
+ delete = False
+ if f.index in delete_faces: continue
+ '''
+ cen = f.calc_center_median()
+ for e in f.edges:
+ mid = (e.verts[0].co + e.verts[1].co)/2
+ vec1 = e.verts[0].co - e.verts[1].co
+ vec2 = mid - cen
+ ang = Vector.angle(vec1,vec2)
+ length = vec2.length
+ #length = sin(ang)*length
+ if length < self.thickness/2:
+ delete = True
+ '''
+ if False:
+ sides = len(f.verts)
+ for i in range(sides):
+ v = f.verts[i].co
+ v0 = f.verts[(i-1)%sides].co
+ v1 = f.verts[(i+1)%sides].co
+ vec0 = v0 - v
+ vec1 = v1 - v
+ ang = (pi - vec0.angle(vec1))/2
+ length = min(vec0.length, vec1.length)*sin(ang)
+ if length < self.thickness/2:
+ delete = True
+ break
+
+ if delete:
+ delete_faces_poly.append(f.index)
+ else:
+ wireframe_faces_poly.append(f.index)
+ merge_verts += [v for v in f.verts]
+ if len(wireframe_faces_poly) < 2:
+ delete_faces += faces_id
+ not_wireframe_faces += faces_id_neg
+ else:
+ wireframe_faces += wireframe_faces_poly
+ flat_faces += delete_faces_poly
+
+ #wireframe_faces = list(dict.fromkeys(wireframe_faces))
+ bmesh.ops.remove_doubles(bm1, verts=merge_verts, dist=merge_dist)
+ bm1.edges.ensure_lookup_table()
+ bm1.faces.ensure_lookup_table()
+ bm1.faces.index_update()
+
+
+ wireframe_faces = [i for i in wireframe_faces if i not in not_wireframe_faces]
+ wireframe_faces = list(dict.fromkeys(wireframe_faces))
+
+ flat_faces = list(dict.fromkeys(flat_faces))
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe, merge and delete in {:.4f} sec'.format(end_time-start_time))
+
+ poly_me = me.copy()
+ bm1.to_mesh(poly_me)
+ poly_me.update()
+ new_ob = bpy.data.objects.new("Polyhedra", poly_me)
+ context.collection.objects.link(new_ob)
+
+ ############# FRAME #############
+ bm1.faces.index_update()
+ wireframe_faces = [bm1.faces[i] for i in wireframe_faces]
+ original_faces = wireframe_faces
+ #bmesh.ops.remove_doubles(bm1, verts=merge_verts, dist=0.001)
+
+ # detect edge loops
+
+ loops = []
+ boundaries_mat = []
+ neigh_face_center = []
+ face_normals = []
+
+ # compute boundary frames
+ new_faces = []
+ wire_length = []
+ vert_ids = []
+
+ # append regular faces
+
+ for f in original_faces:
+ loop = list(f.verts)
+ loops.append(loop)
+ boundaries_mat.append([f.material_index for v in loop])
+ f.normal_update()
+ face_normals.append([f.normal for v in loop])
+
+ push_verts = []
+ inner_loops = []
+
+ for loop_index, loop in enumerate(loops):
+ is_boundary = loop_index < len(neigh_face_center)
+ materials = boundaries_mat[loop_index]
+ new_loop = []
+ loop_ext = [loop[-1]] + loop + [loop[0]]
+
+ # calc tangents
+ tangents = []
+ for i in range(len(loop)):
+ # vertices
+ vert0 = loop_ext[i]
+ vert = loop_ext[i+1]
+ vert1 = loop_ext[i+2]
+ # edge vectors
+ vec0 = (vert0.co - vert.co).normalized()
+ vec1 = (vert.co - vert1.co).normalized()
+ # tangent
+ _vec1 = -vec1
+ _vec0 = -vec0
+ ang = (pi - vec0.angle(vec1))/2
+ normal = face_normals[loop_index][i]
+ tan0 = normal.cross(vec0)
+ tan1 = normal.cross(vec1)
+ tangent = (tan0 + tan1).normalized()/sin(ang)*self.thickness/2
+ tangents.append(tangent)
+
+ # calc correct direction for boundaries
+ mult = -1
+ if is_boundary:
+ dir_val = 0
+ for i in range(len(loop)):
+ surf_point = neigh_face_center[loop_index][i]
+ tangent = tangents[i]
+ vert = loop_ext[i+1]
+ dir_val += tangent.dot(vert.co - surf_point)
+ if dir_val > 0: mult = 1
+
+ # add vertices
+ for i in range(len(loop)):
+ vert = loop_ext[i+1]
+ area = 1
+ new_co = vert.co + tangents[i] * mult * area
+ # add vertex
+ new_vert = bm1.verts.new(new_co)
+ new_loop.append(new_vert)
+ vert_ids.append(vert.index)
+ new_loop.append(new_loop[0])
+
+ # add faces
+ #materials += [materials[0]]
+ for i in range(len(loop)):
+ v0 = loop_ext[i+1]
+ v1 = loop_ext[i+2]
+ v2 = new_loop[i+1]
+ v3 = new_loop[i]
+ face_verts = [v1,v0,v3,v2]
+ if mult == -1: face_verts = [v0,v1,v2,v3]
+ new_face = bm1.faces.new(face_verts)
+ # Material by original edges
+ piece_id = 0
+ new_face.select = True
+ new_faces.append(new_face)
+ wire_length.append((v0.co - v1.co).length)
+ max_segment = max(wire_length)/self.subdivisions
+ #for f,l in zip(new_faces,wire_length):
+ # f.material_index = min(int(l/max_segment), self.subdivisions-1)
+ bm1.verts.ensure_lookup_table()
+ push_verts += [v.index for v in loop_ext]
+
+ # At this point topology han been build, but not yet thickened
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe, frames in {:.4f} sec'.format(end_time-start_time))
+
+ bm1.verts.ensure_lookup_table()
+ bm1.edges.ensure_lookup_table()
+ bm1.faces.ensure_lookup_table()
+ bm1.verts.index_update()
+
+ ### Displace vertices ###
+
+ circle_center = [0]*len(bm1.verts)
+ circle_normal = [0]*len(bm1.verts)
+
+ smooth_corners = [True] * len(bm1.verts)
+ corners = [[] for i in range(len(bm1.verts))]
+ normals = [0]*len(bm1.verts)
+ vertices = [0]*len(bm1.verts)
+ # Define vectors direction
+ for f in new_faces:
+ v0 = f.verts[0]
+ v1 = f.verts[1]
+ id = v0.index
+ corners[id].append((v1.co - v0.co).normalized())
+ normals[id] = v0.normal.copy()
+ vertices[id] = v0
+ smooth_corners[id] = False
+ # Displace vertices
+ for i, vecs in enumerate(corners):
+ if len(vecs) > 0:
+ v = vertices[i]
+ nor = normals[i]
+ ang = 0
+ for vec in vecs:
+ ang += nor.angle(vec)
+ ang /= len(vecs)
+ div = sin(ang)
+ if div == 0: div = 1
+ v.co += nor*self.thickness/2/div
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe, corners displace in {:.4f} sec'.format(end_time-start_time))
+
+ # Removing original flat faces
+
+ flat_faces = [bm1.faces[i] for i in flat_faces]
+ for f in flat_faces:
+ f.material_index = self.subdivisions+1
+ for v in f.verts:
+ if smooth_corners[v.index]:
+ v.co += v.normal*self.thickness/2
+ smooth_corners[v.index] = False
+ delete_faces = delete_faces + [f.index for f in original_faces]
+ delete_faces = list(dict.fromkeys(delete_faces))
+ delete_faces = [bm1.faces[i] for i in delete_faces]
+ bmesh.ops.delete(bm1, geom=delete_faces, context='FACES')
+
+ bmesh.ops.remove_doubles(bm1, verts=bm1.verts, dist=merge_dist)
+ bm1.faces.ensure_lookup_table()
+ bm1.edges.ensure_lookup_table()
+ bm1.verts.ensure_lookup_table()
+
+ if self.dissolve_inners:
+ bm1.edges.index_update()
+ dissolve_edges = []
+ for f in bm1.faces:
+ e = f.edges[2]
+ if e not in dissolve_edges:
+ dissolve_edges.append(e)
+ bmesh.ops.dissolve_edges(bm1, edges=dissolve_edges, use_verts=True, use_face_split=True)
+
+ all_lines = [[] for e in me.edges]
+ all_end_points = [[] for e in me.edges]
+ for v in bm1.verts: v.select_set(False)
+ for f in bm1.faces: f.select_set(False)
+
+ _me = me.copy()
+ bm1.to_mesh(me)
+ me.update()
+ new_ob = bpy.data.objects.new("Wireframe", me)
+ context.collection.objects.link(new_ob)
+ for o in context.scene.objects: o.select_set(False)
+ new_ob.select_set(True)
+ context.view_layer.objects.active = new_ob
+ me = _me
+
+ bm1.free()
+ bpy.data.meshes.remove(_me)
+ #new_ob.location = ob.location
+ new_ob.matrix_world = ob.matrix_world
+
+ end_time = time.time()
+ print('Tissue: Polyhedra wireframe in {:.4f} sec'.format(end_time-start_time))
+ return {'FINISHED'}