Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
#include <glm/glm.hpp>
#include <cuda_runtime.h>
#include "AdjacentMatrix.h"
__device__ __host__ glm::dvec3
middle_point(const glm::dvec3 &p0, const glm::dvec3 &n0, const glm::dvec3 &p1, const glm::dvec3 &n1) {
/* How was this derived?
*
* Minimize \|x-p0\|^2 + \|x-p1\|^2, where
* dot(n0, x) == dot(n0, p0)
* dot(n1, x) == dot(n1, p1)
*
* -> Lagrange multipliers, set derivative = 0
* Use first 3 equalities to write x in terms of
* lambda_1 and lambda_2. Substitute that into the last
* two equations and solve for the lambdas. Finally,
* add a small epsilon term to avoid issues when n1=n2.
*/
double n0p0 = glm::dot(n0, p0), n0p1 = glm::dot(n0, p1),
n1p0 = glm::dot(n1, p0), n1p1 = glm::dot(n1, p1),
n0n1 = glm::dot(n0, n1),
denom = 1.0f / (1.0f - n0n1*n0n1 + 1e-4f),
lambda_0 = 2.0f*(n0p1 - n0p0 - n0n1*(n1p0 - n1p1))*denom,
lambda_1 = 2.0f*(n1p0 - n1p1 - n0n1*(n0p1 - n0p0))*denom;
return 0.5 * (p0 + p1) - 0.25 * (n0 * lambda_0 + n1 * lambda_1);
}
__device__ __host__ glm::dvec3
position_round_4(const glm::dvec3 &o, const glm::dvec3 &q,
const glm::dvec3 &n, const glm::dvec3 &p,
double scale) {
double inv_scale = 1.0 / scale;
glm::dvec3 t = glm::cross(n, q);
glm::dvec3 d = p - o;
return o +
q * std::round(glm::dot(q, d) * inv_scale) * scale +
t * std::round(glm::dot(t, d) * inv_scale) * scale;
}
__device__ __host__ glm::dvec3
position_floor_4(const glm::dvec3 &o, const glm::dvec3 &q,
const glm::dvec3 &n, const glm::dvec3 &p,
double scale) {
double inv_scale = 1.0 / scale;
glm::dvec3 t = glm::cross(n,q);
glm::dvec3 d = p - o;
return o +
q * std::floor(glm::dot(q, d) * inv_scale) * scale +
t * std::floor(glm::dot(t, d) * inv_scale) * scale;
}
__device__ __host__ double cudaSignum(double value) {
return std::copysign((double)1, value);
}
__device__ __host__ void
compat_orientation_extrinsic_4(const glm::dvec3 &q0, const glm::dvec3 &n0,
const glm::dvec3 &q1, const glm::dvec3 &n1, glm::dvec3& value1, glm::dvec3& value2) {
const glm::dvec3 A[2] = { q0, glm::cross(n0, q0) };
const glm::dvec3 B[2] = { q1, glm::cross(n1, q1) };
double best_score = -1e10;
int best_a = 0, best_b = 0;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
double score = std::abs(glm::dot(A[i], B[j]));
if (score > best_score + 1e-6) {
best_a = i;
best_b = j;
best_score = score;
}
}
}
const double dp = glm::dot(A[best_a], B[best_b]);
value1 = A[best_a];
value2 = B[best_b] * cudaSignum(dp);
}
__device__ __host__ void
compat_position_extrinsic_4(
const glm::dvec3 &p0, const glm::dvec3 &n0, const glm::dvec3 &q0, const glm::dvec3 &o0,
const glm::dvec3 &p1, const glm::dvec3 &n1, const glm::dvec3 &q1, const glm::dvec3 &o1,
double scale, glm::dvec3& v1, glm::dvec3& v2) {
glm::dvec3 t0 = glm::cross(n0, q0), t1 = glm::cross(n1, q1);
glm::dvec3 middle = middle_point(p0, n0, p1, n1);
glm::dvec3 o0p = position_floor_4(o0, q0, n0, middle, scale);
glm::dvec3 o1p = position_floor_4(o1, q1, n1, middle, scale);
double best_cost = 1e10;
int best_i = -1, best_j = -1;
for (int i = 0; i<4; ++i) {
glm::dvec3 o0t = o0p + (q0 * ((i & 1) * scale) + t0 * (((i & 2) >> 1) * scale));
for (int j = 0; j<4; ++j) {
glm::dvec3 o1t = o1p + (q1 * ((j & 1) * scale) + t1 * (((j & 2) >> 1) * scale));
glm::dvec3 t = o0t - o1t;
double cost = glm::dot(t, t);
if (cost < best_cost) {
best_i = i;
best_j = j;
best_cost = cost;
}
}
}
v1 = o0p + (q0 * ((best_i & 1) * scale) + t0 * (((best_i & 2) >> 1) * scale)),
v2 = o1p + (q1 * ((best_j & 1) * scale) + t1 * (((best_j & 2) >> 1) * scale));
}
__global__
void cudaUpdateOrientation(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj, int* adjOffset, int num_adj) {
int pi = blockIdx.x * blockDim.x + threadIdx.x;
// for (int pi = 0; pi < num_phases; ++pi) {
if (pi >= num_phases)
return;
int i = phase[pi];
glm::dvec3 n_i = N[i];
double weight_sum = 0.0f;
glm::dvec3 sum = Q[i];
for (int l = adjOffset[i]; l < adjOffset[i + 1]; ++l) {
Link link = adj[l];
const int j = link.id;
const double weight = link.weight;
if (weight == 0)
continue;
glm::dvec3 n_j = N[j];
glm::dvec3 q_j = Q[j];
glm::dvec3 value1, value2;
compat_orientation_extrinsic_4(sum, n_i, q_j, n_j, value1, value2);
sum = value1 * weight_sum + value2 * weight;
sum -= n_i*glm::dot(n_i, sum);
weight_sum += weight;
double norm = glm::length(sum);
if (norm > 2.93873587705571876e-39f)
sum /= norm;
}
if (weight_sum > 0) {
Q[i] = sum;
}
// }
}
__global__
void cudaPropagateOrientationUpper(glm::dvec3* srcField, glm::ivec2* toUpper, glm::dvec3* N, glm::dvec3* destField, int num_orientation) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
// for (int i = 0; i < num_orientation; ++i) {
if (i >= num_orientation)
return;
for (int k = 0; k < 2; ++k) {
int dest = toUpper[i][k];
if (dest == -1)
continue;
glm::dvec3 q = srcField[i];
glm::dvec3 n = N[dest];
destField[dest] = q - n * glm::dot(n, q);
}
// }
}
__global__
void cudaPropagateOrientationLower(glm::ivec2* toUpper, glm::dvec3* Q, glm::dvec3* N, glm::dvec3* Q_next, glm::dvec3* N_next, int num_toUpper) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
// for (int i = 0; i < num_toUpper; ++i) {
if (i >= num_toUpper)
return;
glm::ivec2 upper = toUpper[i];
glm::dvec3 q0 = Q[upper[0]];
glm::dvec3 n0 = N[upper[0]];
glm::dvec3 q, q1, n1, value1, value2;
if (upper[1] != -1) {
q1 = Q[upper[1]];
n1 = N[upper[1]];
compat_orientation_extrinsic_4(q0, n0, q1, n1, value1, value2);
q = value1 + value2;
}
else {
q = q0;
}
glm::dvec3 n = N_next[i];
q -= glm::dot(n, q) * n;
double len = q.x * q.x + q.y * q.y + q.z * q.z;
if (len > 2.93873587705571876e-39f)
q /= sqrt(len);
Q_next[i] = q;
// }
}
__global__
void cudaUpdatePosition(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj, int* adjOffset, int num_adj, glm::dvec3* V, glm::dvec3* O, double scale) {
int pi = blockIdx.x * blockDim.x + threadIdx.x;
// for (int pi = 0; pi < num_phases; ++pi) {
if (pi >= num_phases)
return;
int i = phase[pi];
glm::dvec3 n_i = N[i], v_i = V[i];
glm::dvec3 q_i = Q[i];
glm::dvec3 sum = O[i];
double weight_sum = 0.0f;
for (int l = adjOffset[i]; l < adjOffset[i + 1]; ++l) {
Link link = adj[l];
int j = link.id;
const double weight = link.weight;
if (weight == 0)
continue;
glm::dvec3 n_j = N[j], v_j = V[j];
glm::dvec3 q_j = Q[j], o_j = O[j];
glm::dvec3 v1, v2;
compat_position_extrinsic_4(
v_i, n_i, q_i, sum, v_j, n_j, q_j, o_j, scale, v1, v2);
sum = v1*weight_sum +v2*weight;
weight_sum += weight;
if (weight_sum > 2.93873587705571876e-39f)
sum /= weight_sum;
sum -= glm::dot(n_i, sum - v_i)*n_i;
}
if (weight_sum > 0) {
O[i] = position_round_4(sum, q_i, n_i, v_i, scale);
}
// }
}
__global__
void cudaPropagatePositionUpper(glm::dvec3* srcField, glm::ivec2* toUpper, glm::dvec3* N, glm::dvec3* V, glm::dvec3* destField, int num_position) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
// for (int i = 0; i < num_position; ++i) {
if (i >= num_position)
return;
for (int k = 0; k < 2; ++k) {
int dest = toUpper[i][k];
if (dest == -1)
continue;
glm::dvec3 o = srcField[i], n = N[dest], v = V[dest];
o -= n * glm::dot(n, o - v);
destField[dest] = o;
}
// }
}
void UpdateOrientation(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj, int* adjOffset, int num_adj) {
cudaUpdateOrientation << <(num_phases + 255) / 256, 256 >> >(phase, num_phases, N, Q, adj, adjOffset, num_adj);
// cudaUpdateOrientation(phase, num_phases, N, Q, adj, adjOffset, num_adj);
}
void PropagateOrientationUpper(glm::dvec3* srcField, int num_orientation, glm::ivec2* toUpper, glm::dvec3* N, glm::dvec3* destField) {
cudaPropagateOrientationUpper << <(num_orientation + 255) / 256, 256 >> >(srcField, toUpper, N, destField, num_orientation);
// cudaPropagateOrientationUpper(srcField, toUpper, N, destField, num_orientation);
}
void PropagateOrientationLower(glm::ivec2* toUpper, glm::dvec3* Q, glm::dvec3* N, glm::dvec3* Q_next, glm::dvec3* N_next, int num_toUpper) {
cudaPropagateOrientationLower << <(num_toUpper + 255) / 256, 256 >> >(toUpper, Q, N, Q_next, N_next, num_toUpper);
// cudaPropagateOrientationLower(toUpper, Q, N, Q_next, N_next, num_toUpper);
}
void UpdatePosition(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj, int* adjOffset, int num_adj, glm::dvec3* V, glm::dvec3* O, double scale) {
cudaUpdatePosition << <(num_phases + 255) / 256, 256 >> >(phase, num_phases, N, Q, adj, adjOffset, num_adj, V, O, scale);
// cudaUpdatePosition(phase, num_phases, N, Q, adj, adjOffset, num_adj, V, O, scale);
}
void PropagatePositionUpper(glm::dvec3* srcField, int num_position, glm::ivec2* toUpper, glm::dvec3* N, glm::dvec3* V, glm::dvec3* destField) {
cudaPropagatePositionUpper << <(num_position + 255) / 256, 256 >> >(srcField, toUpper, N, V, destField, num_position);
// cudaPropagatePositionUpper(srcField, toUpper, N, V, destField, num_position);
}

View File

@@ -0,0 +1,35 @@
#include "config.hpp"
#include "adjacent-matrix.hpp"
#include "dedge.hpp"
#include <fstream>
namespace qflow {
void generate_adjacency_matrix_uniform(
const MatrixXi &F, const VectorXi &V2E, const VectorXi &E2E,
const VectorXi &nonManifold, AdjacentMatrix& adj) {
adj.resize(V2E.size());
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < adj.size(); ++i) {
int start = V2E[i];
int edge = start;
if (start == -1)
continue;
do {
int base = edge % 3, f = edge / 3;
int opp = E2E[edge], next = dedge_next_3(opp);
if (adj[i].empty())
adj[i].push_back(Link(F((base + 2) % 3, f)));
if (opp == -1 || next != start) {
adj[i].push_back(Link(F((base + 1) % 3, f)));
if (opp == -1)
break;
}
edge = next;
} while (edge != start);
}
}
} // namespace qflow

View File

@@ -0,0 +1,37 @@
#ifndef ADJACENT_MATRIX_H_
#define ADJACENT_MATRIX_H_
#include <vector>
namespace qflow {
struct Link
{
Link(){}
Link(int _id, double _w = 1)
: id(_id), weight(_w)
{}
inline bool operator<(const Link &link) const { return id < link.id; }
int id;
double weight;
};
struct TaggedLink {
int id;
unsigned char flag;
TaggedLink(){}
TaggedLink(int id) : id(id), flag(0) { }
bool used() const { return flag & 1; }
void markUsed() { flag |= 1; }
TaggedLink& operator=(const Link& l) {
flag = 0;
id = l.id;
return *this;
}
};
typedef std::vector<std::vector<Link> > AdjacentMatrix;
} // namespace qflow
#endif

View File

@@ -0,0 +1,102 @@
#ifndef COMPARE_KEY_H_
#define COMPARE_KEY_H_
#include <iostream>
#include <map>
namespace qflow {
struct Key2i
{
Key2i(int x, int y)
: key(std::make_pair(x, y))
{}
bool operator==(const Key2i& other) const
{
return key == other.key;
}
bool operator<(const Key2i& other) const
{
return key < other.key;
}
std::pair<int, int> key;
};
struct Key3i
{
Key3i(int x, int y, int z)
: key(std::make_pair(x, std::make_pair(y, z)))
{}
bool operator==(const Key3i& other) const
{
return key == other.key;
}
bool operator<(const Key3i& other) const
{
return key < other.key;
}
std::pair<int, std::pair<int, int> > key;
};
struct Key3f
{
Key3f(double x, double y, double z, double threshold)
: key(std::make_pair(x / threshold, std::make_pair(y / threshold, z / threshold)))
{}
bool operator==(const Key3f& other) const
{
return key == other.key;
}
bool operator<(const Key3f& other) const
{
return key < other.key;
}
std::pair<int, std::pair<int, int> > key;
};
struct KeySorted2i
{
KeySorted2i(int x, int y)
: key(std::make_pair(x, y))
{
if (x > y)
std::swap(key.first, key.second);
}
bool operator==(const KeySorted2i& other) const
{
return key == other.key;
}
bool operator<(const KeySorted2i& other) const
{
return key < other.key;
}
std::pair<int, int> key;
};
struct KeySorted3i
{
KeySorted3i(int x, int y, int z)
: key(std::make_pair(x, std::make_pair(y, z)))
{
if (key.first > key.second.first)
std::swap(key.first, key.second.first);
if (key.first > key.second.second)
std::swap(key.first, key.second.second);
if (key.second.first > key.second.second)
std::swap(key.second.first, key.second.second);
}
bool operator==(const Key3i& other) const
{
return key == other.key;
}
bool operator<(const Key3i& other) const
{
return key < other.key;
}
std::pair<int, std::pair<int, int> > key;
};
} // namespace qflow
#endif

View File

@@ -0,0 +1,34 @@
#ifndef CONFIG_H_
#define CONFIG_H_
// Move settings to cmake to make CMake happy :)
// #define WITH_SCALE
// #define WITH_CUDA
const int GRAIN_SIZE = 1024;
#ifdef LOG_OUTPUT
#define lprintf(...) printf(__VA_ARGS__)
#define lputs(...) puts(__VA_ARGS__)
#else
#define lprintf(...) void(0)
#define lputs(...) void(0)
#endif
#include <chrono>
namespace qflow {
// simulation of Windows GetTickCount()
unsigned long long inline GetCurrentTime64() {
using namespace std::chrono;
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
} // namespace qflow
#endif

View File

@@ -0,0 +1,487 @@
#include "dedge.hpp"
#include "config.hpp"
#include <atomic>
#include <fstream>
#include <iostream>
#include <set>
#include <vector>
#include "compare-key.hpp"
#ifdef WITH_TBB
#include "tbb/tbb.h"
#endif
namespace qflow {
inline int dedge_prev(int e, int deg) { return (e % deg == 0u) ? e + (deg - 1) : e - 1; }
inline bool atomicCompareAndExchange(volatile int* v, uint32_t newValue, int oldValue) {
#if defined(_WIN32)
return _InterlockedCompareExchange(reinterpret_cast<volatile long*>(v), (long)newValue,
(long)oldValue) == (long)oldValue;
#else
return __sync_bool_compare_and_swap(v, oldValue, newValue);
#endif
}
const int INVALID = -1;
#undef max
#undef min
bool compute_direct_graph(MatrixXd& V, MatrixXi& F, VectorXi& V2E, VectorXi& E2E,
VectorXi& boundary, VectorXi& nonManifold) {
V2E.resize(V.cols());
V2E.setConstant(INVALID);
uint32_t deg = F.rows();
std::vector<std::pair<uint32_t, uint32_t>> tmp(F.size());
#ifdef WITH_TBB
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t)F.cols(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t>& range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F(i, f), idx_next = F((i + 1) % deg, f),
edge_id = deg * f + i;
if (idx_cur >= V.cols() || idx_next >= V.cols())
throw std::runtime_error(
"Mesh data contains an out-of-bounds vertex reference!");
if (idx_cur == idx_next) continue;
tmp[edge_id] = std::make_pair(idx_next, INVALID);
if (!atomicCompareAndExchange(&V2E[idx_cur], edge_id, INVALID)) {
uint32_t idx = V2E[idx_cur];
while (!atomicCompareAndExchange((int*)&tmp[idx].second, edge_id, INVALID))
idx = tmp[idx].second;
}
}
}
});
#else
for (int f = 0; f < F.cols(); ++f) {
for (unsigned int i = 0; i < deg; ++i) {
unsigned int idx_cur = F(i, f), idx_next = F((i + 1) % deg, f), edge_id = deg * f + i;
if (idx_cur >= V.cols() || idx_next >= V.cols())
throw std::runtime_error("Mesh data contains an out-of-bounds vertex reference!");
if (idx_cur == idx_next) continue;
tmp[edge_id] = std::make_pair(idx_next, -1);
if (V2E[idx_cur] == -1)
V2E[idx_cur] = edge_id;
else {
unsigned int idx = V2E[idx_cur];
while (tmp[idx].second != -1) {
idx = tmp[idx].second;
}
tmp[idx].second = edge_id;
}
}
}
#endif
nonManifold.resize(V.cols());
nonManifold.setConstant(false);
E2E.resize(F.cols() * deg);
E2E.setConstant(INVALID);
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int f = 0; f < F.cols(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F(i, f), idx_next = F((i + 1) % deg, f), edge_id_cur = deg * f + i;
if (idx_cur == idx_next) continue;
uint32_t it = V2E[idx_next], edge_id_opp = INVALID;
while (it != INVALID) {
if (tmp[it].first == idx_cur) {
if (edge_id_opp == INVALID) {
edge_id_opp = it;
} else {
nonManifold[idx_cur] = true;
nonManifold[idx_next] = true;
edge_id_opp = INVALID;
break;
}
}
it = tmp[it].second;
}
if (edge_id_opp != INVALID && edge_id_cur < edge_id_opp) {
E2E[edge_id_cur] = edge_id_opp;
E2E[edge_id_opp] = edge_id_cur;
}
}
}
std::atomic<uint32_t> nonManifoldCounter(0), boundaryCounter(0), isolatedCounter(0);
boundary.resize(V.cols());
boundary.setConstant(false);
/* Detect boundary regions of the mesh and adjust vertex->edge pointers*/
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < V.cols(); ++i) {
uint32_t edge = V2E[i];
if (edge == INVALID) {
isolatedCounter++;
continue;
}
if (nonManifold[i]) {
nonManifoldCounter++;
V2E[i] = INVALID;
continue;
}
/* Walk backwards to the first boundary edge (if any) */
uint32_t start = edge, v2e = INVALID;
do {
v2e = std::min(v2e, edge);
uint32_t prevEdge = E2E[dedge_prev(edge, deg)];
if (prevEdge == INVALID) {
/* Reached boundary -- update the vertex->edge link */
v2e = edge;
boundary[i] = true;
boundaryCounter++;
break;
}
edge = prevEdge;
} while (edge != start);
V2E[i] = v2e;
}
#ifdef LOG_OUTPUT
printf("counter triangle %d %d\n", (int)boundaryCounter, (int)nonManifoldCounter);
#endif
return true;
std::vector<std::vector<int>> vert_to_edges(V2E.size());
for (int i = 0; i < F.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
int v = F(j, i);
vert_to_edges[v].push_back(i * 3 + j);
}
}
std::vector<int> colors(F.cols() * 3, -1);
bool update = false;
int num_v = V.cols();
std::map<int, int> new_vertices;
for (int i = 0; i < vert_to_edges.size(); ++i) {
int num_color = 0;
for (int j = 0; j < vert_to_edges[i].size(); ++j) {
int deid0 = vert_to_edges[i][j];
if (colors[deid0] == -1) {
int deid = deid0;
do {
colors[deid] = num_color;
if (num_color != 0) F(deid % 3, deid / 3) = num_v;
deid = deid / 3 * 3 + (deid + 2) % 3;
deid = E2E[deid];
} while (deid != deid0);
num_color += 1;
if (num_color > 1) {
update = true;
new_vertices[num_v] = i;
num_v += 1;
}
}
}
}
if (update) {
V.conservativeResize(3, num_v);
for (auto& p : new_vertices) {
V.col(p.first) = V.col(p.second);
}
return false;
}
return true;
}
void compute_direct_graph_quad(std::vector<Vector3d>& V, std::vector<Vector4i>& F, std::vector<int>& V2E, std::vector<int>& E2E, VectorXi& boundary, VectorXi& nonManifold) {
V2E.clear();
E2E.clear();
boundary = VectorXi();
nonManifold = VectorXi();
V2E.resize(V.size(), INVALID);
uint32_t deg = 4;
std::vector<std::pair<uint32_t, uint32_t>> tmp(F.size() * deg);
#ifdef WITH_TBB
tbb::parallel_for(
tbb::blocked_range<uint32_t>(0u, (uint32_t)F.size(), GRAIN_SIZE),
[&](const tbb::blocked_range<uint32_t>& range) {
for (uint32_t f = range.begin(); f != range.end(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg],
edge_id = deg * f + i;
if (idx_cur >= V.size() || idx_next >= V.size())
throw std::runtime_error(
"Mesh data contains an out-of-bounds vertex reference!");
if (idx_cur == idx_next) continue;
tmp[edge_id] = std::make_pair(idx_next, INVALID);
if (!atomicCompareAndExchange(&V2E[idx_cur], edge_id, INVALID)) {
uint32_t idx = V2E[idx_cur];
while (!atomicCompareAndExchange((int*)&tmp[idx].second, edge_id, INVALID))
idx = tmp[idx].second;
}
}
}
});
#else
for (int f = 0; f < F.size(); ++f) {
for (unsigned int i = 0; i < deg; ++i) {
unsigned int idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg], edge_id = deg * f + i;
if (idx_cur >= V.size() || idx_next >= V.size())
throw std::runtime_error("Mesh data contains an out-of-bounds vertex reference!");
if (idx_cur == idx_next) continue;
tmp[edge_id] = std::make_pair(idx_next, -1);
if (V2E[idx_cur] == -1) {
V2E[idx_cur] = edge_id;
}
else {
unsigned int idx = V2E[idx_cur];
while (tmp[idx].second != -1) {
idx = tmp[idx].second;
}
tmp[idx].second = edge_id;
}
}
}
#endif
nonManifold.resize(V.size());
nonManifold.setConstant(false);
E2E.resize(F.size() * deg, INVALID);
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int f = 0; f < F.size(); ++f) {
for (uint32_t i = 0; i < deg; ++i) {
uint32_t idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg], edge_id_cur = deg * f + i;
if (idx_cur == idx_next) continue;
uint32_t it = V2E[idx_next], edge_id_opp = INVALID;
while (it != INVALID) {
if (tmp[it].first == idx_cur) {
if (edge_id_opp == INVALID) {
edge_id_opp = it;
} else {
nonManifold[idx_cur] = true;
nonManifold[idx_next] = true;
edge_id_opp = INVALID;
break;
}
}
it = tmp[it].second;
}
if (edge_id_opp != INVALID && edge_id_cur < edge_id_opp) {
E2E[edge_id_cur] = edge_id_opp;
E2E[edge_id_opp] = edge_id_cur;
}
}
}
std::atomic<uint32_t> nonManifoldCounter(0), boundaryCounter(0), isolatedCounter(0);
boundary.resize(V.size());
boundary.setConstant(false);
/* Detect boundary regions of the mesh and adjust vertex->edge pointers*/
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < V.size(); ++i) {
uint32_t edge = V2E[i];
if (edge == INVALID) {
isolatedCounter++;
continue;
}
if (nonManifold[i]) {
nonManifoldCounter++;
V2E[i] = INVALID;
continue;
}
/* Walk backwards to the first boundary edge (if any) */
uint32_t start = edge, v2e = INVALID;
do {
v2e = std::min(v2e, edge);
uint32_t prevEdge = E2E[dedge_prev(edge, deg)];
if (prevEdge == INVALID) {
/* Reached boundary -- update the vertex->edge link */
v2e = edge;
boundary[i] = true;
boundaryCounter++;
break;
}
edge = prevEdge;
} while (edge != start);
V2E[i] = v2e;
}
#ifdef LOG_OUTPUT
printf("counter %d %d\n", (int)boundaryCounter, (int)nonManifoldCounter);
#endif
}
void remove_nonmanifold(std::vector<Vector4i>& F, std::vector<Vector3d>& V) {
typedef std::pair<uint32_t, uint32_t> Edge;
int degree = 4;
std::map<uint32_t, std::map<uint32_t, std::pair<uint32_t, uint32_t>>> irregular;
std::vector<std::set<int>> E(V.size());
std::vector<std::set<int>> VF(V.size());
auto kill_face_single = [&](uint32_t f) {
if (F[f][0] == INVALID) return;
for (int i = 0; i < degree; ++i) E[F[f][i]].erase(F[f][(i + 1) % degree]);
F[f].setConstant(INVALID);
};
auto kill_face = [&](uint32_t f) {
if (degree == 4 && F[f][2] == F[f][3]) {
auto it = irregular.find(F[f][2]);
if (it != irregular.end()) {
for (auto& item : it->second) {
kill_face_single(item.second.second);
}
}
}
kill_face_single(f);
};
uint32_t nm_edge = 0, nm_vert = 0;
for (uint32_t f = 0; f < (uint32_t)F.size(); ++f) {
if (F[f][0] == INVALID) continue;
if (degree == 4 && F[f][2] == F[f][3]) {
/* Special handling of irregular faces */
irregular[F[f][2]][F[f][0]] = std::make_pair(F[f][1], f);
continue;
}
bool nonmanifold = false;
for (uint32_t e = 0; e < degree; ++e) {
uint32_t v0 = F[f][e], v1 = F[f][(e + 1) % degree], v2 = F[f][(e + 2) % degree];
if (E[v0].find(v1) != E[v0].end() || (degree == 4 && E[v0].find(v2) != E[v0].end()))
nonmanifold = true;
}
if (nonmanifold) {
nm_edge++;
F[f].setConstant(INVALID);
continue;
}
for (uint32_t e = 0; e < degree; ++e) {
uint32_t v0 = F[f][e], v1 = F[f][(e + 1) % degree], v2 = F[f][(e + 2) % degree];
E[v0].insert(v1);
if (degree == 4) E[v0].insert(v2);
VF[v0].insert(f);
}
}
std::vector<Edge> edges;
for (auto item : irregular) {
bool nonmanifold = false;
auto face = item.second;
edges.clear();
uint32_t cur = face.begin()->first, stop = cur;
while (true) {
uint32_t pred = cur;
cur = face[cur].first;
uint32_t next = face[cur].first, it = 0;
while (true) {
++it;
if (next == pred) break;
if (E[cur].find(next) != E[cur].end() && it == 1) nonmanifold = true;
edges.push_back(Edge(cur, next));
next = face[next].first;
}
if (cur == stop) break;
}
if (nonmanifold) {
nm_edge++;
for (auto& i : item.second) F[i.second.second].setConstant(INVALID);
continue;
} else {
for (auto e : edges) {
E[e.first].insert(e.second);
for (auto e2 : face) VF[e.first].insert(e2.second.second);
}
}
}
/* Check vertices */
std::set<uint32_t> v_marked, v_unmarked, f_adjacent;
std::function<void(uint32_t)> dfs = [&](uint32_t i) {
v_marked.insert(i);
v_unmarked.erase(i);
for (uint32_t f : VF[i]) {
if (f_adjacent.find(f) == f_adjacent.end()) /* if not part of adjacent face */
continue;
for (uint32_t j = 0; j < degree; ++j) {
uint32_t k = F[f][j];
if (v_unmarked.find(k) == v_unmarked.end() || /* if not unmarked OR */
v_marked.find(k) != v_marked.end()) /* if already marked */
continue;
dfs(k);
}
}
};
for (uint32_t i = 0; i < (uint32_t)V.size(); ++i) {
v_marked.clear();
v_unmarked.clear();
f_adjacent.clear();
for (uint32_t f : VF[i]) {
if (F[f][0] == INVALID) continue;
for (uint32_t k = 0; k < degree; ++k) v_unmarked.insert(F[f][k]);
f_adjacent.insert(f);
}
if (v_unmarked.empty()) continue;
v_marked.insert(i);
v_unmarked.erase(i);
dfs(*v_unmarked.begin());
if (v_unmarked.size() > 0) {
nm_vert++;
for (uint32_t f : f_adjacent) kill_face(f);
}
}
if (nm_vert > 0 || nm_edge > 0) {
std::cout << "Non-manifold elements: vertices=" << nm_vert << ", edges=" << nm_edge
<< std::endl;
}
uint32_t nFaces = 0, nFacesOrig = F.size();
for (uint32_t f = 0; f < (uint32_t)F.size(); ++f) {
if (F[f][0] == INVALID) continue;
if (nFaces != f) {
F[nFaces] = F[f];
}
++nFaces;
}
if (nFacesOrig != nFaces) {
F.resize(nFaces);
std::cout << "Faces reduced from " << nFacesOrig << " -> " << nFaces << std::endl;
}
}
} // namespace qflow

