29 const bNode *node =
b.node_or_null();
31 if (node !=
nullptr) {
33 b.add_input(data_type,
"Value")
35 .description(
"The values the mean and median will be calculated from");
38 b.add_input<
decl::Int>(
"Group ID",
"Group Index")
41 .description(
"An index used to group values together for multiple separate operations");
43 if (node !=
nullptr) {
45 b.add_output(data_type,
"Mean")
46 .field_source_reference_all()
47 .description(
"The sum of all values in each group divided by the size of said group");
48 b.add_output(data_type,
"Median")
50 .field_source_reference_all()
52 "The middle value in each group when all values are sorted from lowest to highest");
65 node->
custom2 = int16_t(AttrDomain::Point);
72 switch (socket.
type) {
126 if constexpr (std::is_same<T, float3>::value) {
141 const auto middle_itr = values.
begin() + values.
size() / 2;
142 std::nth_element(values.
begin(), middle_itr, values.
end());
143 if (values.
size() % 2 == 0) {
144 const auto left_middle_itr = std::max_element(values.
begin(), middle_itr);
164 input_(std::move(
input)),
165 group_index_(std::move(group_index)),
166 source_domain_(source_domain),
167 operation_(operation)
176 if (domain_size == 0) {
182 evaluator.
add(input_);
183 evaluator.
add(group_index_);
191 using T = decltype(dummy);
192 if constexpr (is_same_any_v<T, int, float, float3>) {
193 const VArraySpan<T> values = g_values.typed<T>();
195 if (operation_ == Operation::Mean) {
196 if (group_indices.is_single()) {
197 const T mean = std::reduce(values.begin(), values.end(), T()) / domain_size;
198 g_outputs = VArray<T>::ForSingle(mean, domain_size);
201 Map<int, std::pair<T, int>> sum_and_counts;
202 for (const int i : values.index_range()) {
203 auto &pair = sum_and_counts.lookup_or_add(group_indices[i], std::make_pair(T(), 0));
204 pair.first = pair.first + values[i];
205 pair.second = pair.second + 1;
208 Array<T> outputs(domain_size);
209 for (const int i : values.index_range()) {
210 const auto &pair = sum_and_counts.lookup(group_indices[i]);
211 outputs[i] = pair.first / pair.second;
213 g_outputs = VArray<T>::ForContainer(std::move(outputs));
217 if (group_indices.is_single()) {
218 Array<T> sorted_values(values);
219 T median = calculate_median<T>(sorted_values);
220 g_outputs = VArray<T>::ForSingle(median, domain_size);
223 Map<int, Vector<T>> groups;
224 for (const int i : values.index_range()) {
225 groups.lookup_or_add(group_indices[i], Vector<T>()).append(values[i]);
229 for (MutableMapItem<int, Vector<T>> group : groups.items()) {
230 medians.add(group.key, calculate_median<T>(group.value));
233 Array<T> outputs(domain_size);
234 for (const int i : values.index_range()) {
235 outputs[i] = medians.lookup(group_indices[i]);
237 g_outputs = VArray<T>::ForContainer(std::move(outputs));
243 return attributes.adapt_domain(std::move(g_outputs), source_domain_, context.domain());
248 input_.node().for_each_field_input_recursive(
fn);
249 group_index_.node().for_each_field_input_recursive(
fn);
260 return input_ == other_field->input_ && group_index_ == other_field->group_index_ &&
261 source_domain_ == other_field->source_domain_ &&
262 operation_ == other_field->operation_;
270 return source_domain_;
283 GField{std::make_shared<FieldAverageInput>(
289 GField{std::make_shared<FieldAverageInput>(
297 {
CD_PROP_FLOAT,
"FLOAT", 0,
"Float",
"Floating-point value"},
298 {
CD_PROP_FLOAT3,
"FLOAT_VECTOR", 0,
"Vector",
"3D vector with floating-point values"},
299 {0,
nullptr, 0,
nullptr,
nullptr},
305 "Type of data the outputs are calculated from",
316 int(AttrDomain::Point),
326 ntype.
ui_name =
"Field Average";
327 ntype.
ui_description =
"Calculate the mean and median of a given field";
#define NODE_CLASS_CONVERTER
#define BLT_I18NCONTEXT_ID_NODETREE
#define CTX_IFACE_(context, msgid)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
unsigned long long int uint64_t
const CPPType & type() const
constexpr int64_t size() const
constexpr T * end() const
constexpr T * begin() const
constexpr IndexRange index_range() const
int domain_size(const AttrDomain domain) const
int add(GField field, GVArray *varray_ptr)
const GVArray & get_evaluated(const int field_index) const
const bke::bNodeType & node_type() const
void add_item(std::string socket_name, SocketLinkOperation::LinkSocketFn fn, int weight=0)
const bNodeSocket & other_socket() const
eNodeSocketInOut in_out() const
T extract_input(StringRef identifier)
void set_output(StringRef identifier, T &&value)
bool output_is_required(StringRef identifier) const
const bNode & node() const
bNode & add_node(StringRef idname)
void update_and_connect_available_socket(bNode &new_node, StringRef socket_name)
Vector< SocketDeclaration * > inputs
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void node_register_type(bNodeType &ntype)
T midpoint(const T &a, const T &b)
static void node_init(bNodeTree *, bNode *node)
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_register()
static void node_geo_exec(GeoNodeExecParams params)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static void node_rna(StructRNA *srna)
static void node_declare(NodeDeclarationBuilder &b)
static T calculate_median(MutableSpan< T > values)
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)
uint64_t get_default_hash(const T &v, const Args &...args)
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[]
blender::nodes::NodeDeclaration * static_declaration
std::string ui_description
void(*) draw_buttons(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGeometryExecFunction geometry_node_execute
void(*) initfunc(bNodeTree *ntree, bNode *node)
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)