# -*- coding: utf-8 -*-
# -*- python -*-
#
# PropertyTopomesh
#
# Copyright 2016 INRIA - CIRAD - INRA
#
# File author(s): Guillaume Cerutti <guillaume.cerutti@inria.fr>
#
# File contributor(s): Guillaume Cerutti <guillaume.cerutti@inria.fr>
#
# Distributed under the Cecill-C License.
# See accompanying file LICENSE.txt or copy at
# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
# OpenaleaLab Website : http://virtualplants.github.io/
#
###############################################################################
import numpy as np
from cellcomplex.utils import array_dict
from cellcomplex.property_topomesh.creation import triangle_topomesh, quad_topomesh, poly_topomesh, dual_topomesh
from cellcomplex.property_topomesh.analysis import compute_topomesh_property, compute_topomesh_triangle_properties
from cellcomplex.property_topomesh.extraction import star_interface_topomesh
from cellcomplex.property_topomesh.optimization import topomesh_triangle_split, property_topomesh_vertices_deformation
from cellcomplex.property_topomesh.composition import append_topomesh, fuse_topomesh_identical_vertices
from cellcomplex.property_topomesh.utils.delaunay_tools import delaunay_triangulation
[docs]def square_topomesh(side_length = 1):
points = {}
points[0] = [0,0,0]
points[1] = [side_length,0,0]
points[2] = [0,side_length,0]
points[3] = [side_length,side_length,0]
triangles = [[0,1,2],[3,2,1]]
return triangle_topomesh(triangles, points)
[docs]def hexagon_topomesh(side_length = 1, angle=0.):
points = {}
points[0] = [0,0,0]
for p in range(6):
points[p+1] = [side_length*np.cos(p*np.pi/3.+angle),side_length*np.sin(p*np.pi/3.+angle),0]
triangles = []
for p in range(6):
triangles += [[0,p + 1,(p+1)%6 + 1]]
return triangle_topomesh(triangles, points)
[docs]def icosahedron_topomesh(center=np.array([0,0,0],float),radius=10.):
ico_points = {}
ico_points[0] = center + radius*np.array([ 0. , -1. , 0. ])
ico_points[1] = center + radius*np.array([ 0.7236 , -0.447215, 0.52572 ])
ico_points[2] = center + radius*np.array([-0.276385, -0.447215, 0.85064 ])
ico_points[3] = center + radius*np.array([-0.894425, -0.447215, 0. ])
ico_points[4] = center + radius*np.array([-0.276385, -0.447215, -0.85064 ])
ico_points[5] = center + radius*np.array([ 0.7236 , -0.447215, -0.52572 ])
ico_points[6] = center + radius*np.array([ 0.276385, 0.447215, 0.85064 ])
ico_points[7] = center + radius*np.array([-0.7236 , 0.447215, 0.52572 ])
ico_points[8] = center + radius*np.array([-0.7236 , 0.447215, -0.52572 ])
ico_points[9] = center + radius*np.array([ 0.276385, 0.447215, -0.85064 ])
ico_points[10] = center + radius*np.array([ 0.894425, 0.447215, 0. ])
ico_points[11] = center + radius*np.array([ 0. , 1. , 0. ])
ico_triangles = [[ 0, 1, 2],
[ 0, 1, 5],
[ 0, 2, 3],
[ 0, 3, 4],
[ 0, 4, 5],
[ 1, 5, 10],
[ 1, 2, 6],
[ 2, 3, 7],
[ 3, 4, 8],
[ 4, 5, 9],
[ 1, 6, 10],
[ 2, 6, 7],
[ 3, 7, 8],
[ 4, 8, 9],
[ 5, 9, 10],
[ 6, 10, 11],
[ 6, 7, 11],
[ 7, 8, 11],
[ 8, 9, 11],
[ 9, 10, 11]]
return triangle_topomesh(ico_triangles, ico_points)
[docs]def truncated_octahedron_topomesh(center=np.array([0,0,0],float),radius=10.,ratio=1.):
points = []
points.append(np.array([0,-1,-1]))
points.append(np.array([0,-1,1]))
points.append(np.array([0,1,1]))
points.append(np.array([0,1,-1]))
points.append(np.array([np.sqrt(2),0,0]))
points.append(np.array([-np.sqrt(2),0,0]))
triangles = []
triangles.append(np.array([0,1,4]))
triangles.append(np.array([1,2,4]))
triangles.append(np.array([2,3,4]))
triangles.append(np.array([3,0,4]))
triangles.append(np.array([1,0,5]))
triangles.append(np.array([2,1,5]))
triangles.append(np.array([3,2,5]))
triangles.append(np.array([0,3,5]))
triangles = np.array(triangles)
for t in triangles:
points.append(((ratio+1)*points[t[0]]+ratio*points[t[1]])/(2*ratio+1.))
points.append(((ratio+1)*points[t[1]]+ratio*points[t[2]])/(2*ratio+1.))
points.append(((ratio+1)*points[t[2]]+ratio*points[t[0]])/(2*ratio+1.))
for t in triangles:
points.append(np.mean([points[p] for p in t],axis=1))
points = np.array(points)
points = center + radius*points
points = array_dict(dict(zip(np.arange(len(points)),points)))
faces = []
faces.append([6,16,27,19])
faces.append([9,7,18,22])
faces.append([12,10,21,25])
faces.append([15,13,24,28])
faces.append([8,11,14,17])
faces.append([20,23,26,29])
faces.append([6,16,8,11,7,18])
faces.append([9,7,11,14,10,21])
faces.append([12,10,14,17,13,24])
faces.append([15,13,17,8,16,27])
#faces.append([9,21,25,23,20,22])
faces.append([15,28,26,29,19,27])
faces.append([12,25,23,26,28,24])
faces.append([9,22,20,23,25,21])
faces.append([6,19,29,20,22,18])
# faces.append([9,7,11,14,10,21])
# faces.append([12,10,14,17,13,24])
# faces.append([15,13,17,8,16,27])
topomesh = poly_topomesh(faces,points)
return topomesh
[docs]def hexagonal_prism_topomesh(center=np.array([0,0,0],float),radius=10.,height=5.,angle=np.pi/6.):
points = {}
for p in range(6):
points[p] = [radius*np.cos(p*np.pi/3.+angle),radius*np.sin(p*np.pi/3.+angle),0]
top_hexagon_topomesh = poly_topomesh([[0,1,2,3,4,5]],points)
top_hexagon_topomesh.update_wisp_property('barycenter',0,center+top_hexagon_topomesh.wisp_property('barycenter',0).values()+np.array([0,0,height/2.]))
bottom_hexagon_topomesh = poly_topomesh([[0,1,2,3,4,5]],points)
bottom_hexagon_topomesh.update_wisp_property('barycenter',0,center+bottom_hexagon_topomesh.wisp_property('barycenter',0).values()-np.array([0,0,height/2.]))
top_positions = top_hexagon_topomesh.wisp_property('barycenter',0)
bottom_positions = bottom_hexagon_topomesh.wisp_property('barycenter',0)
side_topomeshes = []
for t_e, b_e in zip(top_hexagon_topomesh.wisps(1),bottom_hexagon_topomesh.wisps(1)):
top_vertices = list(top_hexagon_topomesh.borders(1,t_e))
bottom_vertices = list(bottom_hexagon_topomesh.borders(1,b_e))
points = {}
points[0] = top_positions[top_vertices[0]]
points[1] = top_positions[top_vertices[1]]
points[2] = bottom_positions[bottom_vertices[1]]
points[3] = bottom_positions[bottom_vertices[0]]
side_topomeshes.append(poly_topomesh([[0,1,2,3]], points))
topomesh,_ = append_topomesh(top_hexagon_topomesh,bottom_hexagon_topomesh)
for side_topomesh in side_topomeshes:
topomesh,_ = append_topomesh(topomesh,side_topomesh)
fuse_topomesh_identical_vertices(topomesh)
if not 0 in topomesh.wisps(3):
topomesh.add_wisp(3,0)
cells_to_remove = [c for c in topomesh.wisps(3) if c!=0]
for f in topomesh.wisps(2):
if not 0 in topomesh.regions(2,f):
topomesh.link(3,0,f)
for c in cells_to_remove:
topomesh.remove_wisp(3,c)
return topomesh
[docs]def sphere_topomesh(radius=1.0,center=np.zeros(3)):
from cellcomplex.utils import array_dict
from cellcomplex.property_topomesh.optimization import topomesh_triangle_split
ico = icosahedron_topomesh()
topomesh = topomesh_triangle_split(topomesh_triangle_split(ico))
positions = topomesh.wisp_property('barycenter',0)
new_positions = array_dict(center + radius*positions.values()/np.linalg.norm(positions.values(),axis=1)[:,np.newaxis],positions.keys())
topomesh.update_wisp_property('barycenter',0,new_positions)
return topomesh
[docs]def triangular_grid_topomesh(size=1, voxelsize=1., center=np.zeros(3)):
"""
"""
x = []
y = []
for n in range(size+1,2*size+2):
x += list((np.arange(n)-((n-1)/2.))*voxelsize*np.sqrt(3.))
y += [((2*size+1 - n))*voxelsize*3./2. for p in range(n)]
for n in range(size+1,2*size+1)[::-1]:
x += list((np.arange(n)-((n-1)/2.))*voxelsize*np.sqrt(3.))
y += [-((2*size+1 - n))*voxelsize*3./2. for p in range(n)]
x = np.array(x) + center[0]
y = np.array(y) + center[1]
z = np.zeros_like(x) + center[2]
positions = array_dict(dict(list(zip(list(range(len(x))),np.transpose([x,y,z])))))
triangles = positions.keys()[delaunay_triangulation(positions.values())]
topomesh = triangle_topomesh(triangles,positions)
return topomesh
[docs]def square_grid_topomesh(size=1, voxelsize=1., center=np.zeros(3)):
"""
"""
x = y = np.linspace(-size,size,2*size+1)*voxelsize
x += center[0]
y += center[1]
xx,yy = np.meshgrid(x,y)
zz = np.zeros_like(xx) + center[2]
square_bl = np.concatenate([np.arange(2*size+1)[:-1]+i*(2*size+1) for i in np.arange(2*size+1)[:-1]])
square_br = np.concatenate([np.arange(2*size+1)[1:]+i*(2*size+1) for i in np.arange(2*size+1)[:-1]])
square_tl = np.concatenate([np.arange(2*size+1)[:-1]+i*(2*size+1) for i in np.arange(2*size+1)[1:]])
square_tr = np.concatenate([np.arange(2*size+1)[1:]+i*(2*size+1) for i in np.arange(2*size+1)[1:]])
squares = np.transpose([square_bl,square_tl,square_tr,square_br])
positions = dict(list(zip(list(range(len(np.ravel(xx)))),np.transpose([np.ravel(xx),np.ravel(yy),np.ravel(zz)]))))
topomesh = quad_topomesh(squares,positions,faces_as_cells=True)
return topomesh
[docs]def hexagonal_grid_topomesh(size=1, voxelsize=1., center=np.zeros(3)):
"""
"""
x = []
y = []
p = []
for n in range(size+1,2*size+2):
for r in [0,1]:
p += list(len(p)+np.arange(n+r))
x += list((np.arange(n+r)-((n+r-1)/2.))*voxelsize*np.sqrt(3.))
row_y = (((2*size+1 - n)+1)//2)*3
row_side = (((2*size+1 - n)+1)%2)
y += list((row_y+(2*row_side-1)-(row_side==r)*(2*row_side-1)/2.)*np.ones(n+r)*voxelsize)
for n in range(size+1,2*size+2)[::-1]:
for r in [1,0]:
p += list(len(p)+np.arange(n+r))
x += list((np.arange(n+r)-((n+r-1)/2.))*voxelsize*np.sqrt(3.))
row_y = (((2*size+1 - n)+1)//2)*3
row_side = (((2*size+1 - n)+1)%2)
y += list(-(row_y+(2*row_side-1)-(row_side==r)*(2*row_side-1)/2.)*np.ones(n+r)*voxelsize)
x = np.array(x) + center[0]
y = np.array(y) + center[1]
z = np.zeros_like(x) + center[2]
positions = dict(list(zip(list(range(len(x))),np.transpose([x,y,z]))))
index_list = []
row_length_list = []
row_gaps = []
start = 0
for n in range(size+1,2*size+1):
index_list += list(np.arange(n)+start)
start += n+n+1
row_length_list += list(n*np.ones(n,int))
for h in range(n):
row_gaps += [[n+1,n+1,n+1,-(n+2),-(n+1)]]
for n in [2*size+1]:
index_list += list(np.arange(n)+start)
start += n+n+2
row_length_list += list(n*np.ones(n,int))
for h in range(n):
row_gaps += [[n+1,n+1,n,-(n+1),-(n+1)]]
for n in range(2*size,size,-1):
index_list += list(np.arange(n)+start)
start += n+n+3
row_length_list += list(n*np.ones(n,int))
for h in range(n):
row_gaps += [[n+2,n+1,n,-(n+1),-(n+1)]]
hexagons = []
for i,n,gaps in zip(index_list,row_length_list,row_gaps):
hexagon = [i]
for p, gap in zip(range(5),gaps):
hexagon += [hexagon[-1]+gap]
hexagons += [hexagon]
topomesh = poly_topomesh(hexagons,positions,faces_as_cells=True)
return topomesh
[docs]def circle_topomesh(radius = 1., n_points = 100, center=np.zeros(3,float)):
circle_thetas = np.linspace(-np.pi,np.pi-2*np.pi/float(n_points),n_points)
circle_points = center + np.transpose([radius*np.cos(circle_thetas),radius*np.sin(circle_thetas),np.zeros_like(circle_thetas)])
circle_positions = dict(zip(range(n_points),circle_points))
circle_vertices = np.arange(n_points)
return poly_topomesh([circle_vertices],circle_positions)
[docs]def circle_voronoi_topomesh(size = 1,voxelsize = 1.,circle_size = 100.,z_coef = 0.,iterations=None,return_triangulation=False):
n_cells = 3*size*(size-1)+1
radius = size*voxelsize
circle_thetas = np.linspace(-np.pi,np.pi-2*np.pi/float(circle_size),circle_size)
circle_points = np.transpose([radius*np.cos(circle_thetas),radius*np.sin(circle_thetas)])
cell_thetas = np.array([np.pi*np.random.randint(-180,180)/180. for c in range(n_cells)])
cell_distances = 0.5*radius*np.sqrt([np.random.rand() for c in range(n_cells)])
cell_points = np.transpose([cell_distances*np.cos(cell_thetas),cell_distances*np.sin(cell_thetas)])
omega_forces = dict(repulsion=0.5)
sigma_deformation = 2.*radius/float(n_cells)
if iterations is None:
iterations = n_cells//2
for iteration in range(iterations):
cell_to_cell_vectors = np.array([[p-q for q in cell_points] for p in cell_points])
cell_to_cell_distances = np.linalg.norm(cell_to_cell_vectors,axis=2)/radius
cell_to_circle_vectors = np.array([[p-q for q in circle_points] for p in cell_points])
cell_to_circle_distances = np.linalg.norm(cell_to_circle_vectors,axis=2)/radius
deformation_force = np.zeros_like(cell_points)
cell_repulsion_force = np.nansum(cell_to_cell_vectors/np.power(cell_to_cell_distances,3)[:,:,np.newaxis],axis=1)
circle_repulsion_force = np.nansum(cell_to_circle_vectors/np.power(cell_to_circle_distances,3)[:,:,np.newaxis],axis=1)
deformation_force += omega_forces['repulsion']*cell_repulsion_force
deformation_force += 1.5*(n_cells/float(circle_size))*omega_forces['repulsion']*circle_repulsion_force
deformation_force_amplitude = np.linalg.norm(deformation_force,axis=1)
deformation_force = np.minimum(1.0,sigma_deformation/deformation_force_amplitude)[:,np.newaxis] * deformation_force
cell_points += deformation_force
cell_points = np.minimum(1.0,radius/(np.linalg.norm(cell_points,axis=1)))[:,np.newaxis] * cell_points
all_positions = array_dict(np.transpose([np.concatenate([cell_points[:,0],circle_points[:,0]]),np.concatenate([cell_points[:,1],circle_points[:,1]]),np.zeros(int(np.round(n_cells+circle_size)))]),keys=np.arange(int(np.round(n_cells+circle_size))).astype(int))
triangles = all_positions.keys()[delaunay_triangulation(all_positions.values())]
radial_distances = np.linalg.norm(all_positions.values(),axis=1)
radial_z = z_coef*np.power(radial_distances/radius,2)
all_positions = array_dict(np.transpose([all_positions.values()[:,0],all_positions.values()[:,1],radial_z]),keys=all_positions.keys())
triangulation_topomesh = triangle_topomesh(triangles,all_positions)
cell_topomesh = dual_topomesh(triangulation_topomesh,2,vertex_positions='voronoi')
if return_triangulation:
return cell_topomesh, triangulation_topomesh
else:
return cell_topomesh
[docs]def vtk_ellipsoid_topomesh(ellipsoid_radius=50.0, ellipsoid_scales=[1,1,1], ellipsoid_axes=np.diag(np.ones(3)), ellipsoid_center=np.zeros(3), subdivisions=3):
"""
"""
import vtk
from cellcomplex.property_topomesh.utils.triangular_mesh_tools import vtk_polydata_to_triangular_mesh
ico = vtk.vtkPlatonicSolidSource()
ico.SetSolidTypeToIcosahedron()
ico.Update()
subdivide = vtk.vtkLoopSubdivisionFilter()
subdivide.SetNumberOfSubdivisions(subdivisions)
subdivide.SetInputConnection(ico.GetOutputPort())
subdivide.Update()
scale_transform = vtk.vtkTransform()
scale_factor = ellipsoid_radius/(np.sqrt(2)/2.)
scale_transform.Scale(scale_factor,scale_factor,scale_factor)
ellipsoid_sphere = vtk.vtkTransformPolyDataFilter()
ellipsoid_sphere.SetInputData(subdivide.GetOutput())
ellipsoid_sphere.SetTransform(scale_transform)
ellipsoid_sphere.Update()
ellipsoid_transform = vtk.vtkTransform()
axes_transform = vtk.vtkLandmarkTransform()
source_points = vtk.vtkPoints()
source_points.InsertNextPoint([1,0,0])
source_points.InsertNextPoint([0,1,0])
source_points.InsertNextPoint([0,0,1])
target_points = vtk.vtkPoints()
target_points.InsertNextPoint(ellipsoid_axes[0])
target_points.InsertNextPoint(ellipsoid_axes[1])
target_points.InsertNextPoint(ellipsoid_axes[2])
axes_transform.SetSourceLandmarks(source_points)
axes_transform.SetTargetLandmarks(target_points)
axes_transform.SetModeToRigidBody()
axes_transform.Update()
ellipsoid_transform.SetMatrix(axes_transform.GetMatrix())
ellipsoid_transform.Scale(ellipsoid_scales[0],ellipsoid_scales[1],ellipsoid_scales[2])
center_transform = vtk.vtkTransform()
center_transform.Translate(ellipsoid_center[0],ellipsoid_center[1],ellipsoid_center[2])
center_transform.Concatenate(ellipsoid_transform)
ellipsoid_ellipsoid = vtk.vtkTransformPolyDataFilter()
ellipsoid_ellipsoid.SetInputData(ellipsoid_sphere.GetOutput())
ellipsoid_ellipsoid.SetTransform(center_transform)
ellipsoid_ellipsoid.Update()
ellipsoid_mesh = vtk_polydata_to_triangular_mesh(ellipsoid_ellipsoid.GetOutput())
triangles = np.array([t for t in ellipsoid_mesh.triangles.values()])
topomesh = triangle_topomesh(triangles,ellipsoid_mesh.points)
return topomesh
[docs]def hexagonal_prism_layered_grid_topomesh(size=1, n_layers=1,center=np.array([0,0,0]), radius=10, height_factor=2/3., triangulate=True, smoothing=False):
topomesh = hexagonal_prism_topomesh(center=center,radius=radius,height=height_factor*radius,angle=np.pi/6.)
for layer in range(n_layers):
for row in np.arange(-size,size+1):
for col in (np.arange(2*size+1 - np.abs(row) - layer) - (size-layer/2.-(np.abs(row))/2.)):
layer_row = row-layer/3.
if ((layer_row!=0) or (col!=0) or (layer!=0))&(np.abs(layer_row)<=(size-(layer-1)/2.)):
topomesh,_ = append_topomesh(topomesh, hexagonal_prism_topomesh(center=np.array([2.*col*radius*np.sqrt(3.)/2.,2.*layer_row*radius*3./4.,-layer*height_factor*radius]),radius=radius,height=height_factor*radius,angle=np.pi/6.))
for layer in range(n_layers):
for row in np.arange(-size,size+1):
for col in (np.arange(2*size+1 - np.abs(row) - layer) - (size-layer/2.-(np.abs(row))/2.)):
layer_row = row-layer/3.
print(layer,layer_row, col)
if triangulate:
topomesh = star_interface_topomesh(topomesh)
fuse_topomesh_identical_vertices(topomesh)
if triangulate and smoothing:
topomesh = topomesh_triangle_split(topomesh)
for cid in topomesh.wisps(3):
for n_cid in topomesh.border_neighbors(3,cid):
if (n_cid<cid) and (not (n_cid,cid) in topomesh._interface[3].values()):
iid = topomesh._interface[3].add((n_cid,cid),None)
compute_topomesh_property(topomesh,'vertices',degree=1)
compute_topomesh_property(topomesh,'vertices',degree=2)
compute_topomesh_property(topomesh,'vertices',degree=3)
if triangulate and smoothing:
compute_topomesh_triangle_properties(topomesh)
property_topomesh_vertices_deformation(topomesh,omega_forces=dict(taubin_smoothing=0.65,planarization=1),iterations=100)
# property_topomesh_vertices_deformation(topomesh,omega_forces=dict(taubin_smoothing=0.65,regularization=0.01,planarization=1),iterations=50)
# topomesh = property_topomesh_isotropic_remeshing(topomesh,maximal_length=3.,iterations=10)
return topomesh
[docs]def truncated_octahedra_layered_grid_topomesh(size=1, n_layers=1, center=np.array([0,0,0]), radius=10, triangulate=True, smoothing=False):
topomesh = truncated_octahedron_topomesh(center=center,radius=radius)
for layer in range(n_layers):
for row in np.arange(-size,size+1):
for col in (np.arange(2*size+1 - np.abs(row) - layer) - (size-layer/2.-(np.abs(row))/2.)):
print(layer,row, col)
if (row!=0) or (col!=0) or (layer!=0):
topomesh,_ = append_topomesh(topomesh, truncated_octahedron_topomesh(center=np.array([4.*col*radius*np.sqrt(2)/3,4.*row*radius/3,-4.*layer*radius/3]),radius=radius))
fuse_topomesh_identical_vertices(topomesh)
if triangulate:
topomesh = star_interface_topomesh(topomesh)
if triangulate and smoothing:
topomesh = topomesh_triangle_split(topomesh)
for cid in topomesh.wisps(3):
for n_cid in topomesh.border_neighbors(3,cid):
if (n_cid<cid) and (not (n_cid,cid) in topomesh._interface[3].values()):
iid = topomesh._interface[3].add((n_cid,cid),None)
compute_topomesh_property(topomesh,'vertices',degree=1)
compute_topomesh_property(topomesh,'vertices',degree=2)
compute_topomesh_property(topomesh,'vertices',degree=3)
if triangulate and smoothing:
compute_topomesh_triangle_properties(topomesh)
property_topomesh_vertices_deformation(topomesh,omega_forces=dict(taubin_smoothing=0.65,planarization=1),iterations=100)
# property_topomesh_vertices_deformation(topomesh,omega_forces=dict(taubin_smoothing=0.65,regularization=0.01,planarization=1),iterations=50)
# topomesh = property_topomesh_isotropic_remeshing(topomesh,maximal_length=3.,iterations=10)
return topomesh