Skip to content

Commit

Permalink
Require fixed vertices on LSCM init
Browse files Browse the repository at this point in the history
Also remove unecessary transform after LSCM solve and fix local_to_world
transforms.
  • Loading branch information
davreev committed Aug 20, 2024
1 parent 473046b commit 18c6d0b
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 49 deletions.
57 changes: 40 additions & 17 deletions src/least_squares_conformal_map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ namespace dr
template <typename Real, typename Index>
struct LeastSquaresConformalMap
{
void init(
bool init(
Span<Vec3<Real> const> const& vertex_positions,
Span<Vec3<Index> const> const& face_vertices,
Span<Vec2<Index> const> const& boundary_edge_vertices)
Span<Vec2<Index> const> const& boundary_edge_vertices,
Vec2<Index> const& fixed_vertices)
{
Index const num_verts = vertex_positions.size();
Index const n = num_verts << 1;
Expand Down Expand Up @@ -61,34 +62,47 @@ struct LeastSquaresConformalMap

// Create quadratic form Q = 2 A - Ld
Q_ = Real{2.0} * A_ - Ld_;
status_ = Status_Initialized;

// Initialize solver
set_fixed(fixed_vertices);
if (solver_.init(Q_, [&](Index i) { return is_fixed(i); }))
{
status_ = Status_Initialized;
return true;
}
else
{
status_ = Status_Default;
return false;
}
}

bool solve(Vec2<Index> const& fixed_vertices, Span<Vec2<Real>> const& result)
bool reinit(Vec2<Index> const& fixed_vertices)
{
assert(is_init());
Index const num_verts = x_.size() >> 1;

auto const is_fixed = [=](Index const index) {
Index const v = index % num_verts;
return v == fixed_vertices[0] || v == fixed_vertices[1];
};

// Init solver
if (!solver_.init(Q_, is_fixed))
// Initialize solver
set_fixed(fixed_vertices);
if (!solver_.init(Q_, [&](Index i) { return is_fixed(i); }))
{
status_ = Status_Initialized;
status_ = Status_Default;
return false;
}

return true;
}

void solve(Span<Vec2<Real>> const& result)
{
assert(is_init());

// Assign fixed vertices
x_.reshaped(num_verts, 2)(fixed_vertices, Eigen::all) = //
as_mat(result)(Eigen::all, fixed_vertices).transpose();
x_(fixed_({0, 2})) = result[fixed_[0]];
x_(fixed_({1, 3})) = result[fixed_[1]];

// Solve remaining vertices
solver_.solve(x_);
as_mat(result) = x_.reshaped(num_verts, 2).transpose();
return true;
as_mat(result) = x_.reshaped(x_.size() >> 1, 2).transpose();
}

bool is_init() const { return status_ != Status_Default; }
Expand All @@ -108,7 +122,16 @@ struct LeastSquaresConformalMap
SparseMat<Real, Index> Q_{};
DynamicArray<Triplet<Real, Index>> coeffs_{};
Vec<Real> x_{};
Vec4<Index> fixed_{};
Status status_{};

void set_fixed(Vec2<Index> const& vertices)
{
fixed_({0, 1}) = vertices;
fixed_({2, 3}) = vertices.array() + (x_.size() >> 1);
}

bool is_fixed(Index const index) const { return (fixed_.array() == index).any(); }
};

} // namespace dr
28 changes: 22 additions & 6 deletions src/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,22 +499,38 @@ void update(void* /*context*/)