View File

@@ -0,0 +1,25 @@
#ifndef DEDGE_H_
#define DEDGE_H_
#include <Eigen/Core>
#include <Eigen/Dense>
#include <vector>
namespace qflow {
using namespace Eigen;
inline int dedge_prev_3(int e) { return (e % 3 == 0) ? e + 2 : e - 1; }
inline int dedge_next_3(int e) { return (e % 3 == 2) ? e - 2 : e + 1; }
bool compute_direct_graph(MatrixXd& V, MatrixXi& F, VectorXi& V2E,
VectorXi& E2E, VectorXi& boundary, VectorXi& nonManifold);
void compute_direct_graph_quad(std::vector<Vector3d>& V, std::vector<Vector4i>& F, std::vector<int>& V2E,
std::vector<int>& E2E, VectorXi& boundary, VectorXi& nonManifold);
void remove_nonmanifold(std::vector<Vector4i> &F, std::vector<Vector3d> &V);
} // namespace qflow
#endif

View File

@@ -0,0 +1,151 @@
#ifndef DISAJOINT_TREE_H_
#define DISAJOINT_TREE_H_
#include <vector>
namespace qflow {
class DisajointTree {
public:
DisajointTree() {}
DisajointTree(int n) {
parent.resize(n);
rank.resize(n, 1);
for (int i = 0; i < n; ++i) parent[i] = i;
}
int Parent(int x) {
if (x == parent[x]) return x;
int y = Parent(parent[x]);
parent[x] = y;
return y;
}
int Index(int x) { return indices[x]; }
int IndexToParent(int x) {return indices_to_parent[x]; };
void MergeFromTo(int x, int y) {
int px = Parent(x);
int py = Parent(y);
if (px == py) return;
rank[py] += rank[px];
parent[px] = py;
}
void Merge(int x, int y) {
int px = Parent(x);
int py = Parent(y);
if (px == py) return;
if (rank[px] < rank[py]) {
rank[py] += rank[px];
parent[px] = py;
} else {
rank[px] += rank[py];
parent[py] = px;
}
}
// renumber the root so that it is consecutive.
void BuildCompactParent() {
std::vector<int> compact_parent;
compact_parent.resize(parent.size());
compact_num = 0;
for (int i = 0; i < parent.size(); ++i) {
if (parent[i] == i) {
compact_parent[i] = compact_num++;
indices_to_parent.push_back(i);
}
}
indices.resize(parent.size());
for (int i = 0; i < parent.size(); ++i) {
indices[i] = compact_parent[Parent(i)];
}
}
int CompactNum() { return compact_num; }
int compact_num;
std::vector<int> parent;
std::vector<int> indices, indices_to_parent;
std::vector<int> rank;
};
class DisajointOrientTree {
public:
DisajointOrientTree() {}
DisajointOrientTree(int n) {
parent.resize(n);
rank.resize(n, 1);
for (int i = 0; i < n; ++i) parent[i] = std::make_pair(i, 0);
}
int Parent(int j) {
if (j == parent[j].first) return j;
int k = Parent(parent[j].first);
parent[j].second = (parent[j].second + parent[parent[j].first].second) % 4;
parent[j].first = k;
return k;
}
int Orient(int j) {
if (j == parent[j].first) return parent[j].second;
return (parent[j].second + Orient(parent[j].first)) % 4;
}
int Index(int x) { return indices[x]; }
void MergeFromTo(int v0, int v1, int orient0, int orient1) {
int p0 = Parent(v0);
int p1 = Parent(v1);
if (p0 == p1) return;
int orientp0 = Orient(v0);
int orientp1 = Orient(v1);
if (p0 == p1) {
return;
}
rank[p1] += rank[p0];
parent[p0].first = p1;
parent[p0].second = (orient0 - orient1 + orientp1 - orientp0 + 8) % 4;
}
void Merge(int v0, int v1, int orient0, int orient1) {
int p0 = Parent(v0);
int p1 = Parent(v1);
if (p0 == p1) {
return;
}
int orientp0 = Orient(v0);
int orientp1 = Orient(v1);
if (p0 == p1) {
return;
}
if (rank[p1] < rank[p0]) {
rank[p0] += rank[p1];
parent[p1].first = p0;
parent[p1].second = (orient1 - orient0 + orientp0 - orientp1 + 8) % 4;
} else {
rank[p1] += rank[p0];
parent[p0].first = p1;
parent[p0].second = (orient0 - orient1 + orientp1 - orientp0 + 8) % 4;
}
}
void BuildCompactParent() {
std::vector<int> compact_parent;
compact_parent.resize(parent.size());
compact_num = 0;
for (int i = 0; i < parent.size(); ++i) {
if (parent[i].first == i) {
compact_parent[i] = compact_num++;
}
}
indices.resize(parent.size());
for (int i = 0; i < parent.size(); ++i) {
indices[i] = compact_parent[Parent(i)];
}
}
int CompactNum() { return compact_num; }
int compact_num;
std::vector<std::pair<int, int>> parent;
std::vector<int> indices;
std::vector<int> rank;
};
} // namespace qflow
#endif

View File

@@ -0,0 +1,163 @@
#if !defined(__UNIONFIND_H)
#define __UNIONFIND_H
#include <vector>
#include <atomic>
#include <iostream>
namespace qflow {
/**
* Lock-free parallel disjoint set data structure (aka UNION-FIND)
* with path compression and union by rank
*
* Supports concurrent find(), same() and unite() calls as described
* in the paper
*
* "Wait-free Parallel Algorithms for the Union-Find Problem"
* by Richard J. Anderson and Heather Woll
*
* In addition, this class supports optimistic locking (try_lock/unlock)
* of disjoint sets and a combined unite+unlock operation.
*
* \author Wenzel Jakob
*/
class DisjointSets {
public:
DisjointSets(uint32_t size) : mData(size) {
for (uint32_t i = 0; i<size; ++i)
mData[i] = (uint32_t)i;
}
uint32_t find(uint32_t id) const {
while (id != parent(id)) {
uint64_t value = mData[id];
uint32_t new_parent = parent((uint32_t)value);
uint64_t new_value =
(value & 0xFFFFFFFF00000000ULL) | new_parent;
/* Try to update parent (may fail, that's ok) */
if (value != new_value)
mData[id].compare_exchange_weak(value, new_value);
id = new_parent;
}
return id;
}
bool same(uint32_t id1, uint32_t id2) const {
for (;;) {
id1 = find(id1);
id2 = find(id2);
if (id1 == id2)
return true;
if (parent(id1) == id1)
return false;
}
}
uint32_t unite(uint32_t id1, uint32_t id2) {
for (;;) {
id1 = find(id1);
id2 = find(id2);
if (id1 == id2)
return id1;
uint32_t r1 = rank(id1), r2 = rank(id2);
if (r1 > r2 || (r1 == r2 && id1 < id2)) {
std::swap(r1, r2);
std::swap(id1, id2);
}
uint64_t oldEntry = ((uint64_t)r1 << 32) | id1;
uint64_t newEntry = ((uint64_t)r1 << 32) | id2;
if (!mData[id1].compare_exchange_strong(oldEntry, newEntry))
continue;
if (r1 == r2) {
oldEntry = ((uint64_t)r2 << 32) | id2;
newEntry = ((uint64_t)(r2 + 1) << 32) | id2;
/* Try to update the rank (may fail, that's ok) */
mData[id2].compare_exchange_weak(oldEntry, newEntry);
}
break;
}
return id2;
}
/**
* Try to lock the a disjoint union identified by one
* of its elements (this can occasionally fail when there
* are concurrent operations). The parameter 'id' will be
* updated to store the current representative ID of the
* union
*/
bool try_lock(uint32_t &id) {
const uint64_t lock_flag = 1ULL << 63;
id = find(id);
uint64_t value = mData[id];
if ((value & lock_flag) || (uint32_t)value != id)
return false;
// On IA32/x64, a PAUSE instruction is recommended for CAS busy loops
#if defined(__i386__) || defined(__amd64__)
__asm__ __volatile__("pause\n");
#endif
return mData[id].compare_exchange_strong(value, value | lock_flag);
}
void unlock(uint32_t id) {
const uint64_t lock_flag = 1ULL << 63;
mData[id] &= ~lock_flag;
}
/**
* Return the representative index of the set that results from merging
* locked disjoint sets 'id1' and 'id2'
*/
uint32_t unite_index_locked(uint32_t id1, uint32_t id2) const {
uint32_t r1 = rank(id1), r2 = rank(id2);
return (r1 > r2 || (r1 == r2 && id1 < id2)) ? id1 : id2;
}
/**
* Atomically unite two locked disjoint sets and unlock them. Assumes
* that here are no other concurrent unite() involving the same sets
*/
uint32_t unite_unlock(uint32_t id1, uint32_t id2) {
uint32_t r1 = rank(id1), r2 = rank(id2);
if (r1 > r2 || (r1 == r2 && id1 < id2)) {
std::swap(r1, r2);
std::swap(id1, id2);
}
mData[id1] = ((uint64_t)r1 << 32) | id2;
mData[id2] = ((uint64_t)(r2 + ((r1 == r2) ? 1 : 0)) << 32) | id2;
return id2;
}
uint32_t size() const { return (uint32_t)mData.size(); }
uint32_t rank(uint32_t id) const {
return ((uint32_t)(mData[id] >> 32)) & 0x7FFFFFFFu;
}
uint32_t parent(uint32_t id) const {
return (uint32_t)mData[id];
}
friend std::ostream &operator<<(std::ostream &os, const DisjointSets &f) {
for (size_t i = 0; i<f.mData.size(); ++i)
os << i << ": parent=" << f.parent(i) << ", rank=" << f.rank(i) << std::endl;
return os;
}
mutable std::vector<std::atomic<uint64_t>> mData;
};
} // namespace qflow
#endif /* __UNIONFIND_H */

View File

@@ -0,0 +1,481 @@
#ifndef FIELD_MATH_H_
#define FIELD_MATH_H_
#ifdef WITH_CUDA
# include <glm/glm.hpp>
#endif
#include <Eigen/Core>
#include <Eigen/Dense>
#include <algorithm>
#include <vector>
namespace qflow {
using namespace Eigen;
struct DEdge
{
DEdge()
: x(0), y(0)
{}
DEdge(int _x, int _y) {
if (_x > _y)
x = _y, y = _x;
else
x = _x, y = _y;
}
bool operator<(const DEdge& e) const {
return (x < e.x) || (x == e.x && y < e.y);
}
bool operator==(const DEdge& e) const {
return x == e.x && y == e.y;
}
bool operator!=(const DEdge& e) const {
return x != e.x || y != e.y;
}
int x, y;
};
inline int get_parents(std::vector<std::pair<int, int>>& parents, int j) {
if (j == parents[j].first) return j;
int k = get_parents(parents, parents[j].first);
parents[j].second = (parents[j].second + parents[parents[j].first].second) % 4;
parents[j].first = k;
return k;
}
inline int get_parents_orient(std::vector<std::pair<int, int>>& parents, int j) {
if (j == parents[j].first) return parents[j].second;
return (parents[j].second + get_parents_orient(parents, parents[j].first)) % 4;
}
inline double fast_acos(double x) {
double negate = double(x < 0.0f);
x = std::abs(x);
double ret = -0.0187293f;
ret *= x;
ret = ret + 0.0742610f;
ret *= x;
ret = ret - 0.2121144f;
ret *= x;
ret = ret + 1.5707288f;
ret = ret * std::sqrt(1.0f - x);
ret = ret - 2.0f * negate * ret;
return negate * (double)M_PI + ret;
}
inline double signum(double value) { return std::copysign((double)1, value); }
/// Always-positive modulo function (assumes b > 0)
inline int modulo(int a, int b) {
int r = a % b;
return (r < 0) ? r + b : r;
}
inline Vector3d rotate90_by(const Vector3d &q, const Vector3d &n, int amount) {
return ((amount & 1) ? (n.cross(q)) : q) * (amount < 2 ? 1.0f : -1.0f);
}
inline Vector2i rshift90(Vector2i shift, int amount) {
if (amount & 1) shift = Vector2i(-shift.y(), shift.x());
if (amount >= 2) shift = -shift;
return shift;
}
inline std::pair<int, int> compat_orientation_extrinsic_index_4(const Vector3d &q0,
const Vector3d &n0,
const Vector3d &q1,
const Vector3d &n1) {
const Vector3d A[2] = {q0, n0.cross(q0)};
const Vector3d B[2] = {q1, n1.cross(q1)};
double best_score = -std::numeric_limits<double>::infinity();
int best_a = 0, best_b = 0;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
double score = std::abs(A[i].dot(B[j]));
if (score > best_score) {
best_a = i;
best_b = j;
best_score = score;
}
}
}
if (A[best_a].dot(B[best_b]) < 0) best_b += 2;
return std::make_pair(best_a, best_b);
}
inline std::pair<Vector3d, Vector3d> compat_orientation_extrinsic_4(const Vector3d &q0,
const Vector3d &n0,
const Vector3d &q1,
const Vector3d &n1) {
const Vector3d A[2] = {q0, n0.cross(q0)};
const Vector3d B[2] = {q1, n1.cross(q1)};
double best_score = -std::numeric_limits<double>::infinity();
int best_a = 0, best_b = 0;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
double score = std::abs(A[i].dot(B[j]));
if (score > best_score + 1e-6) {
best_a = i;
best_b = j;
best_score = score;
}
}
}
const double dp = A[best_a].dot(B[best_b]);
return std::make_pair(A[best_a], B[best_b] * signum(dp));
}
inline Vector3d middle_point(const Vector3d &p0, const Vector3d &n0, const Vector3d &p1,
const Vector3d &n1) {
/* How was this derived?
*
* Minimize \|x-p0\|^2 + \|x-p1\|^2, where
* dot(n0, x) == dot(n0, p0)
* dot(n1, x) == dot(n1, p1)
*
* -> Lagrange multipliers, set derivative = 0
* Use first 3 equalities to write x in terms of
* lambda_1 and lambda_2. Substitute that into the last
* two equations and solve for the lambdas. Finally,
* add a small epsilon term to avoid issues when n1=n2.
*/
double n0p0 = n0.dot(p0), n0p1 = n0.dot(p1), n1p0 = n1.dot(p0), n1p1 = n1.dot(p1),
n0n1 = n0.dot(n1), denom = 1.0f / (1.0f - n0n1 * n0n1 + 1e-4f),
lambda_0 = 2.0f * (n0p1 - n0p0 - n0n1 * (n1p0 - n1p1)) * denom,
lambda_1 = 2.0f * (n1p0 - n1p1 - n0n1 * (n0p1 - n0p0)) * denom;
return 0.5f * (p0 + p1) - 0.25f * (n0 * lambda_0 + n1 * lambda_1);
}
inline Vector3d position_floor_4(const Vector3d &o, const Vector3d &q, const Vector3d &n,
const Vector3d &p, double scale_x, double scale_y,
double inv_scale_x, double inv_scale_y) {
Vector3d t = n.cross(q);
Vector3d d = p - o;
return o + q * std::floor(q.dot(d) * inv_scale_x) * scale_x +
t * std::floor(t.dot(d) * inv_scale_y) * scale_y;
}
inline std::pair<Vector3d, Vector3d> compat_position_extrinsic_4(
const Vector3d &p0, const Vector3d &n0, const Vector3d &q0, const Vector3d &o0,
const Vector3d &p1, const Vector3d &n1, const Vector3d &q1, const Vector3d &o1, double scale_x,
double scale_y, double inv_scale_x, double inv_scale_y, double scale_x_1, double scale_y_1,
double inv_scale_x_1, double inv_scale_y_1) {
Vector3d t0 = n0.cross(q0), t1 = n1.cross(q1);
Vector3d middle = middle_point(p0, n0, p1, n1);
Vector3d o0p =
position_floor_4(o0, q0, n0, middle, scale_x, scale_y, inv_scale_x, inv_scale_y);
Vector3d o1p =
position_floor_4(o1, q1, n1, middle, scale_x_1, scale_y_1, inv_scale_x_1, inv_scale_y_1);
double best_cost = std::numeric_limits<double>::infinity();
int best_i = -1, best_j = -1;
for (int i = 0; i < 4; ++i) {
Vector3d o0t = o0p + (q0 * (i & 1) * scale_x + t0 * ((i & 2) >> 1) * scale_y);
for (int j = 0; j < 4; ++j) {
Vector3d o1t = o1p + (q1 * (j & 1) * scale_x_1 + t1 * ((j & 2) >> 1) * scale_y_1);
double cost = (o0t - o1t).squaredNorm();
if (cost < best_cost) {
best_i = i;
best_j = j;
best_cost = cost;
}
}
}
return std::make_pair(
o0p + (q0 * (best_i & 1) * scale_x + t0 * ((best_i & 2) >> 1) * scale_y),
o1p + (q1 * (best_j & 1) * scale_x_1 + t1 * ((best_j & 2) >> 1) * scale_y_1));
}
inline Vector3d position_round_4(const Vector3d &o, const Vector3d &q, const Vector3d &n,
const Vector3d &p, double scale_x, double scale_y,
double inv_scale_x, double inv_scale_y) {
Vector3d t = n.cross(q);
Vector3d d = p - o;
return o + q * std::round(q.dot(d) * inv_scale_x) * scale_x +
t * std::round(t.dot(d) * inv_scale_y) * scale_y;
}
inline Vector2i position_floor_index_4(const Vector3d &o, const Vector3d &q, const Vector3d &n,
const Vector3d &p, double /* unused */, double /* unused */,
double inv_scale_x, double inv_scale_y) {
Vector3d t = n.cross(q);
Vector3d d = p - o;
return Vector2i((int)std::floor(q.dot(d) * inv_scale_x),
(int)std::floor(t.dot(d) * inv_scale_y));
}
inline std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_4(
const Vector3d &p0, const Vector3d &n0, const Vector3d &q0, const Vector3d &o0,
const Vector3d &p1, const Vector3d &n1, const Vector3d &q1, const Vector3d &o1, double scale_x,
double scale_y, double inv_scale_x, double inv_scale_y, double scale_x_1, double scale_y_1,
double inv_scale_x_1, double inv_scale_y_1, double *error) {
Vector3d t0 = n0.cross(q0), t1 = n1.cross(q1);
Vector3d middle = middle_point(p0, n0, p1, n1);
Vector2i o0p =
position_floor_index_4(o0, q0, n0, middle, scale_x, scale_y, inv_scale_x, inv_scale_y);
Vector2i o1p = position_floor_index_4(o1, q1, n1, middle, scale_x_1, scale_y_1, inv_scale_x_1,
inv_scale_y_1);
double best_cost = std::numeric_limits<double>::infinity();
int best_i = -1, best_j = -1;
for (int i = 0; i < 4; ++i) {
Vector3d o0t =
o0 + (q0 * ((i & 1) + o0p[0]) * scale_x + t0 * (((i & 2) >> 1) + o0p[1]) * scale_y);
for (int j = 0; j < 4; ++j) {
Vector3d o1t = o1 + (q1 * ((j & 1) + o1p[0]) * scale_x_1 +
t1 * (((j & 2) >> 1) + o1p[1]) * scale_y_1);
double cost = (o0t - o1t).squaredNorm();
if (cost < best_cost) {
best_i = i;
best_j = j;
best_cost = cost;
}
}
}
if (error) *error = best_cost;
return std::make_pair(Vector2i((best_i & 1) + o0p[0], ((best_i & 2) >> 1) + o0p[1]),
Vector2i((best_j & 1) + o1p[0], ((best_j & 2) >> 1) + o1p[1]));
}
inline void coordinate_system(const Vector3d &a, Vector3d &b, Vector3d &c) {
if (std::abs(a.x()) > std::abs(a.y())) {
double invLen = 1.0f / std::sqrt(a.x() * a.x() + a.z() * a.z());
c = Vector3d(a.z() * invLen, 0.0f, -a.x() * invLen);
} else {
double invLen = 1.0f / std::sqrt(a.y() * a.y() + a.z() * a.z());
c = Vector3d(0.0f, a.z() * invLen, -a.y() * invLen);
}
b = c.cross(a);
}
inline Vector3d rotate_vector_into_plane(Vector3d q, const Vector3d &source_normal,
const Vector3d &target_normal) {
const double cosTheta = source_normal.dot(target_normal);
if (cosTheta < 0.9999f) {
if (cosTheta < -0.9999f) return -q;
Vector3d axis = source_normal.cross(target_normal);
q = q * cosTheta + axis.cross(q) +
axis * (axis.dot(q) * (1.0 - cosTheta) / axis.dot(axis));
}
return q;
}
inline Vector3d Travel(Vector3d p, const Vector3d &dir, double &len, int &f, VectorXi &E2E,
MatrixXd &V, MatrixXi &F, MatrixXd &NF,
std::vector<MatrixXd> &triangle_space, double *tx = 0, double *ty = 0) {
Vector3d N = NF.col(f);
Vector3d pt = (dir - dir.dot(N) * N).normalized();
int prev_id = -1;
int count = 0;
while (len > 0) {
count += 1;
Vector3d t1 = V.col(F(1, f)) - V.col(F(0, f));
Vector3d t2 = V.col(F(2, f)) - V.col(F(0, f));
Vector3d N = NF.col(f);
// printf("point dis: %f\n", (p - V.col(F(1, f))).dot(N));
int edge_id = f * 3;
double max_len = 1e30;
bool found = false;
int next_id, next_f;
Matrix3d m, n;
m.col(0) = t1;
m.col(1) = t2;
m.col(2) = N;
n = m.inverse();
MatrixXd &T = triangle_space[f];
VectorXd coord = T * Vector3d(p - V.col(F(0, f)));
VectorXd dirs = (T * pt);
double lens[3];
lens[0] = -coord.y() / dirs.y();
lens[1] = (1 - coord.x() - coord.y()) / (dirs.x() + dirs.y());
lens[2] = -coord.x() / dirs.x();
for (int fid = 0; fid < 3; ++fid) {
if (fid + edge_id == prev_id) continue;
if (lens[fid] >= 0 && lens[fid] < max_len) {
max_len = lens[fid];
next_id = E2E[edge_id + fid];
next_f = next_id;
if (next_f != -1) next_f /= 3;
found = true;
}
}
if (!found) {
printf("error...\n");
exit(0);
}
// printf("status: %f %f %d\n", len, max_len, f);
if (max_len >= len) {
if (tx && ty) {
*tx = coord.x() + dirs.x() * len;
*ty = coord.y() + dirs.y() * len;
}
p = p + len * pt;
len = 0;
return p;
}
p = V.col(F(0, f)) + t1 * (coord.x() + dirs.x() * max_len) +
t2 * (coord.y() + dirs.y() * max_len);
len -= max_len;
if (next_f == -1) {
if (tx && ty) {
*tx = coord.x() + dirs.x() * max_len;
*ty = coord.y() + dirs.y() * max_len;
}
return p;
}
pt = rotate_vector_into_plane(pt, NF.col(f), NF.col(next_f));
f = next_f;
prev_id = next_id;
}
return p;
}
inline Vector3d TravelField(Vector3d p, Vector3d &pt, double &len, int &f, VectorXi &E2E,
MatrixXd &V, MatrixXi &F, MatrixXd &NF, MatrixXd &QF, MatrixXd &QV,
MatrixXd &NV, std::vector<MatrixXd> &triangle_space, double *tx = 0,
double *ty = 0, Vector3d *dir_unfold = 0) {
Vector3d N = NF.col(f);
pt = (pt - pt.dot(N) * N).normalized();
int prev_id = -1;
int count = 0;
std::vector<Vector3d> Ns;
auto FaceQFromVertices = [&](int f, double tx, double ty) {
const Vector3d &n = NF.col(f);
const Vector3d &q_1 = QV.col(F(0, f)), &q_2 = QV.col(F(1, f)), &q_3 = QV.col(F(2, f));
const Vector3d &n_1 = NV.col(F(0, f)), &n_2 = NV.col(F(1, f)), &n_3 = NV.col(F(2, f));
Vector3d q_1n = rotate_vector_into_plane(q_1, n_1, n);
Vector3d q_2n = rotate_vector_into_plane(q_2, n_2, n);
Vector3d q_3n = rotate_vector_into_plane(q_3, n_3, n);
auto orient = compat_orientation_extrinsic_4(q_1n, n, q_2n, n);
Vector3d q = (orient.first * tx + orient.second * ty).normalized();
orient = compat_orientation_extrinsic_4(q, n, q_3n, n);
q = (orient.first * (tx + ty) + orient.second * (1 - tx - ty)).normalized();
return q;
};
auto BestQFromGivenQ = [&](const Vector3d &n, const Vector3d &q, const Vector3d &given_q) {
Vector3d q_1 = n.cross(q);
double t1 = q.dot(given_q);
double t2 = q_1.dot(given_q);
if (fabs(t1) > fabs(t2)) {
if (t1 > 0.0)
return Vector3d(q);
else
return Vector3d(-q);
} else {
if (t2 > 0.0)
return Vector3d(q_1);
else
return Vector3d(-q_1);
}
};
while (len > 0) {
count += 1;
Vector3d t1 = V.col(F(1, f)) - V.col(F(0, f));
Vector3d t2 = V.col(F(2, f)) - V.col(F(0, f));
Vector3d N = NF.col(f);
Ns.push_back(N);
// printf("point dis: %f\n", (p - V.col(F(1, f))).dot(N));
int edge_id = f * 3;
double max_len = 1e30;
bool found = false;
int next_id = -1, next_f = -1;
Matrix3d m, n;
m.col(0) = t1;
m.col(1) = t2;
m.col(2) = N;
n = m.inverse();
MatrixXd &T = triangle_space[f];
VectorXd coord = T * Vector3d(p - V.col(F(0, f)));
VectorXd dirs = (T * pt);
double lens[3];
lens[0] = -coord.y() / dirs.y();
lens[1] = (1 - coord.x() - coord.y()) / (dirs.x() + dirs.y());
lens[2] = -coord.x() / dirs.x();
for (int fid = 0; fid < 3; ++fid) {
if (fid + edge_id == prev_id) continue;
if (lens[fid] >= 0 && lens[fid] < max_len) {
max_len = lens[fid];
next_id = E2E[edge_id + fid];
next_f = next_id;
if (next_f != -1) next_f /= 3;
found = true;
}
}
double w1 = (coord.x() + dirs.x() * max_len);
double w2 = (coord.y() + dirs.y() * max_len);
if (w1 < 0) w1 = 0.0f;
if (w2 < 0) w2 = 0.0f;
if (w1 + w2 > 1) {
double w = w1 + w2;
w1 /= w;
w2 /= w;
}
if (!found) {
printf("error...\n");
exit(0);
}
// printf("status: %f %f %d\n", len, max_len, f);
if (max_len >= len) {
if (tx && ty) {
*tx = w1;
*ty = w2;
}
Vector3d ideal_q = FaceQFromVertices(f, *tx, *ty);
*dir_unfold = BestQFromGivenQ(NF.col(f), ideal_q, *dir_unfold);
for (int i = Ns.size() - 1; i > 0; --i) {
*dir_unfold = rotate_vector_into_plane(*dir_unfold, Ns[i], Ns[i - 1]);
}
p = p + len * pt;
len = 0;
return p;
}
p = V.col(F(0, f)) + t1 * w1 + t2 * w2;
len -= max_len;
if (next_f == -1) {
if (tx && ty) {
*tx = w1;
*ty = w2;
}
Vector3d ideal_q = FaceQFromVertices(f, *tx, *ty);
*dir_unfold = BestQFromGivenQ(NF.col(f), ideal_q, *dir_unfold);
for (int i = Ns.size() - 1; i > 0; --i) {
*dir_unfold = rotate_vector_into_plane(*dir_unfold, Ns[i], Ns[i - 1]);
}
return p;
}
pt = rotate_vector_into_plane(pt, NF.col(f), NF.col(next_f));
// pt = BestQFromGivenQ(NF.col(next_f), QF.col(next_f), pt);
if (dir_unfold) {
*dir_unfold = BestQFromGivenQ(NF.col(next_f), QF.col(next_f), *dir_unfold);
}
f = next_f;
prev_id = next_id;
}
return p;
}
} // namespace qflow
#endif

