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
18// clang-format off
19#include <lagrange/utils/warnoff.h>
20#include <polyscope/polyscope.h>
21#include <polyscope/point_cloud.h>
22#include <polyscope/surface_mesh.h>
23#include <polyscope/curve_network.h>
24#include <lagrange/utils/warnon.h>
25// clang-format on
26
27namespace lagrange::polyscope {
28
29inline bool show_as_vector(lagrange::AttributeUsage usage)
30{
31 // Attribute usages that can be shown as vectors in Polyscope
32 switch (usage) {
33 case lagrange::AttributeUsage::Vector: [[fallthrough]];
34 case lagrange::AttributeUsage::Position: [[fallthrough]];
35 case lagrange::AttributeUsage::Normal: [[fallthrough]];
36 case lagrange::AttributeUsage::Tangent: [[fallthrough]];
38 default: return false;
39 }
40}
41
42inline ::polyscope::VectorType vector_type(lagrange::AttributeUsage usage)
43{
44 switch (usage) {
45 case lagrange::AttributeUsage::Vector: return ::polyscope::VectorType::AMBIENT;
46 default: return ::polyscope::VectorType::STANDARD;
47 }
48}
49
50// Convert integral color attributes to floating point
51template <typename ValueType>
52auto as_color_matrix(const lagrange::Attribute<ValueType>& attr) -> decltype(auto)
53{
54 return matrix_view(attr)
55 .template cast<float>()
56 // Int -> float conversion
57 .unaryExpr([&](float x) { return std::is_floating_point_v<ValueType> ? x : x / 255.f; })
58 // Gamma correction
59 .unaryExpr([](float x) {
60 float gamma = 2.2f;
61 return std::pow(x, gamma);
62 });
63};
64
65template <typename PolyscopeStructure>
67{
68 using PolyscopeType = std::decay_t<PolyscopeStructure>;
69 static constexpr bool IsMesh = std::is_same_v<PolyscopeType, ::polyscope::SurfaceMesh>;
70 static constexpr bool IsEdge = std::is_same_v<PolyscopeType, ::polyscope::CurveNetwork>;
71
72 using QuantityType = std::conditional_t<
73 IsMesh,
74 ::polyscope::SurfaceMeshQuantity,
75 std::conditional_t<
76 IsEdge,
77 ::polyscope::CurveNetworkQuantity,
78 ::polyscope::PointCloudQuantity>>;
79};
80
81template <typename PolyscopeStructure>
82using QuantityType = typename PolyscopeTrait<PolyscopeStructure>::QuantityType;
83
84// Register a mesh attribute with Polyscope. Returns true if the attribute was registered.
85template <typename PolyscopeStructure, typename ValueType>
86auto register_attribute(
87 PolyscopeStructure* ps_struct,
88 std::string_view name_,
89 const lagrange::Attribute<ValueType>& attr) -> QuantityType<PolyscopeStructure>*
90{
91 using Usage = lagrange::AttributeUsage;
92 std::string name(name_); // TODO: Fix Polyscope's function signature to accept string_view
93
94 using PolyscopeType = std::decay_t<PolyscopeStructure>;
95 constexpr bool IsMesh = std::is_same_v<PolyscopeType, ::polyscope::SurfaceMesh>;
96 constexpr bool IsEdge = std::is_same_v<PolyscopeType, ::polyscope::CurveNetwork>;
97
98 switch (attr.get_element_type()) {
100 if (attr.get_usage() == Usage::Scalar) {
101 lagrange::logger().info("Registering scalar vertex attribute: {}", name);
102 if constexpr (IsMesh) {
103 return ps_struct->addVertexScalarQuantity(name, vector_view(attr));
104 } else if constexpr (IsEdge) {
105 return ps_struct->addNodeScalarQuantity(name, vector_view(attr));
106 } else {
107 return ps_struct->addScalarQuantity(name, vector_view(attr));
108 }
109 } else if (attr.get_num_channels() == 3) {
110 if (show_as_vector(attr.get_usage())) {
111 lagrange::logger().info("Registering vector vertex attribute: {}", name);
112 ::polyscope::VectorType vt = vector_type(attr.get_usage());
113 if constexpr (IsMesh) {
114 return ps_struct->addVertexVectorQuantity(name, matrix_view(attr), vt);
115 } else if constexpr (IsEdge) {
116 return ps_struct->addNodeVectorQuantity(name, matrix_view(attr), vt);
117 } else {
118 return ps_struct->addVectorQuantity(name, matrix_view(attr), vt);
119 }
120 } else if (attr.get_usage() == Usage::Color) {
121 lagrange::logger().info("Registering color vertex attribute: {}", name);
122 if constexpr (IsMesh) {
123 return ps_struct->addVertexColorQuantity(name, as_color_matrix(attr));
124 } else if constexpr (IsEdge) {
125 return ps_struct->addNodeColorQuantity(name, as_color_matrix(attr));
126 } else {
127 return ps_struct->addColorQuantity(name, as_color_matrix(attr));
128 }
129 }
130 } else if (attr.get_num_channels() == 2) {
131 if (show_as_vector(attr.get_usage())) {
132 lagrange::logger().info("Registering 2D vector vertex attribute: {}", name);
133 ::polyscope::VectorType vt = vector_type(attr.get_usage());
134 if constexpr (IsMesh) {
135 return ps_struct->addVertexVectorQuantity2D(name, matrix_view(attr), vt);
136 } else if constexpr (IsEdge) {
137 return ps_struct->addNodeVectorQuantity2D(name, matrix_view(attr), vt);
138 } else {
139 return ps_struct->addVectorQuantity2D(name, matrix_view(attr), vt);
140 }
141 }
142 } else if (attr.get_usage() == Usage::UV && attr.get_num_channels() == 2) {
143 if constexpr (IsMesh) {
144 lagrange::logger().info("Registering UV vertex attribute: {}", name);
145 return ps_struct->addVertexParameterizationQuantity(name, matrix_view(attr));
146 }
147 }
148 // TODO: If usage == Position, maybe add a toggle to switch between various vertex
149 // positions
150 break;
152 if (attr.get_usage() == Usage::Scalar) {
153 lagrange::logger().info("Registering scalar facet attribute: {}", name);
154 if constexpr (IsMesh) {
155 return ps_struct->addFaceScalarQuantity(name, vector_view(attr));
156 } else if constexpr (IsEdge) {
157 return ps_struct->addEdgeScalarQuantity(name, vector_view(attr));
158 }
159 } else if (attr.get_num_channels() == 3) {
160 if (show_as_vector(attr.get_usage())) {
161 lagrange::logger().info("Registering vector facet attribute: {}", name);
162 ::polyscope::VectorType vt = vector_type(attr.get_usage());
163 if constexpr (IsMesh) {
164 return ps_struct->addFaceVectorQuantity(name, matrix_view(attr), vt);
165 } else if constexpr (IsEdge) {
166 return ps_struct->addEdgeVectorQuantity(name, matrix_view(attr), vt);
167 }
168 } else if (attr.get_usage() == Usage::Color) {
169 lagrange::logger().info("Registering color facet attribute: {}", name);
170 if constexpr (IsMesh) {
171 return ps_struct->addFaceColorQuantity(name, as_color_matrix(attr));
172 } else if constexpr (IsEdge) {
173 return ps_struct->addEdgeColorQuantity(name, as_color_matrix(attr));
174 }
175 }
176 } else if (attr.get_num_channels() == 2) {
177 if (show_as_vector(attr.get_usage())) {
178 lagrange::logger().info("Registering vector facet attribute: {}", name);
179 ::polyscope::VectorType vt = vector_type(attr.get_usage());
180 if constexpr (IsMesh) {
181 return ps_struct->addFaceVectorQuantity2D(name, matrix_view(attr), vt);
182 } else if constexpr (IsEdge) {
183 return ps_struct->addEdgeVectorQuantity2D(name, matrix_view(attr), vt);
184 }
185 }
186 }
187 break;
189 if constexpr (IsMesh) {
190 if (attr.get_usage() == Usage::Scalar) {
191 lagrange::logger().info("Registering scalar edge attribute: {}", name);
192 return ps_struct->addEdgeScalarQuantity(name, vector_view(attr));
193 }
194 }
195 break;
197 if constexpr (IsMesh) {
198 if (attr.get_usage() == Usage::UV && attr.get_num_channels() == 2) {
199 lagrange::logger().info("Registering UV corner attribute: {}", name);
200 return ps_struct->addParameterizationQuantity(name, matrix_view(attr));
201 }
202 }
203 break;
204 default: break;
205 }
206
207 return nullptr;
208}
209
210template <typename PolyscopeStructure, typename Scalar, typename Index>
211void register_attributes(PolyscopeStructure* ps_struct, const SurfaceMesh<Scalar, Index>& mesh)
212{
213 // Register mesh attributes supported by Polyscope
214 seq_foreach_named_attribute_read(mesh, [&](auto name, auto&& attr) {
215 if (mesh.attr_name_is_reserved(name)) {
216 return;
217 }
218 using AttributeType = std::decay_t<decltype(attr)>;
219 if constexpr (!AttributeType::IsIndexed) {
220 if (!register_attribute(ps_struct, name, attr)) {
221 lagrange::logger().warn("Skipping unsupported attribute: {}", name);
222 }
223 }
224 });
225}
226
227} // namespace lagrange::polyscope
Derived attribute class that stores the actual information.
Definition Attribute.h:153
static bool attr_name_is_reserved(std::string_view name)
Check whether the given name corresponds to a reserved attribute.
Definition SurfaceMesh.cpp:1416
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
Definition register_attributes.h:67