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 = cotangent_tri_weight_v3(v1, v2, v3) / laplacian_edge_count(sys->edgehash, i2, i3); t2 = cotangent_tri_weight_v3(v2, v3, v1) / laplacian_edge_count(sys->edgehash, i3, i1); t3 = cotangent_tri_weight_v3(v3, v1, v2) / laplacian_edge_count(sys->edgehash, i1, i2); EIG_linear_solver_matrix_add(sys->context, i1, i1, (t2 + t3) * varea[i1]); EIG_linear_solver_matrix_add(sys->context, i2, i2, (t1 + t3) * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i3, i3, (t1 + t2) * varea[i3]); EIG_linear_solver_matrix_add(sys->context, i1, i2, -t3 * varea[i1]); EIG_linear_solver_matrix_add(sys->context, i2, i1, -t3 * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i2, i3, -t1 * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i3, i2, -t1 * varea[i3]); EIG_linear_solver_matrix_add(sys->context, i3, i1, -t2 * varea[i3]); EIG_linear_solver_matrix_add(sys->context, 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]; } }
/** * 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 no[3]; float w2, w3; int i = 3, j, ti; int idv[3]; for (ti = 0; ti < sys->total_tris; ti++) { const unsigned int *vidt = sys->tris[ti]; const float *co[3]; co[0] = sys->co[vidt[0]]; co[1] = sys->co[vidt[1]]; co[2] = sys->co[vidt[2]]; normal_tri_v3(no, UNPACK3(co)); add_v3_v3(sys->no[vidt[0]], no); add_v3_v3(sys->no[vidt[1]], no); add_v3_v3(sys->no[vidt[2]], no); for (j = 0; j < 3; j++) { const float *v1, *v2, *v3; idv[0] = vidt[j]; idv[1] = vidt[(j + 1) % i]; idv[2] = vidt[(j + 2) % i]; v1 = sys->co[idv[0]]; v2 = sys->co[idv[1]]; v3 = sys->co[idv[2]]; w2 = cotangent_tri_weight_v3(v3, v1, v2); w3 = cotangent_tri_weight_v3(v2, v3, v1); sys->delta[idv[0]][0] += v1[0] * (w2 + w3); sys->delta[idv[0]][1] += v1[1] * (w2 + w3); sys->delta[idv[0]][2] += v1[2] * (w2 + w3); sys->delta[idv[0]][0] -= v2[0] * w2; sys->delta[idv[0]][1] -= v2[1] * w2; sys->delta[idv[0]][2] -= v2[2] * w2; sys->delta[idv[0]][0] -= v3[0] * w3; sys->delta[idv[0]][1] -= v3[1] * w3; sys->delta[idv[0]][2] -= v3[2] * w3; nlMatrixAdd(idv[0], idv[1], -w2); nlMatrixAdd(idv[0], idv[2], -w3); nlMatrixAdd(idv[0], idv[0], w2 + w3); } } }
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 = cotangent_tri_weight_v3(v1, v2, v3); t2 = cotangent_tri_weight_v3(v2, v3, v1); t3 = cotangent_tri_weight_v3(v3, v1, v2); if (angle_v3v3v3(v2, v1, v3) > DEG2RADF(90.0f)) obtuse = 1; else if (angle_v3v3v3(v1, v2, v3) > DEG2RADF(90.0f)) obtuse = 2; else if (angle_v3v3v3(v1, v3, v2) > DEG2RADF(90.0f)) 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 fill_laplacian_matrix(LaplacianSystem *sys) { float *v1, *v2, *v3, *v4; float w2, w3, w4; int i, j; bool 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 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(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]); } } }
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]; bool 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] : NULL; 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 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; } } else { w1 = cotangent_tri_weight_v3(v1, v2, v3) / 2.0f; w2 = cotangent_tri_weight_v3(v2, v3, v1) / 2.0f; w3 = cotangent_tri_weight_v3(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]; } } }
/** * 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 = (cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2)) / 2.0f; w3 = (cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3)) / 2.0f; w4 = (cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(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 = cotangent_tri_weight_v3(v3, v1, v2); w3 = cotangent_tri_weight_v3(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); } } }
static void init_laplacian_matrix(LaplacianSystem *sys) { float *v1, *v2; float w1, w2, w3; float areaf; int i; unsigned int idv1, idv2; 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->numPolys; i++) { const MPoly *mp = &sys->mpoly[i]; const MLoop *l_next = &sys->mloop[mp->loopstart]; const MLoop *l_term = l_next + mp->totloop; const MLoop *l_prev = l_term - 2; const MLoop *l_curr = l_term - 1; for (; l_next != l_term; l_prev = l_curr, l_curr = l_next, l_next++) { const float *v_prev = sys->vertexCos[l_prev->v]; const float *v_curr = sys->vertexCos[l_curr->v]; const float *v_next = sys->vertexCos[l_next->v]; const unsigned int l_curr_index = l_curr - sys->mloop; sys->numNeFa[l_curr->v] += 1; areaf = area_tri_v3(v_prev, v_curr, v_next); if (areaf < sys->min_area) { sys->zerola[l_curr->v] = 1; } sys->ring_areas[l_prev->v] += areaf; sys->ring_areas[l_curr->v] += areaf; sys->ring_areas[l_next->v] += areaf; w1 = cotangent_tri_weight_v3(v_curr, v_next, v_prev) / 2.0f; w2 = cotangent_tri_weight_v3(v_next, v_prev, v_curr) / 2.0f; w3 = cotangent_tri_weight_v3(v_prev, v_curr, v_next) / 2.0f; sys->fweights[l_curr_index][0] += w1; sys->fweights[l_curr_index][1] += w2; sys->fweights[l_curr_index][2] += w3; sys->vweights[l_curr->v] += w2 + w3; sys->vweights[l_next->v] += w1 + w3; sys->vweights[l_prev->v] += 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]; } } }