diff options
Diffstat (limited to 'extern/quadriflow/src/subdivide.cpp')
-rw-r--r-- | extern/quadriflow/src/subdivide.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/extern/quadriflow/src/subdivide.cpp b/extern/quadriflow/src/subdivide.cpp new file mode 100644 index 00000000000..c408bbc6394 --- /dev/null +++ b/extern/quadriflow/src/subdivide.cpp @@ -0,0 +1,516 @@ +#include "subdivide.hpp" + +#include <fstream> +#include <queue> + +#include "dedge.hpp" +#include "disajoint-tree.hpp" +#include "field-math.hpp" +#include "parametrizer.hpp" + +namespace qflow { + +void subdivide(MatrixXi &F, MatrixXd &V, VectorXd& rho, VectorXi &V2E, VectorXi &E2E, VectorXi &boundary, + VectorXi &nonmanifold, double maxLength) { + typedef std::pair<double, int> Edge; + + std::priority_queue<Edge> queue; + + maxLength *= maxLength; + + for (int i = 0; i < E2E.size(); ++i) { + int v0 = F(i % 3, i / 3), v1 = F((i + 1) % 3, i / 3); + if (nonmanifold[v0] || nonmanifold[v1]) continue; + double length = (V.col(v0) - V.col(v1)).squaredNorm(); + if (length > maxLength || length > std::max(maxLength * 0.75, std::min(rho[v0], rho[v1]) * 1.0)) { + int other = E2E[i]; + if (other == -1 || other > i) queue.push(Edge(length, i)); + } + } + + int nV = V.cols(), nF = F.cols(), nSplit = 0; + /* + / v0 \ + v1p 1 | 0 v0p + \ v1 / + + / v0 \ + / 1 | 0 \ + v1p - vn - v0p + \ 2 | 3 / + \ v1 / + + f0: vn, v0p, v0 + f1: vn, v0, v1p + f2: vn, v1p, v1 + f3: vn, v1, v0p + */ + int counter = 0; + while (!queue.empty()) { + counter += 1; + Edge edge = queue.top(); + queue.pop(); + int e0 = edge.second, e1 = E2E[e0]; + bool is_boundary = e1 == -1; + int f0 = e0 / 3, f1 = is_boundary ? -1 : (e1 / 3); + int v0 = F(e0 % 3, f0), v0p = F((e0 + 2) % 3, f0), v1 = F((e0 + 1) % 3, f0); + if ((V.col(v0) - V.col(v1)).squaredNorm() != edge.first) { + continue; + } + int v1p = is_boundary ? -1 : F((e1 + 2) % 3, f1); + int vn = nV++; + nSplit++; + /* Update V */ + if (nV > V.cols()) { + V.conservativeResize(V.rows(), V.cols() * 2); + rho.conservativeResize(V.cols() * 2); + V2E.conservativeResize(V.cols()); + boundary.conservativeResize(V.cols()); + nonmanifold.conservativeResize(V.cols()); + } + + /* Update V */ + V.col(vn) = (V.col(v0) + V.col(v1)) * 0.5f; + rho[vn] = 0.5f * (rho[v0], rho[v1]); + nonmanifold[vn] = false; + boundary[vn] = is_boundary; + + /* Update F and E2E */ + int f2 = is_boundary ? -1 : (nF++); + int f3 = nF++; + if (nF > F.cols()) { + F.conservativeResize(F.rows(), std::max(nF, (int)F.cols() * 2)); + E2E.conservativeResize(F.cols() * 3); + } + + /* Update F */ + F.col(f0) << vn, v0p, v0; + if (!is_boundary) { + F.col(f1) << vn, v0, v1p; + F.col(f2) << vn, v1p, v1; + } + F.col(f3) << vn, v1, v0p; + + /* Update E2E */ + const int e0p = E2E[dedge_prev_3(e0)], e0n = E2E[dedge_next_3(e0)]; + +#define sE2E(a, b) \ + E2E[a] = b; \ + if (b != -1) E2E[b] = a; + sE2E(3 * f0 + 0, 3 * f3 + 2); + sE2E(3 * f0 + 1, e0p); + sE2E(3 * f3 + 1, e0n); + if (is_boundary) { + sE2E(3 * f0 + 2, -1); + sE2E(3 * f3 + 0, -1); + } else { + const int e1p = E2E[dedge_prev_3(e1)], e1n = E2E[dedge_next_3(e1)]; + sE2E(3 * f0 + 2, 3 * f1 + 0); + sE2E(3 * f1 + 1, e1n); + sE2E(3 * f1 + 2, 3 * f2 + 0); + sE2E(3 * f2 + 1, e1p); + sE2E(3 * f2 + 2, 3 * f3 + 0); + } +#undef sE2E + + /* Update V2E */ + V2E[v0] = 3 * f0 + 2; + V2E[vn] = 3 * f0 + 0; + V2E[v1] = 3 * f3 + 1; + V2E[v0p] = 3 * f0 + 1; + if (!is_boundary) V2E[v1p] = 3 * f1 + 2; + + auto schedule = [&](int f) { + for (int i = 0; i < 3; ++i) { + double length = (V.col(F(i, f)) - V.col(F((i + 1) % 3, f))).squaredNorm(); + if (length > maxLength + || length > std::max(maxLength * 0.75, std::min(rho[F(i, f)], rho[F((i + 1) % 3, f)]) * 1.0)) + queue.push(Edge(length, f * 3 + i)); + } + }; + + schedule(f0); + if (!is_boundary) { + schedule(f2); + schedule(f1); + }; + schedule(f3); + } + F.conservativeResize(F.rows(), nF); + V.conservativeResize(V.rows(), nV); + rho.conservativeResize(nV); + V2E.conservativeResize(nV); + boundary.conservativeResize(nV); + nonmanifold.conservativeResize(nV); + E2E.conservativeResize(nF * 3); +} + +void subdivide_edgeDiff(MatrixXi &F, MatrixXd &V, MatrixXd &N, MatrixXd &Q, MatrixXd &O, MatrixXd* S, + VectorXi &V2E, VectorXi &E2E, VectorXi &boundary, VectorXi &nonmanifold, + std::vector<Vector2i> &edge_diff, std::vector<DEdge> &edge_values, + std::vector<Vector3i> &face_edgeOrients, std::vector<Vector3i> &face_edgeIds, + std::vector<int>& sharp_edges, std::map<int, int> &singularities, int max_len) { + struct EdgeLink { + int id; + double length; + Vector2i diff; + int maxlen() const { return std::max(abs(diff[0]), abs(diff[1])); } + bool operator<(const EdgeLink &link) const { return maxlen() < link.maxlen(); } + }; + + struct FaceOrient { + int orient; + Vector3i d; + Vector3d q; + Vector3d n; + }; + + std::vector<FaceOrient> face_spaces(F.cols()); + std::priority_queue<EdgeLink> queue; + std::vector<Vector2i> diffs(E2E.size()); + for (int i = 0; i < F.cols(); ++i) { + for (int j = 0; j < 3; ++j) { + int eid = i * 3 + j; + diffs[eid] = rshift90(edge_diff[face_edgeIds[i][j]], face_edgeOrients[i][j]); + } + } + for (int i = 0; i < F.cols(); ++i) { + FaceOrient orient{}; + orient.q = Q.col(F(0, i)); + orient.n = N.col(F(0, i)); + int orient_diff[3]; + for (int j = 0; j < 3; ++j) { + int final_orient = face_edgeOrients[i][j]; + int eid = face_edgeIds[i][j]; + auto value = compat_orientation_extrinsic_index_4( + Q.col(edge_values[eid].x), N.col(edge_values[eid].x), orient.q, orient.n); + int target_orient = (value.second - value.first + 4) % 4; + if (F(j, i) == edge_values[eid].y) target_orient = (target_orient + 2) % 4; + orient_diff[j] = (final_orient - target_orient + 4) % 4; + } + if (orient_diff[0] == orient_diff[1]) + orient.orient = orient_diff[0]; + else if (orient_diff[0] == orient_diff[2]) + orient.orient = orient_diff[2]; + else if (orient_diff[1] == orient_diff[2]) + orient.orient = orient_diff[1]; + orient.d = Vector3i((orient_diff[0] - orient.orient + 4) % 4, + (orient_diff[1] - orient.orient + 4) % 4, + (orient_diff[2] - orient.orient + 4) % 4); + face_spaces[i] = (orient); + } + for (int i = 0; i < E2E.size(); ++i) { + int v0 = F(i % 3, i / 3), v1 = F((i + 1) % 3, i / 3); + if (nonmanifold[v0] || nonmanifold[v1]) continue; + double length = (V.col(v0) - V.col(v1)).squaredNorm(); + Vector2i diff = diffs[i]; + if (abs(diff[0]) > max_len || abs(diff[1]) > max_len) { + int other = E2E[i]; + if (other == -1 || other > i) { + EdgeLink e; + e.id = i; + e.length = length; + e.diff = diff; + queue.push(e); + } + } + } + auto AnalyzeOrient = [&](int f0, const Vector3i &d) { + for (int j = 0; j < 3; ++j) { + int orient = face_spaces[f0].orient + d[j]; + int v = std::min(F(j, f0), F((j + 1) % 3, f0)); + auto value = compat_orientation_extrinsic_index_4( + Q.col(v), N.col(v), face_spaces[f0].q, face_spaces[f0].n); + if (F(j, f0) != v) orient += 2; + face_edgeOrients[f0][j] = (orient + value.second - value.first + 4) % 4; + } + face_spaces[f0].d = d; + for (int j = 0; j < 3; ++j) { + int eid = face_edgeIds[f0][j]; + int orient = face_edgeOrients[f0][j]; + auto diff = rshift90(diffs[f0 * 3 + j], (4 - orient) % 4); + edge_diff[eid] = diff; + } + }; + auto FixOrient = [&](int f0) { + for (int j = 0; j < 3; ++j) { + auto diff = edge_diff[face_edgeIds[f0][j]]; + if (rshift90(diff, face_edgeOrients[f0][j]) != diffs[f0 * 3 + j]) { + int orient = 0; + while (orient < 4 && rshift90(diff, orient) != diffs[f0 * 3 + j]) orient += 1; + face_spaces[f0].d[j] = + (face_spaces[f0].d[j] + orient - face_edgeOrients[f0][j]) % 4; + face_edgeOrients[f0][j] = orient; + } + } + }; + /* + auto Length = [&](int f0) { + int l = 0; + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 2; ++k) { + l += abs(diffs[f0*3+j][k]); + } + printf("<%d %d> ", diffs[f0*3+j][0], diffs[f0*3+j][1]); + } + printf("\n"); + return l; + }; + */ + int nV = V.cols(), nF = F.cols(), nSplit = 0; + /* + / v0 \ + v1p 1 | 0 v0p + \ v1 / + + / v0 \ + / 1 | 0 \ + v1p - vn - v0p + \ 2 | 3 / + \ v1 / + + f0: vn, v0p, v0 + f1: vn, v0, v1p + f2: vn, v1p, v1 + f3: vn, v1, v0p + */ + int counter = 0; + while (!queue.empty()) { + counter += 1; + EdgeLink edge = queue.top(); + queue.pop(); + + int e0 = edge.id, e1 = E2E[e0]; + bool is_boundary = e1 == -1; + int f0 = e0 / 3, f1 = is_boundary ? -1 : (e1 / 3); + int v0 = F(e0 % 3, f0), v0p = F((e0 + 2) % 3, f0), v1 = F((e0 + 1) % 3, f0); + if ((V.col(v0) - V.col(v1)).squaredNorm() != edge.length) { + continue; + } + if (abs(diffs[e0][0]) < 2 && abs(diffs[e0][1]) < 2) continue; + if (f1 != -1) { + face_edgeOrients.push_back(Vector3i()); + sharp_edges.push_back(0); + sharp_edges.push_back(0); + sharp_edges.push_back(0); + face_edgeIds.push_back(Vector3i()); + } + int v1p = is_boundary ? -1 : F((e1 + 2) % 3, f1); + int vn = nV++; + nSplit++; + if (nV > V.cols()) { + V.conservativeResize(V.rows(), V.cols() * 2); + N.conservativeResize(N.rows(), N.cols() * 2); + Q.conservativeResize(Q.rows(), Q.cols() * 2); + O.conservativeResize(O.rows(), O.cols() * 2); + if (S) + S->conservativeResize(S->rows(), S->cols() * 2); + V2E.conservativeResize(V.cols()); + boundary.conservativeResize(V.cols()); + nonmanifold.conservativeResize(V.cols()); + } + + V.col(vn) = (V.col(v0) + V.col(v1)) * 0.5; + N.col(vn) = N.col(v0); + Q.col(vn) = Q.col(v0); + O.col(vn) = (O.col(v0) + O.col(v1)) * 0.5; + if (S) + S->col(vn) = S->col(v0); + + nonmanifold[vn] = false; + boundary[vn] = is_boundary; + + int eid = face_edgeIds[f0][e0 % 3]; + int sharp_eid = sharp_edges[e0]; + int eid01 = face_edgeIds[f0][(e0 + 1) % 3]; + int sharp_eid01 = sharp_edges[f0 * 3 + (e0 + 1) % 3]; + int eid02 = face_edgeIds[f0][(e0 + 2) % 3]; + int sharp_eid02 = sharp_edges[f0 * 3 + (e0 + 2) % 3]; + + int eid0, eid1, eid0p, eid1p; + int sharp_eid0, sharp_eid1, sharp_eid0p, sharp_eid1p; + + eid0 = eid; + sharp_eid0 = sharp_eid; + edge_values[eid0] = DEdge(v0, vn); + + eid1 = edge_values.size(); + sharp_eid1 = sharp_eid; + edge_values.push_back(DEdge(vn, v1)); + edge_diff.push_back(Vector2i()); + + eid0p = edge_values.size(); + sharp_eid0p = 0; + edge_values.push_back(DEdge(vn, v0p)); + edge_diff.push_back(Vector2i()); + + int f2 = is_boundary ? -1 : (nF++); + int f3 = nF++; + sharp_edges.push_back(0); + sharp_edges.push_back(0); + sharp_edges.push_back(0); + face_edgeIds.push_back(Vector3i()); + face_edgeOrients.push_back(Vector3i()); + + if (nF > F.cols()) { + F.conservativeResize(F.rows(), std::max(nF, (int)F.cols() * 2)); + face_spaces.resize(F.cols()); + E2E.conservativeResize(F.cols() * 3); + diffs.resize(F.cols() * 3); + } + + auto D01 = diffs[e0]; + auto D1p = diffs[e0 / 3 * 3 + (e0 + 1) % 3]; + auto Dp0 = diffs[e0 / 3 * 3 + (e0 + 2) % 3]; + + Vector2i D0n = D01 / 2; + + auto orients1 = face_spaces[f0]; + F.col(f0) << vn, v0p, v0; + face_edgeIds[f0] = Vector3i(eid0p, eid02, eid0); + sharp_edges[f0 * 3] = sharp_eid0p; + sharp_edges[f0 * 3 + 1] = sharp_eid02; + sharp_edges[f0 * 3 + 2] = sharp_eid0; + + diffs[f0 * 3] = D01 + D1p - D0n; + diffs[f0 * 3 + 1] = Dp0; + diffs[f0 * 3 + 2] = D0n; + int o1 = e0 % 3, o2 = e1 % 3; + AnalyzeOrient(f0, Vector3i(0, orients1.d[(o1 + 2) % 3], orients1.d[o1])); + if (!is_boundary) { + auto orients2 = face_spaces[f1]; + int eid11 = face_edgeIds[f1][(e1 + 1) % 3]; + int sharp_eid11 = sharp_edges[f1 * 3 + (e1 + 1) % 3]; + int eid12 = face_edgeIds[f1][(e1 + 2) % 3]; + int sharp_eid12 = sharp_edges[f1 * 3 + (e1 + 2) % 3]; + + auto Ds10 = diffs[e1]; + auto Ds0p = diffs[e1 / 3 * 3 + (e1 + 1) % 3]; + + auto Dsp1 = diffs[e1 / 3 * 3 + (e1 + 2) % 3]; + int orient = 0; + while (rshift90(D01, orient) != Ds10) orient += 1; + Vector2i Dsn0 = rshift90(D0n, orient); + + F.col(f1) << vn, v0, v1p; + eid1p = edge_values.size(); + sharp_eid1p = 0; + edge_values.push_back(DEdge(vn, v1p)); + edge_diff.push_back(Vector2i()); + + sharp_edges[f1 * 3] = sharp_eid0; + sharp_edges[f1 * 3 + 1] = sharp_eid11; + sharp_edges[f1 * 3 + 2] = sharp_eid1p; + face_edgeIds[f1] = (Vector3i(eid0, eid11, eid1p)); + diffs[f1 * 3] = Dsn0; + diffs[f1 * 3 + 1] = Ds0p; + diffs[f1 * 3 + 2] = Dsp1 + (Ds10 - Dsn0); + + AnalyzeOrient(f1, Vector3i(orients2.d[o2], orients2.d[(o2 + 1) % 3], 0)); + + face_spaces[f2] = face_spaces[f1]; + sharp_edges[f2 * 3] = sharp_eid1p; + sharp_edges[f2 * 3 + 1] = sharp_eid12; + sharp_edges[f2 * 3 + 2] = sharp_eid1; + face_edgeIds[f2] = (Vector3i(eid1p, eid12, eid1)); + F.col(f2) << vn, v1p, v1; + diffs[f2 * 3] = -Dsp1 - (Ds10 - Dsn0); + diffs[f2 * 3 + 1] = Dsp1; + diffs[f2 * 3 + 2] = Ds10 - Dsn0; + + AnalyzeOrient(f2, Vector3i(0, orients2.d[(o2 + 2) % 3], orients2.d[o2])); + } + face_spaces[f3] = face_spaces[f0]; + sharp_edges[f3 * 3] = sharp_eid1; + sharp_edges[f3 * 3 + 1] = sharp_eid01; + sharp_edges[f3 * 3 + 2] = sharp_eid0p; + face_edgeIds[f3] = (Vector3i(eid1, eid01, eid0p)); + F.col(f3) << vn, v1, v0p; + diffs[f3 * 3] = D01 - D0n; + diffs[f3 * 3 + 1] = D1p; + diffs[f3 * 3 + 2] = D0n - (D01 + D1p); + + AnalyzeOrient(f3, Vector3i(orients1.d[o1], orients1.d[(o1 + 1) % 3], 0)); + + FixOrient(f0); + if (!is_boundary) { + FixOrient(f1); + FixOrient(f2); + } + FixOrient(f3); + + const int e0p = E2E[dedge_prev_3(e0)], e0n = E2E[dedge_next_3(e0)]; + +#define sE2E(a, b) \ + E2E[a] = b; \ + if (b != -1) E2E[b] = a; + sE2E(3 * f0 + 0, 3 * f3 + 2); + sE2E(3 * f0 + 1, e0p); + sE2E(3 * f3 + 1, e0n); + if (is_boundary) { + sE2E(3 * f0 + 2, -1); + sE2E(3 * f3 + 0, -1); + } else { + const int e1p = E2E[dedge_prev_3(e1)], e1n = E2E[dedge_next_3(e1)]; + sE2E(3 * f0 + 2, 3 * f1 + 0); + sE2E(3 * f1 + 1, e1n); + sE2E(3 * f1 + 2, 3 * f2 + 0); + sE2E(3 * f2 + 1, e1p); + sE2E(3 * f2 + 2, 3 * f3 + 0); + } +#undef sE2E + + V2E[v0] = 3 * f0 + 2; + V2E[vn] = 3 * f0 + 0; + V2E[v1] = 3 * f3 + 1; + V2E[v0p] = 3 * f0 + 1; + if (!is_boundary) V2E[v1p] = 3 * f1 + 2; + + auto schedule = [&](int f) { + for (int i = 0; i < 3; ++i) { + if (abs(diffs[f * 3 + i][0]) > max_len || abs(diffs[f * 3 + i][1]) > max_len) { + EdgeLink e; + e.id = f * 3 + i; + e.length = (V.col(F((i + 1) % 3, f)) - V.col(F(i, f))).squaredNorm(); + e.diff = diffs[f * 3 + i]; + queue.push(e); + } + } + }; + + schedule(f0); + if (!is_boundary) { + schedule(f2); + schedule(f1); + }; + schedule(f3); + } + F.conservativeResize(F.rows(), nF); + V.conservativeResize(V.rows(), nV); + N.conservativeResize(V.rows(), nV); + Q.conservativeResize(V.rows(), nV); + O.conservativeResize(V.rows(), nV); + if (S) + S->conservativeResize(S->rows(), nV); + V2E.conservativeResize(nV); + boundary.conservativeResize(nV); + nonmanifold.conservativeResize(nV); + E2E.conservativeResize(nF * 3); + for (int i = 0; i < F.cols(); ++i) { + for (int j = 0; j < 3; ++j) { + auto diff = edge_diff[face_edgeIds[i][j]]; + if (abs(diff[0]) > 1 || abs(diff[1]) > 1) { + printf("wrong init %d %d!\n", face_edgeIds[i][j], i * 3 + j); + exit(0); + } + } + } + for (int i = 0; i < edge_diff.size(); ++i) { + if (abs(edge_diff[i][0]) > 1 || abs(edge_diff[i][1]) > 1) { + printf("wrong...\n"); + exit(0); + } + } +} + +} // namespace qflow |