Add Chromium-only Blender WebEngine parity work
This commit is contained in:
281
blender-5.2.0/extern/quadriflow/src/Optimizer.cu
vendored
Normal file
281
blender-5.2.0/extern/quadriflow/src/Optimizer.cu
vendored
Normal 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);
|
||||
}
|
||||
35
blender-5.2.0/extern/quadriflow/src/adjacent-matrix.cpp
vendored
Normal file
35
blender-5.2.0/extern/quadriflow/src/adjacent-matrix.cpp
vendored
Normal 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
|
||||
37
blender-5.2.0/extern/quadriflow/src/adjacent-matrix.hpp
vendored
Normal file
37
blender-5.2.0/extern/quadriflow/src/adjacent-matrix.hpp
vendored
Normal 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
|
||||
102
blender-5.2.0/extern/quadriflow/src/compare-key.hpp
vendored
Normal file
102
blender-5.2.0/extern/quadriflow/src/compare-key.hpp
vendored
Normal 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
|
||||
34
blender-5.2.0/extern/quadriflow/src/config.hpp
vendored
Normal file
34
blender-5.2.0/extern/quadriflow/src/config.hpp
vendored
Normal 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
|
||||
487
blender-5.2.0/extern/quadriflow/src/dedge.cpp
vendored
Normal file
487
blender-5.2.0/extern/quadriflow/src/dedge.cpp
vendored
Normal 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
|
||||
25
blender-5.2.0/extern/quadriflow/src/dedge.hpp
vendored
Normal file
25
blender-5.2.0/extern/quadriflow/src/dedge.hpp
vendored
Normal 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
|
||||
151
blender-5.2.0/extern/quadriflow/src/disajoint-tree.hpp
vendored
Normal file
151
blender-5.2.0/extern/quadriflow/src/disajoint-tree.hpp
vendored
Normal 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
|
||||
163
blender-5.2.0/extern/quadriflow/src/dset.hpp
vendored
Normal file
163
blender-5.2.0/extern/quadriflow/src/dset.hpp
vendored
Normal 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 */
|
||||
481
blender-5.2.0/extern/quadriflow/src/field-math.hpp
vendored
Normal file
481
blender-5.2.0/extern/quadriflow/src/field-math.hpp
vendored
Normal 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
|
||||
344
blender-5.2.0/extern/quadriflow/src/flow.hpp
vendored
Normal file
344
blender-5.2.0/extern/quadriflow/src/flow.hpp
vendored
Normal 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
|
||||
1358
blender-5.2.0/extern/quadriflow/src/hierarchy.cpp
vendored
Normal file
1358
blender-5.2.0/extern/quadriflow/src/hierarchy.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
99
blender-5.2.0/extern/quadriflow/src/hierarchy.hpp
vendored
Normal file
99
blender-5.2.0/extern/quadriflow/src/hierarchy.hpp
vendored
Normal 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
|
||||
160
blender-5.2.0/extern/quadriflow/src/loader.cpp
vendored
Normal file
160
blender-5.2.0/extern/quadriflow/src/loader.cpp
vendored
Normal 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
|
||||
15
blender-5.2.0/extern/quadriflow/src/loader.hpp
vendored
Normal file
15
blender-5.2.0/extern/quadriflow/src/loader.hpp
vendored
Normal 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
|
||||
295
blender-5.2.0/extern/quadriflow/src/localsat.cpp
vendored
Normal file
295
blender-5.2.0/extern/quadriflow/src/localsat.cpp
vendored
Normal 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
|
||||
31
blender-5.2.0/extern/quadriflow/src/localsat.hpp
vendored
Normal file
31
blender-5.2.0/extern/quadriflow/src/localsat.hpp
vendored
Normal 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
|
||||
130
blender-5.2.0/extern/quadriflow/src/main.cpp
vendored
Normal file
130
blender-5.2.0/extern/quadriflow/src/main.cpp
vendored
Normal 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;
|
||||
}
|
||||
44
blender-5.2.0/extern/quadriflow/src/merge-vertex.cpp
vendored
Normal file
44
blender-5.2.0/extern/quadriflow/src/merge-vertex.cpp
vendored
Normal 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
|
||||
14
blender-5.2.0/extern/quadriflow/src/merge-vertex.hpp
vendored
Normal file
14
blender-5.2.0/extern/quadriflow/src/merge-vertex.hpp
vendored
Normal 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
|
||||
1421
blender-5.2.0/extern/quadriflow/src/optimizer.cpp
vendored
Normal file
1421
blender-5.2.0/extern/quadriflow/src/optimizer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
56
blender-5.2.0/extern/quadriflow/src/optimizer.hpp
vendored
Normal file
56
blender-5.2.0/extern/quadriflow/src/optimizer.hpp
vendored
Normal 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
|
||||
583
blender-5.2.0/extern/quadriflow/src/parametrizer-flip.cpp
vendored
Normal file
583
blender-5.2.0/extern/quadriflow/src/parametrizer-flip.cpp
vendored
Normal 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
|
||||
425
blender-5.2.0/extern/quadriflow/src/parametrizer-int.cpp
vendored
Normal file
425
blender-5.2.0/extern/quadriflow/src/parametrizer-int.cpp
vendored
Normal 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
|
||||
615
blender-5.2.0/extern/quadriflow/src/parametrizer-mesh.cpp
vendored
Normal file
615
blender-5.2.0/extern/quadriflow/src/parametrizer-mesh.cpp
vendored
Normal 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
|
||||
119
blender-5.2.0/extern/quadriflow/src/parametrizer-scale.cpp
vendored
Normal file
119
blender-5.2.0/extern/quadriflow/src/parametrizer-scale.cpp
vendored
Normal 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
|
||||
142
blender-5.2.0/extern/quadriflow/src/parametrizer-sing.cpp
vendored
Normal file
142
blender-5.2.0/extern/quadriflow/src/parametrizer-sing.cpp
vendored
Normal 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
|
||||
254
blender-5.2.0/extern/quadriflow/src/parametrizer.cpp
vendored
Normal file
254
blender-5.2.0/extern/quadriflow/src/parametrizer.cpp
vendored
Normal 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
|
||||
178
blender-5.2.0/extern/quadriflow/src/parametrizer.hpp
vendored
Normal file
178
blender-5.2.0/extern/quadriflow/src/parametrizer.hpp
vendored
Normal 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
|
||||
429
blender-5.2.0/extern/quadriflow/src/post-solver.cpp
vendored
Normal file
429
blender-5.2.0/extern/quadriflow/src/post-solver.cpp
vendored
Normal 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
|
||||
64
blender-5.2.0/extern/quadriflow/src/post-solver.hpp
vendored
Normal file
64
blender-5.2.0/extern/quadriflow/src/post-solver.hpp
vendored
Normal 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 */
|
||||
127
blender-5.2.0/extern/quadriflow/src/serialize.hpp
vendored
Normal file
127
blender-5.2.0/extern/quadriflow/src/serialize.hpp
vendored
Normal 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
|
||||
517
blender-5.2.0/extern/quadriflow/src/subdivide.cpp
vendored
Normal file
517
blender-5.2.0/extern/quadriflow/src/subdivide.cpp
vendored
Normal 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
|
||||
18
blender-5.2.0/extern/quadriflow/src/subdivide.hpp
vendored
Normal file
18
blender-5.2.0/extern/quadriflow/src/subdivide.hpp
vendored
Normal 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
|
||||
Reference in New Issue
Block a user