void draw(void* /*context*/)
{
auto const make_local_to_world = [&]() -> Mat4<f32> {
constexpr auto make_local_to_world = []() -> Mat4<f32> {
if (state.shape.mesh)
{
if (state.params.flatten)
{
static Mat3<f32> const r_s = //
mat(vec(0.0f, 0.0f, 1.0f), vec(0.0f, 1.0f, 0.0f), vec(-1.0f, 0.0f, 0.0f))
* mat<3>(2.0f);
if (state.params.solve_method == SolveTexCoords::Method_None)
{
static auto const r = mat(
vec(0.0f, 1.0f, 0.0f),
vec(0.0f, 0.0f, 1.0f),
vec(1.0f, 0.0f, 0.0f));

auto const [cen, rad] = state.shape.mesh->bounds;
f32 const s = 1.0f / rad;
return make_affine(s * r, -cen * s);
}
else
{
static auto const r = mat(
vec(0.0f, 0.0f, 1.0f),
vec(0.0f, 1.0f, 0.0f),
vec(-1.0f, 0.0f, 0.0f));

return make_affine(r_s);
return make_affine(r);
}
}
else
{
// Fit to unit sphere
auto const [cen, rad] = state.shape.mesh->bounds;
return make_scale_translate(vec<3>(1.0f / rad), -cen);
f32 const s = 1.0f / rad;
return make_scale_translate(vec<3>(s), -cen * s);
}
}
else
Expand Down
51 changes: 25 additions & 26 deletions src/tasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void collect_boundary_edge_verts(
DynamicArray<Vec2<i32>>& result)
{
result.clear();
const isize num_edges = edge_faces.size();
isize const num_edges = edge_faces.size();

for (isize e = 0; e < num_edges; ++e)
{
Expand All @@ -20,20 +20,6 @@ void collect_boundary_edge_verts(
}
}

void transform_tex_coords(Span<Vec2<f32>> const& tex_coords, Vec2<i32> const& ref_verts)
{
constexpr auto perp_ccw = [](Vec2<f32> const& a) { return vec(-a[1], a[0]); };

auto const [v0, v1] = expand(ref_verts);
Vec2<f32> const d = tex_coords[v1] - tex_coords[v0];

// Align ref verts to x axis and center on origin
Mat2<f32> const r_s = mat(d, perp_ccw(d)).transpose() / d.squaredNorm();
Vec2<f32> const t = -(tex_coords[v0] + d * 0.5f);
for (auto& p : tex_coords)
p = r_s * (p + t);
}

} // namespace

void LoadMeshAsset::operator()()
Expand All @@ -47,7 +33,7 @@ void ExtractMeshBoundary::operator()()
auto const tri_verts = as_span(input.mesh->faces.vertex_ids);
VertsToEdge<i32>::make_from_tris(tri_verts, verts_to_edge_);

const isize num_edges = verts_to_edge_.size();
isize const num_edges = verts_to_edge_.size();
edge_tris_.resize(num_edges);
collect_edge_tris(tri_verts, verts_to_edge_, as_span(edge_tris_));

Expand Down Expand Up @@ -84,24 +70,25 @@ void SolveTexCoords::operator()()
case Method_LeastSquaresConformal:
{
auto& solver = solvers_.lscm;
solver.init(
bool const ok = solver.init(
as_span(input.mesh->vertices.positions),
as_span(input.mesh->faces.vertex_ids),
input.boundary_edge_verts);

// Set positions of fixed vertices
as_mat(tc)(Eigen::all, input.ref_verts) = //
mat(col(0.0f, -1.0f), col(0.0f, 1.0f));
input.boundary_edge_verts,
input.ref_verts);

// Solve for remaining vertices
if (!solver.solve(input.ref_verts, tc))
if (!ok)
{
output.tex_coords = {};
output.error = Error_SolveFailed;
return;
}

transform_tex_coords(tc, input.ref_verts);
// Assign coords of fixed vertices
tc[input.ref_verts[0]] = {-1.0f, 0.0f};
tc[input.ref_verts[1]] = {1.0f, 0.0f};

// Solve for remaining vertices
solver.solve(tc);
break;
}
case Method_SpectralConformal:
Expand All @@ -119,7 +106,19 @@ void SolveTexCoords::operator()()
return;
}

transform_tex_coords(tc, input.ref_verts);
// Apply conformal xform that places ref verts at (1.0, 0.0) and (-1.0, 0.0)
{
constexpr auto perp_ccw = [](Vec2<f32> const& a) { return vec(-a[1], a[0]); };

auto const [v0, v1] = expand(input.ref_verts);
Vec2<f32> const d = tc[v1] - tc[v0];

Mat2<f32> const r_s = mat(d, perp_ccw(d)).transpose() * (2.0f / d.squaredNorm());
Vec2<f32> const t = -(tc[v0] + d * 0.5f);

for (auto& p : tc)
p = r_s * (p + t);
}
break;
}
default:
Expand Down

0 comments on commit 18c6d0b

Please sign in to comment.