Blender V5.0
node_geo_mesh_topology_edges_of_vertex.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "DNA_mesh_types.h"
6
7#include "BKE_mesh_mapping.hh"
8
9#include "BLI_array_utils.hh"
10
11#include "node_geometry_util.hh"
12
14
16{
17 b.add_input<decl::Int>("Vertex Index")
18 .implicit_field(NODE_DEFAULT_INPUT_INDEX_FIELD)
19 .description("The vertex to retrieve data from. Defaults to the vertex from the context")
20 .structure_type(StructureType::Field);
21 b.add_input<decl::Float>("Weights").supports_field().hide_value().description(
22 "Values used to sort the edges connected to the vertex. Uses indices by default");
23 b.add_input<decl::Int>("Sort Index")
24 .min(0)
25 .supports_field()
26 .description("Which of the sorted edges to output");
27 b.add_output<decl::Int>("Edge Index")
28 .field_source_reference_all()
29 .description("An edge connected to the face, chosen by the sort index");
30 b.add_output<decl::Int>("Total").field_source().reference_pass({0}).description(
31 "The number of edges connected to each vertex");
32}
33
35 const Field<int> vert_index_;
36 const Field<int> sort_index_;
37 const Field<float> sort_weight_;
38
39 public:
40 EdgesOfVertInput(Field<int> vert_index, Field<int> sort_index, Field<float> sort_weight)
41 : bke::MeshFieldInput(CPPType::get<int>(), "Edge of Vertex"),
42 vert_index_(std::move(vert_index)),
43 sort_index_(std::move(sort_index)),
44 sort_weight_(std::move(sort_weight))
45 {
47 }
48
50 const AttrDomain domain,
51 const IndexMask &mask) const final
52 {
53 const IndexRange vert_range(mesh.verts_num);
54 const Span<int2> edges = mesh.edges();
55 Array<int> map_offsets;
56 Array<int> map_indices;
58 edges, mesh.verts_num, map_offsets, map_indices);
59
60 const bke::MeshFieldContext context{mesh, domain};
61 fn::FieldEvaluator evaluator{context, &mask};
62 evaluator.add(vert_index_);
63 evaluator.add(sort_index_);
64 evaluator.evaluate();
65 const VArray<int> vert_indices = evaluator.get_evaluated<int>(0);
66 const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1);
67
68 const bke::MeshFieldContext edge_context{mesh, AttrDomain::Edge};
69 fn::FieldEvaluator edge_evaluator{edge_context, mesh.edges_num};
70 edge_evaluator.add(sort_weight_);
71 edge_evaluator.evaluate();
72 const VArray<float> all_sort_weights = edge_evaluator.get_evaluated<float>(0);
73 const bool use_sorting = !all_sort_weights.is_single();
74
75 Array<int> edge_of_vertex(mask.min_array_size());
76 mask.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
77 /* Reuse arrays to avoid allocation. */
78 Array<float> sort_weights;
79 Array<int> sort_indices;
80
81 for (const int selection_i : segment) {
82 const int vert_i = vert_indices[selection_i];
83 const int index_in_sort = indices_in_sort[selection_i];
84 if (!vert_range.contains(vert_i)) {
85 edge_of_vertex[selection_i] = 0;
86 continue;
87 }
88
89 const Span<int> edges = vert_to_edge_map[vert_i];
90 if (edges.is_empty()) {
91 edge_of_vertex[selection_i] = 0;
92 continue;
93 }
94
95 const int index_in_sort_wrapped = mod_i(index_in_sort, edges.size());
96 if (use_sorting) {
97 /* Retrieve a compressed array of weights for each edge. */
98 sort_weights.reinitialize(edges.size());
99 IndexMaskMemory memory;
100 all_sort_weights.materialize_compressed(IndexMask::from_indices<int>(edges, memory),
101 sort_weights.as_mutable_span());
102
103 /* Sort a separate array of compressed indices corresponding to the compressed weights.
104 * This allows using `materialize_compressed` to avoid virtual function call overhead
105 * when accessing values in the sort weights. However, it means a separate array of
106 * indices within the compressed array is necessary for sorting. */
107 sort_indices.reinitialize(edges.size());
109 std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) {
110 return sort_weights[a] < sort_weights[b];
111 });
112
113 edge_of_vertex[selection_i] = edges[sort_indices[index_in_sort_wrapped]];
114 }
115 else {
116 edge_of_vertex[selection_i] = edges[index_in_sort_wrapped];
117 }
118 }
119 });
120
121 return VArray<int>::from_container(std::move(edge_of_vertex));
122 }
123
124 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
125 {
126 vert_index_.node().for_each_field_input_recursive(fn);
127 sort_index_.node().for_each_field_input_recursive(fn);
128 sort_weight_.node().for_each_field_input_recursive(fn);
129 }
130
132 {
133 return 98762349875636;
134 }
135
136 bool is_equal_to(const fn::FieldNode &other) const final
137 {
138 if (const auto *typed = dynamic_cast<const EdgesOfVertInput *>(&other)) {
139 return typed->vert_index_ == vert_index_ && typed->sort_index_ == sort_index_ &&
140 typed->sort_weight_ == sort_weight_;
141 }
142 return false;
143 }
144
145 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
146 {
147 return AttrDomain::Point;
148 }
149};
150
152 public:
153 EdgesOfVertCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Face Index")
154 {
156 }
157
159 const AttrDomain domain,
160 const IndexMask & /*mask*/) const final
161 {
162 if (domain != AttrDomain::Point) {
163 return {};
164 }
165 Array<int> counts(mesh.verts_num, 0);
166 array_utils::count_indices(mesh.edges().cast<int>(), counts);
167 return VArray<int>::from_container(std::move(counts));
168 }
169
171 {
172 return 436758278618374;
173 }
174
175 bool is_equal_to(const fn::FieldNode &other) const final
176 {
177 return dynamic_cast<const EdgesOfVertCountInput *>(&other) != nullptr;
178 }
179
180 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
181 {
182 return AttrDomain::Point;
183 }
184};
185
187{
188 const Field<int> vert_index = params.extract_input<Field<int>>("Vertex Index");
189 if (params.output_is_required("Total")) {
190 params.set_output("Total",
191 Field<int>(std::make_shared<bke::EvaluateAtIndexInput>(
192 vert_index,
193 Field<int>(std::make_shared<EdgesOfVertCountInput>()),
194 AttrDomain::Point)));
195 }
196 if (params.output_is_required("Edge Index")) {
197 params.set_output("Edge Index",
198 Field<int>(std::make_shared<EdgesOfVertInput>(
199 vert_index,
200 params.extract_input<Field<int>>("Sort Index"),
201 params.extract_input<Field<float>>("Weights"))));
202 }
203}
204
205static void node_register()
206{
207 static blender::bke::bNodeType ntype;
208 geo_node_type_base(&ntype, "GeometryNodeEdgesOfVertex", GEO_NODE_MESH_TOPOLOGY_EDGES_OF_VERTEX);
209 ntype.ui_name = "Edges of Vertex";
210 ntype.ui_description = "Retrieve the edges connected to each vertex";
211 ntype.enum_name_legacy = "EDGES_OF_VERTEX";
212 ntype.nclass = NODE_CLASS_INPUT;
214 ntype.declare = node_declare;
216}
218
219} // namespace blender::nodes::node_geo_mesh_topology_edges_of_vertex_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#define GEO_NODE_MESH_TOPOLOGY_EDGES_OF_VERTEX
#define final(a, b, c)
Definition BLI_hash.h:19
MINLINE int mod_i(int i, int n)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
unsigned long long int uint64_t
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * end() const
Definition BLI_array.hh:325
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
const T * begin() const
Definition BLI_array.hh:321
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray from_container(ContainerT container)
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:677
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &mask) const final
EdgesOfVertInput(Field< int > vert_index, Field< int > sort_index, Field< float > sort_weight)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void count_indices(Span< int > indices, MutableSpan< int > counts)
void fill_index_range(MutableSpan< T > span, const T start=0)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
#define min(a, b)
Definition sort.cc:36
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362