View File

@@ -0,0 +1,344 @@
#ifndef FLOW_H_
#define FLOW_H_
#include <Eigen/Core>
#include <list>
#include <map>
#include <vector>
#include "config.hpp"
#include "../patches/boykov_kolmogorov_max_flow.hpp"
#include <lemon/network_simplex.h>
#include <lemon/preflow.h>
#include <lemon/smart_graph.h>
using namespace Eigen;
namespace qflow {
class MaxFlowHelper {
public:
MaxFlowHelper() {}
virtual ~MaxFlowHelper(){};
virtual void resize(int n, int m) = 0;
virtual void addEdge(int x, int y, int c, int rc, int v, int cost = 1) = 0;
virtual int compute() = 0;
virtual void applyTo(std::vector<Vector2i>& edge_diff) = 0;
};
class BoykovMaxFlowHelper : public MaxFlowHelper {
public:
BoykovMaxFlowHelper() = default;
void resize(int n, int m) override {
num_verts = n;
num_edges = 0;
flow.resize(num_verts, m * 2);
}
int compute() override {
return flow.max_flow(0, num_verts - 1);
}
void addEdge(int x, int y, int c, int rc, int v, int cost = 1) override {
const int e1 = num_edges++;
const int e2 = num_edges++;
flow.set_edge(e1, e2, x, y, c);
flow.set_edge(e2, e1, y, x, rc);
if (v != -1) {
edge_to_variables.emplace_back(v, -1);
edge_to_variables.emplace_back(v, 1);
}
else {
edge_to_variables.emplace_back(-1, -1);
edge_to_variables.emplace_back(-1, -1);
}
}
void applyTo(std::vector<Vector2i>& edge_diff) override {
for (int vert = 0; vert < num_verts; vert++) {
for (int edge : flow.vertex_out_edges(vert)) {
const int capacity = flow.edge_capacity(edge);
const int residual_capacity = flow.edge_residual_capacity(edge);
if (capacity > 0) {
int flow = (capacity - residual_capacity);
if (flow > 0) {
std::pair<int, int> e2v = edge_to_variables[edge];
if (e2v.first != -1) {
edge_diff[e2v.first / 2][e2v.first % 2] += e2v.second * flow;
}
}
}
}
}
}
private:
BoykovKolmogorovMaxFlow flow;
std::vector<std::pair<int, int>> edge_to_variables;
int num_verts = 0;
int num_edges = 0;
};
class NetworkSimplexFlowHelper : public MaxFlowHelper {
public:
using Weight = int;
using Capacity = int;
using Graph = lemon::SmartDigraph;
using Node = Graph::Node;
using Arc = Graph::Arc;
template <typename ValueType>
using ArcMap = lemon::SmartDigraph::ArcMap<ValueType>;
using Preflow = lemon::Preflow<lemon::SmartDigraph, ArcMap<Capacity>>;
using NetworkSimplex = lemon::NetworkSimplex<lemon::SmartDigraph, Capacity, Weight>;
public:
NetworkSimplexFlowHelper() : cost(graph), capacity(graph), flow(graph), variable(graph) {}
~NetworkSimplexFlowHelper(){};
void resize(int n, int m) {
nodes.reserve(n);
for (int i = 0; i < n; ++i) nodes.push_back(graph.addNode());
}
void addEdge(int x, int y, int c, int rc, int v, int cst = 1) {
assert(x >= 0);
assert(v >= -1);
if (c) {
auto e1 = graph.addArc(nodes[x], nodes[y]);
cost[e1] = cst;
capacity[e1] = c;
variable[e1] = std::make_pair(v, 1);
}
if (rc) {
auto e2 = graph.addArc(nodes[y], nodes[x]);
cost[e2] = cst;
capacity[e2] = rc;
variable[e2] = std::make_pair(v, -1);
}
}
int compute() {
Preflow pf(graph, capacity, nodes.front(), nodes.back());
NetworkSimplex ns(graph);
// Run preflow to find maximum flow
lprintf("push-relabel flow... ");
pf.runMinCut();
int maxflow = pf.flowValue();
// Run network simplex to find minimum cost maximum flow
ns.costMap(cost).upperMap(capacity).stSupply(nodes.front(), nodes.back(), maxflow);
auto status = ns.run();
switch (status) {
case NetworkSimplex::OPTIMAL:
ns.flowMap(flow);
break;
case NetworkSimplex::INFEASIBLE:
lputs("NetworkSimplex::INFEASIBLE");
assert(0);
break;
default:
lputs("Unknown: NetworkSimplex::Default");
assert(0);
break;
}
return maxflow;
}
void applyTo(std::vector<Vector2i>& edge_diff) {
for (Graph::ArcIt e(graph); e != lemon::INVALID; ++e) {
int var = variable[e].first;
if (var == -1) continue;
int sgn = variable[e].second;
edge_diff[var / 2][var % 2] -= sgn * flow[e];
}
}
private:
Graph graph;
ArcMap<Weight> cost;
ArcMap<Capacity> capacity;
ArcMap<Capacity> flow;
ArcMap<std::pair<int, int>> variable;
std::vector<Node> nodes;
std::vector<Arc> edges;
};
#ifdef WITH_GUROBI
#include <gurobi_c++.h>
class GurobiFlowHelper : public MaxFlowHelper {
public:
GurobiFlowHelper() {}
virtual ~GurobiFlowHelper(){};
virtual void resize(int n, int m) {
nodes.resize(n * 2);
edges.resize(m);
}
virtual void addEdge(int x, int y, int c, int rc, int v, int cost = 1) {
nodes[x * 2 + 0].push_back(vars.size());
nodes[y * 2 + 1].push_back(vars.size());
vars.push_back(model.addVar(0, c, 0, GRB_INTEGER));
edges.push_back(std::make_pair(v, 1));
nodes[y * 2 + 0].push_back(vars.size());
nodes[x * 2 + 1].push_back(vars.size());
vars.push_back(model.addVar(0, rc, 0, GRB_INTEGER));
edges.push_back(std::make_pair(v, -1));
}
virtual int compute() {
std::cerr << "compute" << std::endl;
int ns = nodes.size() / 2;
int flow;
for (int i = 1; i < ns - 1; ++i) {
GRBLinExpr cons = 0;
for (auto n : nodes[2 * i + 0]) cons += vars[n];
for (auto n : nodes[2 * i + 1]) cons -= vars[n];
model.addConstr(cons == 0);
}
// first pass, maximum flow
GRBLinExpr outbound = 0;
{
lprintf("first pass\n");
for (auto& n : nodes[0]) outbound += vars[n];
for (auto& n : nodes[1]) outbound -= vars[n];
model.setObjective(outbound, GRB_MAXIMIZE);
model.optimize();
flow = (int)model.get(GRB_DoubleAttr_ObjVal);
lprintf("Gurobi result: %d\n", flow);
}
// second pass, minimum cost flow
{
lprintf("second pass\n");
model.addConstr(outbound == flow);
GRBLinExpr cost = 0;
for (auto& v : vars) cost += v;
model.setObjective(cost, GRB_MINIMIZE);
model.optimize();
double optimal_cost = (int)model.get(GRB_DoubleAttr_ObjVal);
lprintf("Gurobi result: %.3f\n", optimal_cost);
}
return flow;
}
virtual void applyTo(std::vector<Vector2i>& edge_diff) { assert(0); };
private:
GRBEnv env = GRBEnv();
GRBModel model = GRBModel(env);
std::vector<GRBVar> vars;
std::vector<std::pair<int, int>> edges;
std::vector<std::vector<int>> nodes;
};
#endif
class ECMaxFlowHelper : public MaxFlowHelper {
public:
struct FlowInfo {
int id;
int capacity, flow;
int v, d;
FlowInfo* rev;
};
struct SearchInfo {
SearchInfo(int _id, int _prev_id, FlowInfo* _info)
: id(_id), prev_id(_prev_id), info(_info) {}
int id;
int prev_id;
FlowInfo* info;
};
ECMaxFlowHelper() { num = 0; }
int num;
std::vector<FlowInfo*> variable_to_edge;
void resize(int n, int m) {
graph.resize(n);
variable_to_edge.resize(m, 0);
num = n;
}
void addEdge(int x, int y, int c, int rc, int v, int cost = 0) {
FlowInfo flow;
flow.id = y;
flow.capacity = c;
flow.flow = 0;
flow.v = v;
flow.d = -1;
graph[x].push_back(flow);
auto& f1 = graph[x].back();
flow.id = x;
flow.capacity = rc;
flow.flow = 0;
flow.v = v;
flow.d = 1;
graph[y].push_back(flow);
auto& f2 = graph[y].back();
f2.rev = &f1;
f1.rev = &f2;
}
int compute() {
int total_flow = 0;
int count = 0;
while (true) {
count += 1;
std::vector<int> vhash(num, 0);
std::vector<SearchInfo> q;
q.push_back(SearchInfo(0, -1, 0));
vhash[0] = 1;
int q_front = 0;
bool found = false;
while (q_front < q.size()) {
int vert = q[q_front].id;
for (auto& l : graph[vert]) {
if (vhash[l.id] || l.capacity <= l.flow) continue;
q.push_back(SearchInfo(l.id, q_front, &l));
vhash[l.id] = 1;
if (l.id == num - 1) {
found = true;
break;
}
}
if (found) break;
q_front += 1;
}
if (q_front == q.size()) break;
int loc = q.size() - 1;
while (q[loc].prev_id != -1) {
q[loc].info->flow += 1;
q[loc].info->rev->flow -= 1;
loc = q[loc].prev_id;
// int prev_v = q[loc].id;
// applyFlow(prev_v, current_v, 1);
// applyFlow(current_v, prev_v, -1);
}
total_flow += 1;
}
return total_flow;
}
void applyTo(std::vector<Vector2i>& edge_diff) {
for (int i = 0; i < graph.size(); ++i) {
for (auto& flow : graph[i]) {
if (flow.flow > 0 && flow.v != -1) {
if (flow.flow > 0) {
edge_diff[flow.v / 2][flow.v % 2] += flow.d * flow.flow;
if (abs(edge_diff[flow.v / 2][flow.v % 2]) > 2) {
}
}
}
}
}
}
void applyFlow(int v1, int v2, int flow) {
for (auto& it : graph[v1]) {
if (it.id == v2) {
it.flow += flow;
break;
}
}
}
std::vector<std::list<FlowInfo>> graph;
};
} // namespace qflow
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
#ifndef HIERARCHY_H_
#define HIERARCHY_H_
#ifdef WITH_CUDA
# include <glm/glm.hpp>
#endif
#include <map>
#include <vector>
#include "adjacent-matrix.hpp"
#include "config.hpp"
#include "serialize.hpp"
#define RCPOVERFLOW 2.93873587705571876e-39f
using namespace Eigen;
namespace qflow {
class Hierarchy {
public:
Hierarchy();
void Initialize(double scale, int with_scale = 0);
void DownsampleGraph(const AdjacentMatrix adj, const MatrixXd& V, const MatrixXd& N,
const VectorXd& A, MatrixXd& V_p, MatrixXd& N_p, VectorXd& A_p,
MatrixXi& to_upper, VectorXi& to_lower, AdjacentMatrix& adj_p);
void generate_graph_coloring_deterministic(const AdjacentMatrix& adj, int size,
std::vector<std::vector<int>>& phases);
void FixFlip();
int FixFlipSat(int depth, int threshold = 0);
void PushDownwardFlip(int depth);
void PropagateEdge();
void DownsampleEdgeGraph(std::vector<Vector3i>& FQ, std::vector<Vector3i>& F2E,
std::vector<Vector2i>& edge_diff,
std::vector<int>& allow_changes, int level);
void UpdateGraphValue(std::vector<Vector3i>& FQ, std::vector<Vector3i>& F2E,
std::vector<Vector2i>& edge_diff);
enum { MAX_DEPTH = 25 };
void SaveToFile(FILE* fp);
void LoadFromFile(FILE* fp);
void clearConstraints();
void propagateConstraints();
double mScale;
int rng_seed;
MatrixXi mF; // mF(i, j) i \in [0, 3) ith index in face j
VectorXi mE2E; // inverse edge
std::vector<AdjacentMatrix> mAdj;
std::vector<MatrixXd> mV;
std::vector<MatrixXd> mN;
std::vector<VectorXd> mA;
std::vector<std::vector<std::vector<int>>> mPhases;
// parameters
std::vector<MatrixXd> mQ;
std::vector<MatrixXd> mO;
std::vector<VectorXi> mToLower;
std::vector<MatrixXi> mToUpper; // mToUpper[h](i, j) \in V; i \in [0, 2); j \in V
std::vector<MatrixXd> mS;
std::vector<MatrixXd> mK;
// constraints
std::vector<MatrixXd> mCQ;
std::vector<MatrixXd> mCO;
std::vector<VectorXd> mCQw;
std::vector<VectorXd> mCOw;
int with_scale;
// upper: fine to coarse
std::vector<std::vector<int>> mToUpperFaces; // face correspondance
std::vector<std::vector<int>> mSing;
std::vector<std::vector<int>> mToUpperEdges; // edge correspondance
std::vector<std::vector<int>> mToUpperOrients; // rotation of edges from fine to coarse
std::vector<std::vector<Vector3i>> mFQ; // face_edgeOrients
std::vector<std::vector<Vector3i>> mF2E; // face_edgeIds
std::vector<std::vector<Vector2i>> mE2F; // undirect edges to face ID
std::vector<std::vector<int> > mAllowChanges;
std::vector<std::vector<Vector2i>> mEdgeDiff; // face_edgeDiff
#ifdef WITH_CUDA
std::vector<Link*> cudaAdj;
std::vector<int*> cudaAdjOffset;
std::vector<glm::dvec3*> cudaN;
std::vector<glm::dvec3*> cudaV;
std::vector<glm::dvec3*> cudaQ;
std::vector<glm::dvec3*> cudaO;
std::vector<std::vector<int*>> cudaPhases;
std::vector<glm::ivec2*> cudaToUpper;
void CopyToDevice();
void CopyToHost();
#endif
};
} // namespace qflow
#endif

View File

@@ -0,0 +1,160 @@
//
// loader.cpp
// Loop
//
// Created by Jingwei on 10/22/17.
// Copyright © 2017 Jingwei. All rights reserved.
//
#include "loader.hpp"
#include <cstdint>
#include <fstream>
#include <unordered_map>
namespace qflow {
inline std::vector<std::string> &str_tokenize(const std::string &s, char delim, std::vector<std::string> &elems, bool include_empty = false) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
if (!item.empty() || include_empty)
elems.push_back(item);
return elems;
}
inline std::vector<std::string> str_tokenize(const std::string &s, char delim, bool include_empty) {
std::vector<std::string> elems;
str_tokenize(s, delim, elems, include_empty);
return elems;
}
inline uint32_t str_to_uint32_t(const std::string &str) {
char *end_ptr = nullptr;
uint32_t result = (uint32_t)strtoul(str.c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
throw std::runtime_error("Could not parse unsigned integer \"" + str + "\"");
return result;
}
void load(const char* filename, MatrixXd& V, MatrixXi& F)
{
/// Vertex indices used by the OBJ format
struct obj_vertex {
uint32_t p = (uint32_t)-1;
uint32_t n = (uint32_t)-1;
uint32_t uv = (uint32_t)-1;
inline obj_vertex() { }
inline obj_vertex(const std::string &string) {
std::vector<std::string> tokens = str_tokenize(string, '/', true);
if (tokens.size() < 1 || tokens.size() > 3)
throw std::runtime_error("Invalid vertex data: \"" + string + "\"");
p = str_to_uint32_t(tokens[0]);
#if 0
if (tokens.size() >= 2 && !tokens[1].empty())
uv = str_to_uint32_t(tokens[1]);
if (tokens.size() >= 3 && !tokens[2].empty())
n = str_to_uint32_t(tokens[2]);
#endif
}
inline bool operator==(const obj_vertex &v) const {
return v.p == p && v.n == n && v.uv == uv;
}
};
/// Hash function for obj_vertex
struct obj_vertexHash {
std::size_t operator()(const obj_vertex &v) const {
size_t hash = std::hash<uint32_t>()(v.p);
hash = hash * 37 + std::hash<uint32_t>()(v.uv);
hash = hash * 37 + std::hash<uint32_t>()(v.n);
return hash;
}
};
typedef std::unordered_map<obj_vertex, uint32_t, obj_vertexHash> VertexMap;
std::ifstream is(filename);
std::vector<Vector3d> positions;
//std::vector<Vector2d> texcoords;
//std::vector<Vector3d> normals;
std::vector<uint32_t> indices;
std::vector<obj_vertex> vertices;
VertexMap vertexMap;
std::string line_str;
while (std::getline(is, line_str)) {
std::istringstream line(line_str);
std::string prefix;
line >> prefix;
if (prefix == "v") {
Vector3d p;
line >> p.x() >> p.y() >> p.z();
positions.push_back(p);
}
else if (prefix == "vt") {
/*
Vector2d tc;
line >> tc.x() >> tc.y();
texcoords.push_back(tc);
*/
}
else if (prefix == "vn") {
/*
Vector3d n;
line >> n.x() >> n.y() >> n.z();
normals.push_back(n);
*/
}
else if (prefix == "f") {
std::string v1, v2, v3, v4;
line >> v1 >> v2 >> v3 >> v4;
obj_vertex tri[6];
int nVertices = 3;
tri[0] = obj_vertex(v1);
tri[1] = obj_vertex(v2);
tri[2] = obj_vertex(v3);
if (!v4.empty()) {
/* This is a quad, split into two triangles */
tri[3] = obj_vertex(v4);
tri[4] = tri[0];
tri[5] = tri[2];
nVertices = 6;
}
/* Convert to an indexed vertex list */
for (int i = 0; i<nVertices; ++i) {
const obj_vertex &v = tri[i];
VertexMap::const_iterator it = vertexMap.find(v);
if (it == vertexMap.end()) {
vertexMap[v] = (uint32_t)vertices.size();
indices.push_back((uint32_t)vertices.size());
vertices.push_back(v);
}
else {
indices.push_back(it->second);
}
}
}
}
F.resize(3, indices.size() / 3);
memcpy(F.data(), indices.data(), sizeof(uint32_t)*indices.size());
V.resize(3, vertices.size());
for (uint32_t i = 0; i<vertices.size(); ++i)
V.col(i) = positions.at(vertices[i].p - 1);
}
} // namespace qflow

