Blender  V3.3
node_geo_attribute_statistic.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <algorithm>
4 #include <numeric>
5 
6 #include "UI_interface.h"
7 #include "UI_resources.h"
8 
9 #include "BLI_math_base_safe.h"
10 
12 
13 #include "node_geometry_util.hh"
14 
16 
18 {
19  b.add_input<decl::Geometry>(N_("Geometry"));
20  b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
21  b.add_input<decl::Float>(N_("Attribute")).hide_value().supports_field();
22  b.add_input<decl::Vector>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
23 
24  b.add_output<decl::Float>(N_("Mean"));
25  b.add_output<decl::Float>(N_("Median"));
26  b.add_output<decl::Float>(N_("Sum"));
27  b.add_output<decl::Float>(N_("Min"));
28  b.add_output<decl::Float>(N_("Max"));
29  b.add_output<decl::Float>(N_("Range"));
30  b.add_output<decl::Float>(N_("Standard Deviation"));
31  b.add_output<decl::Float>(N_("Variance"));
32 
33  b.add_output<decl::Vector>(N_("Mean"), "Mean_001");
34  b.add_output<decl::Vector>(N_("Median"), "Median_001");
35  b.add_output<decl::Vector>(N_("Sum"), "Sum_001");
36  b.add_output<decl::Vector>(N_("Min"), "Min_001");
37  b.add_output<decl::Vector>(N_("Max"), "Max_001");
38  b.add_output<decl::Vector>(N_("Range"), "Range_001");
39  b.add_output<decl::Vector>(N_("Standard Deviation"), "Standard Deviation_001");
40  b.add_output<decl::Vector>(N_("Variance"), "Variance_001");
41 }
42 
43 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
44 {
45  uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
46  uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
47 }
48 
50 {
51  node->custom1 = CD_PROP_FLOAT;
52  node->custom2 = ATTR_DOMAIN_POINT;
53 }
54 
56 {
57  bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first;
58  bNodeSocket *socket_selection = socket_geo->next;
59  bNodeSocket *socket_float_attr = socket_selection->next;
60  bNodeSocket *socket_float3_attr = socket_float_attr->next;
61 
62  bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first;
63  bNodeSocket *socket_float_median = socket_float_mean->next;
64  bNodeSocket *socket_float_sum = socket_float_median->next;
65  bNodeSocket *socket_float_min = socket_float_sum->next;
66  bNodeSocket *socket_float_max = socket_float_min->next;
67  bNodeSocket *socket_float_range = socket_float_max->next;
68  bNodeSocket *socket_float_std = socket_float_range->next;
69  bNodeSocket *socket_float_variance = socket_float_std->next;
70 
71  bNodeSocket *socket_vector_mean = socket_float_variance->next;
72  bNodeSocket *socket_vector_median = socket_vector_mean->next;
73  bNodeSocket *socket_vector_sum = socket_vector_median->next;
74  bNodeSocket *socket_vector_min = socket_vector_sum->next;
75  bNodeSocket *socket_vector_max = socket_vector_min->next;
76  bNodeSocket *socket_vector_range = socket_vector_max->next;
77  bNodeSocket *socket_vector_std = socket_vector_range->next;
78  bNodeSocket *socket_vector_variance = socket_vector_std->next;
79 
80  const eCustomDataType data_type = static_cast<eCustomDataType>(node->custom1);
81 
82  nodeSetSocketAvailability(ntree, socket_float_attr, data_type == CD_PROP_FLOAT);
83  nodeSetSocketAvailability(ntree, socket_float_mean, data_type == CD_PROP_FLOAT);
84  nodeSetSocketAvailability(ntree, socket_float_median, data_type == CD_PROP_FLOAT);
85  nodeSetSocketAvailability(ntree, socket_float_sum, data_type == CD_PROP_FLOAT);
86  nodeSetSocketAvailability(ntree, socket_float_min, data_type == CD_PROP_FLOAT);
87  nodeSetSocketAvailability(ntree, socket_float_max, data_type == CD_PROP_FLOAT);
88  nodeSetSocketAvailability(ntree, socket_float_range, data_type == CD_PROP_FLOAT);
89  nodeSetSocketAvailability(ntree, socket_float_std, data_type == CD_PROP_FLOAT);
90  nodeSetSocketAvailability(ntree, socket_float_variance, data_type == CD_PROP_FLOAT);
91 
92  nodeSetSocketAvailability(ntree, socket_float3_attr, data_type == CD_PROP_FLOAT3);
93  nodeSetSocketAvailability(ntree, socket_vector_mean, data_type == CD_PROP_FLOAT3);
94  nodeSetSocketAvailability(ntree, socket_vector_median, data_type == CD_PROP_FLOAT3);
95  nodeSetSocketAvailability(ntree, socket_vector_sum, data_type == CD_PROP_FLOAT3);
96  nodeSetSocketAvailability(ntree, socket_vector_min, data_type == CD_PROP_FLOAT3);
97  nodeSetSocketAvailability(ntree, socket_vector_max, data_type == CD_PROP_FLOAT3);
98  nodeSetSocketAvailability(ntree, socket_vector_range, data_type == CD_PROP_FLOAT3);
99  nodeSetSocketAvailability(ntree, socket_vector_std, data_type == CD_PROP_FLOAT3);
100  nodeSetSocketAvailability(ntree, socket_vector_variance, data_type == CD_PROP_FLOAT3);
101 }
102 
103 static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
104 {
105  switch (socket.type) {
106  case SOCK_FLOAT:
107  case SOCK_BOOLEAN:
108  case SOCK_INT:
109  return CD_PROP_FLOAT;
110  case SOCK_VECTOR:
111  case SOCK_RGBA:
112  return CD_PROP_FLOAT3;
113  default:
114  return {};
115  }
116 }
117 
119 {
120  const bNodeType &node_type = params.node_type();
121  const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
122  search_link_ops_for_declarations(params, declaration.inputs().take_front(2));
123 
124  const std::optional<eCustomDataType> type = node_type_from_other_socket(params.other_socket());
125  if (!type) {
126  return;
127  }
128 
129  if (params.in_out() == SOCK_IN) {
130  params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams &params) {
131  bNode &node = params.add_node(node_type);
132  node.custom1 = *type;
133  params.update_and_connect_available_socket(node, "Attribute");
134  });
135  }
136  else {
137  for (const StringRefNull name :
138  {"Mean", "Median", "Sum", "Min", "Max", "Range", "Standard Deviation", "Variance"}) {
139  params.add_item(IFACE_(name.c_str()), [node_type, name, type](LinkSearchOpParams &params) {
140  bNode &node = params.add_node(node_type);
141  node.custom1 = *type;
142  params.update_and_connect_available_socket(node, name);
143  });
144  }
145  }
146 }
147 
148 template<typename T> static T compute_sum(const Span<T> data)
149 {
150  return std::accumulate(data.begin(), data.end(), T());
151 }
152 
153 static float compute_variance(const Span<float> data, const float mean)
154 {
155  if (data.size() <= 1) {
156  return 0.0f;
157  }
158 
159  float sum_of_squared_differences = std::accumulate(
160  data.begin(), data.end(), 0.0f, [mean](float accumulator, float value) {
161  float difference = mean - value;
162  return accumulator + difference * difference;
163  });
164 
165  return sum_of_squared_differences / (data.size() - 1);
166 }
167 
169 {
170  if (data.is_empty()) {
171  return 0.0f;
172  }
173 
174  const float median = data[data.size() / 2];
175 
176  /* For spans of even length, the median is the average of the middle two elements. */
177  if (data.size() % 2 == 0) {
178  return (median + data[data.size() / 2 - 1]) * 0.5f;
179  }
180  return median;
181 }
182 
184 {
185  GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
186  const bNode &node = params.node();
187  const eCustomDataType data_type = static_cast<eCustomDataType>(node.custom1);
188  const eAttrDomain domain = static_cast<eAttrDomain>(node.custom2);
190 
191  const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
192 
193  switch (data_type) {
194  case CD_PROP_FLOAT: {
195  const Field<float> input_field = params.get_input<Field<float>>("Attribute");
197  for (const GeometryComponent *component : components) {
198  const std::optional<AttributeAccessor> attributes = component->attributes();
199  if (!attributes.has_value()) {
200  continue;
201  }
202  if (attributes->domain_supported(domain)) {
203  GeometryComponentFieldContext field_context{*component, domain};
204  const int domain_num = attributes->domain_size(domain);
205 
206  fn::FieldEvaluator data_evaluator{field_context, domain_num};
207  data_evaluator.add(input_field);
208  data_evaluator.set_selection(selection_field);
209  data_evaluator.evaluate();
210  const VArray<float> component_data = data_evaluator.get_evaluated<float>(0);
211  const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
212 
213  const int next_data_index = data.size();
214  data.resize(next_data_index + selection.size());
215  MutableSpan<float> selected_data = data.as_mutable_span().slice(next_data_index,
216  selection.size());
217  for (const int i : selection.index_range()) {
218  selected_data[i] = component_data[selection[i]];
219  }
220  }
221  }
222 
223  float mean = 0.0f;
224  float median = 0.0f;
225  float sum = 0.0f;
226  float min = 0.0f;
227  float max = 0.0f;
228  float range = 0.0f;
229  float standard_deviation = 0.0f;
230  float variance = 0.0f;
231  const bool sort_required = params.output_is_required("Min") ||
232  params.output_is_required("Max") ||
233  params.output_is_required("Range") ||
234  params.output_is_required("Median");
235  const bool sum_required = params.output_is_required("Sum") ||
236  params.output_is_required("Mean");
237  const bool variance_required = params.output_is_required("Standard Deviation") ||
238  params.output_is_required("Variance");
239 
240  if (data.size() != 0) {
241  if (sort_required) {
242  std::sort(data.begin(), data.end());
243  median = median_of_sorted_span(data);
244 
245  min = data.first();
246  max = data.last();
247  range = max - min;
248  }
249  if (sum_required || variance_required) {
250  sum = compute_sum<float>(data);
251  mean = sum / data.size();
252 
253  if (variance_required) {
254  variance = compute_variance(data, mean);
255  standard_deviation = std::sqrt(variance);
256  }
257  }
258  }
259 
260  if (sum_required) {
261  params.set_output("Sum", sum);
262  params.set_output("Mean", mean);
263  }
264  if (sort_required) {
265  params.set_output("Min", min);
266  params.set_output("Max", max);
267  params.set_output("Range", range);
268  params.set_output("Median", median);
269  }
270  if (variance_required) {
271  params.set_output("Standard Deviation", standard_deviation);
272  params.set_output("Variance", variance);
273  }
274  break;
275  }
276  case CD_PROP_FLOAT3: {
277  const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001");
279  for (const GeometryComponent *component : components) {
280  const std::optional<AttributeAccessor> attributes = component->attributes();
281  if (!attributes.has_value()) {
282  continue;
283  }
284  if (attributes->domain_supported(domain)) {
285  GeometryComponentFieldContext field_context{*component, domain};
286  const int domain_num = attributes->domain_size(domain);
287 
288  fn::FieldEvaluator data_evaluator{field_context, domain_num};
289  data_evaluator.add(input_field);
290  data_evaluator.set_selection(selection_field);
291  data_evaluator.evaluate();
292  const VArray<float3> component_data = data_evaluator.get_evaluated<float3>(0);
293  const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
294 
295  const int next_data_index = data.size();
296  data.resize(data.size() + selection.size());
297  MutableSpan<float3> selected_data = data.as_mutable_span().slice(next_data_index,
298  selection.size());
299  for (const int i : selection.index_range()) {
300  selected_data[i] = component_data[selection[i]];
301  }
302  }
303  }
304 
305  float3 median{0};
306  float3 min{0};
307  float3 max{0};
308  float3 range{0};
309  float3 sum{0};
310  float3 mean{0};
311  float3 variance{0};
312  float3 standard_deviation{0};
313  const bool sort_required = params.output_is_required("Min_001") ||
314  params.output_is_required("Max_001") ||
315  params.output_is_required("Range_001") ||
316  params.output_is_required("Median_001");
317  const bool sum_required = params.output_is_required("Sum_001") ||
318  params.output_is_required("Mean_001");
319  const bool variance_required = params.output_is_required("Standard Deviation_001") ||
320  params.output_is_required("Variance_001");
321 
322  Array<float> data_x;
323  Array<float> data_y;
324  Array<float> data_z;
325  if (sort_required || variance_required) {
326  data_x.reinitialize(data.size());
327  data_y.reinitialize(data.size());
328  data_z.reinitialize(data.size());
329  for (const int i : data.index_range()) {
330  data_x[i] = data[i].x;
331  data_y[i] = data[i].y;
332  data_z[i] = data[i].z;
333  }
334  }
335 
336  if (data.size() != 0) {
337  if (sort_required) {
338  std::sort(data_x.begin(), data_x.end());
339  std::sort(data_y.begin(), data_y.end());
340  std::sort(data_z.begin(), data_z.end());
341 
342  const float x_median = median_of_sorted_span(data_x);
343  const float y_median = median_of_sorted_span(data_y);
344  const float z_median = median_of_sorted_span(data_z);
345  median = float3(x_median, y_median, z_median);
346 
347  min = float3(data_x.first(), data_y.first(), data_z.first());
348  max = float3(data_x.last(), data_y.last(), data_z.last());
349  range = max - min;
350  }
351  if (sum_required || variance_required) {
352  sum = compute_sum(data.as_span());
353  mean = sum / data.size();
354 
355  if (variance_required) {
356  const float x_variance = compute_variance(data_x, mean.x);
357  const float y_variance = compute_variance(data_y, mean.y);
358  const float z_variance = compute_variance(data_z, mean.z);
359  variance = float3(x_variance, y_variance, z_variance);
360  standard_deviation = float3(
361  std::sqrt(variance.x), std::sqrt(variance.y), std::sqrt(variance.z));
362  }
363  }
364  }
365 
366  if (sum_required) {
367  params.set_output("Sum_001", sum);
368  params.set_output("Mean_001", mean);
369  }
370  if (sort_required) {
371  params.set_output("Min_001", min);
372  params.set_output("Max_001", max);
373  params.set_output("Range_001", range);
374  params.set_output("Median_001", median);
375  }
376  if (variance_required) {
377  params.set_output("Standard Deviation_001", standard_deviation);
378  params.set_output("Variance_001", variance);
379  }
380  break;
381  }
382  default:
383  break;
384  }
385 }
386 
387 } // namespace blender::nodes::node_geo_attribute_statistic_cc
388 
390 {
392 
393  static bNodeType ntype;
394 
396  &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE);
397 
404  nodeRegisterType(&ntype);
405 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define GEO_NODE_ATTRIBUTE_STATISTIC
Definition: BKE_node.h:1426
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
#define NODE_CLASS_ATTRIBUTE
Definition: BKE_node.h:360
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
sqrt(x)+1/max(0
#define UNUSED(x)
#define IFACE_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
eCustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ SOCK_IN
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
static T sum(const btAlignedObjectArray< T > &items)
const T & first() const
Definition: BLI_array.hh:269
const T & last(const int64_t n=0) const
Definition: BLI_array.hh:284
const T * begin() const
Definition: BLI_array.hh:309
void reinitialize(const int64_t new_size)
Definition: BLI_array.hh:387
const T * end() const
Definition: BLI_array.hh:313
Span< SocketDeclarationPtr > inputs() const
OperationNode * node
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define T
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static float median_of_sorted_span(const Span< float > data)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static float compute_variance(const Span< float > data, const float mean)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_update(bNodeTree *ntree, bNode *node)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, Span< SocketDeclarationPtr > declarations)
vec_base< float, 3 > float3
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
Definition: node.cc:1082
void register_node_type_geo_attribute_statistic()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
#define min(a, b)
Definition: sort.c:35
blender::Vector< const GeometryComponent * > get_components_for_read() const
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition: BKE_node.h:335
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480