def compute_curvature(FV, VertexNormals, FaceNormals, Avertex, Acorner, up, vp): """ CalcFaceCurvature recives a list of vertices and faces in FV structure and the normal at each vertex and calculates the second fundemental matrix and the curvature using least squares :param FV: face-vertex data structure containing a list of vertices and a list of faces :param VertexNormals: n*3 matrix ( n = number of vertices ) containing the normal at each vertex :param FaceNormals: m*3 matrix ( m = number of faces ) containing the normal of each face :param Avertex: :param Acorner: :param up: :param vp: :return: FaceSFM - an m*1 cell matrix second fundemental VertexSFM - an n*w cell matrix second fundementel wfp - corner voronoi weights """ print("Calculating curvature tensors ... Please wait") "Matrix of each face at each cell" FaceSFM, VertexSFM = list(), list() for i in range(FV.faces.shape[0]): FaceSFM.append([[0, 0], [0, 0]]) for i in range(FV.vertices.shape[0]): VertexSFM.append([[0, 0], [0, 0]]) Kn = np.zeros((1, FV.faces.shape[0])) " Get all the edge vectors " e0 = FV.vertices[FV.faces[:, 2], :] - FV.vertices[FV.faces[:, 1], :] e1 = FV.vertices[FV.faces[:, 0], :] - FV.vertices[FV.faces[:, 2], :] e2 = FV.vertices[FV.faces[:, 1], :] - FV.vertices[FV.faces[:, 0], :] " Normalize edge vectors " e0_norm = tut.unitize(e0) # e1_norm = normr(e1) # e2_norm = normr(e2) wfp = np.array(np.zeros((FV.faces.shape[0], 3))) for i in range(FV.faces.shape[0]): "Calculate Curvature Per Face" "set face coordinate frame" nf = FaceNormals[i, :] t = e0_norm[i, :] B = np.cross(nf, t) B = B / (np.linalg.norm(B)) "extract relevant normals in face vertices" n0 = VertexNormals[FV.faces[i][0], :] n1 = VertexNormals[FV.faces[i][1], :] n2 = VertexNormals[FV.faces[i][2], :] " solve least squares problem of th form Ax=b " A = np.array([[np.dot(e0[i, :], t), np.dot(e0[i, :], B), 0], [0, np.dot(e0[i, :], t), np.dot(e0[i, :], B)], [np.dot(e1[i, :], t), np.dot(e1[i, :], B), 0], [0, np.dot(e1[i, :], t), np.dot(e1[i, :], B)], [np.dot(e2[i, :], t), np.dot(e2[i, :], B), 0], [0, np.dot(e2[i, :], t), np.dot(e2[i, :], B)]]) b = np.array([ np.dot(n2 - n1, t), np.dot(n2 - n1, B), np.dot(n0 - n2, t), np.dot(n0 - n2, B), np.dot(n1 - n0, t), np.dot(n1 - n0, B) ]) "Resolving by least mean square method because " "A is not a square matrix" x = np.linalg.lstsq(A, b, None) FaceSFM[i] = np.array([[x[0][0], x[0][1]], [x[0][1], x[0][2]]]) Kn[0][i] = np.dot(np.array([1, 0]), np.dot(FaceSFM[i], np.array([[1.], [0.]]))) """ Calculate curvature per vertex Calculate voronoi weights """ wfp[i][0] = Acorner[i][0] / Avertex[FV.faces[i][0]] wfp[i][1] = Acorner[i][1] / Avertex[FV.faces[i][1]] wfp[i][2] = Acorner[i][2] / Avertex[FV.faces[i][2]] "Calculate new coordinate system and project the tensor" for j in range(3): new_ku, new_kuv, new_kv = \ project_curvature_tensor(t, B, nf, x[0][0], x[0][1], x[0][2], up[FV.faces[i][j], :], vp[FV.faces[i][j], :]) VertexSFM[FV.faces[i][j]] += np.dot( wfp[i][j], np.array([[new_ku, new_kuv], [new_kuv, new_kv]])) print('Finished Calculating curvature tensors') return FaceSFM, VertexSFM, wfp
def on_mouse_double_click(self, x, y): res = self._scene.camera.resolution fov_y = np.radians(self._scene.camera.fov[1] / 2.0) fov_x = fov_y * (res[0] / float(res[1])) half_fov = np.stack([fov_x, fov_y]) right_top = np.tan(half_fov) right_top *= 1 - (1.0 / res) left_bottom = -right_top right, top = right_top left, bottom = left_bottom xy_vec = tu.grid_linspace(bounds=[[left, top], [right, bottom]], count=res).astype(np.float64) pixels = tu.grid_linspace(bounds=[[0, 0], [res[0] - 1, res[1] - 1]], count=res).astype(np.int64) assert xy_vec.shape == pixels.shape transform = self._scene.camera_transform vectors = tu.unitize(np.column_stack((xy_vec, -np.ones_like(xy_vec[:, :1])))) vectors = tf.transform_points(vectors, transform, translate=False) origins = (np.ones_like(vectors) * tf.translation_from_matrix(transform)) indices = np.where(np.all(pixels == np.array([x, y]), axis=1)) if len(indices) > 0 and len(indices[0]) > 0: pixel_id = indices[0][0] ray_origin = np.expand_dims(origins[pixel_id], 0) ray_direction = np.expand_dims(vectors[pixel_id], 0) # print(x, y, pixel_id, ray_origin, ray_direction) mesh = self._scene.geometry['geometry_0'] locations, index_ray, index_tri = mesh.ray.intersects_location( ray_origins=ray_origin, ray_directions=ray_direction) if locations.size == 0: return ray_origins = np.tile(ray_origin, [locations.shape[0], 1]) distances = np.linalg.norm(locations - ray_origins, axis=1) idx = np.argsort(distances) # sort by disctances # color closest hit tri_color = mesh.visual.face_colors[index_tri[idx[0]]] if not np.alltrue(tri_color == [255, 0, 0, 255]): tri_color = [255, 0, 0, 255] else: # unselect triangle tri_color = [200, 200, 200, 255] mesh.visual.face_colors[index_tri[idx[0]]] = tri_color # collect clicked triangle ids tri_ids = np.where(np.all(mesh.visual.face_colors == [255, 0, 0, 255], axis=-1))[0] if len(tri_ids) >= self._settings_loader.min_triangles: # get center of triangles barycentric = mesh.triangles_center[tri_ids] joint_x = np.mean(barycentric[:, 0]) joint_y = np.mean(barycentric[:, 1]) joint_z = np.mean(barycentric[:, 2]) joint = np.stack([joint_x, joint_y, joint_z]) if 'joint_0' in self._scene.geometry: self._scene.delete_geometry('joint_0') joint = np.expand_dims(joint, 0) joint = PointCloud(joint, process=False) self._scene.add_geometry(joint, geom_name='joint_0') if self.view['rays']: from trimesh import load_path ray_visualize = load_path(np.hstack((ray_origin, ray_origin + ray_direction)).reshape(-1, 2, 3)) self._scene.add_geometry(ray_visualize, geom_name='cam_rays') # draw path where camera ray hits with mesh (only take 2 closest hits) path = np.hstack(locations[:2]).reshape(-1, 2, 3) ray_visualize = load_path(path) self._scene.add_geometry(ray_visualize, geom_name='cam_rays_hits')
def calcvertex_normals(FV, N): """ CalcVertexNormals calculates the normals and voronoi areas at each vertex INPUT: FV - triangle mesh in face vertex structure N - face normals OUTPUT - VertexNormals - [Nv X 3] matrix of normals at each vertex Avertex - [NvX1] voronoi area at each vertex Acorner - [NfX3] slice of the voronoi area at each face corner """ print("Calculating vertex normals .... Please wait") "Get all the edge vectors" e0 = np.array(FV.vertices[FV.faces[:, 2], :] - FV.vertices[FV.faces[:, 1], :]) e1 = np.array(FV.vertices[FV.faces[:, 0], :] - FV.vertices[FV.faces[:, 2], :]) e2 = np.array(FV.vertices[FV.faces[:, 1], :] - FV.vertices[FV.faces[:, 0], :]) "Normalize edge vectors " e0_norm = tut.unitize(e0) e1_norm = tut.unitize(e1) e2_norm = tut.unitize(e2) de0 = np.sqrt((e0[:, 0])**2 + (e0[:, 1])**2 + (e0[:, 2])**2) de1 = np.sqrt((e1[:, 0])**2 + (e1[:, 1])**2 + (e1[:, 2])**2) de2 = np.sqrt((e2[:, 0])**2 + (e2[:, 1])**2 + (e2[:, 2])**2) l2 = np.array([de0**2, de1**2, de2**2]) l2 = np.transpose(l2) """ using ew to compute the cot of the angles for the voronoi area calculation ew is the triangle barycenter. We check later if it's inside or outside the triangle """ ew = np.array([ l2[:, 0] * (l2[:, 1] + l2[:, 2] - l2[:, 0]), l2[:, 1] * (l2[:, 2] + l2[:, 0] - l2[:, 1]), l2[:, 2] * (l2[:, 0] + l2[:, 1] - l2[:, 2]) ]) s = (de0 + de1 + de2) / 2 "Af - face area vector" Af = np.sqrt(s * (s - de0) * (s - de1) * (s - de2)) "herons formula for triangle area, " "could have also used 0.5 * norm(cross(e0,e1)) " "Calc weights" Acorner = np.zeros((np.shape(FV.faces)[0], 3)) Avertex = np.zeros((np.shape(FV.vertices)[0], 1)) "Compute vertex normals" VertexNormals, up, vp = \ np.zeros((np.shape(FV.vertices)[0], 3)),\ np.zeros((np.shape(FV.vertices)[0], 3)),\ np.zeros((np.shape(FV.vertices)[0], 3)) for i in range(np.shape(FV.faces)[0]): wfv1 = Af[i] / ((de1[i]**2) * (de2[i]**2)) wfv2 = Af[i] / ((de0[i]**2) * (de2[i]**2)) wfv3 = Af[i] / ((de1[i]**2) * (de0[i]**2)) VertexNormals[FV.faces[i][0], :] += wfv1 * N[i, :] VertexNormals[FV.faces[i][1], :] += wfv2 * N[i, :] VertexNormals[FV.faces[i][2], :] += wfv3 * N[i, :] """ Calculate areas for weights according to Mayar et al. [2002] Check if the triangle is obtuse, right or acute """ "Changed shape for ew" if ew[0][i] <= 0: Acorner[i][1] = -0.25 * l2[i][2] * Af[i] / \ (np.dot(e0[i, :], np.transpose(e2[i, :]))) Acorner[i][2] = -0.25 * l2[i][1] * Af[i] / \ (np.dot(e0[i, :], np.transpose(e1[i, :]))) Acorner[i][0] = Af[i] - Acorner[i][2] - Acorner[i][1] elif ew[1][i] <= 0: Acorner[i][2] = -0.25 * l2[i][0] * Af[i] / \ (np.dot(e1[i, :], np.transpose(e0[i, :]))) Acorner[i][0] = -0.25 * l2[i][2] * Af[i] / \ (np.dot(e1[i, :], np.transpose(e2[i, :]))) Acorner[i][1] = Af[i] - Acorner[i][2] - Acorner[i][0] elif ew[2][i] <= 0: Acorner[i][0] = -0.25 * l2[i][1] * Af[i] / \ (np.dot(e2[i, :], np.transpose(e1[i, :]))) Acorner[i][1] = -0.25 * l2[i][0] * Af[i] / \ (np.dot(e2[i, :], np.transpose(e0[i, :]))) Acorner[i][2] = Af[i] - Acorner[i][1] - Acorner[i][0] else: ewscale = 0.5 * Af[i] / (ew[0][i] + ew[1][i] + ew[2][i]) Acorner[i][0] = ewscale * (ew[1][i] + ew[2][i]) Acorner[i][1] = ewscale * (ew[0][i] + ew[2][i]) Acorner[i][2] = ewscale * (ew[1][i] + ew[0][i]) Avertex[FV.faces[i][0]] += Acorner[i][0] Avertex[FV.faces[i][1]] += Acorner[i][1] Avertex[FV.faces[i][2]] += Acorner[i][2] " Calcul initial coordinate system " up[FV.faces[i][0], :] = e2_norm[i, :] up[FV.faces[i][1], :] = e0_norm[i, :] up[FV.faces[i][2], :] = e1_norm[i, :] VertexNormals = tut.unitize(VertexNormals) " Calcul initial vertex coordinate system" for i in range(np.shape(FV.vertices)[0]): up[i, :] = np.cross(up[i, :], VertexNormals[i, :]) up[i, :] = up[i, :] / np.linalg.norm(up[i, :]) vp[i, :] = np.cross(VertexNormals[i, :], up[i, :]) print("Finished calculating vertex normals") return VertexNormals, Avertex, Acorner, up, vp