View File

@@ -0,0 +1,15 @@
#ifndef __LOADER_H
#define __LOADER_H
#include <Eigen/Core>
#include <vector>
namespace qflow {
using namespace Eigen;
void load(const char* filename, MatrixXd& V, MatrixXi& F);
} // namespace qflow
#endif

View File

@@ -0,0 +1,295 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "localsat.hpp"
#include "config.hpp"
#include "dedge.hpp"
#include "field-math.hpp"
#include <Eigen/Core>
#include <deque>
#include <memory>
#include <utility>
#include <vector>
namespace qflow {
const int max_depth = 0;
using namespace Eigen;
SolverStatus RunCNF(const std::string &fin_name, int n_variable, int timeout,
const std::vector<std::vector<int>> &sat_clause, std::vector<int> &value) {
int n_sat_variable = 3 * n_variable;
auto fout_name = fin_name + ".result.txt";
FILE *fout = fopen(fin_name.c_str(), "w");
fprintf(fout, "p cnf %d %d\n", n_sat_variable, (int)sat_clause.size());
for (auto &c : sat_clause) {
for (auto e : c) fprintf(fout, "%d ", e);
fputs("0\n", fout);
}
fclose(fout);
char cmd[100];
snprintf(cmd, 99, "rm %s > /dev/null 2>&1", fout_name.c_str());
system(cmd);
snprintf(cmd, 99, "timeout %d minisat %s %s > /dev/null 2>&1", timeout, fin_name.c_str(),
fout_name.c_str());
int exit_code = system(cmd);
FILE *fin = fopen(fout_name.c_str(), "r");
char buf[16] = {0};
fscanf(fin, "%15s", buf);
lprintf(" MiniSAT:");
if (strcmp(buf, "SAT") != 0) {
fclose(fin);
if (exit_code == 124) {
lprintf(" Timeout! ");
return SolverStatus::Timeout;
}
lprintf(" Unsatisfiable! ");
return SolverStatus::Unsat;
};
lprintf(" Satisfiable! ");
for (int i = 0; i < n_variable; ++i) {
int sign[3];
fscanf(fin, "%d %d %d", sign + 0, sign + 1, sign + 2);
int nvalue = -2;
for (int j = 0; j < 3; ++j) {
assert(abs(sign[j]) == 3 * i + j + 1);
if ((sign[j] > 0) == (value[i] != j - 1)) {
assert(nvalue == -2);
nvalue = j - 1;
}
}
value[i] = nvalue;
}
fclose(fin);
return SolverStatus::Sat;
}
SolverStatus SolveSatProblem(int n_variable, std::vector<int> &value,
const std::vector<bool> flexible, // NOQA
const std::vector<Vector3i> &variable_eq,
const std::vector<Vector3i> &constant_eq,
const std::vector<Vector4i> &variable_ge,
const std::vector<Vector2i> &constant_ge,
int timeout) {
for (int v : value) assert(-1 <= v && v <= +1);
auto VAR = [&](int i, int v) {
int index = 1 + 3 * i + v + 1;
// We initialize the SAT problem by setting all the variable to false.
// This is because minisat by default will try false first.
if (v == value[i]) index = -index;
return index;
};
int n_flexible = 0;
std::vector<std::vector<int>> sat_clause;
std::vector<bool> sat_ishard;
auto add_clause = [&](const std::vector<int> &clause, bool hard) {
sat_clause.push_back(clause);
sat_ishard.push_back(hard);
};
for (int i = 0; i < n_variable; ++i) {
add_clause({-VAR(i, -1), -VAR(i, 0)}, true);
add_clause({-VAR(i, +1), -VAR(i, 0)}, true);
add_clause({-VAR(i, -1), -VAR(i, +1)}, true);
add_clause({VAR(i, -1), VAR(i, 0), VAR(i, +1)}, true);
if (!flexible[i]) {
add_clause({VAR(i, value[i])}, true);
} else {
++n_flexible;
}
}
for (int i = 0; i < (int)variable_eq.size(); ++i) {
auto &var = variable_eq[i];
auto &cst = constant_eq[i];
for (int v0 = -1; v0 <= 1; ++v0)
for (int v1 = -1; v1 <= 1; ++v1)
for (int v2 = -1; v2 <= 1; ++v2)
if (cst[0] * v0 + cst[1] * v1 + cst[2] * v2 != 0) {
add_clause({-VAR(var[0], v0), -VAR(var[1], v1), -VAR(var[2], v2)}, true);
}
}
for (int i = 0; i < (int)variable_ge.size(); ++i) {
auto &var = variable_ge[i];
auto &cst = constant_ge[i];
for (int v0 = -1; v0 <= 1; ++v0)
for (int v1 = -1; v1 <= 1; ++v1)
for (int v2 = -1; v2 <= 1; ++v2)
for (int v3 = -1; v3 <= 1; ++v3)
if (cst[0] * v0 * v1 - cst[1] * v2 * v3 < 0) {
add_clause({-VAR(var[0], v0), -VAR(var[1], v1), -VAR(var[2], v2),
-VAR(var[3], v3)},
false);
}
}
int nflip_before = 0, nflip_after = 0;
for (int i = 0; i < (int)variable_ge.size(); ++i) {
auto &var = variable_ge[i];
auto &cst = constant_ge[i];
if (value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1] < 0)
nflip_before++;
}
lprintf(" [SAT] nvar: %6d nflip: %3d ", n_flexible * 2, nflip_before);
auto rcnf = RunCNF("test.out", n_variable, timeout, sat_clause, value);
for (int i = 0; i < (int)variable_eq.size(); ++i) {
auto &var = variable_eq[i];
auto &cst = constant_eq[i];
assert(cst[0] * value[var[0]] + cst[1] * value[var[1]] + cst[2] * value[var[2]] == 0);
}
for (int i = 0; i < (int)variable_ge.size(); ++i) {
auto &var = variable_ge[i];
auto &cst = constant_ge[i];
int area = value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1];
if (area < 0) ++nflip_after;
}
lprintf("nflip: %3d\n", nflip_after);
return rcnf;
}
void ExportLocalSat(std::vector<Vector2i> &edge_diff, const std::vector<Vector3i> &face_edgeIds,
const std::vector<Vector3i> &face_edgeOrients, const MatrixXi &F,
const VectorXi &V2E, const VectorXi &E2E) {
int flip_count = 0;
int flip_count1 = 0;
std::vector<int> value(2 * edge_diff.size());
for (int i = 0; i < (int)edge_diff.size(); ++i) {
value[2 * i + 0] = edge_diff[i][0];
value[2 * i + 1] = edge_diff[i][1];
}
std::deque<std::pair<int, int>> Q;
std::vector<bool> mark_vertex(V2E.size(), false);
assert(F.cols() == (int)face_edgeIds.size());
std::vector<Vector3i> variable_eq(face_edgeIds.size() * 2);
std::vector<Vector3i> constant_eq(face_edgeIds.size() * 2);
std::vector<Vector4i> variable_ge(face_edgeIds.size());
std::vector<Vector2i> constant_ge(face_edgeIds.size());
VectorXd face_area(F.cols());
for (int i = 0; i < (int)face_edgeIds.size(); ++i) {
Vector2i diff[3];
Vector2i var[3];
Vector2i cst[3];
for (int j = 0; j < 3; ++j) {
int edgeid = face_edgeIds[i][j];
diff[j] = rshift90(edge_diff[edgeid], face_edgeOrients[i][j]);
var[j] = rshift90(Vector2i(edgeid * 2 + 1, edgeid * 2 + 2), face_edgeOrients[i][j]);
cst[j] = var[j].array().sign();
var[j] = var[j].array().abs() - 1;
}
assert(diff[0] + diff[1] + diff[2] == Vector2i::Zero());
variable_eq[2 * i + 0] = Vector3i(var[0][0], var[1][0], var[2][0]);
constant_eq[2 * i + 0] = Vector3i(cst[0][0], cst[1][0], cst[2][0]);
variable_eq[2 * i + 1] = Vector3i(var[0][1], var[1][1], var[2][1]);
constant_eq[2 * i + 1] = Vector3i(cst[0][1], cst[1][1], cst[2][1]);
face_area[i] = diff[0][0] * diff[1][1] - diff[0][1] * diff[1][0];
if (face_area[i] < 0) {
printf("[SAT] Face %d's area < 0\n", i);
for (int j = 0; j < 3; ++j) {
int v = F(j, i);
if (mark_vertex[v]) continue;
Q.push_back(std::make_pair(v, 0));
mark_vertex[v] = true;
}
flip_count += 1;
}
variable_ge[i] = Vector4i(var[0][0], var[1][1], var[0][1], var[1][0]);
constant_ge[i] = Vector2i(cst[0][0] * cst[1][1], cst[0][1] * cst[1][0]);
}
for (int i = 0; i < (int)variable_eq.size(); ++i) {
auto &var = variable_eq[i];
auto &cst = constant_eq[i];
assert((0 <= var.array()).all());
assert((var.array() < value.size()).all());
assert(cst[0] * value[var[0]] + cst[1] * value[var[1]] + cst[2] * value[var[2]] == 0);
}
for (int i = 0; i < (int)variable_ge.size(); ++i) {
auto &var = variable_ge[i];
auto &cst = constant_ge[i];
assert((0 <= variable_ge[i].array()).all());
assert((variable_ge[i].array() < value.size()).all());
if (value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1] < 0) {
assert(face_area[i] < 0);
flip_count1++;
}
}
assert(flip_count == flip_count1);
// BFS
printf("[SAT] Start BFS: Q.size() = %d\n", (int)Q.size());
int mark_count = Q.size();
while (!Q.empty()) {
int vertex = Q.front().first;
int depth = Q.front().second;
Q.pop_front();
mark_count++;
int e0 = V2E(vertex);
for (int e = e0;;) {
int v = F((e + 1) % 3, e / 3);
if (!mark_vertex[v]) {
int undirected_edge_id = face_edgeIds[e / 3][e % 3];
int undirected_edge_length = edge_diff[undirected_edge_id].array().abs().sum() > 0;
int ndepth = depth + undirected_edge_length;
if (ndepth <= max_depth) {
if (undirected_edge_length == 0)
Q.push_front(std::make_pair(v, ndepth));
else
Q.push_back(std::make_pair(v, ndepth));
mark_vertex[v] = true;
}
}
e = dedge_next_3(E2E(e));
if (e == e0) break;
}
}
printf("[SAT] Mark %d vertices out of %d\n", mark_count, (int)V2E.size());
std::vector<bool> flexible(value.size(), false);
for (int i = 0; i < (int)face_edgeIds.size(); ++i) {
for (int j = 0; j < 3; ++j) {
int edgeid = face_edgeIds[i][j];
if (mark_vertex[F(j, i)] || mark_vertex[F((j + 1) % 3, i)]) {
flexible[edgeid * 2 + 0] = true;
flexible[edgeid * 2 + 1] = true;
} else {
assert(face_area[i] >= 0);
}
}
}
SolveSatProblem(value.size(), value, flexible, variable_eq, constant_eq, variable_ge,
constant_ge);
for (int i = 0; i < edge_diff.size(); ++i) {
edge_diff[i][0] = value[2 * i + 0];
edge_diff[i][1] = value[2 * i + 1];
}
}
} // namespace qflow

View File

@@ -0,0 +1,31 @@
#ifndef __LOCAL_SAT_H
#define __LOCAL_SAT_H
#include <Eigen/Core>
#include <vector>
namespace qflow {
using namespace Eigen;
enum class SolverStatus {
Sat,
Unsat,
Timeout,
};
SolverStatus SolveSatProblem(int n_variable, std::vector<int> &value,
const std::vector<bool> flexible, // NOQA
const std::vector<Vector3i> &variable_eq,
const std::vector<Vector3i> &constant_eq,
const std::vector<Vector4i> &variable_ge,
const std::vector<Vector2i> &constant_ge,
int timeout = 8);
void ExportLocalSat(std::vector<Vector2i> &edge_diff, const std::vector<Vector3i> &face_edgeIds,
const std::vector<Vector3i> &face_edgeOrients, const MatrixXi &F,
const VectorXi &V2E, const VectorXi &E2E);
} // namespace qflow
#endif

View File

@@ -0,0 +1,130 @@
#include "config.hpp"
#include "field-math.hpp"
#include "optimizer.hpp"
#include "parametrizer.hpp"
#include <stdlib.h>
#ifdef WITH_CUDA
#include <cuda_runtime.h>
#endif
using namespace qflow;
Parametrizer field;
int main(int argc, char** argv) {
setbuf(stdout, NULL);
#ifdef WITH_CUDA
cudaFree(0);
#endif
int t1, t2;
std::string input_obj, output_obj;
int faces = -1;
for (int i = 0; i < argc; ++i) {
if (strcmp(argv[i], "-f") == 0) {
sscanf(argv[i + 1], "%d", &faces);
} else if (strcmp(argv[i], "-i") == 0) {
input_obj = argv[i + 1];
} else if (strcmp(argv[i], "-o") == 0) {
output_obj = argv[i + 1];
} else if (strcmp(argv[i], "-sharp") == 0) {
field.flag_preserve_sharp = 1;
} else if (strcmp(argv[i], "-boundary") == 0) {
field.flag_preserve_boundary = 1;
} else if (strcmp(argv[i], "-adaptive") == 0) {
field.flag_adaptive_scale = 1;
} else if (strcmp(argv[i], "-mcf") == 0) {
field.flag_minimum_cost_flow = 1;
} else if (strcmp(argv[i], "-sat") == 0) {
field.flag_aggresive_sat = 1;
} else if (strcmp(argv[i], "-seed") == 0) {
field.hierarchy.rng_seed = atoi(argv[i + 1]);
}
}
printf("%d %s %s\n", faces, input_obj.c_str(), output_obj.c_str());
if (input_obj.size() >= 1) {
field.Load(input_obj.c_str());
} else {
assert(0);
// field.Load((std::string(DATA_PATH) + "/fertility.obj").c_str());
}
printf("Initialize...\n");
t1 = GetCurrentTime64();
field.Initialize(faces);
t2 = GetCurrentTime64();
printf("Use %lf seconds\n", (t2 - t1) * 1e-3);
if (field.flag_preserve_boundary) {
printf("Add boundary constrains...\n");
Hierarchy& mRes = field.hierarchy;
mRes.clearConstraints();
for (uint32_t i = 0; i < 3 * mRes.mF.cols(); ++i) {
if (mRes.mE2E[i] == -1) {
uint32_t i0 = mRes.mF(i % 3, i / 3);
uint32_t i1 = mRes.mF((i + 1) % 3, i / 3);
Vector3d p0 = mRes.mV[0].col(i0), p1 = mRes.mV[0].col(i1);
Vector3d edge = p1 - p0;
if (edge.squaredNorm() > 0) {
edge.normalize();
mRes.mCO[0].col(i0) = p0;
mRes.mCO[0].col(i1) = p1;
mRes.mCQ[0].col(i0) = mRes.mCQ[0].col(i1) = edge;
mRes.mCQw[0][i0] = mRes.mCQw[0][i1] = mRes.mCOw[0][i0] = mRes.mCOw[0][i1] =
1.0;
}
}
}
mRes.propagateConstraints();
}
printf("Solve Orientation Field...\n");
t1 = GetCurrentTime64();
Optimizer::optimize_orientations(field.hierarchy);
field.ComputeOrientationSingularities();
t2 = GetCurrentTime64();
printf("Use %lf seconds\n", (t2 - t1) * 1e-3);
if (field.flag_adaptive_scale == 1) {
printf("Estimate Slop...\n");
t1 = GetCurrentTime64();
field.EstimateSlope();
t2 = GetCurrentTime64();
printf("Use %lf seconds\n", (t2 - t1) * 1e-3);
}
printf("Solve for scale...\n");
t1 = GetCurrentTime64();
Optimizer::optimize_scale(field.hierarchy, field.rho, field.flag_adaptive_scale);
field.flag_adaptive_scale = 1;
t2 = GetCurrentTime64();
printf("Use %lf seconds\n", (t2 - t1) * 1e-3);
printf("Solve for position field...\n");
t1 = GetCurrentTime64();
Optimizer::optimize_positions(field.hierarchy, field.flag_adaptive_scale);
field.ComputePositionSingularities();
t2 = GetCurrentTime64();
printf("Use %lf seconds\n", (t2 - t1) * 1e-3);
t1 = GetCurrentTime64();
printf("Solve index map...\n");
if (!field.ComputeIndexMap()) {
fprintf(stderr, "Failed to solve result, exiting!\n");
return 1;
}
t2 = GetCurrentTime64();
printf("Indexmap Use %lf seconds\n", (t2 - t1) * 1e-3);
printf("Writing the file...\n");
if (output_obj.size() < 1) {
assert(0);
// field.OutputMesh((std::string(DATA_PATH) + "/result.obj").c_str());
} else {
field.OutputMesh(output_obj.c_str());
}
printf("finish...\n");
// field.LoopFace(2);
return 0;
}

View File

@@ -0,0 +1,44 @@
#include "merge-vertex.hpp"
#include "compare-key.hpp"
#include <map>
#include <vector>
namespace qflow {
void merge_close(MatrixXd& V, MatrixXi& F, double threshold)
{
std::map<Key3f, int> vid_maps;
std::vector<int> vid_compress(V.cols());
for (int i = 0; i < V.cols(); ++i) {
Key3f key(V(0, i), V(1, i), V(2, i), threshold);
if (vid_maps.count(key)) {
vid_compress[i] = vid_maps[key];
}
else {
V.col(vid_maps.size()) = V.col(i);
vid_compress[i] = vid_maps.size();
vid_maps[key] = vid_compress[i];
}
}
printf("Compress Vertex from %d to %d...\n", (int)V.cols(), (int)vid_maps.size());
MatrixXd newV(3, vid_maps.size());
memcpy(newV.data(), V.data(), sizeof(double) * 3 * vid_maps.size());
V = std::move(newV);
int f_num = 0;
for (int i = 0; i < F.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
F(j, f_num) = vid_compress[F(j, i)];
}
if (F(0, f_num) != F(1, f_num) && F(0, f_num) != F(2, f_num) && F(1, f_num) != F(2, f_num)) {
f_num++;
}
}
printf("Compress Face from %d to %d...\n", (int)F.cols(), f_num);
MatrixXi newF(3, f_num);
memcpy(newF.data(), F.data(), sizeof(int) * 3 * f_num);
F = std::move(newF);
}
} // namespace qflow

View File

@@ -0,0 +1,14 @@
#ifndef MERGE_VERTEX_H_
#define MERGE_VERTEX_H_
#include <Eigen/Core>
namespace qflow {
using namespace Eigen;
void merge_close(MatrixXd& V, MatrixXi& F, double threshold);
} // namespace qflow
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
#ifndef OPTIMIZER_H_
#define OPTIMIZER_H_
#include "config.hpp"
#include "field-math.hpp"
#include "hierarchy.hpp"
namespace qflow {
class Optimizer {
public:
Optimizer();
static void optimize_orientations(Hierarchy& mRes);
static void optimize_scale(Hierarchy& mRes, VectorXd& rho, int adaptive);
static void optimize_positions(Hierarchy& mRes, int with_scale = 0);
static void optimize_integer_constraints(Hierarchy& mRes, std::map<int, int>& singularities,
bool use_minimum_cost_flow);
static void optimize_positions_fixed(
Hierarchy& mRes, std::vector<DEdge>& edge_values, std::vector<Vector2i>& edge_diff,
std::set<int>& sharp_vertices,
std::map<int, std::pair<Vector3d, Vector3d>>& sharp_constraints, int with_scale = 0);
static void optimize_positions_sharp(
Hierarchy& mRes, std::vector<DEdge>& edge_values, std::vector<Vector2i>& edge_diff,
std::vector<int>& sharp_edges, std::set<int>& sharp_vertices,
std::map<int, std::pair<Vector3d, Vector3d>>& sharp_constraints, int with_scale = 0);
static void optimize_positions_dynamic(
MatrixXi& F, MatrixXd& V, MatrixXd& N, MatrixXd& Q, std::vector<std::vector<int>>& Vset,
std::vector<Vector3d>& O_compact, std::vector<Vector4i>& F_compact,
std::vector<int>& V2E_compact, std::vector<int>& E2E_compact, double mScale,
std::vector<Vector3d>& diffs, std::vector<int>& diff_count,
std::map<std::pair<int, int>, int>& o2e, std::vector<int>& sharp_o,
std::map<int, std::pair<Vector3d, Vector3d>>& compact_sharp_constraints, int with_scale);
#ifdef WITH_CUDA
static void optimize_orientations_cuda(Hierarchy& mRes);
static void optimize_positions_cuda(Hierarchy& mRes);
#endif
};
#ifdef WITH_CUDA
extern void UpdateOrientation(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj,
int* adjOffset, int num_adj);
extern void PropagateOrientationUpper(glm::dvec3* srcField, int num_orientation,
glm::ivec2* toUpper, glm::dvec3* N, glm::dvec3* destField);
extern void PropagateOrientationLower(glm::ivec2* toUpper, glm::dvec3* Q, glm::dvec3* N,
glm::dvec3* Q_next, glm::dvec3* N_next, int num_toUpper);
extern void UpdatePosition(int* phase, int num_phases, glm::dvec3* N, glm::dvec3* Q, Link* adj,
int* adjOffset, int num_adj, glm::dvec3* V, glm::dvec3* O,
double scale);
extern void PropagatePositionUpper(glm::dvec3* srcField, int num_position, glm::ivec2* toUpper,
glm::dvec3* N, glm::dvec3* V, glm::dvec3* destField);
#endif
} // namespace qflow
#endif

View File

