static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3) { float t1, t2, t3; float *varea= sys->varea, *v1, *v2, *v3; v1= sys->verts[i1]; v2= sys->verts[i2]; v3= sys->verts[i3]; /* instead of *0.5 we divided by the number of faces of the edge, it still needs to be verified that this is indeed the correct thing to do! */ t1= cotan_weight(v1, v2, v3)/laplacian_edge_count(sys->edgehash, i2, i3); t2= cotan_weight(v2, v3, v1)/laplacian_edge_count(sys->edgehash, i3, i1); t3= cotan_weight(v3, v1, v2)/laplacian_edge_count(sys->edgehash, i1, i2); nlMatrixAdd(i1, i1, (t2+t3)*varea[i1]); nlMatrixAdd(i2, i2, (t1+t3)*varea[i2]); nlMatrixAdd(i3, i3, (t1+t2)*varea[i3]); nlMatrixAdd(i1, i2, -t3*varea[i1]); nlMatrixAdd(i2, i1, -t3*varea[i2]); nlMatrixAdd(i2, i3, -t1*varea[i2]); nlMatrixAdd(i3, i2, -t1*varea[i3]); nlMatrixAdd(i3, i1, -t2*varea[i3]); nlMatrixAdd(i1, i3, -t2*varea[i1]); if(sys->storeweights) { sys->fweights[f][0]= t1*varea[i1]; sys->fweights[f][1]= t2*varea[i2]; sys->fweights[f][2]= t3*varea[i3]; } }
static void laplacian_triangle_area(LaplacianSystem *sys, int i1, int i2, int i3) { float t1, t2, t3, len1, len2, len3, area; float *varea= sys->varea, *v1, *v2, *v3; int obtuse = 0; v1= sys->verts[i1]; v2= sys->verts[i2]; v3= sys->verts[i3]; t1= cotan_weight(v1, v2, v3); t2= cotan_weight(v2, v3, v1); t3= cotan_weight(v3, v1, v2); if(RAD2DEGF(angle_v3v3v3(v2, v1, v3)) > 90) obtuse= 1; else if(RAD2DEGF(angle_v3v3v3(v1, v2, v3)) > 90) obtuse= 2; else if(RAD2DEGF(angle_v3v3v3(v1, v3, v2)) > 90) obtuse= 3; if (obtuse > 0) { area= area_tri_v3(v1, v2, v3); varea[i1] += (obtuse == 1)? area: area*0.5f; varea[i2] += (obtuse == 2)? area: area*0.5f; varea[i3] += (obtuse == 3)? area: area*0.5f; } else { len1= len_v3v3(v2, v3); len2= len_v3v3(v1, v3); len3= len_v3v3(v1, v2); t1 *= len1*len1; t2 *= len2*len2; t3 *= len3*len3; varea[i1] += (t2 + t3)*0.25f; varea[i2] += (t1 + t3)*0.25f; varea[i3] += (t1 + t2)*0.25f; } }
static void init_laplacian_matrix(LaplacianSystem *sys) { float *v1, *v2, *v3, *v4; float w1, w2, w3, w4; float areaf; int i, j; unsigned int idv1, idv2, idv3, idv4, idv[4]; int has_4_vert; for (i = 0; i < sys->numEdges; i++) { idv1 = sys->medges[i].v1; idv2 = sys->medges[i].v2; v1 = sys->vertexCos[idv1]; v2 = sys->vertexCos[idv2]; sys->numNeEd[idv1] = sys->numNeEd[idv1] + 1; sys->numNeEd[idv2] = sys->numNeEd[idv2] + 1; w1 = len_v3v3(v1, v2); if (w1 < sys->min_area) { sys->zerola[idv1] = 1; sys->zerola[idv2] = 1; } else { w1 = 1.0f / w1; } sys->eweights[i] = w1; } for (i = 0; i < sys->numFaces; i++) { has_4_vert = ((&sys->mfaces[i])->v4) ? 1 : 0; idv1 = sys->mfaces[i].v1; idv2 = sys->mfaces[i].v2; idv3 = sys->mfaces[i].v3; idv4 = has_4_vert ? sys->mfaces[i].v4 : 0; sys->numNeFa[idv1] += 1; sys->numNeFa[idv2] += 1; sys->numNeFa[idv3] += 1; if (has_4_vert) sys->numNeFa[idv4] += 1; v1 = sys->vertexCos[idv1]; v2 = sys->vertexCos[idv2]; v3 = sys->vertexCos[idv3]; v4 = has_4_vert ? sys->vertexCos[idv4] : 0; if (has_4_vert) { areaf = area_quad_v3(v1, v2, v3, sys->vertexCos[sys->mfaces[i].v4]); } else { areaf = area_tri_v3(v1, v2, v3); } if (fabsf(areaf) < sys->min_area) { sys->zerola[idv1] = 1; sys->zerola[idv2] = 1; sys->zerola[idv3] = 1; if (has_4_vert) sys->zerola[idv4] = 1; } if (has_4_vert) { sys->ring_areas[idv1] += average_area_quad_v3(v1, v2, v3, v4); sys->ring_areas[idv2] += average_area_quad_v3(v2, v3, v4, v1); sys->ring_areas[idv3] += average_area_quad_v3(v3, v4, v1, v2); sys->ring_areas[idv4] += average_area_quad_v3(v4, v1, v2, v3); } else { sys->ring_areas[idv1] += areaf; sys->ring_areas[idv2] += areaf; sys->ring_areas[idv3] += areaf; } if (has_4_vert) { idv[0] = idv1; idv[1] = idv2; idv[2] = idv3; idv[3] = idv4; for (j = 0; j < 4; j++) { idv1 = idv[j]; idv2 = idv[(j + 1) % 4]; idv3 = idv[(j + 2) % 4]; idv4 = idv[(j + 3) % 4]; v1 = sys->vertexCos[idv1]; v2 = sys->vertexCos[idv2]; v3 = sys->vertexCos[idv3]; v4 = sys->vertexCos[idv4]; w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; } } else { w1 = cotan_weight(v1, v2, v3) / 2.0f; w2 = cotan_weight(v2, v3, v1) / 2.0f; w3 = cotan_weight(v3, v1, v2) / 2.0f; sys->fweights[i][0] = sys->fweights[i][0] + w1; sys->fweights[i][1] = sys->fweights[i][1] + w2; sys->fweights[i][2] = sys->fweights[i][2] + w3; sys->vweights[idv1] = sys->vweights[idv1] + w2 + w3; sys->vweights[idv2] = sys->vweights[idv2] + w1 + w3; sys->vweights[idv3] = sys->vweights[idv3] + w1 + w2; } } for (i = 0; i < sys->numEdges; i++) { idv1 = sys->medges[i].v1; idv2 = sys->medges[i].v2; /* if is boundary, apply scale-dependent umbrella operator only with neighboors in boundary */ if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2]) { sys->vlengths[idv1] += sys->eweights[i]; sys->vlengths[idv2] += sys->eweights[i]; } } }
static void fill_laplacian_matrix(LaplacianSystem *sys) { float *v1, *v2, *v3, *v4; float w2, w3, w4; int i, j; int has_4_vert; unsigned int idv1, idv2, idv3, idv4, idv[4]; for (i = 0; i < sys->numFaces; i++) { idv1 = sys->mfaces[i].v1; idv2 = sys->mfaces[i].v2; idv3 = sys->mfaces[i].v3; has_4_vert = ((&sys->mfaces[i])->v4) ? 1 : 0; if (has_4_vert) { idv[0] = sys->mfaces[i].v1; idv[1] = sys->mfaces[i].v2; idv[2] = sys->mfaces[i].v3; idv[3] = sys->mfaces[i].v4; for (j = 0; j < 4; j++) { idv1 = idv[j]; idv2 = idv[(j + 1) % 4]; idv3 = idv[(j + 2) % 4]; idv4 = idv[(j + 3) % 4]; v1 = sys->vertexCos[idv1]; v2 = sys->vertexCos[idv2]; v3 = sys->vertexCos[idv3]; v4 = sys->vertexCos[idv4]; w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); w2 = w2 / 4.0f; w3 = w3 / 4.0f; w4 = w4 / 4.0f; if (sys->numNeEd[idv1] == sys->numNeFa[idv1] && sys->zerola[idv1] == 0) { nlMatrixAdd(idv1, idv2, w2 * sys->vweights[idv1]); nlMatrixAdd(idv1, idv3, w3 * sys->vweights[idv1]); nlMatrixAdd(idv1, idv4, w4 * sys->vweights[idv1]); } } } else { /* Is ring if number of faces == number of edges around vertice*/ if (sys->numNeEd[idv1] == sys->numNeFa[idv1] && sys->zerola[idv1] == 0) { nlMatrixAdd(idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); nlMatrixAdd(idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); } if (sys->numNeEd[idv2] == sys->numNeFa[idv2] && sys->zerola[idv2] == 0) { nlMatrixAdd(idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); nlMatrixAdd(idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); } if (sys->numNeEd[idv3] == sys->numNeFa[idv3] && sys->zerola[idv3] == 0) { nlMatrixAdd(idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); nlMatrixAdd(idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); } } } for (i = 0; i < sys->numEdges; i++) { idv1 = sys->medges[i].v1; idv2 = sys->medges[i].v2; /* Is boundary */ if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2] && sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { nlMatrixAdd(idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); nlMatrixAdd(idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); } } }
/** * This method computes the Laplacian Matrix and Differential Coordinates for all vertex in the mesh. * The Linear system is LV = d * Where L is Laplacian Matrix, V as the vertexes in Mesh, d is the differential coordinates * The Laplacian Matrix is computes as a * Lij = sum(Wij) (if i == j) * Lij = Wij (if i != j) * Wij is weight between vertex Vi and vertex Vj, we use cotangent weight * * The Differential Coordinate is computes as a * di = Vi * sum(Wij) - sum(Wij * Vj) * Where : * di is the Differential Coordinate i * sum (Wij) is the sum of all weights between vertex Vi and its vertexes neighbors (Vj) * sum (Wij * Vj) is the sum of the product between vertex neighbor Vj and weight Wij for all neighborhood. * * This Laplacian Matrix is described in the paper: * Desbrun M. et.al, Implicit fairing of irregular meshes using diffusion and curvature flow, SIGGRAPH '99, pag 317-324, * New York, USA * * The computation of Laplace Beltrami operator on Hybrid Triangle/Quad Meshes is described in the paper: * Pinzon A., Romero E., Shape Inflation With an Adapted Laplacian Operator For Hybrid Quad/Triangle Meshes, * Conference on Graphics Patterns and Images, SIBGRAPI, 2013 * * The computation of Differential Coordinates is described in the paper: * Sorkine, O. Laplacian Surface Editing. Proceedings of the EUROGRAPHICS/ACM SIGGRAPH Symposium on Geometry Processing, * 2004. p. 179-188. */ static void initLaplacianMatrix(LaplacianSystem *sys) { float v1[3], v2[3], v3[3], v4[3], no[3]; float w2, w3, w4; int i, j, fi; bool has_4_vert; unsigned int idv1, idv2, idv3, idv4; for (fi = 0; fi < sys->total_faces; fi++) { const unsigned int *vidf = sys->faces[fi]; idv1 = vidf[0]; idv2 = vidf[1]; idv3 = vidf[2]; idv4 = vidf[3]; has_4_vert = vidf[3] ? 1 : 0; if (has_4_vert) { normal_quad_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3], sys->co[idv4]); add_v3_v3(sys->no[idv4], no); i = 4; } else { normal_tri_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3]); i = 3; } add_v3_v3(sys->no[idv1], no); add_v3_v3(sys->no[idv2], no); add_v3_v3(sys->no[idv3], no); for (j = 0; j < i; j++) { idv1 = vidf[j]; idv2 = vidf[(j + 1) % i]; idv3 = vidf[(j + 2) % i]; idv4 = has_4_vert ? vidf[(j + 3) % i] : 0; copy_v3_v3(v1, sys->co[idv1]); copy_v3_v3(v2, sys->co[idv2]); copy_v3_v3(v3, sys->co[idv3]); if (has_4_vert) { copy_v3_v3(v4, sys->co[idv4]); } if (has_4_vert) { w2 = (cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2)) / 2.0f; w3 = (cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3)) / 2.0f; w4 = (cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1)) / 2.0f; sys->delta[idv1][0] -= v4[0] * w4; sys->delta[idv1][1] -= v4[1] * w4; sys->delta[idv1][2] -= v4[2] * w4; nlRightHandSideAdd(0, idv1, -v4[0] * w4); nlRightHandSideAdd(1, idv1, -v4[1] * w4); nlRightHandSideAdd(2, idv1, -v4[2] * w4); nlMatrixAdd(idv1, idv4, -w4); } else { w2 = cotan_weight(v3, v1, v2); w3 = cotan_weight(v2, v3, v1); w4 = 0.0f; } sys->delta[idv1][0] += v1[0] * (w2 + w3 + w4); sys->delta[idv1][1] += v1[1] * (w2 + w3 + w4); sys->delta[idv1][2] += v1[2] * (w2 + w3 + w4); sys->delta[idv1][0] -= v2[0] * w2; sys->delta[idv1][1] -= v2[1] * w2; sys->delta[idv1][2] -= v2[2] * w2; sys->delta[idv1][0] -= v3[0] * w3; sys->delta[idv1][1] -= v3[1] * w3; sys->delta[idv1][2] -= v3[2] * w3; nlMatrixAdd(idv1, idv2, -w2); nlMatrixAdd(idv1, idv3, -w3); nlMatrixAdd(idv1, idv1, w2 + w3 + w4); } } }