Wildmeshing Toolkit
Loading...
Searching...
No Matches
OffsetPotential.hpp
1#pragma once
2
3#include <wmtk/Types.hpp>
4#include <wmtk/envelope/Envelope.hpp>
5
6#include <wmtk/threading/enumerable_thread_specific.hpp>
7#include "SimplicialComplexBVH.hpp"
8
9#include <polysolve/nonlinear/Problem.hpp>
10
11#include <cmath>
12#include <memory>
13#include <string>
14#include <vector>
15
16namespace wmtk::components::topological_offset {
17
35template <int DIM>
37{
38 static_assert(DIM == 2 || DIM == 3, "the offset potential exists in 2D and 3D only");
39
40public:
41 using VecD = Eigen::Matrix<double, DIM, 1>;
42 using MatD = Eigen::Matrix<double, DIM, DIM>;
43
48
50 double target_level() const { return m_c; }
51
53 double delta() const { return m_delta; }
54
57 double dhat() const { return m_dhat; }
58
71 double level_set_slope() const { return m_grad_ref; }
72
73 virtual double value(const VecD& p) const = 0;
75 virtual bool is_euclidean() const { return false; }
76 virtual VecD gradient(const VecD& p) const = 0;
77 virtual MatD hessian(const VecD& p) const = 0;
78
85 virtual double residual_length(const VecD& p) const = 0;
86
98 virtual double relative_residual(const VecD& p) const = 0;
99
101 virtual bool within_support(const VecD& p) const = 0;
102
109 virtual bool is_inside_offset(const VecD& p) const = 0;
110
112 virtual std::string describe_active(const VecD& p) const = 0;
113
114protected:
117 OffsetPotential(const double delta, const double dhat)
118 : m_delta(delta)
119 , m_dhat(dhat)
120 {}
121
122 double m_delta = 0.;
123 double m_dhat = 0.;
124 double m_c = 0.;
127 double m_grad_ref = 1.;
128};
129
132
133
171template <int DIM>
173{
174 static_assert(DIM == 2 || DIM == 3, "the offset potential exists in 2D and 3D only");
175
176public:
177 using VecD = typename OffsetPotential<DIM>::VecD;
178 using MatD = typename OffsetPotential<DIM>::MatD;
179
199 const MatrixXd& V,
200 const MatrixXi& E,
201 const MatrixXi& F,
202 const std::vector<int>& P,
203 double delta,
204 double dhat_factor);
205
206 ~SmoothOffsetPotential() override;
207
208 double value(const VecD& p) const override;
209 VecD gradient(const VecD& p) const override;
210 MatD hessian(const VecD& p) const override;
211
224 double residual_length(const VecD& p) const override;
225
242 double relative_residual(const VecD& p) const override;
243
245 bool within_support(const VecD& p) const override { return value(p) > 0.; }
246
248 bool is_inside_offset(const VecD& p) const override { return value(p) >= m_c; }
249
252 std::string describe_active(const VecD& p) const override;
253
254private:
255 // Dependent base members: name them unqualified in the definitions below.
256 using OffsetPotential<DIM>::m_delta;
257 using OffsetPotential<DIM>::m_dhat;
258 using OffsetPotential<DIM>::m_c;
260
263 SmoothOffsetPotential(double delta, double dhat_factor, int /*calibration tag*/);
264
265 void build(const MatrixXd& V, const MatrixXi& E, const MatrixXi& F, const std::vector<int>& P);
266
270 bool level_set_distance(const VecD& p, double& t) const;
271
273 void value_gradient(const VecD& p, double& v, VecD& g) const;
274
277 struct Impl;
278 std::unique_ptr<Impl> m_impl;
279};
280
281using SmoothOffsetPotential2D = SmoothOffsetPotential<2>;
282using SmoothOffsetPotential3D = SmoothOffsetPotential<3>;
283
284
313template <int DIM>
315{
316 static_assert(DIM == 2 || DIM == 3, "the offset potential exists in 2D and 3D only");
317
318public:
319 bool is_euclidean() const override { return true; }
320 using VecD = typename OffsetPotential<DIM>::VecD;
321 using MatD = typename OffsetPotential<DIM>::MatD;
322
330 EuclideanOffsetPotential(const std::shared_ptr<SampleEnvelope>& envelope, double delta);
331
341 EuclideanOffsetPotential(const std::shared_ptr<SimplicialComplexBVH>& bvh, double delta);
342
343 double value(const VecD& p) const override;
344 VecD gradient(const VecD& p) const override;
345 MatD hessian(const VecD& p) const override;
346
349 double residual_length(const VecD& p) const override
350 {
351 return std::abs(value(p) - m_c) / m_grad_ref;
352 }
353
357 double relative_residual(const VecD& p) const override { return (value(p) - m_c) / m_c; }
358
360 bool within_support(const VecD& p) const override { return true; }
361
364 bool is_inside_offset(const VecD& p) const override { return value(p) <= m_c; }
365
366 std::string describe_active(const VecD& p) const override;
367
368private:
371 void nearest_feature(const VecD& p, VecD& foot, int& dim, VecD& dir) const;
372
373 using OffsetPotential<DIM>::m_delta;
375 using OffsetPotential<DIM>::m_dhat;
376 using OffsetPotential<DIM>::m_c;
377
380 std::shared_ptr<SampleEnvelope> m_envelope;
381 std::shared_ptr<SimplicialComplexBVH> m_bvh;
382};
383
386
387
410template <int DIM>
411class OffsetEnergy : public polysolve::nonlinear::Problem
412{
413public:
414 using typename polysolve::nonlinear::Problem::Scalar;
415 using typename polysolve::nonlinear::Problem::THessian;
416 using typename polysolve::nonlinear::Problem::TVector;
417
418 using VecD = Eigen::Matrix<double, DIM, 1>;
419 using MatD = Eigen::Matrix<double, DIM, DIM>;
420
427 const std::shared_ptr<const OffsetPotential<DIM>>& potential,
428 double weight = 1.,
429 bool gauss_newton = true,
430 bool distance_residual = false);
431
432 double value(const TVector& x) override;
433 void gradient(const TVector& x, TVector& gradv) override;
434 void hessian(const TVector& x, THessian& hessian) override
435 {
436 log_and_throw_error("Sparse functions do not exist, use dense solver");
437 }
438 void hessian(const TVector& x, MatrixXd& hessian) override;
439
440 void solution_changed(const TVector& new_x) override {}
441
442private:
443 std::shared_ptr<const OffsetPotential<DIM>> m_potential;
444 double m_weight;
445 bool m_gauss_newton;
446 bool m_distance_residual;
450 bool root_distance(const VecD& p, double& s, VecD& n) const;
453 void residual(const VecD& p, double& r, VecD& dr) const;
454 mutable double m_last_root = 0.;
455 mutable bool m_cache_valid = false;
456 mutable VecD m_cache_p;
457 mutable double m_cache_r = 0.;
458 mutable VecD m_cache_dr;
459};
460
462
481class AlignEnergy2D : public polysolve::nonlinear::Problem
482{
483public:
484 using typename polysolve::nonlinear::Problem::Scalar;
485 using typename polysolve::nonlinear::Problem::THessian;
486 using typename polysolve::nonlinear::Problem::TVector;
487 struct Edge
488 {
489 Eigen::Vector2d q;
490 double sigma;
497 double agree = 1.;
498 };
500 const std::shared_ptr<const OffsetPotential2D>& potential,
501 std::vector<Edge> edges,
502 double outward_sign,
503 double weight);
505 void residual(const Eigen::Vector2d& x, const Edge& e, double& r, Eigen::Vector2d& J) const;
506 double value(const TVector& x) override;
507 void gradient(const TVector& x, TVector& gradv) override;
508 void hessian(const TVector& x, THessian& hessian) override
509 {
510 log_and_throw_error("Sparse functions do not exist, use dense solver");
511 }
512 void hessian(const TVector& x, MatrixXd& hessian) override;
513 void solution_changed(const TVector& new_x) override {}
514
515private:
516 std::shared_ptr<const OffsetPotential2D> m_potential;
517 std::vector<Edge> m_edges;
518 double m_sign, m_weight;
519};
520using OffsetEnergy3D = OffsetEnergy<3>;
521
535class AlignEnergy3D : public polysolve::nonlinear::Problem
536{
537public:
538 using typename polysolve::nonlinear::Problem::Scalar;
539 using typename polysolve::nonlinear::Problem::THessian;
540 using typename polysolve::nonlinear::Problem::TVector;
541 struct Face
542 {
543 Eigen::Vector3d q1, q2;
544 double sigma;
549 double agree = 1.;
550 };
552 const std::shared_ptr<const OffsetPotential3D>& potential,
553 std::vector<Face> faces,
554 double outward_sign,
555 double weight);
557 void residual(const Eigen::Vector3d& x, const Face& f, double& r, Eigen::Vector3d& J) const;
558 double value(const TVector& x) override;
559 void gradient(const TVector& x, TVector& gradv) override;
560 void hessian(const TVector& x, THessian& hessian) override
561 {
562 log_and_throw_error("Sparse functions do not exist, use dense solver");
563 }
564 void hessian(const TVector& x, MatrixXd& hessian) override;
565 void solution_changed(const TVector& new_x) override {}
566
567private:
568 std::shared_ptr<const OffsetPotential3D> m_potential;
569 std::vector<Face> m_faces;
570 double m_sign, m_weight;
571};
572
617class StencilEnergy3D : public polysolve::nonlinear::Problem
618{
619public:
620 using typename polysolve::nonlinear::Problem::Scalar;
621 using typename polysolve::nonlinear::Problem::THessian;
622 using typename polysolve::nonlinear::Problem::TVector;
623
625 struct Sample
626 {
627 double a, b, c;
628 };
631 struct Face
632 {
633 Eigen::Vector3d q1, q2;
634 std::vector<Sample> samples;
635 double weight = 1.;
636 };
637
639 const std::shared_ptr<const OffsetPotential3D>& potential,
640 std::vector<Face> faces,
641 double weight);
642
643 double value(const TVector& x) override;
644 void gradient(const TVector& x, TVector& gradv) override;
645 void hessian(const TVector& x, THessian& hessian) override
646 {
647 log_and_throw_error("Sparse functions do not exist, use dense solver");
648 }
649 void hessian(const TVector& x, MatrixXd& hessian) override;
650 void solution_changed(const TVector& new_x) override {}
651
652private:
655 bool residual_at(const Eigen::Vector3d& p, double& r, Eigen::Vector3d* dr) const;
656
657 std::shared_ptr<const OffsetPotential3D> m_potential;
658 std::vector<Face> m_faces;
659 double m_weight;
660 double m_c = 1.;
661};
662
681class RestAMIPSEnergy2D : public polysolve::nonlinear::Problem
682{
683public:
684 using typename polysolve::nonlinear::Problem::Scalar;
685 using typename polysolve::nonlinear::Problem::THessian;
686 using typename polysolve::nonlinear::Problem::TVector;
687 struct Cell
688 {
689 Eigen::Vector2d q1, q2;
690 Eigen::Matrix2d rest_inv;
691 };
692 RestAMIPSEnergy2D(std::vector<Cell> cells, double weight);
693
694 double value(const TVector& x) override;
695 void gradient(const TVector& x, TVector& gradv) override;
696 void hessian(const TVector& x, THessian& hessian) override
697 {
698 log_and_throw_error("Sparse functions do not exist, use dense solver");
699 }
700 void hessian(const TVector& x, MatrixXd& hessian) override;
701 void solution_changed(const TVector& new_x) override {}
702 bool is_step_valid(const TVector& x0, const TVector& x1) override;
703
704private:
706 bool cell_F(const Eigen::Vector2d& x, const Cell& c, Eigen::Matrix2d& F, double& d) const;
707 std::vector<Cell> m_cells;
708 double m_weight;
709};
710
723class RestAMIPSEnergy3D : public polysolve::nonlinear::Problem
724{
725public:
726 using typename polysolve::nonlinear::Problem::Scalar;
727 using typename polysolve::nonlinear::Problem::THessian;
728 using typename polysolve::nonlinear::Problem::TVector;
729 struct Cell
730 {
731 Eigen::Vector3d q1, q2, q3;
732 Eigen::Matrix3d rest_inv;
733 };
734 RestAMIPSEnergy3D(std::vector<Cell> cells, double weight);
735
736 double value(const TVector& x) override;
737 void gradient(const TVector& x, TVector& gradv) override;
738 void hessian(const TVector& x, THessian& hessian) override
739 {
740 log_and_throw_error("Sparse functions do not exist, use dense solver");
741 }
742 void hessian(const TVector& x, MatrixXd& hessian) override;
743 void solution_changed(const TVector& new_x) override {}
744 bool is_step_valid(const TVector& x0, const TVector& x1) override;
745
746private:
748 bool cell_F(const Eigen::Vector3d& x, const Cell& c, Eigen::Matrix3d& F, double& d) const;
749 std::vector<Cell> m_cells;
750 double m_weight;
751};
752
753} // namespace wmtk::components::topological_offset
First-order offset term: each front edge at the vertex against the field.
Definition OffsetPotential.hpp:482
void residual(const Eigen::Vector2d &x, const Edge &e, double &r, Eigen::Vector2d &J) const
r_e and its derivative for one edge; r = 0 with J = 0 where grad Phi vanishes.
Definition OffsetPotential.cpp:950
The 3D twin of AlignEnergy2D: each incident front FACE at the vertex against the field.
Definition OffsetPotential.hpp:536
void residual(const Eigen::Vector3d &x, const Face &f, double &r, Eigen::Vector3d &J) const
r_f and its derivative for one face; r = 0 with J = 0 where grad Phi or the face vanishes.
Definition OffsetPotential.cpp:1478
The Euclidean offset: Phi = d(p, input complex), level set d = delta.
Definition OffsetPotential.hpp:315
std::string describe_active(const VecD &p) const override
Diagnostic: what the field is made of at p, one contribution per line.
Definition OffsetPotential.cpp:1323
double relative_residual(const VecD &p) const override
Definition OffsetPotential.hpp:357
bool is_euclidean() const override
The Euclidean field: value() is the distance, so a distance residual is the plain one.
Definition OffsetPotential.hpp:319
std::shared_ptr< SampleEnvelope > m_envelope
Definition OffsetPotential.hpp:380
bool is_inside_offset(const VecD &p) const override
Definition OffsetPotential.hpp:364
void nearest_feature(const VecD &p, VecD &foot, int &dim, VecD &dir) const
Definition OffsetPotential.cpp:1218
bool within_support(const VecD &p) const override
Everywhere. d has no compact support.
Definition OffsetPotential.hpp:360
double residual_length(const VecD &p) const override
Definition OffsetPotential.hpp:349
The offset term of the smoothing objective: w * (Phi(x) - c)^2.
Definition OffsetPotential.hpp:412
void residual(const VecD &p, double &r, VecD &dr) const
Definition OffsetPotential.cpp:1102
bool root_distance(const VecD &p, double &s, VecD &n) const
Definition OffsetPotential.cpp:1017
double m_last_root
warm start for root_distance(), see there
Definition OffsetPotential.hpp:454
The offset potential: the scalar field on space whose level set the front is placed on.
Definition OffsetPotential.hpp:37
double delta() const
The offset distance the field is calibrated to.
Definition OffsetPotential.hpp:53
virtual bool is_inside_offset(const VecD &p) const =0
Whether p lies inside the offset region – on the complex's side of the level set.
virtual std::string describe_active(const VecD &p) const =0
Diagnostic: what the field is made of at p, one contribution per line.
double m_grad_ref
Definition OffsetPotential.hpp:127
double level_set_slope() const
|d(field)/d(distance)| at the level set on a flat stretch of input.
Definition OffsetPotential.hpp:71
OffsetPotential(const double delta, const double dhat)
Definition OffsetPotential.hpp:117
virtual double residual_length(const VecD &p) const =0
Distance from p to the level set, in length units.
double dhat() const
Definition OffsetPotential.hpp:57
virtual bool is_euclidean() const
The Euclidean field: value() is the distance, so a distance residual is the plain one.
Definition OffsetPotential.hpp:75
double target_level() const
The level value the offset boundary is placed on.
Definition OffsetPotential.hpp:50
virtual double relative_residual(const VecD &p) const =0
The signed distance from p to the level set, as a fraction of delta: > 0 outside the offset region,...
virtual bool within_support(const VecD &p) const =0
Whether p is somewhere the field can give a direction to the level set at all.
AMIPS against a rest shape, for deform_others: the smoothing term of a deformable region's faces.
Definition OffsetPotential.hpp:682
bool cell_F(const Eigen::Vector2d &x, const Cell &c, Eigen::Matrix2d &F, double &d) const
e = tr(F^T F), d = det F at x for one cell; false when d <= 0.
Definition OffsetPotential.cpp:1357
The 3D twin of RestAMIPSEnergy2D: AMIPS of a tet against its rest shape.
Definition OffsetPotential.hpp:724
bool cell_F(const Eigen::Vector3d &x, const Cell &c, Eigen::Matrix3d &F, double &d) const
F and d = det F at x for one cell; false when d <= 0.
Definition OffsetPotential.cpp:1552
The smooth offset potential Phi, and the offset defined as its level set Phi = c.
Definition OffsetPotential.hpp:173
std::string describe_active(const VecD &p) const override
Definition OffsetPotential.cpp:669
bool level_set_distance(const VecD &p, double &t) const
Definition OffsetPotential.cpp:750
bool within_support(const VecD &p) const override
Whether p is inside the support at all, i.e. Phi(p) > 0.
Definition OffsetPotential.hpp:245
double relative_residual(const VecD &p) const override
The same root, signed and over delta: t / delta, > 0 outside the offset region. NaN where residual_le...
Definition OffsetPotential.cpp:916
double residual_length(const VecD &p) const override
The distance from p to the level set Phi = c ALONG THE FIELD, in length units.
Definition OffsetPotential.cpp:909
void value_gradient(const VecD &p, double &v, VecD &g) const
value() and gradient() at p from one collision build per part; see the definition.
Definition OffsetPotential.cpp:704
bool is_inside_offset(const VecD &p) const override
Phi decreases with distance, so the offset region is where it is still above the level.
Definition OffsetPotential.hpp:248
THE offset term of a front vertex's smoothing objective: the mean squared relative error of the field...
Definition OffsetPotential.hpp:618
double m_c
the potential's target level, cached
Definition OffsetPotential.hpp:660
bool residual_at(const Eigen::Vector3d &p, double &r, Eigen::Vector3d *dr) const
Definition OffsetPotential.cpp:1695
Eigen::Vector2d q
the other endpoint
Definition OffsetPotential.hpp:489
double agree
Definition OffsetPotential.hpp:497
double sigma
Definition OffsetPotential.hpp:490
double sigma
Definition OffsetPotential.hpp:544
double agree
Definition OffsetPotential.hpp:549
Eigen::Vector3d q2
the two other corners, in the band tet's orientation
Definition OffsetPotential.hpp:543
Eigen::Matrix2d rest_inv
inverse rest Jacobian [r1-r0, r2-r0]^-1, same corner order
Definition OffsetPotential.hpp:690
Eigen::Vector2d q2
the fixed endpoints, current positions
Definition OffsetPotential.hpp:689
Eigen::Vector3d q3
the fixed corners, current positions
Definition OffsetPotential.hpp:731
Eigen::Matrix3d rest_inv
inverse rest Jacobian [r1-r0, r2-r0, r3-r0]^-1
Definition OffsetPotential.hpp:732
One stencil point's barycentric weights. a is the moving vertex's, so dq_i/dx = a_i I.
Definition OffsetPotential.hpp:626