@@ -0,0 +1,583 @@
#include "dedge.hpp"
#include "parametrizer.hpp"
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <vector>
namespace qflow {
double Parametrizer::QuadEnergy(std::vector<int>& loop_vertices, std::vector<Vector4i>& res_quads,
int level) {
if (loop_vertices.size() < 4) return 0;
if (loop_vertices.size() == 4) {
double energy = 0;
for (int j = 0; j < 4; ++j) {
int v0 = loop_vertices[j];
int v2 = loop_vertices[(j + 1) % 4];
int v1 = loop_vertices[(j + 3) % 4];
Vector3d pt1 = (O_compact[v1] - O_compact[v0]).normalized();
Vector3d pt2 = (O_compact[v2] - O_compact[v0]).normalized();
Vector3d n = pt1.cross(pt2);
double sina = n.norm();
if (n.dot(N_compact[v0]) < 0) sina = -sina;
double cosa = pt1.dot(pt2);
double angle = atan2(sina, cosa) / 3.141592654 * 180.0;
if (angle < 0) angle = 360 + angle;
energy += angle * angle;
}
res_quads.push_back(
Vector4i(loop_vertices[0], loop_vertices[3], loop_vertices[2], loop_vertices[1]));
return energy;
}
double max_energy = 1e30;
for (int seg1 = 2; seg1 < loop_vertices.size(); seg1 += 2) {
for (int seg2 = seg1 + 1; seg2 < loop_vertices.size(); seg2 += 2) {
std::vector<Vector4i> quads[4];
std::vector<int> vertices = {loop_vertices[0], loop_vertices[1], loop_vertices[seg1],
loop_vertices[seg2]};
double energy = 0;
energy += QuadEnergy(vertices, quads[0], level + 1);
if (seg1 > 2) {
std::vector<int> vertices(loop_vertices.begin() + 1, loop_vertices.begin() + seg1);
vertices.push_back(loop_vertices[seg1]);
energy += QuadEnergy(vertices, quads[1], level + 1);
}
if (seg2 != seg1 + 1) {
std::vector<int> vertices(loop_vertices.begin() + seg1,
loop_vertices.begin() + seg2);
vertices.push_back(loop_vertices[seg2]);
energy += QuadEnergy(vertices, quads[2], level + 2);
}
if (seg2 + 1 != loop_vertices.size()) {
std::vector<int> vertices(loop_vertices.begin() + seg2, loop_vertices.end());
vertices.push_back(loop_vertices[0]);
energy += QuadEnergy(vertices, quads[3], level + 1);
}
if (max_energy > energy) {
max_energy = energy;
res_quads.clear();
for (int i = 0; i < 4; ++i) {
for (auto& v : quads[i]) {
res_quads.push_back(v);
}
}
}
}
}
return max_energy;
}
void Parametrizer::FixHoles(std::vector<int>& loop_vertices) {
std::vector<std::vector<int>> loop_vertices_array;
std::unordered_map<int, int> map_loops;
for (int i = 0; i < loop_vertices.size(); ++i) {
if (map_loops.count(loop_vertices[i])) {
int j = map_loops[loop_vertices[i]];
loop_vertices_array.push_back(std::vector<int>());
if (i - j > 3 && (i - j) % 2 == 0) {
for (int k = j; k < i; ++k) {
if (map_loops.count(loop_vertices[k])) {
loop_vertices_array.back().push_back(loop_vertices[k]);
map_loops.erase(loop_vertices[k]);
}
}
}
}
map_loops[loop_vertices[i]] = i;
}
if (map_loops.size() >= 3) {
loop_vertices_array.push_back(std::vector<int>());
for (int k = 0; k < loop_vertices.size(); ++k) {
if (map_loops.count(loop_vertices[k])) {
if (map_loops.count(loop_vertices[k])) {
loop_vertices_array.back().push_back(loop_vertices[k]);
map_loops.erase(loop_vertices[k]);
}
}
}
}
for (int i = 0; i < loop_vertices_array.size(); ++i) {
auto& loop_vertices = loop_vertices_array[i];
if (loop_vertices.size() == 0) return;
std::vector<Vector4i> quads;
#ifdef LOG_OUTPUT
// printf("Compute energy for loop: %d\n", (int)loop_vertices.size());
#endif
QuadEnergy(loop_vertices, quads, 0);
#ifdef LOG_OUTPUT
// printf("quads: %d\n", quads.size());
#endif
for (auto& p : quads) {
bool flag = false;
for (int j = 0; j < 4; ++j) {
int v1 = p[j];
int v2 = p[(j + 1) % 4];
auto key = std::make_pair(v1, v2);
if (Quad_edges.count(key)) {
flag = true;
break;
}
}
if (!flag) {
for (int j = 0; j < 4; ++j) {
int v1 = p[j];
int v2 = p[(j + 1) % 4];
auto key = std::make_pair(v1, v2);
Quad_edges.insert(key);
}
F_compact.push_back(p);
}
}
}
}
void Parametrizer::FixHoles() {
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
int v1 = F_compact[i][j];
int v2 = F_compact[i][(j + 1) % 4];
auto key = std::make_pair(v1, v2);
Quad_edges.insert(key);
}
}
std::vector<int> detected_boundary(E2E_compact.size(), 0);
for (int i = 0; i < E2E_compact.size(); ++i) {
if (detected_boundary[i] != 0 || E2E_compact[i] != -1) continue;
std::vector<int> loop_edges;
int current_e = i;
while (detected_boundary[current_e] == 0) {
detected_boundary[current_e] = 1;
loop_edges.push_back(current_e);
current_e = current_e / 4 * 4 + (current_e + 1) % 4;
while (E2E_compact[current_e] != -1) {
current_e = E2E_compact[current_e];
current_e = current_e / 4 * 4 + (current_e + 1) % 4;
}
}
std::vector<int> loop_vertices(loop_edges.size());
for (int j = 0; j < loop_edges.size(); ++j) {
loop_vertices[j] = F_compact[loop_edges[j] / 4][loop_edges[j] % 4];
}
if (loop_vertices.size() < 25) FixHoles(loop_vertices);
}
}
void Parametrizer::FixFlipHierarchy() {
Hierarchy fh;
fh.DownsampleEdgeGraph(face_edgeOrients, face_edgeIds, edge_diff, allow_changes, -1);
fh.FixFlip();
fh.UpdateGraphValue(face_edgeOrients, face_edgeIds, edge_diff);
}
void Parametrizer::FixFlipSat() {
#ifdef LOG_OUTPUT
printf("Solving SAT!\n");
#endif
if (!this->flag_aggresive_sat) return;
for (int threshold = 1; threshold <= 4; ++threshold) {
lprintf("[FixFlipSat] threshold = %d\n", threshold);
Hierarchy fh;
fh.DownsampleEdgeGraph(face_edgeOrients, face_edgeIds, edge_diff, allow_changes, -1);
int nflip = 0;
for (int depth = std::min(5, (int)fh.mFQ.size() - 1); depth >= 0; --depth) {
nflip = fh.FixFlipSat(depth, threshold);
if (depth > 0) fh.PushDownwardFlip(depth);
if (nflip == 0) break;
}
fh.UpdateGraphValue(face_edgeOrients, face_edgeIds, edge_diff);
if (nflip == 0) break;
}
}
void Parametrizer::AdvancedExtractQuad() {
Hierarchy fh;
fh.DownsampleEdgeGraph(face_edgeOrients, face_edgeIds, edge_diff, allow_changes, -1);
auto& V = hierarchy.mV[0];
auto& F = hierarchy.mF;
disajoint_tree = DisajointTree(V.cols());
auto& diffs = fh.mEdgeDiff.front();
for (int i = 0; i < diffs.size(); ++i) {
if (diffs[i] == Vector2i::Zero()) {
disajoint_tree.Merge(edge_values[i].x, edge_values[i].y);
}
}
disajoint_tree.BuildCompactParent();
auto& F2E = fh.mF2E.back();
auto& E2F = fh.mE2F.back();
auto& EdgeDiff = fh.mEdgeDiff.back();
auto& FQ = fh.mFQ.back();
std::vector<int> edge(E2F.size());
std::vector<int> face(F2E.size());
for (int i = 0; i < diffs.size(); ++i) {
int t = i;
for (int j = 0; j < fh.mToUpperEdges.size(); ++j) {
t = fh.mToUpperEdges[j][t];
if (t < 0) break;
}
if (t >= 0) edge[t] = i;
}
for (int i = 0; i < F.cols(); ++i) {
int t = i;
for (int j = 0; j < fh.mToUpperFaces.size(); ++j) {
t = fh.mToUpperFaces[j][t];
if (t < 0) break;
}
if (t >= 0) face[t] = i;
}
fh.UpdateGraphValue(face_edgeOrients, face_edgeIds, edge_diff);
auto& O = hierarchy.mO[0];
auto& Q = hierarchy.mQ[0];
auto& N = hierarchy.mN[0];
int num_v = disajoint_tree.CompactNum();
Vset.resize(num_v);
O_compact.resize(num_v, Vector3d::Zero());
Q_compact.resize(num_v, Vector3d::Zero());
N_compact.resize(num_v, Vector3d::Zero());
counter.resize(num_v, 0);
for (int i = 0; i < O.cols(); ++i) {
int compact_v = disajoint_tree.Index(i);
Vset[compact_v].push_back(i);
O_compact[compact_v] += O.col(i);
N_compact[compact_v] = N_compact[compact_v] * counter[compact_v] + N.col(i);
N_compact[compact_v].normalize();
if (counter[compact_v] == 0)
Q_compact[compact_v] = Q.col(i);
else {
auto pairs = compat_orientation_extrinsic_4(Q_compact[compact_v], N_compact[compact_v],
Q.col(i), N.col(i));
Q_compact[compact_v] = (pairs.first * counter[compact_v] + pairs.second).normalized();
}
counter[compact_v] += 1;
}
for (int i = 0; i < O_compact.size(); ++i) {
O_compact[i] /= counter[i];
}
BuildTriangleManifold(disajoint_tree, edge, face, edge_values, F2E, E2F, EdgeDiff, FQ);
}
void Parametrizer::BuildTriangleManifold(DisajointTree& disajoint_tree, std::vector<int>& edge,
std::vector<int>& face, std::vector<DEdge>& edge_values,
std::vector<Vector3i>& F2E, std::vector<Vector2i>& E2F,
std::vector<Vector2i>& EdgeDiff,
std::vector<Vector3i>& FQ) {
auto& F = hierarchy.mF;
std::vector<int> E2E(F2E.size() * 3, -1);
for (int i = 0; i < E2F.size(); ++i) {
int v1 = E2F[i][0];
int v2 = E2F[i][1];
int t1 = 0;
int t2 = 2;
if (v1 != -1)
while (F2E[v1][t1] != i) t1 += 1;
if (v2 != -1)
while (F2E[v2][t2] != i) t2 -= 1;
t1 += v1 * 3;
t2 += v2 * 3;
if (v1 != -1)
E2E[t1] = (v2 == -1) ? -1 : t2;
if (v2 != -1)
E2E[t2] = (v1 == -1) ? -1 : t1;
}
std::vector<Vector3i> triangle_vertices(F2E.size(), Vector3i(-1, -1, -1));
int num_v = 0;
std::vector<Vector3d> N, Q, O;
std::vector<std::vector<int>> Vs;
for (int i = 0; i < F2E.size(); ++i) {
for (int j = 0; j < 3; ++j) {
if (triangle_vertices[i][j] != -1) continue;
int f = face[i];
int v = disajoint_tree.Index(F(j, f));
Vs.push_back(Vset[v]);
Q.push_back(Q_compact[v]);
N.push_back(N_compact[v]);
O.push_back(O_compact[v]);
int deid0 = i * 3 + j;
int deid = deid0;
do {
triangle_vertices[deid / 3][deid % 3] = num_v;
deid = E2E[deid / 3 * 3 + (deid + 2) % 3];
} while (deid != deid0 && deid != -1);
if (deid == -1) {
deid = deid0;
do {
deid = E2E[deid];
if (deid == -1)
break;
deid = deid / 3 * 3 + (deid + 1) % 3;
triangle_vertices[deid/3][deid%3] = num_v;
} while (deid != -1);
}
num_v += 1;
}
}
int num_v0 = num_v;
do {
num_v0 = num_v;
std::vector<std::vector<int>> vert_to_dedge(num_v);
for (int i = 0; i < triangle_vertices.size(); ++i) {
Vector3i pt = triangle_vertices[i];
if (pt[0] == pt[1] || pt[1] == pt[2] || pt[2] == pt[0]) {
for (int j = 0; j < 3; ++j) {
int t = E2E[i * 3 + j];
if (t != -1) E2E[t] = -1;
}
for (int j = 0; j < 3; ++j) {
E2E[i * 3 + j] = -1;
}
} else {
for (int j = 0; j < 3; ++j)
vert_to_dedge[triangle_vertices[i][j]].push_back(i * 3 + j);
}
}
std::vector<int> colors(triangle_vertices.size() * 3, -1),
reverse_colors(triangle_vertices.size() * 3, -1);
for (int i = 0; i < vert_to_dedge.size(); ++i) {
int num_color = 0;
for (int j = 0; j < vert_to_dedge[i].size(); ++j) {
int deid = vert_to_dedge[i][j];
if (colors[deid] != -1) continue;
std::list<int> l;
int deid0 = deid;
do {
l.push_back(deid);
deid = deid / 3 * 3 + (deid + 2) % 3;
deid = E2E[deid];
} while (deid != -1 && deid != deid0);
if (deid == -1) {
deid = deid0;
do {
deid = E2E[deid];
if (deid == -1) break;
deid = deid / 3 * 3 + (deid + 1) % 3;
if (deid == deid0) break;
l.push_front(deid);
} while (true);
}
std::vector<int> dedges;
for (auto& e : l) dedges.push_back(e);
std::map<std::pair<int, int>, int> loc;
std::vector<int> deid_colors(dedges.size(), num_color);
num_color += 1;
for (int jj = 0; jj < dedges.size(); ++jj) {
int deid = dedges[jj];
colors[deid] = 0;
int v1 = triangle_vertices[deid / 3][deid % 3];
int v2 = triangle_vertices[deid / 3][(deid + 1) % 3];
std::pair<int, int> pt(v1, v2);
if (loc.count(pt)) {
int s = loc[pt];
for (int k = s; k < jj; ++k) {
int deid1 = dedges[k];
int v11 = triangle_vertices[deid1 / 3][deid1 % 3];
int v12 = triangle_vertices[deid1 / 3][(deid1 + 1) % 3];
std::pair<int, int> pt1(v11, v12);
loc.erase(pt1);
deid_colors[k] = num_color;
}
num_color += 1;
}
loc[pt] = jj;
}
for (int j = 0; j < dedges.size(); ++j) {
int deid = dedges[j];
int color = deid_colors[j];
if (color > 0) {
triangle_vertices[deid / 3][deid % 3] = num_v + color - 1;
}
}
}
if (num_color > 1) {
for (int j = 0; j < num_color - 1; ++j) {
Vs.push_back(Vs[i]);
O.push_back(O[i]);
N.push_back(N[i]);
Q.push_back(Q[i]);
}
num_v += num_color - 1;
}
}
} while (num_v != num_v0);
int offset = 0;
std::vector<Vector3i> triangle_edges, triangle_orients;
for (int i = 0; i < triangle_vertices.size(); ++i) {
Vector3i pt = triangle_vertices[i];
if (pt[0] == pt[1] || pt[1] == pt[2] || pt[2] == pt[0]) continue;
triangle_vertices[offset++] = triangle_vertices[i];
triangle_edges.push_back(F2E[i]);
triangle_orients.push_back(FQ[i]);
}
triangle_vertices.resize(offset);
std::set<int> flip_vertices;
for (int i = 0; i < triangle_vertices.size(); ++i) {
Vector2i d1 = rshift90(EdgeDiff[triangle_edges[i][0]], triangle_orients[i][0]);
Vector2i d2 = rshift90(EdgeDiff[triangle_edges[i][1]], triangle_orients[i][1]);
int area = d1[0] * d2[1] - d1[1] * d2[0];
if (area < 0) {
for (int j = 0; j < 3; ++j) {
flip_vertices.insert(triangle_vertices[i][j]);
}
}
}
MatrixXd NV(3, num_v);
MatrixXi NF(3, triangle_vertices.size());
memcpy(NF.data(), triangle_vertices.data(), sizeof(int) * 3 * triangle_vertices.size());
VectorXi NV2E, NE2E, NB, NN;
compute_direct_graph(NV, NF, NV2E, NE2E, NB, NN);
std::map<DEdge, std::pair<Vector3i, Vector3i>> quads;
for (int i = 0; i < triangle_vertices.size(); ++i) {
for (int j = 0; j < 3; ++j) {
int e = triangle_edges[i][j];
int v1 = triangle_vertices[i][j];
int v2 = triangle_vertices[i][(j + 1) % 3];
int v3 = triangle_vertices[i][(j + 2) % 3];
if (abs(EdgeDiff[e][0]) == 1 && abs(EdgeDiff[e][1]) == 1) {
DEdge edge(v1, v2);
if (quads.count(edge))
quads[edge].second = Vector3i(v1, v2, v3);
else
quads[edge] = std::make_pair(Vector3i(v1, v2, v3), Vector3i(-1, -1, -1));
}
}
}
for (auto& p : quads) {
if (p.second.second[0] != -1 && p.second.first[2] != p.second.second[2]) {
F_compact.push_back(Vector4i(p.second.first[1], p.second.first[2], p.second.first[0],
p.second.second[2]));
}
}
std::swap(Vs, Vset);
std::swap(O_compact, O);
std::swap(N_compact, N);
std::swap(Q_compact, Q);
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
while (true) {
std::vector<int> erasedF(F_compact.size(), 0);
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 3; ++j) {
for (int k = j + 1; k < 4; ++k) {
if (F_compact[i][j] == F_compact[i][k]) {
erasedF[i] = 1;
}
}
}
}
for (int i = 0; i < O_compact.size(); ++i) {
int v = 0;
int e0 = V2E_compact[i];
if (e0 == -1) continue;
std::vector<int> dedges;
int e = e0;
do {
dedges.push_back(e);
v += 1;
e = e / 4 * 4 + (e + 3) % 4;
e = E2E_compact[e];
} while (e != e0 && e != -1);
if (e == -1) {
int e = e0;
while (true) {
e = E2E_compact[e];
if (e == -1) break;
e = e / 4 * 4 + (e + 1) % 4;
v += 1;
dedges.push_back(e);
}
}
if (v == 2) {
// erasedF[dedges[1] / 4] = 1;
// F_compact[dedges[0]/4][dedges[0]%4] =
// F_compact[dedges[1]/4][(dedges[1]+2)%4];
}
}
offset = 0;
for (int i = 0; i < F_compact.size(); ++i) {
if (erasedF[i] == 0) F_compact[offset++] = F_compact[i];
}
if (offset == F_compact.size()) break;
F_compact.resize(offset);
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
}
FixHoles();
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
/*
for (auto& p : flip_vertices) {
int deid0 = V2E_compact[p];
int deid = deid0;
std::list<int> dedges;
if (deid0 != -1) {
do {
dedges.push_back(deid);
deid = E2E_compact[deid/4*4 + (deid+3) % 4];
} while (deid != -1 && deid != deid0);
if (deid == -1) {
deid = deid0;
do {
deid = E2E_compact[deid];
if (deid == -1)
break;
deid = deid/4*4 + (deid +1) % 4;
dedges.push_front(deid);
} while (deid != -1 && deid != deid0);
}
std::set<int> eraseF;
std::set<int> valid_dedges;
std::set<int> boundaries;
std::vector<int> loop_vertices;
for (auto& dedge : dedges) {
int f = dedge / 4;
eraseF.insert(f);
valid_dedges.insert(E2E_compact[f * 4 + (dedge+1)%4]);
valid_dedges.insert(E2E_compact[f * 4 + (dedge+2)%4]);
loop_vertices.push_back(F_compact[f][(dedge+1)%4]);
loop_vertices.push_back(F_compact[f][(dedge+2)%4]);
boundaries.insert(F_compact[f][(dedge+1)%4]);
boundaries.insert(F_compact[f][(dedge+2)%4]);
}
int offset = 0;
auto it = eraseF.begin();
for (int i = 0; i < F_compact.size(); ++i) {
if (it == eraseF.end() || i != *it) {
bool need_erase = false;
for (int j = 0; j < 4; ++j) {
if (valid_dedges.count(i * 4 + j) == 0 && boundaries.count(F_compact[i][j])
&& boundaries.count(F_compact[i][(j + 1) % 4])) { need_erase = true;
}
}
if (!need_erase)
F_compact[offset++] = F_compact[i];
} else {
it++;
}
}
F_compact.resize(offset);
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact,
boundary_compact, nonManifold_compact); std::reverse(loop_vertices.begin(),
loop_vertices.end()); FixHoles(loop_vertices); compute_direct_graph_quad(O_compact, F_compact,
V2E_compact, E2E_compact, boundary_compact, nonManifold_compact);
}
}
FixHoles();
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
*/
}
} // namespace qflow

View File

@@ -0,0 +1,425 @@
#include "parametrizer.hpp"
#include <queue>
#include <unordered_map>
#include <vector>
#include <random>
#include "optimizer.hpp"
namespace qflow {
void Parametrizer::BuildEdgeInfo() {
auto& F = hierarchy.mF;
auto& E2E = hierarchy.mE2E;
edge_diff.clear();
edge_values.clear();
face_edgeIds.resize(F.cols(), Vector3i(-1, -1, -1));
for (int i = 0; i < F.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
int k1 = j, k2 = (j + 1) % 3;
int v1 = F(k1, i);
int v2 = F(k2, i);
DEdge e2(v1, v2);
Vector2i diff2;
int rank2;
if (v1 > v2) {
rank2 = pos_rank(k2, i);
diff2 =
rshift90(Vector2i(-pos_index(k1 * 2, i), -pos_index(k1 * 2 + 1, i)), rank2);
} else {
rank2 = pos_rank(k1, i);
diff2 = rshift90(Vector2i(pos_index(k1 * 2, i), pos_index(k1 * 2 + 1, i)), rank2);
}
int current_eid = i * 3 + k1;
int eid = E2E[current_eid];
int eID1 = face_edgeIds[current_eid / 3][current_eid % 3];
int eID2 = -1;
if (eID1 == -1) {
eID2 = edge_values.size();
edge_values.push_back(e2);
edge_diff.push_back(diff2);
face_edgeIds[i][k1] = eID2;
if (eid != -1) face_edgeIds[eid / 3][eid % 3] = eID2;
} else if (!singularities.count(i)) {
eID2 = face_edgeIds[eid / 3][eid % 3];
edge_diff[eID2] = diff2;
}
}
}
}
void Parametrizer::BuildIntegerConstraints() {
auto& F = hierarchy.mF;
auto& Q = hierarchy.mQ[0];
auto& N = hierarchy.mN[0];
face_edgeOrients.resize(F.cols());
//Random number generator (for shuffling)
std::random_device rd;
std::mt19937 g(rd());
g.seed(hierarchy.rng_seed);
// undirected edge to direct edge
std::vector<std::pair<int, int>> E2D(edge_diff.size(), std::make_pair(-1, -1));
for (int i = 0; i < F.cols(); ++i) {
int v0 = F(0, i);
int v1 = F(1, i);
int v2 = F(2, i);
DEdge e0(v0, v1), e1(v1, v2), e2(v2, v0);
const Vector3i& eid = face_edgeIds[i];
Vector2i variable_id[3];
for (int i = 0; i < 3; ++i) {
variable_id[i] = Vector2i(eid[i] * 2 + 1, eid[i] * 2 + 2);
}
auto index1 =
compat_orientation_extrinsic_index_4(Q.col(v0), N.col(v0), Q.col(v1), N.col(v1));
auto index2 =
compat_orientation_extrinsic_index_4(Q.col(v0), N.col(v0), Q.col(v2), N.col(v2));
int rank1 = (index1.first - index1.second + 4) % 4; // v1 -> v0
int rank2 = (index2.first - index2.second + 4) % 4; // v2 -> v0
int orients[3] = {0}; // == {0, 0, 0}
if (v1 < v0) {
variable_id[0] = -rshift90(variable_id[0], rank1);
orients[0] = (rank1 + 2) % 4;
} else {
orients[0] = 0;
}
if (v2 < v1) {
variable_id[1] = -rshift90(variable_id[1], rank2);
orients[1] = (rank2 + 2) % 4;
} else {
variable_id[1] = rshift90(variable_id[1], rank1);
orients[1] = rank1;
}
if (v2 < v0) {
variable_id[2] = rshift90(variable_id[2], rank2);
orients[2] = rank2;
} else {
variable_id[2] = -variable_id[2];
orients[2] = 2;
}
face_edgeOrients[i] = Vector3i(orients[0], orients[1], orients[2]);
for (int j = 0; j < 3; ++j) {
int eid = face_edgeIds[i][j];
if (E2D[eid].first == -1)
E2D[eid].first = i * 3 + j;
else
E2D[eid].second = i * 3 + j;
}
}
// a face disajoint tree
DisajointOrientTree disajoint_orient_tree = DisajointOrientTree(F.cols());
// merge the whole face graph except for the singularity in which there exists a spanning tree
// which contains consistent orientation
std::vector<int> sharpUE(E2D.size());
for (int i = 0; i < sharp_edges.size(); ++i) {
if (sharp_edges[i]) {
sharpUE[face_edgeIds[i / 3][i % 3]] = 1;
}
}
for (int i = 0; i < E2D.size(); ++i) {
auto& edge_c = E2D[i];
int f0 = edge_c.first / 3;
int f1 = edge_c.second / 3;
if (edge_c.first == -1 || edge_c.second == -1) continue;
if (singularities.count(f0) || singularities.count(f1) || sharpUE[i]) continue;
int orient1 = face_edgeOrients[f0][edge_c.first % 3];
int orient0 = (face_edgeOrients[f1][edge_c.second % 3] + 2) % 4;
disajoint_orient_tree.Merge(f0, f1, orient0, orient1);
}
// merge singularity later
for (auto& f : singularities) {
for (int i = 0; i < 3; ++i) {
if (sharpUE[face_edgeIds[f.first][i]]) continue;
auto& edge_c = E2D[face_edgeIds[f.first][i]];
if (edge_c.first == -1 || edge_c.second == -1) continue;
int v0 = edge_c.first / 3;
int v1 = edge_c.second / 3;
int orient1 = face_edgeOrients[v0][edge_c.first % 3];
int orient0 = (face_edgeOrients[v1][edge_c.second % 3] + 2) % 4;
disajoint_orient_tree.Merge(v0, v1, orient0, orient1);
}
}
for (int i = 0; i < sharpUE.size(); ++i) {
if (sharpUE[i] == 0) continue;
auto& edge_c = E2D[i];
if (edge_c.first == -1 || edge_c.second == -1) continue;
int f0 = edge_c.first / 3;
int f1 = edge_c.second / 3;
int orient1 = face_edgeOrients[f0][edge_c.first % 3];
int orient0 = (face_edgeOrients[f1][edge_c.second % 3] + 2) % 4;
disajoint_orient_tree.Merge(f0, f1, orient0, orient1);
}
// all the face has the same parent. we rotate every face to the space of that parent.
for (int i = 0; i < face_edgeOrients.size(); ++i) {
for (int j = 0; j < 3; ++j) {
face_edgeOrients[i][j] =
(face_edgeOrients[i][j] + disajoint_orient_tree.Orient(i)) % 4;
}
}
std::vector<int> sharp_colors(face_edgeIds.size(), -1);
int num_sharp_component = 0;
// label the connected component connected by non-fixed edges
// we need this because we need sink flow (demand) == source flow (supply) for each component
// rather than global
for (int i = 0; i < sharp_colors.size(); ++i) {
if (sharp_colors[i] != -1) continue;
sharp_colors[i] = num_sharp_component;
std::queue<int> q;
q.push(i);
int counter = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int i = 0; i < 3; ++i) {
int e = face_edgeIds[v][i];
int deid1 = E2D[e].first;
int deid2 = E2D[e].second;
if (deid1 == -1 || deid2 == -1) continue;
if (abs(face_edgeOrients[deid1 / 3][deid1 % 3] -
face_edgeOrients[deid2 / 3][deid2 % 3] + 4) %
4 !=
2 ||
sharpUE[e]) {
continue;
}
for (int k = 0; k < 2; ++k) {
int f = (k == 0) ? E2D[e].first / 3 : E2D[e].second / 3;
if (sharp_colors[f] == -1) {
sharp_colors[f] = num_sharp_component;
q.push(f);
}
}
}
counter += 1;
}
num_sharp_component += 1;
}
{
std::vector<int> total_flows(num_sharp_component);
// check if each component is full-flow
for (int i = 0; i < face_edgeIds.size(); ++i) {
Vector2i diff(0, 0);
for (int j = 0; j < 3; ++j) {
int orient = face_edgeOrients[i][j];
diff += rshift90(edge_diff[face_edgeIds[i][j]], orient);
}
total_flows[sharp_colors[i]] += diff[0] + diff[1];
}
// build "variable"
variables.resize(edge_diff.size() * 2, std::make_pair(Vector2i(-1, -1), 0));
for (int i = 0; i < face_edgeIds.size(); ++i) {
for (int j = 0; j < 3; ++j) {
Vector2i sign = rshift90(Vector2i(1, 1), face_edgeOrients[i][j]);
int eid = face_edgeIds[i][j];
Vector2i index = rshift90(Vector2i(eid * 2, eid * 2 + 1), face_edgeOrients[i][j]);
for (int k = 0; k < 2; ++k) {
auto& p = variables[abs(index[k])];
if (p.first[0] == -1)
p.first[0] = i * 2 + k;
else
p.first[1] = i * 2 + k;
p.second += sign[k];
}
}
}
// fixed variable that might be manually modified.
// modified_variables[component_od][].first = fixed_variable_id
// modified_variables[component_od][].second = 1 if two positive signs -1 if two negative
// signs
std::vector<std::vector<std::pair<int, int>>> modified_variables[2];
for (int i = 0; i < 2; ++i) modified_variables[i].resize(total_flows.size());
for (int i = 0; i < variables.size(); ++i) {
if ((variables[i].first[1] == -1 || variables[i].second != 0) &&
allow_changes[i] == 1) {
int find = sharp_colors[variables[i].first[0] / 2];
int step = std::abs(variables[i].second) % 2;
if (total_flows[find] > 0) {
if (variables[i].second > 0 && edge_diff[i / 2][i % 2] > -1) {
modified_variables[step][find].push_back(std::make_pair(i, -1));
}
if (variables[i].second < 0 && edge_diff[i / 2][i % 2] < 1) {
modified_variables[step][find].push_back(std::make_pair(i, 1));
}
} else if (total_flows[find] < 0) {
if (variables[i].second < 0 && edge_diff[i / 2][i % 2] > -1) {
modified_variables[step][find].push_back(std::make_pair(i, -1));
}
if (variables[i].second > 0 && edge_diff[i / 2][i % 2] < 1) {
modified_variables[step][find].push_back(std::make_pair(i, 1));
}
}
}
}
// uniformly random manually modify variables so that the network has full flow.
for (int i = 0; i < 2; ++i)
for (auto& modified_var : modified_variables[i])
std::shuffle(modified_var.begin(), modified_var.end(), g);
for (int j = 0; j < total_flows.size(); ++j) {
for (int ii = 0; ii < 2; ++ii) {
if (total_flows[j] == 0) continue;
int max_num;
if (ii == 0)
max_num =
std::min(abs(total_flows[j]) / 2, (int)modified_variables[ii][j].size());
else
max_num = std::min(abs(total_flows[j]), (int)modified_variables[ii][j].size());
int dir = (total_flows[j] > 0) ? -1 : 1;
for (int i = 0; i < max_num; ++i) {
auto& info = modified_variables[ii][j][i];
edge_diff[info.first / 2][info.first % 2] += info.second;
if (ii == 0)
total_flows[j] += 2 * dir;
else
total_flows[j] += dir;
}
}
}
}
std::vector<Vector4i> edge_to_constraints(E2D.size() * 2, Vector4i(-1, 0, -1, 0));
for (int i = 0; i < face_edgeIds.size(); ++i) {
for (int j = 0; j < 3; ++j) {
int e = face_edgeIds[i][j];
Vector2i index = rshift90(Vector2i(e * 2 + 1, e * 2 + 2), face_edgeOrients[i][j]);
for (int k = 0; k < 2; ++k) {
int l = abs(index[k]);
int s = index[k] / l;
int ind = l - 1;
int equationID = i * 2 + k;
if (edge_to_constraints[ind][0] == -1) {
edge_to_constraints[ind][0] = equationID;
edge_to_constraints[ind][1] = s;
} else {
edge_to_constraints[ind][2] = equationID;
edge_to_constraints[ind][3] = s;
}
}
}
}
std::vector<std::pair<Vector2i, int>> arcs;
std::vector<int> arc_ids;
DisajointTree tree(face_edgeIds.size() * 2);
for (int i = 0; i < edge_to_constraints.size(); ++i) {
if (allow_changes[i] == 0) continue;
if (edge_to_constraints[i][0] == -1 || edge_to_constraints[i][2] == -1) continue;
if (edge_to_constraints[i][1] == -edge_to_constraints[i][3]) {
int v1 = edge_to_constraints[i][0];
int v2 = edge_to_constraints[i][2];
tree.Merge(v1, v2);
if (edge_to_constraints[i][1] < 0) std::swap(v1, v2);
int current_v = edge_diff[i / 2][i % 2];
arcs.push_back(std::make_pair(Vector2i(v1, v2), current_v));
}
}
tree.BuildCompactParent();
std::vector<int> total_flows(tree.CompactNum());
// check if each component is full-flow
for (int i = 0; i < face_edgeIds.size(); ++i) {
Vector2i diff(0, 0);
for (int j = 0; j < 3; ++j) {
int orient = face_edgeOrients[i][j];
diff += rshift90(edge_diff[face_edgeIds[i][j]], orient);
}
for (int j = 0; j < 2; ++j) {
total_flows[tree.Index(i * 2 + j)] += diff[j];
}
}
// build "variable"
variables.resize(edge_diff.size() * 2);
for (int i = 0; i < variables.size(); ++i) {
variables[i].first = Vector2i(-1, -1);
variables[i].second = 0;
}
for (int i = 0; i < face_edgeIds.size(); ++i) {
for (int j = 0; j < 3; ++j) {
Vector2i sign = rshift90(Vector2i(1, 1), face_edgeOrients[i][j]);
int eid = face_edgeIds[i][j];
Vector2i index = rshift90(Vector2i(eid * 2, eid * 2 + 1), face_edgeOrients[i][j]);
for (int k = 0; k < 2; ++k) {
auto& p = variables[abs(index[k])];
if (p.first[0] == -1)
p.first[0] = i * 2 + k;
else
p.first[1] = i * 2 + k;
p.second += sign[k];
}
}
}
// fixed variable that might be manually modified.
// modified_variables[component_od][].first = fixed_variable_id
// modified_variables[component_od][].second = 1 if two positive signs -1 if two negative signs
std::vector<std::vector<std::pair<int, int>>> modified_variables[2];
for (int i = 0; i < 2; ++i) {
modified_variables[i].resize(total_flows.size());
}
for (int i = 0; i < variables.size(); ++i) {
if ((variables[i].first[1] == -1 || variables[i].second != 0) && allow_changes[i] == 1) {
int find = tree.Index(variables[i].first[0]);
int step = abs(variables[i].second) % 2;
if (total_flows[find] > 0) {
if (variables[i].second > 0 && edge_diff[i / 2][i % 2] > -1) {
modified_variables[step][find].push_back(std::make_pair(i, -1));
}
if (variables[i].second < 0 && edge_diff[i / 2][i % 2] < 1) {
modified_variables[step][find].push_back(std::make_pair(i, 1));
}
} else if (total_flows[find] < 0) {
if (variables[i].second < 0 && edge_diff[i / 2][i % 2] > -1) {
modified_variables[step][find].push_back(std::make_pair(i, -1));
}
if (variables[i].second > 0 && edge_diff[i / 2][i % 2] < 1) {
modified_variables[step][find].push_back(std::make_pair(i, 1));
}
}
}
}
// uniformly random manually modify variables so that the network has full flow.
for (int j = 0; j < 2; ++j) {
for (auto& modified_var : modified_variables[j])
std::shuffle(modified_var.begin(), modified_var.end(), g);
}
for (int j = 0; j < total_flows.size(); ++j) {
for (int ii = 0; ii < 2; ++ii) {
if (total_flows[j] == 0) continue;
int max_num;
if (ii == 0)
max_num = std::min(abs(total_flows[j]) / 2, (int)modified_variables[ii][j].size());
else
max_num = std::min(abs(total_flows[j]), (int)modified_variables[ii][j].size());
int dir = (total_flows[j] > 0) ? -1 : 1;
for (int i = 0; i < max_num; ++i) {
auto& info = modified_variables[ii][j][i];
edge_diff[info.first / 2][info.first % 2] += info.second;
if (ii == 0)
total_flows[j] += 2 * dir;
else
total_flows[j] += dir;
}
}
}
}
void Parametrizer::ComputeMaxFlow() {
hierarchy.DownsampleEdgeGraph(face_edgeOrients, face_edgeIds, edge_diff, allow_changes, 1);
Optimizer::optimize_integer_constraints(hierarchy, singularities, flag_minimum_cost_flow);
hierarchy.UpdateGraphValue(face_edgeOrients, face_edgeIds, edge_diff);
}
} // namespace qflow

