Lagrange
Loading...
Searching...
No Matches
register_attributes.h
1/*
2 * Copyright 2025 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/Logger.h>
15#include <lagrange/SurfaceMesh.h>
16#include <lagrange/foreach_attribute.h>
17#include <lagrange/internal/attribute_string_utils.h>
18
19// clang-format off
20#include <lagrange/utils/warnoff.h>
21#include <polyscope/polyscope.h>
22#include <polyscope/point_cloud.h>
23#include <polyscope/surface_mesh.h>
24#include <polyscope/curve_network.h>
25#include <lagrange/utils/warnon.h>
26// clang-format on
27
28namespace lagrange::polyscope {
29
30inline bool show_as_vector(lagrange::AttributeUsage usage)
31{
32 // Attribute usages that can be shown as vectors in Polyscope
33 switch (usage) {
34 case lagrange::AttributeUsage::Vector: [[fallthrough]];
35 case lagrange::AttributeUsage::Position: [[fallthrough]];
36 case lagrange::AttributeUsage::Normal: [[fallthrough]];
37 case lagrange::AttributeUsage::Tangent: [[fallthrough]];
39 default: return false;
40 }
41}
42
43inline ::polyscope::VectorType vector_type(lagrange::AttributeUsage usage)
44{
45 switch (usage) {
46 case lagrange::AttributeUsage::Vector: return ::polyscope::VectorType::AMBIENT;
47 default: return ::polyscope::VectorType::STANDARD;
48 }
49}
50
51// Convert integral color attributes to floating point
52template <typename ValueType>
53auto as_color_matrix(const lagrange::Attribute<ValueType>& attr) -> decltype(auto)
54{
55 return matrix_view(attr)
56 .template cast<float>()
57 // Int -> float conversion
58 .unaryExpr([&](float x) { return std::is_floating_point_v<ValueType> ? x : x / 255.f; })
59 // Gamma correction
60 .unaryExpr([](float x) {
61 float gamma = 2.2f;
62 return std::pow(x, gamma);
63 });
64};
65
66template <typename PolyscopeStructure>
68{
69 using PolyscopeType = std::decay_t<PolyscopeStructure>;
70 static constexpr bool IsMesh = std::is_same_v<PolyscopeType, ::polyscope::SurfaceMesh>;
71 static constexpr bool IsEdge = std::is_same_v<PolyscopeType, ::polyscope::CurveNetwork>;
72
73 using QuantityType = std::conditional_t<
74 IsMesh,
75 ::polyscope::SurfaceMeshQuantity,
76 std::conditional_t<
77 IsEdge,
78 ::polyscope::CurveNetworkQuantity,
79 ::polyscope::PointCloudQuantity>>;
80};
81
82template <typename PolyscopeStructure>
83using QuantityType = typename PolyscopeTrait<PolyscopeStructure>::QuantityType;
84
85// Register a mesh attribute with Polyscope. Returns true if the attribute was registered.
86template <typename PolyscopeStructure, typename ValueType>
87auto register_attribute(
88 PolyscopeStructure* ps_struct,
89 std::string_view name_,
90 const lagrange::Attribute<ValueType>& attr) -> QuantityType<PolyscopeStructure>*
91{
92 using Usage = lagrange::AttributeUsage;
93 std::string name(name_); // TODO: Fix Polyscope's function signature to accept string_view
94
95 using PolyscopeType = std::decay_t<PolyscopeStructure>;
96 constexpr bool IsMesh = std::is_same_v<PolyscopeType, ::polyscope::SurfaceMesh>;
97 constexpr bool IsEdge = std::is_same_v<PolyscopeType, ::polyscope::CurveNetwork>;
98
99 constexpr auto scalar_data_type = std::is_integral_v<ValueType>
100 ? ::polyscope::DataType::CATEGORICAL
101 : ::polyscope::DataType::STANDARD;
102
103 switch (attr.get_element_type()) {
105 if (attr.get_usage() == Usage::Scalar) {
106 lagrange::logger().info("Registering scalar vertex attribute: {}", name);
107 if constexpr (IsMesh) {
108 return ps_struct->addVertexScalarQuantity(
109 name,
110 vector_view(attr),
111 scalar_data_type);
112 } else if constexpr (IsEdge) {
113 return ps_struct->addNodeScalarQuantity(name, vector_view(attr), scalar_data_type);
114 } else {
115 return ps_struct->addScalarQuantity(name, vector_view(attr), scalar_data_type);
116 }
117 } else if (attr.get_num_channels() == 3 || attr.get_num_channels() == 4) {
118 if (show_as_vector(attr.get_usage())) {
119 lagrange::logger().info("Registering vector vertex attribute: {}", name);
120 ::polyscope::VectorType vt = vector_type(attr.get_usage());
121 auto matrix = matrix_view(attr).template leftCols<3>();
122 if constexpr (IsMesh) {
123 return ps_struct->addVertexVectorQuantity(name, matrix, vt);
124 } else if constexpr (IsEdge) {
125 return ps_struct->addNodeVectorQuantity(name, matrix, vt);
126 } else {
127 return ps_struct->addVectorQuantity(name, matrix, vt);
128 }
129 } else if (attr.get_usage() == Usage::Color) {
130 lagrange::logger().info("Registering color vertex attribute: {}", name);
131 auto matrix = as_color_matrix(attr).template leftCols<3>();
132 if constexpr (IsMesh) {
133 return ps_struct->addVertexColorQuantity(name, matrix);
134 } else if constexpr (IsEdge) {
135 return ps_struct->addNodeColorQuantity(name, matrix);
136 } else {
137 return ps_struct->addColorQuantity(name, matrix);
138 }
139 }
140 } else if (attr.get_num_channels() == 2) {
141 if (show_as_vector(attr.get_usage())) {
142 lagrange::logger().info("Registering 2D vector vertex attribute: {}", name);
143 ::polyscope::VectorType vt = vector_type(attr.get_usage());
144 if constexpr (IsMesh) {
145 return ps_struct->addVertexVectorQuantity2D(name, matrix_view(attr), vt);
146 } else if constexpr (IsEdge) {
147 return ps_struct->addNodeVectorQuantity2D(name, matrix_view(attr), vt);
148 } else {
149 return ps_struct->addVectorQuantity2D(name, matrix_view(attr), vt);
150 }
151 }
152 } else if (attr.get_usage() == Usage::UV && attr.get_num_channels() == 2) {
153 if constexpr (IsMesh) {
154 lagrange::logger().info("Registering UV vertex attribute: {}", name);
155 return ps_struct->addVertexParameterizationQuantity(name, matrix_view(attr));
156 }
157 }
158 // TODO: If usage == Position, maybe add a toggle to switch between various vertex
159 // positions
160 break;
162 if (attr.get_usage() == Usage::Scalar) {
163 lagrange::logger().info("Registering scalar facet attribute: {}", name);
164 if constexpr (IsMesh) {
165 return ps_struct->addFaceScalarQuantity(name, vector_view(attr), scalar_data_type);
166 } else if constexpr (IsEdge) {
167 return ps_struct->addEdgeScalarQuantity(name, vector_view(attr), scalar_data_type);
168 }
169 } else if (attr.get_num_channels() == 3 || attr.get_num_channels() == 4) {
170 if (show_as_vector(attr.get_usage())) {
171 lagrange::logger().info("Registering vector facet attribute: {}", name);
172 ::polyscope::VectorType vt = vector_type(attr.get_usage());
173 auto matrix = matrix_view(attr).template leftCols<3>();
174 if constexpr (IsMesh) {
175 return ps_struct->addFaceVectorQuantity(name, matrix, vt);
176 } else if constexpr (IsEdge) {
177 return ps_struct->addEdgeVectorQuantity(name, matrix, vt);
178 }
179 } else if (attr.get_usage() == Usage::Color) {
180 lagrange::logger().info("Registering color facet attribute: {}", name);
181 auto matrix = as_color_matrix(attr).template leftCols<3>();
182 if constexpr (IsMesh) {
183 return ps_struct->addFaceColorQuantity(name, matrix);
184 } else if constexpr (IsEdge) {
185 return ps_struct->addEdgeColorQuantity(name, matrix);
186 }
187 }
188 } else if (attr.get_num_channels() == 2) {
189 if (show_as_vector(attr.get_usage())) {
190 lagrange::logger().info("Registering vector facet attribute: {}", name);
191 ::polyscope::VectorType vt = vector_type(attr.get_usage());
192 if constexpr (IsMesh) {
193 return ps_struct->addFaceVectorQuantity2D(name, matrix_view(attr), vt);
194 } else if constexpr (IsEdge) {
195 return ps_struct->addEdgeVectorQuantity2D(name, matrix_view(attr), vt);
196 }
197 }
198 }
199 break;
201 if constexpr (IsMesh) {
202 if (attr.get_usage() == Usage::Scalar) {
203 lagrange::logger().info("Registering scalar edge attribute: {}", name);
204 return ps_struct->addEdgeScalarQuantity(name, vector_view(attr), scalar_data_type);
205 }
206 }
207 break;
209 if constexpr (IsMesh) {
210 if (attr.get_usage() == Usage::UV && attr.get_num_channels() == 2) {
211 lagrange::logger().info("Registering UV corner attribute: {}", name);
212 return ps_struct->addParameterizationQuantity(name, matrix_view(attr));
213 }
214 }
215 break;
216 default: break;
217 }
218
219 return nullptr;
220}
221
222template <typename PolyscopeStructure, typename Scalar, typename Index>
223void register_attributes(PolyscopeStructure* ps_struct, const SurfaceMesh<Scalar, Index>& mesh)
224{
225 // Register mesh attributes supported by Polyscope
226 seq_foreach_named_attribute_read(mesh, [&](auto name, auto&& attr) {
227 if (mesh.attr_name_is_reserved(name)) {
228 return;
229 }
230 using AttributeType = std::decay_t<decltype(attr)>;
231 if constexpr (AttributeType::IsIndexed) {
232 lagrange::logger().warn("Skipping indexed attribute: {}", name);
233 } else {
234 if (!register_attribute(ps_struct, name, attr)) {
235 lagrange::logger().warn(
236 "Skipping unsupported attribute: {}, element type: {}, usage: {}, num "
237 "channels: {}",
238 name,
239 lagrange::internal::to_string(attr.get_element_type()),
240 lagrange::internal::to_string(attr.get_usage()),
241 attr.get_num_channels());
242 }
243 }
244 });
245}
246
247} // namespace lagrange::polyscope
Derived attribute class that stores the actual information.
Definition Attribute.h:174
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
AttributeUsage
Usage tag indicating how the attribute should behave under mesh transformations.
Definition AttributeFwd.h:54
@ Position
Mesh attribute must have exactly dim channels.
Definition AttributeFwd.h:57
@ Tangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:59
@ Vector
Mesh attribute can have any number of channels (including 1 channel).
Definition AttributeFwd.h:55
@ Normal
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:58
@ Bitangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:60
@ Edge
Per-edge mesh attributes.
Definition AttributeFwd.h:34
@ Facet
Per-facet mesh attributes.
Definition AttributeFwd.h:31
@ Corner
Per-corner mesh attributes.
Definition AttributeFwd.h:37
@ Vertex
Per-vertex mesh attributes.
Definition AttributeFwd.h:28
void seq_foreach_named_attribute_read(const SurfaceMesh< Scalar, Index > &mesh, Visitor &&vis)
Applies a function to each attribute of a mesh.
Definition foreach_attribute.h:208
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
ConstRowMatrixView< ValueType > matrix_view(const Attribute< ValueType > &attribute)
Returns a read-only view of a given attribute in the form of an Eigen matrix.
Definition views.cpp:35
ConstVectorView< ValueType > vector_view(const Attribute< ValueType > &attribute)
Returns a read-only view of a scalar attribute in the form of an Eigen vector.
Definition views.cpp:51
LA_CORE_API std::string_view to_string(AttributeElement element)
Returns a string representation of an attribute element type.
Definition attribute_string_utils.cpp:22
Definition register_attributes.h:68