25 const bNode *node =
b.node_or_null();
27 b.add_input<
decl::Geometry>(
"Geometry").description(
"Geometry to get the statistics from");
30 if (node !=
nullptr) {
32 b.add_input(data_type,
"Attribute").hide_value().field_on_all();
34 b.add_output(data_type,
N_(
"Mean"));
37 b.add_output(data_type,
N_(
"Sum"));
38 b.add_output(data_type,
N_(
"Min"));
39 b.add_output(data_type,
N_(
"Max"));
40 b.add_output(data_type,
N_(
"Range"));
41 b.add_output(data_type,
N_(
"Standard Deviation"));
42 b.add_output(data_type,
N_(
"Variance"));
55 node->
custom2 = int16_t(AttrDomain::Point);
60 switch (socket.
type) {
89 params.update_and_connect_available_socket(node,
"Attribute");
94 {
"Mean",
"Median",
"Sum",
"Min",
"Max",
"Range",
"Standard Deviation",
"Variance"})
99 params.update_and_connect_available_socket(node,
name);
107 if (
data.size() <= 1) {
111 float sum_of_squared_differences = std::accumulate(
112 data.begin(),
data.end(), 0.0f, [mean](
float accumulator,
float value) {
113 float difference = mean - value;
114 return accumulator + difference * difference;
117 return sum_of_squared_differences /
data.size();
122 if (
data.is_empty()) {
126 const float median =
data[
data.size() / 2];
129 if (
data.size() % 2 == 0) {
130 return (median +
data[
data.size() / 2 - 1]) * 0.5f;
150 const std::optional<AttributeAccessor> attributes = component->attributes();
151 if (!attributes.has_value()) {
154 if (attributes->domain_supported(domain)) {
157 data_evaluator.
add(input_field);
163 const int next_data_index =
data.size();
164 data.resize(next_data_index + selection.size());
177 float standard_deviation = 0.0f;
178 float variance = 0.0f;
179 const bool sort_required =
params.output_is_required(
"Min") ||
180 params.output_is_required(
"Max") ||
181 params.output_is_required(
"Range") ||
182 params.output_is_required(
"Median");
183 const bool sum_required =
params.output_is_required(
"Sum") ||
184 params.output_is_required(
"Mean");
185 const bool variance_required =
params.output_is_required(
"Standard Deviation") ||
186 params.output_is_required(
"Variance");
188 if (
data.size() != 0) {
190 std::sort(
data.begin(),
data.end());
197 if (sum_required || variance_required) {
201 if (variance_required) {
203 standard_deviation = std::sqrt(variance);
210 params.set_output(
"Mean", mean);
215 params.set_output(
"Range", range);
216 params.set_output(
"Median", median);
218 if (variance_required) {
219 params.set_output(
"Standard Deviation", standard_deviation);
220 params.set_output(
"Variance", variance);
228 const std::optional<AttributeAccessor> attributes = component->attributes();
229 if (!attributes.has_value()) {
232 if (attributes->domain_supported(domain)) {
235 data_evaluator.
add(input_field);
241 const int next_data_index =
data.size();
242 data.resize(
data.size() + selection.size());
256 float3 standard_deviation{0};
257 const bool sort_required =
params.output_is_required(
"Min") ||
258 params.output_is_required(
"Max") ||
259 params.output_is_required(
"Range") ||
260 params.output_is_required(
"Median");
261 const bool sum_required =
params.output_is_required(
"Sum") ||
262 params.output_is_required(
"Mean");
263 const bool variance_required =
params.output_is_required(
"Standard Deviation") ||
264 params.output_is_required(
"Variance");
269 if (sort_required || variance_required) {
273 for (
const int i :
data.index_range()) {
280 if (
data.size() != 0) {
282 std::sort(data_x.
begin(), data_x.
end());
283 std::sort(data_y.
begin(), data_y.
end());
284 std::sort(data_z.
begin(), data_z.
end());
289 median =
float3(x_median, y_median, z_median);
295 if (sum_required || variance_required) {
299 if (variance_required) {
303 variance =
float3(x_variance, y_variance, z_variance);
304 standard_deviation =
float3(
305 std::sqrt(variance.x), std::sqrt(variance.y), std::sqrt(variance.z));
312 params.set_output(
"Mean", mean);
317 params.set_output(
"Range", range);
318 params.set_output(
"Median", median);
320 if (variance_required) {
321 params.set_output(
"Standard Deviation", standard_deviation);
322 params.set_output(
"Variance", variance);
337 "The data type the attribute is converted to before calculating the results",
351 "Which domain to read the data from",
354 int(AttrDomain::Point),
363 ntype.
ui_name =
"Attribute Statistic";
365 "Calculate statistics about a data set from a field evaluated on a geometry";
#define NODE_CLASS_ATTRIBUTE
#define GEO_NODE_ATTRIBUTE_STATISTIC
#define CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_ID_NODETREE
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
BMesh const char void * data
static T sum(const btAlignedObjectArray< T > &items)
const T & last(const int64_t n=0) const
void reinitialize(const int64_t new_size)
void set_selection(Field< bool > selection)
int add(GField field, GVArray *varray_ptr)
IndexMask get_evaluated_selection_as_mask() const
const GVArray & get_evaluated(const int field_index) const
Vector< SocketDeclaration * > inputs
T compute_sum(const Span< T > data)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void node_register_type(bNodeType &ntype)
static float median_of_sorted_span(const Span< float > data)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static void node_rna(StructRNA *srna)
static void node_init(bNodeTree *, bNode *node)
static void node_register()
static void node_declare(NodeDeclarationBuilder &b)
static float compute_variance(const Span< float > data, const float mean)
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, Span< SocketDeclaration * > declarations)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
const EnumPropertyItem * enum_items_filter(const EnumPropertyItem *original_item_array, FunctionRef< bool(const EnumPropertyItem &item)> fn)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
const EnumPropertyItem rna_enum_attribute_domain_items[]
const EnumPropertyItem rna_enum_attribute_type_items[]
Vector< const GeometryComponent * > get_components() const
std::string ui_description
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
NodeDeclareFunction declare
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)