View File

@@ -0,0 +1,615 @@
#include "config.hpp"
#include "dedge.hpp"
#include "field-math.hpp"
#include "loader.hpp"
#include "merge-vertex.hpp"
#include "parametrizer.hpp"
#include "subdivide.hpp"
#include "dedge.hpp"
#include <queue>
namespace qflow {
void Parametrizer::NormalizeMesh() {
double maxV[3] = {-1e30, -1e30, -1e30};
double minV[3] = {1e30, 1e30, 1e30};
for (int i = 0; i < V.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
maxV[j] = std::max(maxV[j], V(j, i));
minV[j] = std::min(minV[j], V(j, i));
}
}
double scale =
std::max(std::max(maxV[0] - minV[0], maxV[1] - minV[1]), maxV[2] - minV[2]) * 0.5;
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < V.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
V(j, i) = (V(j, i) - (maxV[j] + minV[j]) * 0.5) / scale;
}
}
#ifdef LOG_OUTPUT
printf("vertices size: %d\n", (int)V.cols());
printf("faces size: %d\n", (int)F.cols());
#endif
this->normalize_scale = scale;
this->normalize_offset = Vector3d(0.5 * (maxV[0] + minV[0]), 0.5 * (maxV[1] + minV[1]), 0.5 * (maxV[2] + minV[2]));
// merge_close(V, F, 1e-6);
}
void Parametrizer::Load(const char* filename) {
load(filename, V, F);
NormalizeMesh();
}
void Parametrizer::Initialize(int faces) {
ComputeMeshStatus();
//ComputeCurvature(V, F, rho);
rho.resize(V.cols(), 1);
for (int i = 0; i < V.cols(); ++i) {
rho[i] = 1;
}
#ifdef PERFORMANCE_TEST
scale = sqrt(surface_area / (V.cols() * 10));
#else
if (faces <= 0) {
scale = sqrt(surface_area / V.cols());
} else {
scale = std::sqrt(surface_area / faces);
}
#endif
double target_len = std::min(scale / 2, average_edge_length * 2);
#ifdef PERFORMANCE_TEST
scale = sqrt(surface_area / V.cols());
#endif
if (target_len < max_edge_length) {
while (!compute_direct_graph(V, F, V2E, E2E, boundary, nonManifold))
;
subdivide(F, V, rho, V2E, E2E, boundary, nonManifold, target_len);
}
while (!compute_direct_graph(V, F, V2E, E2E, boundary, nonManifold))
;
generate_adjacency_matrix_uniform(F, V2E, E2E, nonManifold, adj);
for (int iter = 0; iter < 5; ++iter) {
VectorXd r(rho.size());
for (int i = 0; i < rho.size(); ++i) {
r[i] = rho[i];
for (auto& id : adj[i]) {
r[i] = std::min(r[i], rho[id.id]);
}
}
rho = r;
}
ComputeSharpEdges();
ComputeSmoothNormal();
ComputeVertexArea();
if (flag_adaptive_scale)
ComputeInverseAffine();
#ifdef LOG_OUTPUT
printf("V: %d F: %d\n", (int)V.cols(), (int)F.cols());
#endif
hierarchy.mA[0] = std::move(A);
hierarchy.mAdj[0] = std::move(adj);
hierarchy.mN[0] = std::move(N);
hierarchy.mV[0] = std::move(V);
hierarchy.mE2E = std::move(E2E);
hierarchy.mF = std::move(F);
hierarchy.Initialize(scale, flag_adaptive_scale);
}
void Parametrizer::ComputeMeshStatus() {
surface_area = 0;
average_edge_length = 0;
max_edge_length = 0;
for (int f = 0; f < F.cols(); ++f) {
Vector3d v[3] = {V.col(F(0, f)), V.col(F(1, f)), V.col(F(2, f))};
double area = 0.5f * (v[1] - v[0]).cross(v[2] - v[0]).norm();
surface_area += area;
for (int i = 0; i < 3; ++i) {
double len = (v[(i + 1) % 3] - v[i]).norm();
average_edge_length += len;
if (len > max_edge_length) max_edge_length = len;
}
}
average_edge_length /= (F.cols() * 3);
}
void Parametrizer::ComputeSharpEdges() {
sharp_edges.resize(F.cols() * 3, 0);
if (flag_preserve_boundary) {
for (int i = 0; i < sharp_edges.size(); ++i) {
int re = E2E[i];
if (re == -1) {
sharp_edges[i] = 1;
}
}
}
if (flag_preserve_sharp == 0)
return;
std::vector<Vector3d> face_normals(F.cols());
for (int i = 0; i < F.cols(); ++i) {
Vector3d p1 = V.col(F(0, i));
Vector3d p2 = V.col(F(1, i));
Vector3d p3 = V.col(F(2, i));
face_normals[i] = (p2 - p1).cross(p3 - p1).normalized();
}
double cos_thres = cos(60.0/180.0*3.141592654);
for (int i = 0; i < sharp_edges.size(); ++i) {
int e = i;
int re = E2E[e];
Vector3d& n1 = face_normals[e/3];
Vector3d& n2 = face_normals[re/3];
if (n1.dot(n2) < cos_thres) {
sharp_edges[i] = 1;
}
}
}
void Parametrizer::ComputeSharpO() {
auto& F = hierarchy.mF;
auto& V = hierarchy.mV[0];
auto& O = hierarchy.mO[0];
auto& E2E = hierarchy.mE2E;
DisajointTree tree(V.cols());
for (int i = 0; i < edge_diff.size(); ++i) {
if (edge_diff[i][0] == 0 && edge_diff[i][1] == 0) {
tree.Merge(edge_values[i].x, edge_values[i].y);
}
}
std::map<DEdge, std::vector<Vector3d> > edge_normals;
for (int i = 0; i < F.cols(); ++i) {
int pv[] = {tree.Parent(F(0, i)), tree.Parent(F(1, i)), tree.Parent(F(2, i))};
if (pv[0] == pv[1] || pv[1] == pv[2] || pv[2] == pv[0])
continue;
DEdge e[] = {DEdge(pv[0], pv[1]), DEdge(pv[1], pv[2]), DEdge(pv[2], pv[0])};
Vector3d d1 = O.col(F(1, i)) - O.col(F(0, i));
Vector3d d2 = O.col(F(2, i)) - O.col(F(0, i));
Vector3d n = d1.cross(d2).normalized();
for (int j = 0; j < 3; ++j) {
if (edge_normals.count(e[j]) == 0)
edge_normals[e[j]] = std::vector<Vector3d>();
edge_normals[e[j]].push_back(n);
}
}
std::map<DEdge, int> sharps;
for (auto& info : edge_normals) {
auto& normals = info.second;
bool sharp = false;
for (int i = 0; i < normals.size(); ++i) {
for (int j = i + 1; j < normals.size(); ++j) {
if (normals[i].dot(normals[j]) < cos(60.0 / 180.0 * 3.141592654)) {
sharp = true;
break;
}
}
if (sharp)
break;
}
if (sharp) {
int s = sharps.size();
sharps[info.first] = s;
}
}
for (auto& s : sharp_edges)
s = 0;
std::vector<int> sharp_hash(sharps.size(), 0);
for (int i = 0; i < F.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
int v1 = tree.Parent(F(j, i));
int v2 = tree.Parent(F((j + 1) % 3, i));
DEdge e(v1, v2);
if (sharps.count(e) == 0)
continue;
int id = sharps[e];
if (sharp_hash[id])
continue;
sharp_hash[id] = 1;
sharp_edges[i * 3 + j] = 1;
sharp_edges[E2E[i * 3 + j]] = 1;
}
}
}
void Parametrizer::ComputeSmoothNormal() {
/* Compute face normals */
Nf.resize(3, F.cols());
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int f = 0; f < F.cols(); ++f) {
Vector3d v0 = V.col(F(0, f)), v1 = V.col(F(1, f)), v2 = V.col(F(2, f)),
n = (v1 - v0).cross(v2 - v0);
double norm = n.norm();
if (norm < RCPOVERFLOW) {
n = Vector3d::UnitX();
} else {
n /= norm;
}
Nf.col(f) = n;
}
N.resize(3, V.cols());
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < V2E.rows(); ++i) {
int edge = V2E[i];
if (nonManifold[i] || edge == -1) {
N.col(i) = Vector3d::UnitX();
continue;
}
int stop = edge;
do {
if (sharp_edges[edge])
break;
edge = E2E[edge];
if (edge != -1)
edge = dedge_next_3(edge);
} while (edge != stop && edge != -1);
if (edge == -1)
edge = stop;
else
stop = edge;
Vector3d normal = Vector3d::Zero();
do {
int idx = edge % 3;
Vector3d d0 = V.col(F((idx + 1) % 3, edge / 3)) - V.col(i);
Vector3d d1 = V.col(F((idx + 2) % 3, edge / 3)) - V.col(i);
double angle = fast_acos(d0.dot(d1) / std::sqrt(d0.squaredNorm() * d1.squaredNorm()));
/* "Computing Vertex Normals from Polygonal Facets"
by Grit Thuermer and Charles A. Wuethrich, JGT 1998, Vol 3 */
if (std::isfinite(angle)) normal += Nf.col(edge / 3) * angle;
int opp = E2E[edge];
if (opp == -1) break;
edge = dedge_next_3(opp);
if (sharp_edges[edge])
break;
} while (edge != stop);
double norm = normal.norm();
N.col(i) = norm > RCPOVERFLOW ? Vector3d(normal / norm) : Vector3d::UnitX();
}
}
void Parametrizer::ComputeVertexArea() {
A.resize(V.cols());
A.setZero();
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < V2E.size(); ++i) {
int edge = V2E[i], stop = edge;
if (nonManifold[i] || edge == -1) continue;
double vertex_area = 0;
do {
int ep = dedge_prev_3(edge), en = dedge_next_3(edge);
Vector3d v = V.col(F(edge % 3, edge / 3));
Vector3d vn = V.col(F(en % 3, en / 3));
Vector3d vp = V.col(F(ep % 3, ep / 3));
Vector3d face_center = (v + vp + vn) * (1.0f / 3.0f);
Vector3d prev = (v + vp) * 0.5f;
Vector3d next = (v + vn) * 0.5f;
vertex_area += 0.5f * ((v - prev).cross(v - face_center).norm() +
(v - next).cross(v - face_center).norm());
int opp = E2E[edge];
if (opp == -1) break;
edge = dedge_next_3(opp);
} while (edge != stop);
A[i] = vertex_area;
}
}
void Parametrizer::FixValence()
{
// Remove Valence 2
while (true) {
bool update = false;
std::vector<int> marks(V2E_compact.size(), 0);
std::vector<int> erasedF(F_compact.size(), 0);
for (int i = 0; i < V2E_compact.size(); ++i) {
int deid0 = V2E_compact[i];
if (marks[i] || deid0 == -1)
continue;
int deid = deid0;
std::vector<int> dedges;
do {
dedges.push_back(deid);
int deid1 = deid / 4 * 4 + (deid + 3) % 4;
deid = E2E_compact[deid1];
} while (deid != deid0 && deid != -1);
if (dedges.size() == 2) {
int v1 = F_compact[dedges[0]/4][(dedges[0] + 1)%4];
int v2 = F_compact[dedges[0]/4][(dedges[0] + 2)%4];
int v3 = F_compact[dedges[1]/4][(dedges[1] + 1)%4];
int v4 = F_compact[dedges[1]/4][(dedges[1] + 2)%4];
if (marks[v1] || marks[v2] || marks[v3] || marks[v4])
continue;
marks[v1] = true;
marks[v2] = true;
marks[v3] = true;
marks[v4] = true;
if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) {
erasedF[dedges[0]/4] = 1;
} else {
F_compact[dedges[0]/4] = Vector4i(v1, v2, v3, v4);
}
erasedF[dedges[1]/4] = 1;
update = true;
}
}
if (update) {
int top = 0;
for (int i = 0; i < erasedF.size(); ++i) {
if (erasedF[i] == 0) {
F_compact[top++] = F_compact[i];
}
}
F_compact.resize(top);
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
} else {
break;
}
}
std::vector<std::vector<int> > v_dedges(V2E_compact.size());
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
v_dedges[F_compact[i][j]].push_back(i * 4 + j);
}
}
int top = V2E_compact.size();
for (int i = 0; i < v_dedges.size(); ++i) {
std::map<int, int> groups;
int group_id = 0;
for (int j = 0; j < v_dedges[i].size(); ++j) {
int deid = v_dedges[i][j];
if (groups.count(deid))
continue;
int deid0 = deid;
do {
groups[deid] = group_id;
deid = deid / 4 * 4 + (deid + 3) % 4;
deid = E2E_compact[deid];
} while (deid != deid0 && deid != -1);
if (deid == -1) {
deid = deid0;
while (E2E_compact[deid] != -1) {
deid = E2E_compact[deid];
deid = deid / 4 * 4 + (deid + 1) % 4;
groups[deid] = group_id;
}
}
group_id += 1;
}
if (group_id > 1) {
for (auto& g : groups) {
if (g.second >= 1)
F_compact[g.first/4][g.first%4] = top - 1 + g.second;
}
for (int j = 1; j < group_id; ++j) {
Vset.push_back(Vset[i]);
N_compact.push_back(N_compact[i]);
Q_compact.push_back(Q_compact[i]);
O_compact.push_back(O_compact[i]);
}
top = O_compact.size();
}
}
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
// Decrease Valence
while (true) {
bool update = false;
std::vector<int> marks(V2E_compact.size(), 0);
std::vector<int> valences(V2E_compact.size(), 0);
for (int i = 0; i < V2E_compact.size(); ++i) {
int deid0 = V2E_compact[i];
if (deid0 == -1)
continue;
int deid = deid0;
int count = 0;
do {
count += 1;
int deid1 = E2E_compact[deid];
if (deid1 == -1) {
count += 1;
break;
}
deid = deid1 / 4 * 4 + (deid1 + 1) % 4;
} while (deid != deid0 && deid != -1);
if (deid == -1)
count += 1;
valences[i] = count;
}
std::priority_queue<std::pair<int, int> > prior_queue;
for (int i = 0; i < valences.size(); ++i) {
if (valences[i] > 5)
prior_queue.push(std::make_pair(valences[i], i));
}
while (!prior_queue.empty()) {
auto info = prior_queue.top();
prior_queue.pop();
if (marks[info.second])
continue;
int deid0 = V2E_compact[info.second];
if (deid0 == -1)
continue;
int deid = deid0;
std::vector<int> loop_vertices, loop_dedges;;
bool marked = false;
do {
int v = F_compact[deid/4][(deid+1)%4];
loop_dedges.push_back(deid);
loop_vertices.push_back(v);
if (marks[v])
marked = true;
int deid1 = E2E_compact[deid];
if (deid1 == -1)
break;
deid = deid1 / 4 * 4 + (deid1 + 1) % 4;
} while (deid != deid0 && deid != -1);
if (marked)
continue;
if (deid != -1) {
int step = (info.first + 1) / 2;
std::pair<int, int> min_val(0x7fffffff, 0x7fffffff);
int split_idx = -1;
for (int i = 0; i < loop_vertices.size(); ++i) {
if (i + step >= loop_vertices.size())
continue;
int v1 = valences[loop_vertices[i]];
int v2 = valences[loop_vertices[i + step]];
if (v1 < v2)
std::swap(v1, v2);
auto key = std::make_pair(v1, v2);
if (key < min_val) {
min_val = key;
split_idx = i + 1;
}
}
if (min_val.first >= info.first)
continue;
update = true;
for (int id = split_idx; id < split_idx + step; ++id) {
F_compact[loop_dedges[id]/4][loop_dedges[id]%4] = O_compact.size();
}
F_compact.push_back(Vector4i(O_compact.size(), loop_vertices[(split_idx+loop_vertices.size()-1)%loop_vertices.size()],info.second, loop_vertices[(split_idx + step - 1 + loop_vertices.size()) % loop_vertices.size()]));
} else {
for (int id = loop_vertices.size() / 2; id < loop_vertices.size(); ++id) {
F_compact[loop_dedges[id]/4][loop_dedges[id]%4] = O_compact.size();
}
update = true;
}
marks[info.second] = 1;
for (int i = 0; i < loop_vertices.size(); ++i) {
marks[loop_vertices[i]] = 1;
}
Vset.push_back(Vset[info.second]);
O_compact.push_back(O_compact[info.second]);
N_compact.push_back(N_compact[info.second]);
Q_compact.push_back(Q_compact[info.second]);
}
if (!update) {
break;
} else {
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
}
}
// Remove Zero Valence
std::vector<int> valences(V2E_compact.size(), 0);
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
valences[F_compact[i][j]] = 1;
}
}
top = 0;
std::vector<int> compact_indices(valences.size());
for (int i = 0; i < valences.size(); ++i) {
if (valences[i] == 0)
continue;
N_compact[top] = N_compact[i];
O_compact[top] = O_compact[i];
Q_compact[top] = Q_compact[i];
Vset[top] = Vset[i];
compact_indices[i] = top;
top += 1;
}
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
F_compact[i][j] = compact_indices[F_compact[i][j]];
}
}
N_compact.resize(top);
O_compact.resize(top);
Q_compact.resize(top);
Vset.resize(top);
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
{
compute_direct_graph_quad(O_compact, F_compact, V2E_compact, E2E_compact, boundary_compact,
nonManifold_compact);
std::vector<int> masks(F_compact.size() * 4, 0);
for (int i = 0; i < V2E_compact.size(); ++i) {
int deid0 = V2E_compact[i];
if (deid0 == -1)
continue;
int deid = deid0;
do {
masks[deid] = 1;
deid = E2E_compact[deid];
if (deid == -1) {
break;
}
deid = deid / 4 * 4 + (deid + 1) % 4;
} while (deid != deid0 && deid != -1);
}
std::vector<std::vector<int> > v_dedges(V2E_compact.size());
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
v_dedges[F_compact[i][j]].push_back(i * 4 + j);
}
}
}
std::map<int, int> pts;
for (int i = 0; i < V2E_compact.size(); ++i) {
int deid0 = V2E_compact[i];
if (deid0 == -1)
continue;
int deid = deid0;
int count = 0;
do {
count += 1;
int deid1 = E2E_compact[deid];
if (deid1 == -1)
break;
deid = deid1 / 4 * 4 + (deid1 + 1) % 4;
} while (deid != deid0 && deid != -1);
if (pts.count(count) == 0)
pts[count] = 1;
else
pts[count] += 1;
}
return;
}
void Parametrizer::OutputMesh(const char* obj_name) {
std::ofstream os(obj_name);
for (int i = 0; i < O_compact.size(); ++i) {
auto t = O_compact[i] * this->normalize_scale + this->normalize_offset;
os << "v " << t[0] << " " << t[1] << " " << t[2] << "\n";
}
for (int i = 0; i < F_compact.size(); ++i) {
os << "f " << F_compact[i][0]+1 << " " << F_compact[i][1]+1
<< " " << F_compact[i][2]+1 << " " << F_compact[i][3]+1
<< "\n";
}
os.close();
}
} // namespace qflow

