Files
test2/source/blender/nodes/function/nodes/node_fn_integer_math.cc
илья _ 119fc054f8 Cleanup: BKE: Nodes: Pass-by-reference
Restriction of the nodes api to clearly define never-null function arguments.
Side effects: some assertions and null-check (with early return) were removed.
On the caller side is ensured to never derefer null to pass argument (mainly in RNA).
In addition, one pointer argument now actually a return type.

By-reference return types instead of pointers going to be separate kind of
change since also imply of cleaning up variables created from reference.

Also good future improvement would be to mark a copy-constructor as
explicit for DNA node types.

Pull Request: https://projects.blender.org/blender/blender/pulls/134627
2025-02-19 13:44:11 +01:00

324 lines
11 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <numeric>
#include "BLI_string.h"
#include "RNA_enum_types.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "NOD_inverse_eval_params.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket_search_link.hh"
#include "NOD_value_elem_eval.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_integer_math_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Int>("Value");
b.add_input<decl::Int>("Value", "Value_001");
b.add_input<decl::Int>("Value", "Value_002");
b.add_output<decl::Int>("Value");
};
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_update(bNodeTree *ntree, bNode *node)
{
const bool one_input_ops = ELEM(
node->custom1, NODE_INTEGER_MATH_ABSOLUTE, NODE_INTEGER_MATH_SIGN, NODE_INTEGER_MATH_NEGATE);
const bool three_input_ops = ELEM(node->custom1, NODE_INTEGER_MATH_MULTIPLY_ADD);
bNodeSocket *sockA = static_cast<bNodeSocket *>(node->inputs.first);
bNodeSocket *sockB = sockA->next;
bNodeSocket *sockC = sockB->next;
bke::node_set_socket_availability(*ntree, *sockB, !one_input_ops);
bke::node_set_socket_availability(*ntree, *sockC, three_input_ops);
node_sock_label_clear(sockA);
node_sock_label_clear(sockB);
node_sock_label_clear(sockC);
switch (node->custom1) {
case NODE_INTEGER_MATH_MULTIPLY_ADD:
node_sock_label(sockA, N_("Value"));
node_sock_label(sockB, N_("Multiplier"));
node_sock_label(sockC, N_("Addend"));
break;
case NODE_INTEGER_MATH_POWER:
node_sock_label(sockA, N_("Base"));
node_sock_label(sockB, N_("Exponent"));
break;
}
}
class SocketSearchOp {
public:
std::string socket_name;
NodeIntegerMathOperation operation;
void operator()(LinkSearchOpParams &params)
{
bNode &node = params.add_node("FunctionNodeIntegerMath");
node.custom1 = NodeIntegerMathOperation(operation);
params.update_and_connect_available_socket(node, socket_name);
}
};
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
if (!params.node_tree().typeinfo->validate_link(eNodeSocketDatatype(params.other_socket().type),
SOCK_INT))
{
return;
}
const bool is_integer = params.other_socket().type == SOCK_INT;
const int weight = is_integer ? 0 : -1;
/* Add socket A operations. */
for (const EnumPropertyItem *item = rna_enum_node_integer_math_items;
item->identifier != nullptr;
item++)
{
if (item->name != nullptr && item->identifier[0] != '\0') {
params.add_item(IFACE_(item->name),
SocketSearchOp{"Value", NodeIntegerMathOperation(item->value)},
weight);
}
}
}
static void node_label(const bNodeTree * /*ntree*/, const bNode *node, char *label, int maxlen)
{
const char *name;
bool enum_label = RNA_enum_name(rna_enum_node_integer_math_items, node->custom1, &name);
if (!enum_label) {
name = "Unknown";
}
BLI_strncpy(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name), maxlen);
}
/* Derived from `divide_round_i` but fixed to be safe and handle negative inputs. */
static int safe_divide_round_i(const int a, const int b)
{
const int c = math::abs(b);
return (a >= 0) ? math::safe_divide((2 * a + c), (2 * c)) * math::sign(b) :
-math::safe_divide((2 * -a + c), (2 * c)) * math::sign(b);
}
static const mf::MultiFunction *get_multi_function(const bNode &bnode)
{
NodeIntegerMathOperation operation = NodeIntegerMathOperation(bnode.custom1);
static auto exec_preset = mf::build::exec_presets::AllSpanOrSingle();
static auto add_fn = mf::build::SI2_SO<int, int, int>(
"Add", [](int a, int b) { return a + b; }, exec_preset);
static auto sub_fn = mf::build::SI2_SO<int, int, int>(
"Subtract", [](int a, int b) { return a - b; }, exec_preset);
static auto multiply_fn = mf::build::SI2_SO<int, int, int>(
"Multiply", [](int a, int b) { return a * b; }, exec_preset);
static auto divide_fn = mf::build::SI2_SO<int, int, int>(
"Divide", [](int a, int b) { return math::safe_divide(a, b); }, exec_preset);
static auto divide_floor_fn = mf::build::SI2_SO<int, int, int>(
"Divide Floor",
[](int a, int b) { return (b != 0) ? divide_floor_i(a, b) : 0; },
exec_preset);
static auto divide_ceil_fn = mf::build::SI2_SO<int, int, int>(
"Divide Ceil",
[](int a, int b) { return (b != 0) ? -divide_floor_i(a, -b) : 0; },
exec_preset);
static auto divide_round_fn = mf::build::SI2_SO<int, int, int>(
"Divide Round", [](int a, int b) { return safe_divide_round_i(a, b); }, exec_preset);
static auto pow_fn = mf::build::SI2_SO<int, int, int>(
"Power", [](int a, int b) { return math::pow(a, b); }, exec_preset);
static auto madd_fn = mf::build::SI3_SO<int, int, int, int>(
"Multiply Add", [](int a, int b, int c) { return a * b + c; }, exec_preset);
static auto floored_mod_fn = mf::build::SI2_SO<int, int, int>(
"Floored Modulo",
[](int a, int b) { return b != 0 ? math::mod_periodic(a, b) : 0; },
exec_preset);
static auto mod_fn = mf::build::SI2_SO<int, int, int>(
"Modulo", [](int a, int b) { return b != 0 ? a % b : 0; }, exec_preset);
static auto abs_fn = mf::build::SI1_SO<int, int>(
"Absolute", [](int a) { return math::abs(a); }, exec_preset);
static auto sign_fn = mf::build::SI1_SO<int, int>(
"Sign", [](int a) { return math::sign(a); }, exec_preset);
static auto min_fn = mf::build::SI2_SO<int, int, int>(
"Minimum", [](int a, int b) { return math::min(a, b); }, exec_preset);
static auto max_fn = mf::build::SI2_SO<int, int, int>(
"Maximum", [](int a, int b) { return math::max(a, b); }, exec_preset);
static auto gcd_fn = mf::build::SI2_SO<int, int, int>(
"GCD", [](int a, int b) { return std::gcd(a, b); }, exec_preset);
static auto lcm_fn = mf::build::SI2_SO<int, int, int>(
"LCM", [](int a, int b) { return std::lcm(a, b); }, exec_preset);
static auto negate_fn = mf::build::SI1_SO<int, int>(
"Negate", [](int a) { return -a; }, exec_preset);
switch (operation) {
case NODE_INTEGER_MATH_ADD:
return &add_fn;
case NODE_INTEGER_MATH_SUBTRACT:
return &sub_fn;
case NODE_INTEGER_MATH_MULTIPLY:
return &multiply_fn;
case NODE_INTEGER_MATH_DIVIDE:
return &divide_fn;
case NODE_INTEGER_MATH_DIVIDE_FLOOR:
return &divide_floor_fn;
case NODE_INTEGER_MATH_DIVIDE_CEIL:
return &divide_ceil_fn;
case NODE_INTEGER_MATH_DIVIDE_ROUND:
return &divide_round_fn;
case NODE_INTEGER_MATH_POWER:
return &pow_fn;
case NODE_INTEGER_MATH_MULTIPLY_ADD:
return &madd_fn;
case NODE_INTEGER_MATH_FLOORED_MODULO:
return &floored_mod_fn;
case NODE_INTEGER_MATH_MODULO:
return &mod_fn;
case NODE_INTEGER_MATH_ABSOLUTE:
return &abs_fn;
case NODE_INTEGER_MATH_SIGN:
return &sign_fn;
case NODE_INTEGER_MATH_MINIMUM:
return &min_fn;
case NODE_INTEGER_MATH_MAXIMUM:
return &max_fn;
case NODE_INTEGER_MATH_GCD:
return &gcd_fn;
case NODE_INTEGER_MATH_LCM:
return &lcm_fn;
case NODE_INTEGER_MATH_NEGATE:
return &negate_fn;
}
BLI_assert_unreachable();
return nullptr;
}
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
const mf::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
static void node_eval_elem(value_elem::ElemEvalParams &params)
{
using namespace value_elem;
const NodeIntegerMathOperation op = NodeIntegerMathOperation(params.node.custom1);
switch (op) {
case NODE_INTEGER_MATH_ADD:
case NODE_INTEGER_MATH_SUBTRACT:
case NODE_INTEGER_MATH_MULTIPLY:
case NODE_INTEGER_MATH_DIVIDE: {
IntElem output_elem = params.get_input_elem<IntElem>("Value");
output_elem.merge(params.get_input_elem<IntElem>("Value_001"));
params.set_output_elem("Value", output_elem);
break;
}
default:
break;
}
}
static void node_eval_inverse_elem(value_elem::InverseElemEvalParams &params)
{
const NodeIntegerMathOperation op = NodeIntegerMathOperation(params.node.custom1);
switch (op) {
case NODE_INTEGER_MATH_ADD:
case NODE_INTEGER_MATH_SUBTRACT:
case NODE_INTEGER_MATH_MULTIPLY:
case NODE_INTEGER_MATH_DIVIDE: {
params.set_input_elem("Value", params.get_output_elem<value_elem::IntElem>("Value"));
break;
}
default:
break;
}
}
static void node_eval_inverse(inverse_eval::InverseEvalParams &params)
{
const NodeIntegerMathOperation op = NodeIntegerMathOperation(params.node.custom1);
const StringRef first_input_id = "Value";
const StringRef second_input_id = "Value_001";
const StringRef output_id = "Value";
switch (op) {
case NODE_INTEGER_MATH_ADD: {
params.set_input(first_input_id,
params.get_output<int>(output_id) - params.get_input<int>(second_input_id));
break;
}
case NODE_INTEGER_MATH_SUBTRACT: {
params.set_input(first_input_id,
params.get_output<int>(output_id) + params.get_input<int>(second_input_id));
break;
}
case NODE_INTEGER_MATH_MULTIPLY: {
params.set_input(first_input_id,
math::safe_divide(params.get_output<int>(output_id),
params.get_input<int>(second_input_id)));
break;
}
case NODE_INTEGER_MATH_DIVIDE: {
params.set_input(first_input_id,
params.get_output<int>(output_id) * params.get_input<int>(second_input_id));
break;
}
default: {
break;
}
}
}
static void node_rna(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_node_enum(srna,
"operation",
"Operation",
"",
rna_enum_node_integer_math_items,
NOD_inline_enum_accessors(custom1),
NODE_INTEGER_MATH_ADD);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_NODETREE);
RNA_def_property_update_runtime(prop, rna_Node_socket_update);
}
static void node_register()
{
static blender::bke::bNodeType ntype;
fn_node_type_base(&ntype, "FunctionNodeIntegerMath", FN_NODE_INTEGER_MATH);
ntype.ui_name = "Integer Math";
ntype.enum_name_legacy = "INTEGER_MATH";
ntype.nclass = NODE_CLASS_CONVERTER;
ntype.declare = node_declare;
ntype.labelfunc = node_label;
ntype.updatefunc = node_update;
ntype.build_multi_function = node_build_multi_function;
ntype.draw_buttons = node_layout;
ntype.gather_link_search_ops = node_gather_link_searches;
ntype.eval_elem = node_eval_elem;
ntype.eval_inverse_elem = node_eval_inverse_elem;
ntype.eval_inverse = node_eval_inverse;
blender::bke::node_register_type(ntype);
node_rna(ntype.rna_ext.srna);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_integer_math_cc