16#include <lagrange/Attribute.h>
17#include <lagrange/AttributeTypes.h>
18#include <lagrange/IndexedAttribute.h>
19#include <lagrange/Logger.h>
20#include <lagrange/SurfaceMeshTypes.h>
21#include <lagrange/cast_attribute.h>
22#include <lagrange/compute_uv_orientation.h>
23#include <lagrange/find_matching_attributes.h>
24#include <lagrange/map_attribute.h>
25#include <lagrange/triangulate_polygonal_facets.h>
26#include <lagrange/utils/Error.h>
27#include <lagrange/utils/fmt_eigen.h>
28#include <lagrange/views.h>
29#include <lagrange/weld_indexed_attribute.h>
32#include "ThreadPool.h"
33#define MULTI_THREADING_INCLUDED
34using namespace lagrange::texproc::threadpool;
37#include <lagrange/utils/warnoff.h>
38#include <Misha/RegularGrid.h>
39#include <Src/PreProcessing.h>
40#include <Src/GradientDomain.h>
41#include <lagrange/utils/warnon.h>
42#include <lagrange/utils/fmt/format.h>
43#include <lagrange/utils/fmt/join.h>
46#include <Eigen/Sparse>
50namespace lagrange::texproc {
53template <
typename T,
unsigned N>
54using Vector = MishaK::Point<T, N>;
58using namespace MishaK;
61static const unsigned int Dim = 3;
64static const unsigned int K = 2;
68enum class RequiresIndexedTexcoords { Yes, No };
69enum class CheckFlippedUV { Yes, No };
73template <
unsigned int NumChannels,
typename ValueType>
75 image::experimental::View3D<ValueType> texture,
76 RegularGrid<K, Vector<double, NumChannels>>& grid)
78 unsigned int num_channels =
static_cast<unsigned int>(texture.extent(2));
79 if (num_channels != NumChannels)
la_debug_assert(
"Number of channels don't match");
82 grid.resize(texture.extent(0), texture.extent(1));
83 for (
unsigned int j = 0; j < grid.res(1); j++) {
84 for (
unsigned int i = 0; i < grid.res(0); i++) {
85 for (
unsigned int c = 0; c < NumChannels; c++) {
86 grid(i, j)[c] = texture(i, j, c);
92template <
unsigned int NumChannels,
typename ValueType>
94 const RegularGrid<K, Vector<double, NumChannels>>& grid,
95 image::experimental::View3D<ValueType> texture)
98 for (
unsigned int j = 0; j < grid.res(1); j++) {
99 for (
unsigned int i = 0; i < grid.res(0); i++) {
100 for (
unsigned int c = 0; c < NumChannels; c++) {
101 texture(i, j, c) = grid(i, j)[c];
107template <
typename ValueType>
109 const RegularGrid<K, double>& grid,
110 image::experimental::View3D<ValueType> texture)
113 for (
unsigned int j = 0; j < grid.res(1); j++) {
114 for (
unsigned int i = 0; i < grid.res(0); i++) {
115 texture(i, j, 0) = grid(i, j);
120template <
unsigned int NumChannels>
121void clamp_out_of_range(
122 span<Vector<double, NumChannels>> x,
123 const MishaK::TSP::GradientDomain<double>& gd,
124 std::pair<double, double>
range = {0.0, 1.0},
125 bool mark_out_of_range =
false)
127 size_t num_interior_out_of_range = 0;
128 size_t num_exterior_out_of_range = 0;
130 Vector<double, NumChannels> red;
131 Vector<double, NumChannels> green;
132 for (
unsigned int c = 0; c < NumChannels; c++) {
133 red[c] = (c == 0 ? 1.0 : 0.0);
134 green[c] = (c == 1 ? 1.0 : 0.0);
137 auto is_out_of_range = [&
range](Vector<double, NumChannels> p) {
138 constexpr double eps = 1e-6;
139 for (
unsigned int c = 0; c < NumChannels; c++) {
140 if (p[c] <
range.first - eps || p[c] >
range.second + eps) {
147 for (
size_t n = 0; n < gd.numNodes(); ++n) {
148 const bool is_strictly_out = is_out_of_range(x[n]);
149 for (
unsigned int c = 0; c < NumChannels; c++) {
150 x[n][c] = std::clamp(x[n][c],
range.first,
range.second);
152 if (is_strictly_out) {
153 if (gd.isCovered(n)) {
154 num_interior_out_of_range++;
155 if (mark_out_of_range) {
159 num_exterior_out_of_range++;
160 if (mark_out_of_range) {
166 if (num_interior_out_of_range || num_exterior_out_of_range) {
168 "{} interior and {} exterior texels were out of range and have been clamped.",
169 num_interior_out_of_range,
170 num_exterior_out_of_range);
197template <
typename Scalar>
202 double epsilon = 1e-4)
204 if (std::abs(epsilon) < std::numeric_limits<double>().denorm_min()) {
208 Scalar jitter_scale =
static_cast<Scalar>(epsilon / std::max<unsigned int>(width, height));
210 std::uniform_real_distribution<Scalar> dist(-jitter_scale, jitter_scale);
211 for (
auto& x : texcoords_buffer) {
217template <
typename Scalar>
218Eigen::SparseMatrix<Scalar> laplacian_regularization(Eigen::SparseMatrix<Scalar> S,
Scalar weight)
220 if (std::abs(weight) > std::numeric_limits<Scalar>().denorm_min()) {
225 tbb::parallel_for(
size_t(0),
size_t(S.outerSize()), [&S, weight](
size_t c) {
227 for (typename Eigen::SparseMatrix<Scalar>::InnerIterator it(S, c); it; ++it) {
228 if (it.row() != it.col()) {
229 it.valueRef() -= weight;
233 for (
typename Eigen::SparseMatrix<Scalar>::InnerIterator it(S, c); it; ++it) {
234 if (it.row() == it.col()) {
235 it.valueRef() += weight * count;
244template <
typename Scalar,
typename Index>
251 size_t num_simplices()
const {
return static_cast<size_t>(mesh.get_num_facets()); }
252 size_t num_vertices()
const {
return static_cast<size_t>(mesh.get_num_vertices()); }
253 size_t num_texcoords()
const {
return texcoords.size() / K; }
255 Vector<double, Dim> vertex(
size_t i)
const
257 Vector<double, Dim> p;
258 for (
unsigned int d = 0; d < Dim; d++) {
259 p[d] =
static_cast<double>(vertices[i * Dim + d]);
264 Vector<double, K> texcoord(
size_t i)
const
267 for (
unsigned int k = 0; k < K; k++) {
268 q[k] =
static_cast<double>(texcoords[i * K + k]);
273 Vector<double, K> vflipped_texcoord(
size_t i)
const
276 for (
unsigned int k = 0; k < K; k++) {
277 q[k] =
static_cast<double>(texcoords[i * K + k]);
283 int vertex_index(
size_t f,
unsigned int k)
const
285 return static_cast<int>(vertex_indices[f * (K + 1) + k]);
288 int texture_index(
size_t f,
unsigned int k)
const
290 switch (texture_element) {
294 default:
la_debug_assert(
"Unsupported texture element type");
return 0;
298 Simplex<double, K, K> simplex_texcoords(
size_t f)
const
300 Simplex<double, K, K> s;
301 for (
unsigned int k = 0; k <= K; k++) {
302 s[k] = texcoord(texture_index(f, k));
307 Simplex<double, K, K> vflipped_simplex_texcoords(
size_t f)
const
309 Simplex<double, K, K> s;
310 for (
unsigned int k = 0; k <= K; k++) {
311 s[k] = vflipped_texcoord(texture_index(f, k));
316 Simplex<double, Dim, K> simplex_vertices(
size_t f)
const
318 Simplex<double, Dim, K> s;
319 for (
unsigned int k = 0; k <= K; k++) {
320 s[k] = vertex(vertex_index(f, k));
325 SimplexIndex<K> facet_indices(
size_t f)
const
327 SimplexIndex<K> simplex;
328 for (
unsigned int k = 0; k <= K; ++k) {
329 simplex[k] =
static_cast<int>(vertex_indices[f * (K + 1) + k]);
342template <
typename Scalar,
typename Index>
345 RequiresIndexedTexcoords requires_indexed_texcoords,
346 CheckFlippedUV check_flipped_uv)
358 texcoord_id = res.value();
363 if (!_mesh.template is_attribute_type<Scalar>(texcoord_id)) {
365 "Input uv coordinates do not have the same scalar type as the input points. Casting "
371 if (requires_indexed_texcoords == RequiresIndexedTexcoords::Yes &&
373 logger().warn(
"UV coordinates are not indexed. Welding.");
381 "Number of corners doesn't match the number of simplices");
383 if (check_flipped_uv == CheckFlippedUV::Yes) {
385 UVOrientationOptions orient_options;
388 if (orient_counts.negative > 0) {
390 "The input mesh has {} flipped UV triangle(s). Please fix the input mesh "
391 "before proceeding.",
392 orient_counts.negative));
399 auto& uv_attr = _mesh.template ref_indexed_attribute<Scalar>(texcoord_id);
400 wrapper.texcoords = uv_attr.values().ref_all();
401 wrapper.texture_indices = uv_attr.indices().get_all();
404 auto& uv_attr = _mesh.template ref_attribute<Scalar>(texcoord_id);
405 wrapper.texcoords = uv_attr.ref_all();
406 wrapper.texture_indices = {};
407 wrapper.texture_element = uv_attr.get_element_type();
415template <
typename Scalar,
typename Index>
416Padding create_padding(MeshWrapper<Scalar, Index>& wrapper,
unsigned int width,
unsigned int height)
418 static_assert(
sizeof(std::array<Scalar, 2>) == 2 *
sizeof(
Scalar));
420 reinterpret_cast<std::array<Scalar, 2>*
>(wrapper.texcoords.data()),
421 wrapper.num_texcoords());
423 padding = Padding::init<Scalar>(width, height, texcoords);
424 padding.pad(width, height, texcoords);
AttributeElement get_element_type() const
Gets the attribute element type.
Definition Attribute.h:122
lagrange::span< const ValueType > get_all() const
Returns a read-only view of the buffer spanning num elements x num channels.
Definition Attribute.cpp:530
A general purpose polygonal mesh class.
Definition SurfaceMesh.h:73
std::string_view get_attribute_name(AttributeId id) const
Retrieve attribute name from its id.
Definition SurfaceMesh.cpp:359
const AttributeBase & get_attribute_base(std::string_view name) const
Gets a read-only reference to the base class of attribute given its name.
Definition SurfaceMesh.cpp:1275
bool is_attribute_indexed(std::string_view name) const
Determines whether the specified attribute is indexed.
Definition SurfaceMesh.cpp:1234
const Attribute< Index > & get_corner_to_vertex() const
Gets a read-only reference to the corner -> vertex id attribute.
Definition SurfaceMesh.cpp:1398
Index get_num_facets() const
Retrieves the number of facets.
Definition SurfaceMesh.h:694
const Attribute< Scalar > & get_vertex_to_position() const
Gets a read-only reference to the vertex -> positions attribute.
Definition SurfaceMesh.cpp:1386
Index get_num_corners() const
Retrieves the number of corners.
Definition SurfaceMesh.h:701
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
void weld_indexed_attribute(SurfaceMesh< Scalar, Index > &mesh, AttributeId attr_id, const WeldOptions &options={})
Weld an indexed attribute by combining all corners around a vertex with the same attribute value.
Definition weld_indexed_attribute.cpp:500
AttributeId map_attribute_in_place(SurfaceMesh< Scalar, Index > &mesh, AttributeId id, AttributeElement new_element)
Map attribute values to a different element type.
Definition map_attribute.cpp:292
uint32_t AttributeId
Identified to be used to access an attribute.
Definition AttributeFwd.h:73
AttributeElement
Type of element to which the attribute is attached.
Definition AttributeFwd.h:26
@ UV
Mesh attribute must have exactly 2 channels.
Definition AttributeFwd.h:62
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
@ Value
Values that are not attached to a specific element.
Definition AttributeFwd.h:42
@ Indexed
Indexed mesh attributes.
Definition AttributeFwd.h:45
@ Corner
Per-corner mesh attributes.
Definition AttributeFwd.h:37
@ Vertex
Per-vertex mesh attributes.
Definition AttributeFwd.h:28
AttributeId cast_attribute_in_place(SurfaceMesh< Scalar, Index > &mesh, AttributeId attribute_id)
Cast an attribute in place to a different value type.
Definition cast_attribute.cpp:68
std::optional< AttributeId > find_matching_attribute(const SurfaceMesh< Scalar, Index > &mesh, const AttributeMatcher &options)
Finds the first attribute with the specified usage/element type/number of channels.
Definition find_matching_attributes.cpp:37
void triangulate_polygonal_facets(SurfaceMesh< Scalar, Index > &mesh, const TriangulationOptions &options={})
Triangulate polygonal facets of a mesh using a prescribed set of rules.
Definition triangulate_polygonal_facets.cpp:542
UVOrientationCount compute_uv_orientation(SurfaceMesh< Scalar, Index > &mesh, const UVOrientationOptions &options={})
Compute a per-facet orientation attribute using Shewchuk's exact orient2D predicate.
Definition compute_uv_orientation.cpp:96
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:177
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:197
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition span.h:27
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
@ Error
Throw an error if collision is detected.
Definition MappingPolicy.h:24
std::string_view uv_attribute_name
Input UV attribute name.
Definition compute_uv_orientation.h:30
Definition mesh_utils.h:246