View File

@@ -0,0 +1,119 @@
#include "parametrizer.hpp"
namespace qflow {
void Parametrizer::ComputeInverseAffine()
{
if (flag_adaptive_scale == 0)
return;
triangle_space.resize(F.cols());
#ifdef WITH_OMP
#pragma omp parallel for
#endif
for (int i = 0; i < F.cols(); ++i) {
Matrix3d p, q;
p.col(0) = V.col(F(1, i)) - V.col(F(0, i));
p.col(1) = V.col(F(2, i)) - V.col(F(0, i));
p.col(2) = Nf.col(i);
q = p.inverse();
triangle_space[i].resize(2, 3);
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 3; ++k) {
triangle_space[i](j, k) = q(j, k);
}
}
}
}
void Parametrizer::EstimateSlope() {
auto& mF = hierarchy.mF;
auto& mQ = hierarchy.mQ[0];
auto& mN = hierarchy.mN[0];
auto& mV = hierarchy.mV[0];
FS.resize(2, mF.cols());
FQ.resize(3, mF.cols());
for (int i = 0; i < mF.cols(); ++i) {
const Vector3d& n = Nf.col(i);
const Vector3d &q_1 = mQ.col(mF(0, i)), &q_2 = mQ.col(mF(1, i)), &q_3 = mQ.col(mF(2, i));
const Vector3d &n_1 = mN.col(mF(0, i)), &n_2 = mN.col(mF(1, i)), &n_3 = mN.col(mF(2, i));
Vector3d q_1n = rotate_vector_into_plane(q_1, n_1, n);
Vector3d q_2n = rotate_vector_into_plane(q_2, n_2, n);
Vector3d q_3n = rotate_vector_into_plane(q_3, n_3, n);
auto p = compat_orientation_extrinsic_4(q_1n, n, q_2n, n);
Vector3d q = (p.first + p.second).normalized();
p = compat_orientation_extrinsic_4(q, n, q_3n, n);
q = (p.first * 2 + p.second);
q = q - n * q.dot(n);
FQ.col(i) = q.normalized();
}
for (int i = 0; i < mF.cols(); ++i) {
double step = hierarchy.mScale * 1.f;
const Vector3d &n = Nf.col(i);
Vector3d p = (mV.col(mF(0, i)) + mV.col(mF(1, i)) + mV.col(mF(2, i))) * (1.0 / 3.0);
Vector3d q_x = FQ.col(i), q_y = n.cross(q_x);
Vector3d q_xl = -q_x, q_xr = q_x;
Vector3d q_yl = -q_y, q_yr = q_y;
Vector3d q_yl_unfold = q_y, q_yr_unfold = q_y, q_xl_unfold = q_x, q_xr_unfold = q_x;
int f;
double tx, ty, len;
f = i; len = step;
TravelField(p, q_xl, len, f, hierarchy.mE2E, mV, mF, Nf, FQ, mQ, mN, triangle_space, &tx, &ty, &q_yl_unfold);
f = i; len = step;
TravelField(p, q_xr, len, f, hierarchy.mE2E, mV, mF, Nf, FQ, mQ, mN, triangle_space, &tx, &ty, &q_yr_unfold);
f = i; len = step;
TravelField(p, q_yl, len, f, hierarchy.mE2E, mV, mF, Nf, FQ, mQ, mN, triangle_space, &tx, &ty, &q_xl_unfold);
f = i; len = step;
TravelField(p, q_yr, len, f, hierarchy.mE2E, mV, mF, Nf, FQ, mQ, mN, triangle_space, &tx, &ty, &q_xr_unfold);
double dSx = (q_yr_unfold - q_yl_unfold).dot(q_x) / (2.0f * step);
double dSy = (q_xr_unfold - q_xl_unfold).dot(q_y) / (2.0f * step);
FS.col(i) = Vector2d(dSx, dSy);
}
std::vector<double> areas(mV.cols(), 0.0);
for (int i = 0; i < mF.cols(); ++i) {
Vector3d p1 = mV.col(mF(1, i)) - mV.col(mF(0, i));
Vector3d p2 = mV.col(mF(2, i)) - mV.col(mF(0, i));
double area = p1.cross(p2).norm();
for (int j = 0; j < 3; ++j) {
auto index = compat_orientation_extrinsic_index_4(FQ.col(i), Nf.col(i), mQ.col(mF(j, i)), mN.col(mF(j, i)));
double scaleX = FS.col(i).x(), scaleY = FS.col(i).y();
if (index.first != index.second % 2) {
std::swap(scaleX, scaleY);
}
if (index.second >= 2) {
scaleX = -scaleX;
scaleY = -scaleY;
}
hierarchy.mK[0].col(mF(j, i)) += area * Vector2d(scaleX, scaleY);
areas[mF(j, i)] += area;
}
}
for (int i = 0; i < mV.cols(); ++i) {
if (areas[i] != 0)
hierarchy.mK[0].col(i) /= areas[i];
}
for (int l = 0; l< hierarchy.mK.size() - 1; ++l) {
const MatrixXd &K = hierarchy.mK[l];
MatrixXd &K_next = hierarchy.mK[l + 1];
auto& toUpper = hierarchy.mToUpper[l];
for (int i = 0; i < toUpper.cols(); ++i) {
Vector2i upper = toUpper.col(i);
Vector2d k0 = K.col(upper[0]);
if (upper[1] != -1) {
Vector2d k1 = K.col(upper[1]);
k0 = 0.5 * (k0 + k1);
}
K_next.col(i) = k0;
}
}
}
} // namespace qflow

View File

@@ -0,0 +1,142 @@
#include "config.hpp"
#include "field-math.hpp"
#include "parametrizer.hpp"
namespace qflow {
void Parametrizer::ComputeOrientationSingularities() {
MatrixXd &N = hierarchy.mN[0], &Q = hierarchy.mQ[0];
const MatrixXi& F = hierarchy.mF;
singularities.clear();
for (int f = 0; f < F.cols(); ++f) {
int index = 0;
int abs_index = 0;
for (int k = 0; k < 3; ++k) {
int i = F(k, f), j = F(k == 2 ? 0 : (k + 1), f);
auto value =
compat_orientation_extrinsic_index_4(Q.col(i), N.col(i), Q.col(j), N.col(j));
index += value.second - value.first;
abs_index += std::abs(value.second - value.first);
}
int index_mod = modulo(index, 4);
if (index_mod == 1 || index_mod == 3) {
if (index >= 4 || index < 0) {
Q.col(F(0, f)) = -Q.col(F(0, f));
}
singularities[f] = index_mod;
}
}
}
void Parametrizer::ComputePositionSingularities() {
const MatrixXd &V = hierarchy.mV[0], &N = hierarchy.mN[0], &Q = hierarchy.mQ[0],
&O = hierarchy.mO[0];
const MatrixXi& F = hierarchy.mF;
pos_sing.clear();
pos_rank.resize(F.rows(), F.cols());
pos_index.resize(6, F.cols());
for (int f = 0; f < F.cols(); ++f) {
Vector2i index = Vector2i::Zero();
uint32_t i0 = F(0, f), i1 = F(1, f), i2 = F(2, f);
Vector3d q[3] = {Q.col(i0).normalized(), Q.col(i1).normalized(), Q.col(i2).normalized()};
Vector3d n[3] = {N.col(i0), N.col(i1), N.col(i2)};
Vector3d o[3] = {O.col(i0), O.col(i1), O.col(i2)};
Vector3d v[3] = {V.col(i0), V.col(i1), V.col(i2)};
int best[3];
double best_dp = -std::numeric_limits<double>::infinity();
for (int i = 0; i < 4; ++i) {
Vector3d v0 = rotate90_by(q[0], n[0], i);
for (int j = 0; j < 4; ++j) {
Vector3d v1 = rotate90_by(q[1], n[1], j);
for (int k = 0; k < 4; ++k) {
Vector3d v2 = rotate90_by(q[2], n[2], k);
double dp = std::min(std::min(v0.dot(v1), v1.dot(v2)), v2.dot(v0));
if (dp > best_dp) {
best_dp = dp;
best[0] = i;
best[1] = j;
best[2] = k;
}
}
}
}
pos_rank(0, f) = best[0];
pos_rank(1, f) = best[1];
pos_rank(2, f) = best[2];
for (int k = 0; k < 3; ++k) q[k] = rotate90_by(q[k], n[k], best[k]);
for (int k = 0; k < 3; ++k) {
int kn = k == 2 ? 0 : (k + 1);
double scale_x = hierarchy.mScale, scale_y = hierarchy.mScale,
scale_x_1 = hierarchy.mScale, scale_y_1 = hierarchy.mScale;
if (flag_adaptive_scale) {
scale_x *= hierarchy.mS[0](0, F(k, f));
scale_y *= hierarchy.mS[0](1, F(k, f));
scale_x_1 *= hierarchy.mS[0](0, F(kn, f));
scale_y_1 *= hierarchy.mS[0](1, F(kn, f));
if (best[k] % 2 != 0) std::swap(scale_x, scale_y);
if (best[kn] % 2 != 0) std::swap(scale_x_1, scale_y_1);
}
double inv_scale_x = 1.0 / scale_x, inv_scale_y = 1.0 / scale_y,
inv_scale_x_1 = 1.0 / scale_x_1, inv_scale_y_1 = 1.0 / scale_y_1;
std::pair<Vector2i, Vector2i> value = compat_position_extrinsic_index_4(
v[k], n[k], q[k], o[k], v[kn], n[kn], q[kn], o[kn], scale_x, scale_y, inv_scale_x,
inv_scale_y, scale_x_1, scale_y_1, inv_scale_x_1, inv_scale_y_1, nullptr);
auto diff = value.first - value.second;
index += diff;
pos_index(k * 2, f) = diff[0];
pos_index(k * 2 + 1, f) = diff[1];
}
if (index != Vector2i::Zero()) {
pos_sing[f] = rshift90(index, best[0]);
}
}
}
void Parametrizer::AnalyzeValence() {
auto& F = hierarchy.mF;
std::map<int, int> sing;
for (auto& f : singularities) {
for (int i = 0; i < 3; ++i) {
sing[F(i, f.first)] = f.second;
}
}
auto& F2E = face_edgeIds;
auto& E2E = hierarchy.mE2E;
auto& FQ = face_edgeOrients;
std::set<int> sing1, sing2;
for (int i = 0; i < F2E.size(); ++i) {
for (int j = 0; j < 3; ++j) {
int deid = i * 3 + j;
int sum_int = 0;
std::vector<int> edges;
std::vector<double> angles;
do {
int deid1 = deid / 3 * 3 + (deid + 2) % 3;
deid = E2E[deid1];
sum_int += (FQ[deid / 3][deid % 3] + 6 - FQ[deid1 / 3][deid1 % 3]) % 4;
} while (deid != i * 3 + j);
if (sum_int % 4 == 2) {
printf("OMG! valence = 2\n");
exit(0);
}
if (sum_int % 4 == 1) sing1.insert(F(j, i));
if (sum_int % 4 == 3) sing2.insert(F(j, i));
}
}
int count3 = 0, count4 = 0;
for (auto& s : singularities) {
if (s.second == 1)
count3 += 1;
else
count4 += 1;
}
printf("singularity: <%d %d> <%d %d>\n", (int)sing1.size(), (int)sing2.size(), count3, count4);
}
} // namespace qflow

View File

@@ -0,0 +1,254 @@
#include "parametrizer.hpp"
#include "config.hpp"
#include "dedge.hpp"
#include "field-math.hpp"
#include "flow.hpp"
#include "localsat.hpp"
#include "optimizer.hpp"
#include "subdivide.hpp"
#include "dset.hpp"
#include <Eigen/Sparse>
#include <fstream>
#include <list>
#include <map>
#include <queue>
#include <set>
namespace qflow {
bool Parametrizer::ComputeIndexMap(int with_scale) {
// build edge info
auto& V = hierarchy.mV[0];
auto& F = hierarchy.mF;
auto& Q = hierarchy.mQ[0];
auto& N = hierarchy.mN[0];
auto& O = hierarchy.mO[0];
auto& S = hierarchy.mS[0];
// ComputeOrientationSingularities();
BuildEdgeInfo();
if (flag_preserve_sharp) {
// ComputeSharpO();
}
for (int i = 0; i < sharp_edges.size(); ++i) {
if (sharp_edges[i]) {
int e = face_edgeIds[i / 3][i % 3];
if (edge_diff[e][0] * edge_diff[e][1] != 0) {
Vector3d d = O.col(edge_values[e].y) - O.col(edge_values[e].x);
Vector3d q = Q.col(edge_values[e].x);
Vector3d n = N.col(edge_values[e].x);
Vector3d qy = n.cross(q);
if (abs(q.dot(d)) > qy.dot(d))
edge_diff[e][1] = 0;
else
edge_diff[e][0] = 0;
}
}
}
std::map<int, std::pair<Vector3d, Vector3d>> sharp_constraints;
std::set<int> sharpvert;
for (int i = 0; i < sharp_edges.size(); ++i) {
if (sharp_edges[i]) {
sharpvert.insert(F(i % 3, i / 3));
sharpvert.insert(F((i + 1) % 3, i / 3));
}
}
allow_changes.resize(edge_diff.size() * 2, 1);
for (int i = 0; i < sharp_edges.size(); ++i) {
int e = face_edgeIds[i / 3][i % 3];
if (sharpvert.count(edge_values[e].x) && sharpvert.count(edge_values[e].y)) {
if (sharp_edges[i] != 0) {
for (int k = 0; k < 2; ++k) {
if (edge_diff[e][k] == 0) {
allow_changes[e * 2 + k] = 0;
}
}
}
}
}
#ifdef LOG_OUTPUT
printf("Build Integer Constraints...\n");
#endif
BuildIntegerConstraints();
ComputeMaxFlow();
// potential bug
#ifdef LOG_OUTPUT
printf("subdivide...\n");
#endif
if (!subdivide_edgeDiff(F, V, N, Q, O, &hierarchy.mS[0], V2E, hierarchy.mE2E, boundary, nonManifold,
edge_diff, edge_values, face_edgeOrients, face_edgeIds, sharp_edges,
singularities, 1))
{
return false;
}
allow_changes.clear();
allow_changes.resize(edge_diff.size() * 2, 1);
for (int i = 0; i < sharp_edges.size(); ++i) {
if (sharp_edges[i] == 0) continue;
int e = face_edgeIds[i / 3][i % 3];
for (int k = 0; k < 2; ++k) {
if (edge_diff[e][k] == 0) allow_changes[e * 2 + k] = 0;
}
}
#ifdef LOG_OUTPUT
printf("Fix flip advance...\n");
int t1 = GetCurrentTime64();
#endif
FixFlipHierarchy();
if (!subdivide_edgeDiff(F, V, N, Q, O, &hierarchy.mS[0], V2E, hierarchy.mE2E, boundary, nonManifold,
edge_diff, edge_values, face_edgeOrients, face_edgeIds, sharp_edges,
singularities, 1))
{
return false;
}
FixFlipSat();
#ifdef LOG_OUTPUT
int t2 = GetCurrentTime64();
printf("Flip use %lf\n", (t2 - t1) * 1e-3);
printf("Post Linear Solver...\n");
#endif
std::set<int> sharp_vertices;
for (int i = 0; i < sharp_edges.size(); ++i) {
if (sharp_edges[i] == 1) {
sharp_vertices.insert(F(i % 3, i / 3));
sharp_vertices.insert(F((i + 1) % 3, i / 3));
}
}
Optimizer::optimize_positions_sharp(hierarchy, edge_values, edge_diff, sharp_edges,
sharp_vertices, sharp_constraints, with_scale);
Optimizer::optimize_positions_fixed(hierarchy, edge_values, edge_diff, sharp_vertices,
sharp_constraints, flag_adaptive_scale);
AdvancedExtractQuad();
FixValence();
std::vector<int> sharp_o(O_compact.size(), 0);
std::map<int, std::pair<Vector3d, Vector3d>> compact_sharp_constraints;
for (int i = 0; i < Vset.size(); ++i) {
int sharpv = -1;
for (auto& p : Vset[i]) {
if (sharp_constraints.count(p)) {
sharpv = p;
sharp_o[i] = 1;
if (compact_sharp_constraints.count(i) == 0 ||
compact_sharp_constraints[i].second != Vector3d::Zero()) {
compact_sharp_constraints[i] = sharp_constraints[sharpv];
O_compact[i] = O.col(sharpv);
compact_sharp_constraints[i].first = O_compact[i];
}
}
}
}
std::map<std::pair<int, int>, int> o2e;
for (int i = 0; i < F_compact.size(); ++i) {
for (int j = 0; j < 4; ++j) {
int v1 = F_compact[i][j];
int v2 = F_compact[i][(j + 1) % 4];
o2e[std::make_pair(v1, v2)] = i * 4 + j;
}
}
std::vector<std::vector<int>> v2o(V.cols());
for (int i = 0; i < Vset.size(); ++i) {
for (auto v : Vset[i]) {
v2o[v].push_back(i);
}
}
std::vector<Vector3d> diffs(F_compact.size() * 4, Vector3d(0, 0, 0));
std::vector<int> diff_count(F_compact.size() * 4, 0);
for (int i = 0; i < F.cols(); ++i) {
for (int j = 0; j < 3; ++j) {
int v1 = F(j, i);
int v2 = F((j + 1) % 3, i);
if (v1 != edge_values[face_edgeIds[i][j]].x) continue;
if (edge_diff[face_edgeIds[i][j]].array().abs().sum() != 1) continue;
if (v2o[v1].size() > 1 || v2o[v2].size() > 1) continue;
for (auto o1 : v2o[v1]) {
for (auto o2 : v2o[v2]) {
auto key = std::make_pair(o1, o2);
if (o2e.count(key)) {
int dedge = o2e[key];
Vector3d q_1 = Q.col(v1);
Vector3d q_2 = Q.col(v2);
Vector3d n_1 = N.col(v1);
Vector3d n_2 = N.col(v2);
Vector3d q_1_y = n_1.cross(q_1);
Vector3d q_2_y = n_2.cross(q_2);
auto index = compat_orientation_extrinsic_index_4(q_1, n_1, q_2, n_2);
double s_x1 = S(0, v1), s_y1 = S(1, v1);
double s_x2 = S(0, v2), s_y2 = S(1, v2);
int rank_diff = (index.second + 4 - index.first) % 4;
if (rank_diff % 2 == 1) std::swap(s_x2, s_y2);
Vector3d qd_x = 0.5 * (rotate90_by(q_2, n_2, rank_diff) + q_1);
Vector3d qd_y = 0.5 * (rotate90_by(q_2_y, n_2, rank_diff) + q_1_y);
double scale_x = (with_scale ? 0.5 * (s_x1 + s_x2) : 1) * hierarchy.mScale;
double scale_y = (with_scale ? 0.5 * (s_y1 + s_y2) : 1) * hierarchy.mScale;
Vector2i diff = edge_diff[face_edgeIds[i][j]];
Vector3d C = diff[0] * scale_x * qd_x + diff[1] * scale_y * qd_y;
diff_count[dedge] += 1;
diffs[dedge] += C;
auto key = std::make_pair(o2, o1);
if (o2e.count(key)) {
int dedge = o2e[key];
diff_count[dedge] += 1;
diffs[dedge] -= C;
}
}
}
}
}
}
for (int i = 0; i < F.cols(); ++i) {
Vector2i d1 = rshift90(edge_diff[face_edgeIds[i][0]], face_edgeOrients[i][0]);
Vector2i d2 = rshift90(edge_diff[face_edgeIds[i][1]], face_edgeOrients[i][1]);
if (d1[0] * d2[1] - d1[1] * d2[0] < 0) {
for (int j = 0; j < 3; ++j) {
int v1 = F(j, i);
int v2 = F((j + 1) % 3, i);
for (auto o1 : v2o[v1]) {
for (auto o2 : v2o[v2]) {
auto key = std::make_pair(o1, o2);
if (o2e.count(key)) {
int dedge = o2e[key];
diff_count[dedge] = 0;
diffs[dedge] = Vector3d(0, 0, 0);
}
}
}
}
}
}
for (int i = 0; i < diff_count.size(); ++i) {
if (diff_count[i] != 0) {
diffs[i] /= diff_count[i];
diff_count[i] = 1;
}
}
Optimizer::optimize_positions_dynamic(F, V, N, Q, Vset, O_compact, F_compact, V2E_compact,
E2E_compact, sqrt(surface_area / F_compact.size()),
diffs, diff_count, o2e, sharp_o,
compact_sharp_constraints, flag_adaptive_scale);
// optimize_quad_positions(O_compact, N_compact, Q_compact, F_compact, V2E_compact,
// E2E_compact,
// V, N, Q, O, F, V2E, hierarchy.mE2E, disajoint_tree,
// hierarchy.mScale, false);
return true;
}
} // namespace qflow

View File

@@ -0,0 +1,178 @@
#ifndef PARAMETRIZER_H_
#define PARAMETRIZER_H_
#include <atomic>
#include <condition_variable>
#ifdef WITH_TBB
#include <tbb/tbb.h>
#endif
#include <Eigen/Core>
#include <Eigen/Dense>
#include <list>
#include <map>
#include <set>
#include <unordered_set>
#include "adjacent-matrix.hpp"
#include "disajoint-tree.hpp"
#include "field-math.hpp"
#include "hierarchy.hpp"
#include "post-solver.hpp"
#include "serialize.hpp"
namespace qflow {
using namespace Eigen;
typedef std::pair<unsigned int, unsigned int> Edge;
typedef std::map<int, std::pair<int, int>> SingDictionary;
struct ExpandInfo {
ExpandInfo() {}
int current_v;
int singularity;
int step;
int edge_id;
int prev;
};
class Parametrizer {
public:
Parametrizer() {}
// Mesh Initialization
void Load(const char* filename);
void NormalizeMesh();
void ComputeMeshStatus();
void ComputeSmoothNormal();
void ComputeSharpEdges();
void ComputeSharpO();
void ComputeVertexArea();
void Initialize(int faces);
// Singularity and Mesh property
void AnalyzeValence();
void ComputeOrientationSingularities();
void ComputePositionSingularities();
// Integer Grid Map Pipeline
// Return false when the solver fails.
bool ComputeIndexMap(int with_scale = 0);
void BuildEdgeInfo();
void ComputeMaxFlow();
void MarkInteger();
void BuildIntegerConstraints();
// Fix Flip
void FixFlipHierarchy();
void FixFlipSat();
void FixHoles();
void FixHoles(std::vector<int>& loop_vertices);
void FixValence();
double QuadEnergy(std::vector<int>& loop_vertices, std::vector<Vector4i>& res_quads,
int level);
// Quadmesh and IO
void AdvancedExtractQuad();
void BuildTriangleManifold(DisajointTree& disajoint_tree, std::vector<int>& edge,
std::vector<int>& face, std::vector<DEdge>& edge_values,
std::vector<Vector3i>& F2E, std::vector<Vector2i>& E2F,
std::vector<Vector2i>& EdgeDiff, std::vector<Vector3i>& FQ);
void OutputMesh(const char* obj_name);
std::map<int, int> singularities; // map faceid to valence (1 (valence=3) or 3(valence=5))
std::map<int, Vector2i> pos_sing;
MatrixXi pos_rank; // pos_rank(i, j) i \in [0, 3) jth face ith vertex rotate by its value so
// that all thress vertices are in the same orientation
MatrixXi pos_index; // pos_index(i x 2 + dim, j) i \in [0, 6) jth face ith vertex's
// (t_ij-t_ji)'s dim's dimenstion in the paper
// input mesh
MatrixXd V;
MatrixXd N;
MatrixXd Nf;
MatrixXd FS;
MatrixXd FQ;
MatrixXi F;
double normalize_scale;
Vector3d normalize_offset;
// data structures
VectorXd rho;
VectorXi V2E;
VectorXi E2E;
VectorXi boundary;
VectorXi nonManifold; // nonManifold vertices, in boolean
AdjacentMatrix adj;
Hierarchy hierarchy;
// Mesh Status;
double surface_area;
double scale;
double average_edge_length;
double max_edge_length;
VectorXd A;
// just for test
DisajointTree disajoint_tree;
int compact_num_v;
std::vector<std::vector<int>> Vset;
std::vector<Vector3d> O_compact;
std::vector<Vector3d> Q_compact;
std::vector<Vector3d> N_compact;
std::vector<Vector4i> F_compact;
std::set<std::pair<int, int>> Quad_edges;
std::vector<int> V2E_compact;
std::vector<int> E2E_compact;
VectorXi boundary_compact;
VectorXi nonManifold_compact;
std::vector<int> bad_vertices;
std::vector<double> counter;
std::vector<int>
sharp_edges; // sharp_edges[deid]: whether deid is a sharp edge that should be preserved
std::vector<int> allow_changes; // allow_changes[variable_id]: whether var can be changed
// based on sharp edges
std::vector<Vector2i> edge_diff; // edge_diff[edgeIds[i](j)]: t_ij+t_ji under
// edge_values[edgeIds[i](j)].x's Q value
std::vector<DEdge> edge_values; // see above
std::vector<Vector3i>
face_edgeIds; // face_edgeIds[i](j): ith face jth edge's "undirected edge ID"
// face_edgeOrients[i](j): Rotate from edge_diff space
// (a) initially, to F(0, i)'s Q space
// (b) later on, to a global Q space where some edges are fixed
std::vector<Vector3i> face_edgeOrients;
// variable[i].first: indices of the two equations corresponding to variable i
// variable[i].second: number of positive minus negative of variables' occurances
std::vector<std::pair<Vector2i, int>> variables;
struct QuadInfo {
QuadInfo() : patchId(-1), coordinate(0x10000000, 0x10000000), singular(0), edge(0) {}
int patchId;
Vector2i coordinate;
int singular;
int edge;
};
std::vector<QuadInfo> quad_info;
// scale
void ComputeInverseAffine();
void EstimateSlope();
std::vector<MatrixXd> triangle_space;
// flag
int flag_preserve_sharp = 0;
int flag_preserve_boundary = 0;
int flag_adaptive_scale = 0;
int flag_aggresive_sat = 0;
int flag_minimum_cost_flow = 0;
};
extern void generate_adjacency_matrix_uniform(const MatrixXi& F, const VectorXi& V2E,
const VectorXi& E2E, const VectorXi& nonManifold,
AdjacentMatrix& adj);
} // namespace qflow
#endif

