Blender V4.3
node_geo_input_shortest_edge_paths.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 <queue>
6
7#include "BLI_array_utils.hh"
8#include "BLI_map.hh"
10#include "BLI_set.hh"
11#include "BLI_task.hh"
12
13#include "BKE_mesh.hh"
14#include "BKE_mesh_mapping.hh"
15
16#include "node_geometry_util.hh"
17
19
21{
22 b.add_input<decl::Bool>("End Vertex").default_value(false).hide_value().supports_field();
23 b.add_input<decl::Float>("Edge Cost").default_value(1.0f).hide_value().supports_field();
24 b.add_output<decl::Int>("Next Vertex Index").field_source().reference_pass_all();
25 b.add_output<decl::Float>("Total Cost").field_source().reference_pass_all();
26}
27
28using VertPriority = std::pair<float, int>;
29
30static void shortest_paths(const Mesh &mesh,
31 const GroupedSpan<int> vert_to_edge,
32 const IndexMask end_selection,
33 const VArray<float> &input_cost,
34 MutableSpan<int> r_next_index,
35 MutableSpan<float> r_cost)
36{
37 const Span<int2> edges = mesh.edges();
38 Array<bool> visited(mesh.verts_num, false);
39
40 std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue;
41
42 end_selection.foreach_index([&](const int start_vert_i) {
43 r_cost[start_vert_i] = 0.0f;
44 queue.emplace(0.0f, start_vert_i);
45 });
46
47 /* Though it uses more memory, calculating the adjacent vertex
48 * across each edge beforehand is noticeably faster. */
49 Array<int> other_vertex(vert_to_edge.data.size());
50 threading::parallel_for(vert_to_edge.index_range(), 2048, [&](const IndexRange range) {
51 for (const int vert_i : range) {
52 for (const int edge_i : vert_to_edge.offsets[vert_i]) {
53 other_vertex[edge_i] = bke::mesh::edge_other_vert(edges[vert_to_edge.data[edge_i]],
54 vert_i);
55 }
56 }
57 });
58
59 while (!queue.empty()) {
60 const float cost_i = queue.top().first;
61 const int vert_i = queue.top().second;
62 queue.pop();
63 if (visited[vert_i]) {
64 continue;
65 }
66 visited[vert_i] = true;
67 for (const int index : vert_to_edge.offsets[vert_i]) {
68 const int edge_i = vert_to_edge.data[index];
69 const int neighbor_vert_i = other_vertex[index];
70 if (visited[neighbor_vert_i]) {
71 continue;
72 }
73 const float edge_cost = std::max(0.0f, input_cost[edge_i]);
74 const float new_neighbor_cost = cost_i + edge_cost;
75 if (new_neighbor_cost < r_cost[neighbor_vert_i]) {
76 r_cost[neighbor_vert_i] = new_neighbor_cost;
77 r_next_index[neighbor_vert_i] = vert_i;
78 queue.emplace(new_neighbor_cost, neighbor_vert_i);
79 }
80 }
81 }
82}
83
85 private:
86 Field<bool> end_selection_;
87 Field<float> cost_;
88
89 public:
91 : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"),
92 end_selection_(end_selection),
93 cost_(cost)
94 {
95 category_ = Category::Generated;
96 }
97
99 const AttrDomain domain,
100 const IndexMask & /*mask*/) const final
101 {
102 const bke::MeshFieldContext edge_context{mesh, AttrDomain::Edge};
103 fn::FieldEvaluator edge_evaluator{edge_context, mesh.edges_num};
104 edge_evaluator.add(cost_);
105 edge_evaluator.evaluate();
106 const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
107
108 const bke::MeshFieldContext point_context{mesh, AttrDomain::Point};
109 fn::FieldEvaluator point_evaluator{point_context, mesh.verts_num};
110 point_evaluator.add(end_selection_);
111 point_evaluator.evaluate();
112 const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
113
114 Array<int> next_index(mesh.verts_num, -1);
115 Array<float> cost(mesh.verts_num, FLT_MAX);
116
117 if (end_selection.is_empty()) {
118 array_utils::fill_index_range<int>(next_index);
119 return mesh.attributes().adapt_domain<int>(
120 VArray<int>::ForContainer(std::move(next_index)), AttrDomain::Point, domain);
121 }
122
123 const Span<int2> edges = mesh.edges();
124 Array<int> vert_to_edge_offset_data;
125 Array<int> vert_to_edge_indices;
126 const GroupedSpan<int> vert_to_edge = bke::mesh::build_vert_to_edge_map(
127 edges, mesh.verts_num, vert_to_edge_offset_data, vert_to_edge_indices);
128 shortest_paths(mesh, vert_to_edge, end_selection, input_cost, next_index, cost);
129
130 threading::parallel_for(next_index.index_range(), 1024, [&](const IndexRange range) {
131 for (const int i : range) {
132 if (next_index[i] == -1) {
133 next_index[i] = i;
134 }
135 }
136 });
137 return mesh.attributes().adapt_domain<int>(
138 VArray<int>::ForContainer(std::move(next_index)), AttrDomain::Point, domain);
139 }
140
141 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
142 {
143 end_selection_.node().for_each_field_input_recursive(fn);
145 }
146
147 uint64_t hash() const override
148 {
149 return get_default_hash(end_selection_, cost_);
150 }
151
152 bool is_equal_to(const fn::FieldNode &other) const override
153 {
154 if (const ShortestEdgePathsNextVertFieldInput *other_field =
155 dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other))
156 {
157 return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
158 }
159 return false;
160 }
161
162 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const override
163 {
164 return AttrDomain::Point;
165 }
166};
167
169 private:
170 Field<bool> end_selection_;
171 Field<float> cost_;
172
173 public:
175 : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"),
176 end_selection_(end_selection),
177 cost_(cost)
178 {
179 category_ = Category::Generated;
180 }
181
183 const AttrDomain domain,
184 const IndexMask & /*mask*/) const final
185 {
186 const bke::MeshFieldContext edge_context{mesh, AttrDomain::Edge};
187 fn::FieldEvaluator edge_evaluator{edge_context, mesh.edges_num};
188 edge_evaluator.add(cost_);
189 edge_evaluator.evaluate();
190 const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
191
192 const bke::MeshFieldContext point_context{mesh, AttrDomain::Point};
193 fn::FieldEvaluator point_evaluator{point_context, mesh.verts_num};
194 point_evaluator.add(end_selection_);
195 point_evaluator.evaluate();
196 const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
197
198 if (end_selection.is_empty()) {
199 return mesh.attributes().adapt_domain<float>(
200 VArray<float>::ForSingle(0.0f, mesh.verts_num), AttrDomain::Point, domain);
201 }
202
203 Array<int> next_index(mesh.verts_num, -1);
204 Array<float> cost(mesh.verts_num, FLT_MAX);
205
206 const Span<int2> edges = mesh.edges();
207 Array<int> vert_to_edge_offset_data;
208 Array<int> vert_to_edge_indices;
209 const GroupedSpan<int> vert_to_edge = bke::mesh::build_vert_to_edge_map(
210 edges, mesh.verts_num, vert_to_edge_offset_data, vert_to_edge_indices);
211 shortest_paths(mesh, vert_to_edge, end_selection, input_cost, next_index, cost);
212
213 threading::parallel_for(cost.index_range(), 1024, [&](const IndexRange range) {
214 for (const int i : range) {
215 if (cost[i] == FLT_MAX) {
216 cost[i] = 0;
217 }
218 }
219 });
220 return mesh.attributes().adapt_domain<float>(
221 VArray<float>::ForContainer(std::move(cost)), AttrDomain::Point, domain);
222 }
223
224 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
225 {
226 end_selection_.node().for_each_field_input_recursive(fn);
228 }
229
230 uint64_t hash() const override
231 {
232 return get_default_hash(end_selection_, cost_);
233 }
234
235 bool is_equal_to(const fn::FieldNode &other) const override
236 {
237 if (const ShortestEdgePathsCostFieldInput *other_field =
238 dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other))
239 {
240 return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
241 }
242 return false;
243 }
244
245 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const override
246 {
247 return AttrDomain::Point;
248 }
249};
250
252{
253 Field<bool> end_selection = params.extract_input<Field<bool>>("End Vertex");
254 Field<float> cost = params.extract_input<Field<float>>("Edge Cost");
255
256 Field<int> next_vert_field{
257 std::make_shared<ShortestEdgePathsNextVertFieldInput>(end_selection, cost)};
258 Field<float> cost_field{std::make_shared<ShortestEdgePathsCostFieldInput>(end_selection, cost)};
259 params.set_output("Next Vertex Index", std::move(next_vert_field));
260 params.set_output("Total Cost", std::move(cost_field));
261}
262
263static void node_register()
264{
265 static blender::bke::bNodeType ntype;
266
268 &ntype, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, "Shortest Edge Paths", NODE_CLASS_INPUT);
269 ntype.declare = node_declare;
272}
273NOD_REGISTER_NODE(node_register)
274
275} // namespace blender::nodes::node_geo_input_shortest_edge_paths_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:404
#define NOD_REGISTER_NODE(REGISTER_FUNC)
IndexRange index_range() const
Definition BLI_array.hh:349
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:587
const FieldNode & node() const
Definition FN_field.hh:137
void foreach_index(Fn &&fn) const
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
local_group_size(16, 16) .push_constant(Type b
Set< ComponentNode * > visited
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void shortest_paths(const Mesh &mesh, const GroupedSpan< int > vert_to_edge, const IndexMask end_selection, const VArray< float > &input_cost, MutableSpan< int > r_next_index, MutableSpan< float > r_cost)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
#define FLT_MAX
Definition stdcycles.h:14
unsigned __int64 uint64_t
Definition stdint.h:90
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347