View File

@@ -0,0 +1,429 @@
//
// post-solver.cpp
// parametrize
//
// Created by Jingwei on 2/5/18.
//
#include <algorithm>
#ifdef POST_SOLVER
#include <boost/program_options.hpp>
#endif
#include <cmath>
#include <cstdio>
#include <string>
#include "ceres/ceres.h"
#include "ceres/rotation.h"
#include "post-solver.hpp"
#include "serialize.hpp"
namespace qflow {
/// Coefficient of area constraint. The magnitude is 1 if area is equal to 0.
const double COEFF_AREA = 1;
/// Coefficient of tangent constraint. The magnitude is 0.03 if the bais is reference_length.
/// This is because current tangent constraint is not very accurate.
/// This optimization conflicts with COEFF_AREA.
const double COEFF_TANGENT = 0.02;
/// Coefficient of normal constraint. The magnitude is the arc angle.
const double COEFF_NORMAL = 1;
/// Coefficient of normal constraint. The magnitude is the arc angle.
const double COEFF_FLOW = 1;
/// Coefficient of orthogonal edge. The magnitude is the arc angle.
const double COEFF_ORTH = 1;
/// Coefficient of edge length. The magnitude is the arc angle.
const double COEFF_LENGTH = 1;
/// Number of iterations of the CGNR solver
const int N_ITER = 100;
template <typename T, typename T2>
T DotProduct(const T a[3], const T2 b[3]) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
template <typename T>
T Length2(const T a[3]) {
return DotProduct(a, a);
}
namespace ceres {
inline double min(const double f, const double g) { return std::min(f, g); }
template <typename T, int N>
inline Jet<T, N> min(const Jet<T, N>& f, const Jet<T, N>& g) {
if (f.a < g.a)
return f;
else
return g;
}
} // namespace ceres
bool DEBUG = 0;
struct FaceConstraint {
FaceConstraint(double coeff_area, double coeff_normal, double coeff_flow, double coeff_orth,
double length, Vector3d normal[4], Vector3d Q0[4], Vector3d Q1[4])
: coeff_area(coeff_area),
coeff_normal(coeff_normal),
coeff_flow(coeff_flow),
coeff_orth(coeff_orth),
area0(length * length),
normal0{
normal[0],
normal[1],
normal[2],
normal[3],
},
Q0{Q0[0], Q0[1], Q0[2], Q0[3]},
Q1{Q1[0], Q1[1], Q1[2], Q1[3]} {}
template <typename T>
bool operator()(const T* p0, const T* p1, const T* p2, const T* p3, T* r) const {
const T* p[] = {p0, p1, p2, p3};
r[12] = T();
for (int k = 0; k < 4; ++k) {
auto pc = p[k];
auto pa = p[(k + 1) % 4];
auto pb = p[(k + 3) % 4];
T a[3]{pa[0] - pc[0], pa[1] - pc[1], pa[2] - pc[2]};
T b[3]{pb[0] - pc[0], pb[1] - pc[1], pb[2] - pc[2]};
T length_a = ceres::sqrt(Length2(a));
T length_b = ceres::sqrt(Length2(b));
T aa[3]{a[0] / length_a, a[1] / length_a, a[2] / length_a};
T bb[3]{b[0] / length_b, b[1] / length_b, b[2] / length_b};
r[3 * k + 0] = coeff_orth * DotProduct(aa, bb);
T degree_edge0 = ceres::abs(DotProduct(aa, &Q0[k][0]));
T degree_edge1 = ceres::abs(DotProduct(aa, &Q1[k][0]));
T degree_edge = ceres::min(degree_edge0, degree_edge1);
r[3 * k + 1] = coeff_flow * degree_edge;
T normal[3];
ceres::CrossProduct(a, b, normal);
T area = ceres::sqrt(Length2(normal));
r[12] += area;
assert(area != T());
for (int i = 0; i < 3; ++i) normal[i] /= area;
T degree_normal = DotProduct(normal, &normal0[k][0]) - T(1);
r[3 * k + 2] = coeff_normal * degree_normal * degree_normal;
}
r[12] = coeff_area * (r[12] / (4.0 * area0) - 1.0);
return true;
}
static ceres::CostFunction* create(double coeff_area, double coeff_normal, double coeff_flow,
double coeff_orth, double length, Vector3d normal[4],
Vector3d Q0[4], Vector3d Q1[4]) {
return new ceres::AutoDiffCostFunction<FaceConstraint, 13, 3, 3, 3, 3>(new FaceConstraint(
coeff_area, coeff_normal, coeff_flow, coeff_orth, length, normal, Q0, Q1));
}
double coeff_area;
double coeff_normal;
double coeff_flow;
double coeff_orth;
double area0;
Vector3d normal0[4];
Vector3d Q0[4], Q1[4];
};
struct VertexConstraint {
VertexConstraint(double coeff_tangent, Vector3d normal, double bias, double length)
: coeff{coeff_tangent / length * 10}, bias0{bias}, normal0{normal} {}
template <typename T>
bool operator()(const T* p, T* r) const {
r[0] = coeff * (DotProduct(p, &normal0[0]) - bias0);
return true;
}
static ceres::CostFunction* create(double coeff_tangent, Vector3d normal, double bias,
double length) {
return new ceres::AutoDiffCostFunction<VertexConstraint, 1, 3>(
new VertexConstraint(coeff_tangent, normal, bias, length));
}
double coeff;
double bias0;
Vector3d normal0;
};
void solve(std::vector<Vector3d>& O_quad, std::vector<Vector3d>& N_quad,
std::vector<Vector3d>& Q_quad, std::vector<Vector4i>& F_quad,
std::vector<double>& B_quad, MatrixXd& V, MatrixXd& N, MatrixXd& Q, MatrixXd& O,
MatrixXi& F, double reference_length, double coeff_area, double coeff_tangent,
double coeff_normal, double coeff_flow, double coeff_orth) {
printf("Parameter: \n");
printf(" coeff_area: %.4f\n", coeff_area);
printf(" coeff_tangent: %.4f\n", coeff_tangent);
printf(" coeff_normal: %.4f\n", coeff_normal);
printf(" coeff_flow: %.4f\n", coeff_flow);
printf(" coeff_orth: %.4f\n\n", coeff_orth);
int n_quad = Q_quad.size();
ceres::Problem problem;
std::vector<double> solution(n_quad * 3);
for (int vquad = 0; vquad < n_quad; ++vquad) {
solution[3 * vquad + 0] = O_quad[vquad][0];
solution[3 * vquad + 1] = O_quad[vquad][1];
solution[3 * vquad + 2] = O_quad[vquad][2];
}
// Face constraint (area and normal direction)
for (int fquad = 0; fquad < F_quad.size(); ++fquad) {
auto v = F_quad[fquad];
Vector3d normal[4], Q0[4], Q1[4];
for (int k = 0; k < 4; ++k) {
normal[k] = N_quad[v[k]];
Q0[k] = Q_quad[v[k]];
Q1[k] = Q0[k].cross(normal[k]).normalized();
}
ceres::CostFunction* cost_function = FaceConstraint::create(
coeff_area, coeff_normal, coeff_flow, coeff_orth, reference_length, normal, Q0, Q1);
problem.AddResidualBlock(cost_function, nullptr, &solution[3 * v[0]], &solution[3 * v[1]],
&solution[3 * v[2]], &solution[3 * v[3]]);
}
// Tangent constraint
for (int vquad = 0; vquad < O_quad.size(); ++vquad) {
ceres::CostFunction* cost_function = VertexConstraint::create(
coeff_tangent, N_quad[vquad], B_quad[vquad], reference_length);
problem.AddResidualBlock(cost_function, nullptr, &solution[3 * vquad]);
}
// Flow constraint
ceres::Solver::Options options;
options.num_threads = 1;
options.max_num_iterations = N_ITER;
options.initial_trust_region_radius = 1;
options.linear_solver_type = ceres::CGNR;
options.minimizer_progress_to_stdout = true;
ceres::Solver::Summary summary;
ceres::Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << std::endl;
for (int vquad = 0; vquad < n_quad; ++vquad) {
O_quad[vquad][0] = solution[3 * vquad + 0];
O_quad[vquad][1] = solution[3 * vquad + 1];
O_quad[vquad][2] = solution[3 * vquad + 2];
}
return;
}
void optimize_quad_positions(std::vector<Vector3d>& O_quad, std::vector<Vector3d>& N_quad,
std::vector<Vector3d>& Q_quad, std::vector<Vector4i>& F_quad,
VectorXi& V2E_quad, std::vector<int>& E2E_quad, MatrixXd& V,
MatrixXd& N, MatrixXd& Q, MatrixXd& O, MatrixXi& F, VectorXi& V2E,
VectorXi& E2E, DisajointTree& disajoint_tree, double reference_length,
bool just_serialize) {
printf("Quad mesh info:\n");
printf("Number of vertices with normals and orientations: %d = %d = %d\n", (int)O_quad.size(),
(int)N_quad.size(), (int)Q_quad.size());
printf("Number of faces: %d\n", (int)F_quad.size());
printf("Number of directed edges: %d\n", (int)E2E_quad.size());
// Information for the original mesh
printf("Triangle mesh info:\n");
printf(
"Number of vertices with normals, "
"orientations and associated quad positions: "
"%d = %d = %d = %d\n",
(int)V.cols(), (int)N.cols(), (int)Q.cols(), (int)O.cols());
printf("Number of faces: %d\n", (int)F.cols());
printf("Number of directed edges: %d\n", (int)E2E.size());
printf("Reference length: %.2f\n", reference_length);
int flip_count = 0;
for (int i = 0; i < F_quad.size(); ++i) {
bool flipped = false;
for (int j = 0; j < 4; ++j) {
int v1 = F_quad[i][j];
int v2 = F_quad[i][(j + 1) % 4];
int v3 = F_quad[i][(j + 3) % 4];
Vector3d face_norm = (O_quad[v2] - O_quad[v1]).cross(O_quad[v3] - O_quad[v1]);
Vector3d vertex_norm = N_quad[v1];
if (face_norm.dot(vertex_norm) < 0) {
flipped = true;
}
}
if (flipped) {
flip_count++;
}
}
printf("Flipped Quads: %d\n", flip_count);
int n_quad = O_quad.size();
int n_trig = O.cols();
std::vector<double> B_quad(n_quad); // Average bias for quad vertex
std::vector<int> B_weight(n_quad);
printf("ntrig: %d, disjoint_tree.size: %d\n", n_trig, (int)disajoint_tree.indices.size());
for (int vtrig = 0; vtrig < n_trig; ++vtrig) {
int vquad = disajoint_tree.Index(vtrig);
double b = N_quad[vquad].dot(O.col(vtrig));
B_quad[vquad] += b;
B_weight[vquad] += 1;
}
for (int vquad = 0; vquad < n_quad; ++vquad) {
assert(B_weight[vquad]);
B_quad[vquad] /= B_weight[vquad];
}
puts("Save parameters to post.bin for optimization");
FILE* out = fopen("post.bin", "wb");
assert(out);
Save(out, O_quad);
Save(out, N_quad);
Save(out, Q_quad);
Save(out, F_quad);
Save(out, B_quad);
Save(out, V);
Save(out, N);
Save(out, Q);
Save(out, O);
Save(out, F);
Save(out, reference_length);
fclose(out);
if (!just_serialize) {
puts("Start post optimization");
solve(O_quad, N_quad, Q_quad, F_quad, B_quad, V, N, Q, O, F, reference_length, COEFF_AREA,
COEFF_TANGENT, COEFF_NORMAL, COEFF_FLOW, COEFF_ORTH);
}
}
#ifdef POST_SOLVER
void SaveObj(const std::string& fname, std::vector<Vector3d> O_quad,
std::vector<Vector4i> F_quad) {
std::ofstream os(fname);
for (int i = 0; i < (int)O_quad.size(); ++i) {
os << "v " << O_quad[i][0] << " " << O_quad[i][1] << " " << O_quad[i][2] << "\n";
}
for (int i = 0; i < (int)F_quad.size(); ++i) {
os << "f " << F_quad[i][0] + 1 << " " << F_quad[i][1] + 1 << " " << F_quad[i][2] + 1 << " "
<< F_quad[i][3] + 1 << "\n";
}
os.close();
}
int main(int argc, char* argv[]) {
double coeff_area;
double coeff_tangent;
double coeff_normal;
double coeff_flow;
double coeff_orth;
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options() // clang-format off
("help,h", "produce help message")
("area,a", po::value<double>(&coeff_area)->default_value(COEFF_AREA), "Set the coefficient of area constraint")
("tangent,t", po::value<double>(&coeff_tangent)->default_value(COEFF_TANGENT), "Set the coefficient of tangent constraint")
("normal,n", po::value<double>(&coeff_normal)->default_value(COEFF_NORMAL), "Set the coefficient of normal constraint")
("flow,f", po::value<double>(&coeff_flow)->default_value(COEFF_FLOW), "Set the coefficient of flow (Q) constraint")
("orth,o", po::value<double>(&coeff_orth)->default_value(COEFF_ORTH), "Set the coefficient of orthogonal constraint");
// clang-format on
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return 1;
}
std::vector<Vector3d> O_quad;
std::vector<Vector3d> N_quad;
std::vector<Vector3d> Q_quad;
std::vector<Vector4i> F_quad;
std::vector<double> B_quad;
MatrixXd V;
MatrixXd N;
MatrixXd Q;
MatrixXd O;
MatrixXi F;
double reference_length;
puts("Read parameters from post.bin");
FILE* in = fopen("post.bin", "rb");
assert(in);
Read(in, O_quad);
Read(in, N_quad);
Read(in, Q_quad);
Read(in, F_quad);
Read(in, B_quad);
Read(in, V);
Read(in, N);
Read(in, Q);
Read(in, O);
Read(in, F);
Read(in, reference_length);
fclose(in);
printf("reference_length: %.2f\n", reference_length);
SaveObj("presolver.obj", O_quad, F_quad);
int n_flip = 0;
double sum_degree = 0;
for (int i = 0; i < F_quad.size(); ++i) {
bool flipped = false;
for (int j = 0; j < 4; ++j) {
int v1 = F_quad[i][j];
int v2 = F_quad[i][(j + 1) % 4];
int v3 = F_quad[i][(j + 3) % 4];
Vector3d face_norm =
(O_quad[v2] - O_quad[v1]).cross(O_quad[v3] - O_quad[v1]).normalized();
Vector3d vertex_norm = N_quad[v1];
if (face_norm.dot(vertex_norm) < 0) {
flipped = true;
}
double degree = std::acos(face_norm.dot(vertex_norm));
assert(degree >= 0);
// printf("cos theta = %.2f\n", degree);
sum_degree += degree * degree;
}
n_flip += flipped;
}
printf("n_flip: %d\nsum_degree: %.3f\n", n_flip, sum_degree);
puts("Start post optimization");
solve(O_quad, N_quad, Q_quad, F_quad, B_quad, V, N, Q, O, F, reference_length, coeff_area,
coeff_tangent, coeff_normal, coeff_flow, coeff_orth);
SaveObj("postsolver.obj", O_quad, F_quad);
n_flip = 0;
sum_degree = 0;
for (int i = 0; i < F_quad.size(); ++i) {
bool flipped = false;
for (int j = 0; j < 4; ++j) {
int v1 = F_quad[i][j];
int v2 = F_quad[i][(j + 1) % 4];
int v3 = F_quad[i][(j + 3) % 4];
Vector3d face_norm =
(O_quad[v2] - O_quad[v1]).cross(O_quad[v3] - O_quad[v1]).normalized();
Vector3d vertex_norm = N_quad[v1];
if (face_norm.dot(vertex_norm) < 0) {
flipped = true;
}
double degree = std::acos(face_norm.dot(vertex_norm));
assert(degree >= 0);
sum_degree += degree * degree;
}
n_flip += flipped;
}
printf("n_flip: %d\nsum_degree: %.3f\n", n_flip, sum_degree);
return 0;
}
#endif
} // namespace qflow

View File

@@ -0,0 +1,64 @@
//
// post-solver.hpp
// Parametrize
//
// Created by Jingwei on 2/5/18.
//
#ifndef post_solver_h
#define post_solver_h
#include <Eigen/Core>
#include <vector>
#include "disajoint-tree.hpp"
namespace qflow {
using namespace Eigen;
/*
* TODO: Optimize O_quad, and possibly N_quad
* Input:
* O_quad[i]: initialized i-th vertex position of the quad mesh
* N_quad[i]: initialized i-th vertex normal of the quad mesh
* Q_quad[i]: initialized i-th vertex orientation of the quad mesh, guaranteed to be orthogonal to
* N_quad[i]
* F_quad[i]: 4 vertex index of the i-th quad face
*
* Concept: i-th directed edge is the (i%4)-th edge of the (i/4)-th face of the quad mesh
* V2E_quad[i]: one directed edge from i-th vertex of the quad mesh
* E2E_quad[i]: the reverse directed edge's index of the i-th directed edge of the quad mesh
*
* V.col(i): i-th vertex position of the triangle mesh
* N.col(i): i-th vertex normal of the triangle mesh
* Q.col(i): i-th vertex orientation of the triangle mesh, guaranteed to be orthogonal to N.col(i)
* O.col(i): "quad position" associated with the i-th vertex in the triangle mesh (see InstantMesh
* position field)
* F.col(i): i-th triangle of the triangle mesh
*
* V2E[i]: one directed edge from the i-th vertex of the triangle mesh
* E2E[i]: the reverse directed edge's index of the i-th directed edge of the triangle mesh
*
* j = disajoint_tree.Index(i)
* the j-th vertex of the quad mesh is corresponding to the i-th vertex of the triangle mesh
* the relation is one-to-multiple
* O_quad can be viewed as an average of corresponding O
* N_quad can be viewed as an average of corresponding N
* Q_quad can be viewed as aggregation of corresponding Q
* Method that aggregates qi to qj with weights wi and wj:
* value = compat_orientation_extrinsic_4(qj, nj, qi, ni)
* result = (value.first * wj + value.second * wi).normalized()
*
* Output:
* Optimized O_quad, (possibly N_quad)
*/
void optimize_quad_positions(std::vector<Vector3d>& O_quad, std::vector<Vector3d>& N_quad,
std::vector<Vector3d>& Q_quad, std::vector<Vector4i>& F_quad,
VectorXi& V2E_quad, std::vector<int>& E2E_quad, MatrixXd& V, MatrixXd& N,
MatrixXd& Q, MatrixXd& O, MatrixXi& F, VectorXi& V2E, VectorXi& E2E,
DisajointTree& disajoint_tree, double reference_length,
bool just_serialize = true);
} // namespace qflow
#endif /* post_solver_h */

View File

@@ -0,0 +1,127 @@
#ifndef SERIALIZE_H_
#define SERIALIZE_H_
#include <Eigen/Core>
#include <fstream>
#include <map>
#include <set>
#include <vector>
#include "adjacent-matrix.hpp"
namespace qflow {
template <typename T, int A, int B>
inline void Save(FILE* fp, const Eigen::Matrix<T, A, B>& m) {
int r = m.rows(), c = m.cols();
fwrite(&r, sizeof(int), 1, fp);
fwrite(&c, sizeof(int), 1, fp);
std::vector<T> buffer(r * c);
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) {
buffer[i * c + j] = m(i, j);
}
}
fwrite(buffer.data(), sizeof(T), r * c, fp);
}
template <typename T, int A, int B>
inline void Read(FILE* fp, Eigen::Matrix<T, A, B>& m) {
int r, c;
fread(&r, sizeof(int), 1, fp);
fread(&c, sizeof(int), 1, fp);
std::vector<T> buffer(r * c);
fread(buffer.data(), sizeof(T), r * c, fp);
m.resize(r, c);
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) {
m(i, j) = buffer[i * c + j];
}
}
}
inline void Save(FILE* fp, const Link& p) { fwrite(&p, sizeof(Link), 1, fp); }
inline void Read(FILE* fp, Link& p) { fread(&p, sizeof(Link), 1, fp); }
inline void Save(FILE* fp, const TaggedLink& p) { fwrite(&p, sizeof(TaggedLink), 1, fp); }
inline void Read(FILE* fp, TaggedLink& p) { fread(&p, sizeof(TaggedLink), 1, fp); }
inline void Save(FILE* fp, double p) { fwrite(&p, sizeof(double), 1, fp); }
inline void Read(FILE* fp, double& p) { fread(&p, sizeof(double), 1, fp); }
inline void Save(FILE* fp, int p) { fwrite(&p, sizeof(int), 1, fp); }
inline void Read(FILE* fp, int& p) { fread(&p, sizeof(int), 1, fp); }
template <class T, class F>
inline void Save(FILE* fp, const std::pair<T, F>& p) {
fwrite(&p.first, sizeof(T), 1, fp);
fwrite(&p.second, sizeof(F), 1, fp);
}
template <class T, class F>
inline void Read(FILE* fp, std::pair<T, F>& p) {
fread(&p.first, sizeof(T), 1, fp);
fread(&p.second, sizeof(F), 1, fp);
}
template <class T, class F>
inline void Save(FILE* fp, const std::map<T, F>& p) {
int num = p.size();
fwrite(&num, sizeof(int), 1, fp);
for (auto& s : p) {
fwrite(&s, sizeof(s), 1, fp);
}
}
template <class T, class F>
inline void Read(FILE* fp, std::map<T, F>& p) {
int num;
p.clear();
fread(&num, sizeof(int), 1, fp);
for (int i = 0; i < num; ++i) {
std::pair<T, F> m;
fread(&m, sizeof(m), 1, fp);
p.insert(m);
}
}
template <class T>
void Save(FILE* fp, const std::vector<T>& p) {
int num = p.size();
fwrite(&num, sizeof(int), 1, fp);
for (auto& q : p) {
Save(fp, q);
}
}
template <class T>
void Read(FILE* fp, std::vector<T>& p) {
int num;
fread(&num, sizeof(int), 1, fp);
p.resize(num);
for (auto& q : p) {
Read(fp, q);
}
}
template <class T>
void Save(FILE* fp, const std::set<T>& p) {
std::vector<T> buffer;
buffer.insert(buffer.end(), p.begin(), p.end());
Save(fp, buffer);
}
template <class T>
void Read(FILE* fp, std::set<T>& p) {
std::vector<T> buffer;
Read(fp, buffer);
p.clear();
for (auto& q : buffer) p.insert(q);
}
} // namespace qflow
#endif

View File

@@ -0,0 +1,517 @@
#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);
}
bool 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) {
fprintf(stderr, "wrong init %d %d!\n", face_edgeIds[i][j], i * 3 + j);
return false;
}
}
}
for (int i = 0; i < edge_diff.size(); ++i) {
if (abs(edge_diff[i][0]) > 1 || abs(edge_diff[i][1]) > 1) {
fprintf(stderr, "wrong...\n");
return false;
}
}
return true;
}
} // namespace qflow

View File

@@ -0,0 +1,18 @@
#include <Eigen/Core>
#include <Eigen/Dense>
#include "parametrizer.hpp"
using namespace Eigen;
namespace qflow {
void subdivide(MatrixXi &F, MatrixXd &V, VectorXd& rho, VectorXi &V2E, VectorXi &E2E, VectorXi &boundary,
VectorXi &nonmanifold, double maxLength);
// Return false when solving fails.
bool 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);
} // namespace qflow