WIP: Add a math expression geometry node #115112
|
@ -595,6 +595,7 @@ class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
|
|||
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeMath")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeMix")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeMathExpression")
|
||||
node_add_menu.draw_assets_for_catalog(layout, "Utilities/Math")
|
||||
|
||||
|
||||
|
|
|
@ -1316,6 +1316,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
|
|||
#define GEO_NODE_INPUT_EDGE_SMOOTH 2115
|
||||
#define GEO_NODE_SPLIT_TO_INSTANCES 2116
|
||||
#define GEO_NODE_INPUT_NAMED_LAYER_SELECTION 2117
|
||||
#define GEO_NODE_MATH_EXPRESSION 0x3A84977D // TODO: this
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -1892,6 +1892,12 @@ typedef struct NodeShaderMix {
|
|||
char _pad[3];
|
||||
} NodeShaderMix;
|
||||
|
||||
typedef struct NodeGeometryMathExpression {
|
||||
char variables[1024]; // TODO: maybe change these this to work like NodeInputString
|
||||
char expression[1024];
|
||||
uint32_t output_type;
|
||||
} NodeGeometryMathExpression;
|
||||
|
||||
/* script node mode */
|
||||
enum {
|
||||
NODE_SCRIPT_INTERNAL = 0,
|
||||
|
@ -2760,3 +2766,8 @@ typedef enum NodeCombSepColorMode {
|
|||
NODE_COMBSEP_COLOR_HSV = 1,
|
||||
NODE_COMBSEP_COLOR_HSL = 2,
|
||||
} NodeCombSepColorMode;
|
||||
|
||||
typedef enum GeometryNodeMathExpressionOutput {
|
||||
GEO_NODE_MATH_EXPRESSION_OUTPUT_FLOAT = 0,
|
||||
GEO_NODE_MATH_EXPRESSION_OUTPUT_VECTOR = 1,
|
||||
} GeometryNodeMathExpressionOutput;
|
||||
|
|
|
@ -9327,6 +9327,42 @@ static void def_geo_string_to_curves(StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_geo_math_expression(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem rna_node_geometry_math_expression_output[] = {
|
||||
{GEO_NODE_MATH_EXPRESSION_OUTPUT_FLOAT,
|
||||
"OUTPUT_FLOAT",
|
||||
0,
|
||||
"Float",
|
||||
"Float"},
|
||||
{GEO_NODE_MATH_EXPRESSION_OUTPUT_VECTOR,
|
||||
"OUTPUT_VECTOR",
|
||||
0,
|
||||
"Vector",
|
||||
"Vector"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryMathExpression", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "variables", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Input Variables", "The input variables");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "output_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "output_type");
|
||||
RNA_def_property_enum_items(prop, rna_node_geometry_math_expression_output);
|
||||
RNA_def_property_enum_default(prop, GEO_NODE_MATH_EXPRESSION_OUTPUT_FLOAT);
|
||||
RNA_def_property_ui_text(prop, "Output Type", "Output type of the node");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "expression", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Expression", "The math Expression");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
|
|
@ -370,6 +370,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",Ins
|
|||
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
|
||||
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
|
||||
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
|
||||
DefNode(GeometryNode, GEO_NODE_MATH_EXPRESSION, def_geo_math_expression, "MATH_EXPRESSION", MathExpression, "Math Expression", "Evaluate a math expression")
|
||||
DefNode(GeometryNode, GEO_NODE_MEAN_FILTER_SDF_VOLUME, 0, "MEAN_FILTER_SDF_VOLUME", MeanFilterSDFVolume, "Mean Filter SDF Volume", "Smooth the surface of an SDF volume by applying a mean filter")
|
||||
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
|
||||
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, 0, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
|
||||
|
|
|
@ -119,6 +119,10 @@ set(SRC
|
|||
nodes/node_geo_join_geometry.cc
|
||||
nodes/node_geo_material_replace.cc
|
||||
nodes/node_geo_material_selection.cc
|
||||
nodes/node_geo_math_expression/node_geo_math_expression.cc
|
||||
nodes/node_geo_math_expression/evaluation_context.cc
|
||||
nodes/node_geo_math_expression/expression.cc
|
||||
nodes/node_geo_math_expression/parser.cc
|
||||
nodes/node_geo_mean_filter_sdf_volume.cc
|
||||
nodes/node_geo_merge_by_distance.cc
|
||||
nodes/node_geo_mesh_face_group_boundaries.cc
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include "evaluation_context.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
EvaluationContext::EvaluationContext(const std::function<fn::GField(std::string_view)> variable_cb)
|
||||
: variable_cb(std::move(variable_cb))
|
||||
{
|
||||
}
|
||||
|
||||
fn::GField EvaluationContext::get_variable(std::string_view name)
|
||||
{
|
||||
return variable_cb(name);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "FN_field.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
class EvaluationContext {
|
||||
const std::function<fn::GField(std::string_view)> variable_cb;
|
||||
|
||||
public:
|
||||
EvaluationContext(const std::function<fn::GField(std::string_view)> variable_cb);
|
||||
|
||||
fn::GField get_variable(std::string_view name);
|
||||
};
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,567 @@
|
|||
#include "expression.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
template<typename... T, typename... Args> bool args_are(Args... args)
|
||||
{
|
||||
return (args.cpp_type().template is<T>() && ...);
|
||||
}
|
||||
|
||||
static fn::GField fl_to_fl(fn::GField a, NodeMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl_to_fl(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI1_SO<float, float>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(*fn, {std::move(a)}));
|
||||
}
|
||||
|
||||
static fn::GField fl_fl_to_fl(fn::GField a, fn::GField b, NodeMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl_fl_to_fl(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI2_SO<float, float, float>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(*fn, {std::move(a), std::move(b)}));
|
||||
}
|
||||
|
||||
static fn::GField fl_fl_fl_to_fl(fn::GField a, fn::GField b, fn::GField c, NodeMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl_fl_fl_to_fl(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI3_SO<float, float, float, float>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float>(
|
||||
fn::FieldOperation::Create(*fn, {std::move(a), std::move(b), std::move(c)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_to_fl3(fn::GField a, NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_to_fl3(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI1_SO<float3, float3>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float3>(fn::FieldOperation::Create(*fn, {std::move(a)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_fl3_to_fl3(fn::GField a, fn::GField b, NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_fl3_to_fl3(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI2_SO<float3, float3, float3>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float3>(fn::FieldOperation::Create(*fn, {std::move(a), std::move(b)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_fl3_fl3_to_fl3(fn::GField a,
|
||||
fn::GField b,
|
||||
fn::GField c,
|
||||
NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI3_SO<float3, float3, float3, float3>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float3>(
|
||||
fn::FieldOperation::Create(*fn, {std::move(a), std::move(b), std::move(c)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_fl3_fl_to_fl3(fn::GField a,
|
||||
fn::GField b,
|
||||
fn::GField c,
|
||||
NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_fl3_fl_to_fl3(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI3_SO<float3, float3, float, float3>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float3>(
|
||||
fn::FieldOperation::Create(*fn, {std::move(a), std::move(b), std::move(c)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_fl3_to_fl(fn::GField a, fn::GField b, NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_fl3_to_fl(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI2_SO<float3, float3, float>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(*fn, {std::move(a), std::move(b)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_fl_to_fl3(fn::GField a, fn::GField b, NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_fl_to_fl3(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI2_SO<float3, float, float3>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float3>(fn::FieldOperation::Create(*fn, {std::move(a), std::move(b)}));
|
||||
}
|
||||
|
||||
static fn::GField fl3_to_fl(fn::GField a, NodeVectorMathOperation op)
|
||||
{
|
||||
const mf::MultiFunction *fn = nullptr;
|
||||
|
||||
try_dispatch_float_math_fl3_to_fl(
|
||||
op, [&fn](auto devi_fn, auto function, const blender::nodes::FloatMathOperationInfo &info) {
|
||||
static auto _fn = mf::build::SI1_SO<float3, float>(
|
||||
info.title_case_name.c_str(), function, devi_fn);
|
||||
fn = &_fn;
|
||||
});
|
||||
|
||||
BLI_assert(fn != nullptr);
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(*fn, {std::move(a)}));
|
||||
}
|
||||
|
||||
fn::GField CallExpression::compile(EvaluationContext &ctx)
|
||||
{
|
||||
// At this point the number of arguments has already been validated by the parser.
|
||||
|
||||
// Custom functions.
|
||||
switch (name) {
|
||||
case FunctionName::LERP:
|
||||
return lerp(ctx, args[0].get(), args[1].get(), args[2].get());
|
||||
case FunctionName::VEC:
|
||||
return vec(ctx, args[0].get(), args[1].get(), args[2].get());
|
||||
case FunctionName::X:
|
||||
return x(ctx, args[0].get());
|
||||
case FunctionName::Y:
|
||||
return y(ctx, args[0].get());
|
||||
case FunctionName::Z:
|
||||
return z(ctx, args[0].get());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// The rest is for the built-in math functions.
|
||||
Vector<fn::GField> _args;
|
||||
|
||||
for (auto &arg : args) {
|
||||
_args.append(arg->compile(ctx));
|
||||
}
|
||||
|
||||
auto error = EvaluationError{this, "invalid arguments"};
|
||||
|
||||
// This code was generated with a JSON description of the existing functions.
|
||||
if (_args.size() == 1) {
|
||||
if (args_are<float>(_args[0])) {
|
||||
switch (name) {
|
||||
case FunctionName::EXPONENT:
|
||||
return fl_to_fl(_args[0], NODE_MATH_EXPONENT);
|
||||
case FunctionName::SQRT:
|
||||
return fl_to_fl(_args[0], NODE_MATH_SQRT);
|
||||
case FunctionName::INV_SQRT:
|
||||
return fl_to_fl(_args[0], NODE_MATH_INV_SQRT);
|
||||
case FunctionName::ABSOLUTE:
|
||||
return fl_to_fl(_args[0], NODE_MATH_ABSOLUTE);
|
||||
case FunctionName::RADIANS:
|
||||
return fl_to_fl(_args[0], NODE_MATH_RADIANS);
|
||||
case FunctionName::DEGREES:
|
||||
return fl_to_fl(_args[0], NODE_MATH_DEGREES);
|
||||
case FunctionName::SIGN:
|
||||
return fl_to_fl(_args[0], NODE_MATH_SIGN);
|
||||
case FunctionName::ROUND:
|
||||
return fl_to_fl(_args[0], NODE_MATH_ROUND);
|
||||
case FunctionName::FLOOR:
|
||||
return fl_to_fl(_args[0], NODE_MATH_FLOOR);
|
||||
case FunctionName::CEIL:
|
||||
return fl_to_fl(_args[0], NODE_MATH_CEIL);
|
||||
case FunctionName::FRACTION:
|
||||
return fl_to_fl(_args[0], NODE_MATH_FRACTION);
|
||||
case FunctionName::TRUNC:
|
||||
return fl_to_fl(_args[0], NODE_MATH_TRUNC);
|
||||
case FunctionName::SINE:
|
||||
return fl_to_fl(_args[0], NODE_MATH_SINE);
|
||||
case FunctionName::COSINE:
|
||||
return fl_to_fl(_args[0], NODE_MATH_COSINE);
|
||||
case FunctionName::TANGENT:
|
||||
return fl_to_fl(_args[0], NODE_MATH_TANGENT);
|
||||
case FunctionName::SINH:
|
||||
return fl_to_fl(_args[0], NODE_MATH_SINH);
|
||||
case FunctionName::COSH:
|
||||
return fl_to_fl(_args[0], NODE_MATH_COSH);
|
||||
case FunctionName::TANH:
|
||||
return fl_to_fl(_args[0], NODE_MATH_TANH);
|
||||
case FunctionName::ARCSINE:
|
||||
return fl_to_fl(_args[0], NODE_MATH_ARCSINE);
|
||||
case FunctionName::ARCCOSINE:
|
||||
return fl_to_fl(_args[0], NODE_MATH_ARCCOSINE);
|
||||
case FunctionName::ARCTANGENT:
|
||||
return fl_to_fl(_args[0], NODE_MATH_ARCTANGENT);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (args_are<float3>(_args[0])) {
|
||||
switch (name) {
|
||||
case FunctionName::ABSOLUTE:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_ABSOLUTE);
|
||||
case FunctionName::FLOOR:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_FLOOR);
|
||||
case FunctionName::CEIL:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_CEIL);
|
||||
case FunctionName::FRACTION:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_FRACTION);
|
||||
case FunctionName::SINE:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_SINE);
|
||||
case FunctionName::COSINE:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_COSINE);
|
||||
case FunctionName::TANGENT:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_TANGENT);
|
||||
case FunctionName::LENGTH:
|
||||
return fl3_to_fl(_args[0], NODE_VECTOR_MATH_LENGTH);
|
||||
case FunctionName::NORMALIZE:
|
||||
return fl3_to_fl3(_args[0], NODE_VECTOR_MATH_NORMALIZE);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_args.size() == 2) {
|
||||
if (args_are<float, float>(_args[0], _args[1])) {
|
||||
switch (name) {
|
||||
case FunctionName::ADD:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_ADD);
|
||||
case FunctionName::SUBTRACT:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_SUBTRACT);
|
||||
case FunctionName::MULTIPLY:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_MULTIPLY);
|
||||
case FunctionName::DIVIDE:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_DIVIDE);
|
||||
case FunctionName::POWER:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_POWER);
|
||||
case FunctionName::LOGARITHM:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_LOGARITHM);
|
||||
case FunctionName::MINIMUM:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_MINIMUM);
|
||||
case FunctionName::MAXIMUM:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_MAXIMUM);
|
||||
case FunctionName::LESS_THAN:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_LESS_THAN);
|
||||
case FunctionName::GREATER_THAN:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_GREATER_THAN);
|
||||
case FunctionName::MODULO:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_MODULO);
|
||||
case FunctionName::FLOORED_MODULO:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_FLOORED_MODULO);
|
||||
case FunctionName::SNAP:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_SNAP);
|
||||
case FunctionName::ARCTAN2:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_ARCTAN2);
|
||||
case FunctionName::PINGPONG:
|
||||
return fl_fl_to_fl(_args[0], _args[1], NODE_MATH_PINGPONG);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (args_are<float3, float3>(_args[0], _args[1])) {
|
||||
switch (name) {
|
||||
case FunctionName::ADD:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_ADD);
|
||||
case FunctionName::SUBTRACT:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_SUBTRACT);
|
||||
case FunctionName::MULTIPLY:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_MULTIPLY);
|
||||
case FunctionName::DIVIDE:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_DIVIDE);
|
||||
case FunctionName::MINIMUM:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_MINIMUM);
|
||||
case FunctionName::MAXIMUM:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_MAXIMUM);
|
||||
case FunctionName::MODULO:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_MODULO);
|
||||
case FunctionName::SNAP:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_SNAP);
|
||||
case FunctionName::CROSS_PRODUCT:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_CROSS_PRODUCT);
|
||||
case FunctionName::PROJECT:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_PROJECT);
|
||||
case FunctionName::REFLECT:
|
||||
return fl3_fl3_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_REFLECT);
|
||||
case FunctionName::DOT_PRODUCT:
|
||||
return fl3_fl3_to_fl(_args[0], _args[1], NODE_VECTOR_MATH_DOT_PRODUCT);
|
||||
case FunctionName::DISTANCE:
|
||||
return fl3_fl3_to_fl(_args[0], _args[1], NODE_VECTOR_MATH_DISTANCE);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (args_are<float3, float>(_args[0], _args[1])) {
|
||||
switch (name) {
|
||||
case FunctionName::SCALE:
|
||||
return fl3_fl_to_fl3(_args[0], _args[1], NODE_VECTOR_MATH_SCALE);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_args.size() == 3) {
|
||||
if (args_are<float, float, float>(_args[0], _args[1], _args[2])) {
|
||||
switch (name) {
|
||||
case FunctionName::MULTIPLY_ADD:
|
||||
return fl_fl_fl_to_fl(_args[0], _args[1], _args[2], NODE_MATH_MULTIPLY_ADD);
|
||||
case FunctionName::COMPARE:
|
||||
return fl_fl_fl_to_fl(_args[0], _args[1], _args[2], NODE_MATH_COMPARE);
|
||||
case FunctionName::SMOOTH_MIN:
|
||||
return fl_fl_fl_to_fl(_args[0], _args[1], _args[2], NODE_MATH_SMOOTH_MIN);
|
||||
case FunctionName::SMOOTH_MAX:
|
||||
return fl_fl_fl_to_fl(_args[0], _args[1], _args[2], NODE_MATH_SMOOTH_MAX);
|
||||
case FunctionName::WRAP:
|
||||
return fl_fl_fl_to_fl(_args[0], _args[1], _args[2], NODE_MATH_WRAP);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (args_are<float3, float3, float3>(_args[0], _args[1], _args[2])) {
|
||||
switch (name) {
|
||||
case FunctionName::MULTIPLY_ADD:
|
||||
return fl3_fl3_fl3_to_fl3(_args[0], _args[1], _args[2], NODE_VECTOR_MATH_MULTIPLY_ADD);
|
||||
case FunctionName::WRAP:
|
||||
return fl3_fl3_fl3_to_fl3(_args[0], _args[1], _args[2], NODE_VECTOR_MATH_WRAP);
|
||||
case FunctionName::FACEFORWARD:
|
||||
return fl3_fl3_fl3_to_fl3(_args[0], _args[1], _args[2], NODE_VECTOR_MATH_FACEFORWARD);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (args_are<float3, float3, float>(_args[0], _args[1], _args[2])) {
|
||||
switch (name) {
|
||||
case FunctionName::REFRACT:
|
||||
return fl3_fl3_fl_to_fl3(_args[0], _args[1], _args[2], NODE_VECTOR_MATH_REFRACT);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
fn::GField Expression::add(EvaluationContext &ctx, Expression *left, Expression *right)
|
||||
{
|
||||
fn::GField _left = left->compile(ctx);
|
||||
fn::GField _right = right->compile(ctx);
|
||||
|
||||
if (args_are<float, float>(_left, _right)) {
|
||||
return fl_fl_to_fl(_left, _right, NODE_MATH_ADD);
|
||||
}
|
||||
|
||||
if (args_are<float3, float3>(_left, _right)) {
|
||||
return fl3_fl3_to_fl3(_left, _right, NODE_VECTOR_MATH_ADD);
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::sub(EvaluationContext &ctx, Expression *left, Expression *right)
|
||||
{
|
||||
fn::GField _left = left->compile(ctx);
|
||||
fn::GField _right = right->compile(ctx);
|
||||
|
||||
if (args_are<float, float>(_left, _right)) {
|
||||
return fl_fl_to_fl(_left, _right, NODE_MATH_SUBTRACT);
|
||||
}
|
||||
|
||||
if (args_are<float3, float3>(_left, _right)) {
|
||||
return fl3_fl3_to_fl3(_left, _right, NODE_VECTOR_MATH_SUBTRACT);
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::mul(EvaluationContext &ctx, Expression *left, Expression *right)
|
||||
{
|
||||
fn::GField _left = left->compile(ctx);
|
||||
fn::GField _right = right->compile(ctx);
|
||||
|
||||
if (args_are<float, float>(_left, _right)) {
|
||||
return fl_fl_to_fl(_left, _right, NODE_MATH_MULTIPLY);
|
||||
}
|
||||
|
||||
if (args_are<float3, float>(_left, _right)) {
|
||||
return fl3_fl_to_fl3(_left, _right, NODE_VECTOR_MATH_SCALE);
|
||||
}
|
||||
|
||||
if (args_are<float, float3>(_left, _right)) {
|
||||
return fl3_fl_to_fl3(_right, _left, NODE_VECTOR_MATH_SCALE);
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::div(EvaluationContext &ctx, Expression *left, Expression *right)
|
||||
{
|
||||
fn::GField _left = left->compile(ctx);
|
||||
fn::GField _right = right->compile(ctx);
|
||||
|
||||
if (args_are<float, float>(_left, _right)) {
|
||||
return fl_fl_to_fl(_left, _right, NODE_MATH_DIVIDE);
|
||||
}
|
||||
|
||||
if (args_are<float3, float>(_left, _right)) {
|
||||
auto one_div_right = fl_fl_to_fl(constant(1.0f), _right, NODE_MATH_DIVIDE);
|
||||
return fl3_fl_to_fl3(_left, one_div_right, NODE_VECTOR_MATH_SCALE);
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::negate(EvaluationContext &ctx, Expression *x)
|
||||
{
|
||||
fn::GField _x = x->compile(ctx);
|
||||
|
||||
if (args_are<float>(_x)) {
|
||||
return fl_fl_to_fl(_x, constant(-1.0f), NODE_MATH_MULTIPLY);
|
||||
}
|
||||
|
||||
if (args_are<float3>(_x)) {
|
||||
return fl3_fl_to_fl3(_x, constant(-1.0f), NODE_VECTOR_MATH_SCALE);
|
||||
}
|
||||
|
||||
throw "invalid operand";
|
||||
}
|
||||
|
||||
fn::GField Expression::lerp(EvaluationContext &ctx, Expression *a, Expression *b, Expression *t)
|
||||
{
|
||||
// (b - a) * t + a
|
||||
fn::GField _a = a->compile(ctx);
|
||||
fn::GField _b = b->compile(ctx);
|
||||
fn::GField _t = t->compile(ctx);
|
||||
|
||||
if (args_are<float, float, float>(_a, _b, _t)) {
|
||||
auto b_sub_a = fl_fl_to_fl(_b, _a, NODE_MATH_SUBTRACT);
|
||||
auto mul_t = fl_fl_to_fl(b_sub_a, _t, NODE_MATH_MULTIPLY);
|
||||
return fl_fl_to_fl(mul_t, _a, NODE_MATH_ADD);
|
||||
}
|
||||
|
||||
if (args_are<float3, float3, float>(_a, _b, _t)) {
|
||||
auto b_sub_a = fl3_fl3_to_fl3(_b, _a, NODE_VECTOR_MATH_SUBTRACT);
|
||||
auto mul_t = fl3_fl_to_fl3(b_sub_a, _t, NODE_VECTOR_MATH_SCALE);
|
||||
return fl3_fl3_to_fl3(mul_t, _a, NODE_VECTOR_MATH_ADD);
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::vec(EvaluationContext &ctx, Expression *x, Expression *y, Expression *z)
|
||||
{
|
||||
fn::GField _x = x->compile(ctx);
|
||||
fn::GField _y = y->compile(ctx);
|
||||
fn::GField _z = z->compile(ctx);
|
||||
|
||||
if (args_are<float, float, float>(_x, _y, _z)) {
|
||||
static auto fn = mf::build::SI3_SO<float, float, float, float3>("vec",
|
||||
[](float x, float y, float z) {
|
||||
return float3{x, y, z};
|
||||
});
|
||||
|
||||
return fn::Field<float3>(
|
||||
fn::FieldOperation::Create(fn, {std::move(_x), std::move(_y), std::move(_z)}));
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::x(EvaluationContext &ctx, Expression *v)
|
||||
{
|
||||
auto _v = v->compile(ctx);
|
||||
|
||||
if (args_are<float3>(_v)) {
|
||||
static auto fn = mf::build::SI1_SO<float3, float>("x", [](float3 v) { return v.x; });
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(fn, {std::move(_v)}));
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::y(EvaluationContext &ctx, Expression *v)
|
||||
{
|
||||
auto _v = v->compile(ctx);
|
||||
|
||||
if (args_are<float3>(_v)) {
|
||||
static auto fn = mf::build::SI1_SO<float3, float>("y", [](float3 v) { return v.y; });
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(fn, {std::move(_v)}));
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
fn::GField Expression::z(EvaluationContext &ctx, Expression *v)
|
||||
{
|
||||
auto _v = v->compile(ctx);
|
||||
|
||||
if (args_are<float3>(_v)) {
|
||||
static auto fn = mf::build::SI1_SO<float3, float>("z", [](float3 v) { return v.z; });
|
||||
|
||||
return fn::Field<float>(fn::FieldOperation::Create(fn, {std::move(_v)}));
|
||||
}
|
||||
|
||||
throw "invalid operands";
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,247 @@
|
|||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
#include "FN_field.hh"
|
||||
#include "NOD_math_functions.hh"
|
||||
|
||||
#include "evaluation_context.hh"
|
||||
#include "lexer.hh"
|
||||
#include "parser.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
struct EvaluationError {
|
||||
class Expression *expression;
|
||||
const char *message;
|
||||
};
|
||||
|
||||
class Expression {
|
||||
protected:
|
||||
Token token;
|
||||
|
||||
static int _type_id()
|
||||
{
|
||||
static int type_id = 0;
|
||||
return type_id++;
|
||||
}
|
||||
|
||||
template<typename T> static int _type_id()
|
||||
{
|
||||
static int type_id = _type_id();
|
||||
return type_id;
|
||||
}
|
||||
|
||||
virtual int type_id() const = 0;
|
||||
|
||||
static fn::GField constant(float f)
|
||||
{
|
||||
auto c = std::make_shared<mf::CustomMF_Constant<float>>(f);
|
||||
return fn::Field<float>(fn::FieldOperation::Create(std::move(c)));
|
||||
}
|
||||
|
||||
static fn::GField constant(float3 f3)
|
||||
{
|
||||
auto c = std::make_shared<mf::CustomMF_Constant<float3>>(f3);
|
||||
return fn::Field<float3>(fn::FieldOperation::Create(std::move(c)));
|
||||
}
|
||||
|
||||
public:
|
||||
Expression(Token token) : token(token) {}
|
||||
virtual ~Expression() = default;
|
||||
|
||||
const Token &get_token() const
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
virtual fn::GField compile(EvaluationContext &ctx) = 0;
|
||||
|
||||
template<typename T> T *as()
|
||||
{
|
||||
return type_id() == _type_id<T>() ? static_cast<T *>(this) : nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
static fn::GField add(EvaluationContext &ctx, Expression *left, Expression *right);
|
||||
static fn::GField sub(EvaluationContext &ctx, Expression *left, Expression *right);
|
||||
static fn::GField mul(EvaluationContext &ctx, Expression *left, Expression *right);
|
||||
static fn::GField div(EvaluationContext &ctx, Expression *left, Expression *right);
|
||||
static fn::GField negate(EvaluationContext &ctx, Expression *x);
|
||||
|
||||
static fn::GField lerp(EvaluationContext &ctx, Expression *a, Expression *b, Expression *t);
|
||||
static fn::GField vec(EvaluationContext &ctx, Expression *x, Expression *y, Expression *z);
|
||||
static fn::GField x(EvaluationContext &ctx, Expression *v);
|
||||
static fn::GField y(EvaluationContext &ctx, Expression *v);
|
||||
static fn::GField z(EvaluationContext &ctx, Expression *v);
|
||||
};
|
||||
|
||||
class NumberExpression : public Expression {
|
||||
float _value;
|
||||
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<NumberExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
NumberExpression(float value, Token token) : Expression(token), _value(value) {}
|
||||
|
||||
float value()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
fn::GField compile(EvaluationContext & /*ctx*/) override
|
||||
{
|
||||
return constant(_value);
|
||||
}
|
||||
};
|
||||
|
||||
class GroupExpression : public Expression {
|
||||
std::unique_ptr<Expression> expr;
|
||||
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<GroupExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
GroupExpression(std::unique_ptr<Expression> expr, Token token)
|
||||
: Expression(token), expr(std::move(expr))
|
||||
{
|
||||
}
|
||||
|
||||
fn::GField compile(EvaluationContext &ctx) override
|
||||
{
|
||||
return expr->compile(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
class VariableExpression : public Expression {
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<VariableExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
VariableExpression(Token token) : Expression(token) {}
|
||||
|
||||
fn::GField compile(EvaluationContext &ctx) override
|
||||
{
|
||||
fn::GField value;
|
||||
|
||||
try {
|
||||
value = ctx.get_variable(token.value);
|
||||
}
|
||||
catch (const char *err) {
|
||||
throw EvaluationError{this, err};
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
class CallExpression : public Expression {
|
||||
FunctionName name;
|
||||
const Vector<std::unique_ptr<Expression>> args;
|
||||
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<CallExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
CallExpression(FunctionName name, Vector<std::unique_ptr<Expression>> args, Token token)
|
||||
: Expression(token), name(name), args(std::move(args))
|
||||
{
|
||||
}
|
||||
|
||||
fn::GField compile(EvaluationContext &ctx) override;
|
||||
};
|
||||
|
||||
class UnaryExpression : public Expression {
|
||||
std::unique_ptr<Expression> expr;
|
||||
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<UnaryExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
UnaryExpression(std::unique_ptr<Expression> expr, Token token)
|
||||
: Expression(token), expr(std::move(expr))
|
||||
{
|
||||
}
|
||||
|
||||
fn::GField compile(EvaluationContext &ctx) override
|
||||
{
|
||||
if (token.kind != TokenKind::MINUS) {
|
||||
throw EvaluationError{this, "invalid unary operator"};
|
||||
}
|
||||
|
||||
try {
|
||||
auto number_expr = expr->as<NumberExpression>();
|
||||
|
||||
if (number_expr) {
|
||||
// optimize to constant
|
||||
return constant(-number_expr->value());
|
||||
}
|
||||
|
||||
return negate(ctx, expr.get());
|
||||
}
|
||||
catch (const char *err) {
|
||||
throw EvaluationError{this, err};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BinaryExpression : public Expression {
|
||||
std::unique_ptr<Expression> left;
|
||||
std::unique_ptr<Expression> right;
|
||||
|
||||
protected:
|
||||
int type_id() const override
|
||||
{
|
||||
return _type_id<BinaryExpression>();
|
||||
}
|
||||
|
||||
public:
|
||||
BinaryExpression(std::unique_ptr<Expression> left,
|
||||
std::unique_ptr<Expression> right,
|
||||
Token token)
|
||||
: Expression(token), left(std::move(left)), right(std::move(right))
|
||||
{
|
||||
}
|
||||
|
||||
fn::GField compile(EvaluationContext &ctx) override
|
||||
{
|
||||
try {
|
||||
switch (token.kind) {
|
||||
case TokenKind::PLUS:
|
||||
return add(ctx, left.get(), right.get());
|
||||
case TokenKind::MINUS:
|
||||
return sub(ctx, left.get(), right.get());
|
||||
case TokenKind::MUL:
|
||||
return mul(ctx, left.get(), right.get());
|
||||
case TokenKind::DIV:
|
||||
return div(ctx, left.get(), right.get());
|
||||
default:
|
||||
throw EvaluationError{this, "invalid binary operator"};
|
||||
}
|
||||
}
|
||||
catch (const char *err) {
|
||||
throw EvaluationError{this, err};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
enum class FunctionName {
|
||||
EXPONENT,
|
||||
SQRT,
|
||||
INV_SQRT,
|
||||
ABSOLUTE,
|
||||
RADIANS,
|
||||
DEGREES,
|
||||
SIGN,
|
||||
ROUND,
|
||||
FLOOR,
|
||||
CEIL,
|
||||
FRACTION,
|
||||
TRUNC,
|
||||
SINE,
|
||||
COSINE,
|
||||
TANGENT,
|
||||
SINH,
|
||||
COSH,
|
||||
TANH,
|
||||
ARCSINE,
|
||||
ARCCOSINE,
|
||||
ARCTANGENT,
|
||||
ADD,
|
||||
SUBTRACT,
|
||||
MULTIPLY,
|
||||
DIVIDE,
|
||||
POWER,
|
||||
LOGARITHM,
|
||||
MINIMUM,
|
||||
MAXIMUM,
|
||||
LESS_THAN,
|
||||
GREATER_THAN,
|
||||
MODULO,
|
||||
FLOORED_MODULO,
|
||||
SNAP,
|
||||
ARCTAN2,
|
||||
PINGPONG,
|
||||
MULTIPLY_ADD,
|
||||
COMPARE,
|
||||
SMOOTH_MIN,
|
||||
SMOOTH_MAX,
|
||||
WRAP,
|
||||
CROSS_PRODUCT,
|
||||
PROJECT,
|
||||
REFLECT,
|
||||
DOT_PRODUCT,
|
||||
DISTANCE,
|
||||
FACEFORWARD,
|
||||
REFRACT,
|
||||
LENGTH,
|
||||
SCALE,
|
||||
NORMALIZE,
|
||||
|
||||
// Custom functions.
|
||||
LERP,
|
||||
VEC,
|
||||
X,
|
||||
Y,
|
||||
Z
|
||||
};
|
||||
|
||||
struct FunctionDef {
|
||||
FunctionName name;
|
||||
int arg_count;
|
||||
};
|
||||
|
||||
class FunctionLookup {
|
||||
Map<std::string_view, FunctionDef> funcs;
|
||||
|
||||
public:
|
||||
FunctionLookup()
|
||||
{
|
||||
funcs.add("exponent", {FunctionName::EXPONENT, 1});
|
||||
funcs.add("sqrt", {FunctionName::SQRT, 1});
|
||||
funcs.add("inv_sqrt", {FunctionName::INV_SQRT, 1});
|
||||
funcs.add("absolute", {FunctionName::ABSOLUTE, 1});
|
||||
funcs.add("radians", {FunctionName::RADIANS, 1});
|
||||
funcs.add("degrees", {FunctionName::DEGREES, 1});
|
||||
funcs.add("sign", {FunctionName::SIGN, 1});
|
||||
funcs.add("round", {FunctionName::ROUND, 1});
|
||||
funcs.add("floor", {FunctionName::FLOOR, 1});
|
||||
funcs.add("ceil", {FunctionName::CEIL, 1});
|
||||
funcs.add("fraction", {FunctionName::FRACTION, 1});
|
||||
funcs.add("trunc", {FunctionName::TRUNC, 1});
|
||||
funcs.add("sine", {FunctionName::SINE, 1});
|
||||
funcs.add("cosine", {FunctionName::COSINE, 1});
|
||||
funcs.add("tangent", {FunctionName::TANGENT, 1});
|
||||
funcs.add("sinh", {FunctionName::SINH, 1});
|
||||
funcs.add("cosh", {FunctionName::COSH, 1});
|
||||
funcs.add("tanh", {FunctionName::TANH, 1});
|
||||
funcs.add("arcsine", {FunctionName::ARCSINE, 1});
|
||||
funcs.add("arccosine", {FunctionName::ARCCOSINE, 1});
|
||||
funcs.add("arctangent", {FunctionName::ARCTANGENT, 1});
|
||||
funcs.add("add", {FunctionName::ADD, 2});
|
||||
funcs.add("subtract", {FunctionName::SUBTRACT, 2});
|
||||
funcs.add("multiply", {FunctionName::MULTIPLY, 2});
|
||||
funcs.add("divide", {FunctionName::DIVIDE, 2});
|
||||
funcs.add("power", {FunctionName::POWER, 2});
|
||||
funcs.add("logarithm", {FunctionName::LOGARITHM, 2});
|
||||
funcs.add("minimum", {FunctionName::MINIMUM, 2});
|
||||
funcs.add("maximum", {FunctionName::MAXIMUM, 2});
|
||||
funcs.add("less_than", {FunctionName::LESS_THAN, 2});
|
||||
funcs.add("greater_than", {FunctionName::GREATER_THAN, 2});
|
||||
funcs.add("modulo", {FunctionName::MODULO, 2});
|
||||
funcs.add("floored_modulo", {FunctionName::FLOORED_MODULO, 2});
|
||||
funcs.add("snap", {FunctionName::SNAP, 2});
|
||||
funcs.add("arctan2", {FunctionName::ARCTAN2, 2});
|
||||
funcs.add("pingpong", {FunctionName::PINGPONG, 2});
|
||||
funcs.add("multiply_add", {FunctionName::MULTIPLY_ADD, 3});
|
||||
funcs.add("compare", {FunctionName::COMPARE, 3});
|
||||
funcs.add("smooth_min", {FunctionName::SMOOTH_MIN, 3});
|
||||
funcs.add("smooth_max", {FunctionName::SMOOTH_MAX, 3});
|
||||
funcs.add("wrap", {FunctionName::WRAP, 3});
|
||||
funcs.add("cross_product", {FunctionName::CROSS_PRODUCT, 2});
|
||||
funcs.add("project", {FunctionName::PROJECT, 2});
|
||||
funcs.add("reflect", {FunctionName::REFLECT, 2});
|
||||
funcs.add("dot_product", {FunctionName::DOT_PRODUCT, 2});
|
||||
funcs.add("distance", {FunctionName::DISTANCE, 2});
|
||||
funcs.add("faceforward", {FunctionName::FACEFORWARD, 3});
|
||||
funcs.add("refract", {FunctionName::REFRACT, 3});
|
||||
funcs.add("length", {FunctionName::LENGTH, 1});
|
||||
funcs.add("scale", {FunctionName::SCALE, 2});
|
||||
funcs.add("normalize", {FunctionName::NORMALIZE, 1});
|
||||
|
||||
// Custom functions.
|
||||
funcs.add("lerp", {FunctionName::LERP, 3});
|
||||
funcs.add("vec", {FunctionName::VEC, 3});
|
||||
funcs.add("x", {FunctionName::X, 1});
|
||||
funcs.add("y", {FunctionName::Y, 1});
|
||||
funcs.add("z", {FunctionName::Z, 1});
|
||||
}
|
||||
|
||||
std::optional<FunctionDef> lookup(std::string_view name) const
|
||||
{
|
||||
if (funcs.contains(name)) {
|
||||
return funcs.lookup(name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::optional<FunctionDef> lookup_function(std::string_view name)
|
||||
{
|
||||
static const FunctionLookup funcs{};
|
||||
return funcs.lookup(name);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,196 @@
|
|||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
enum class TokenKind { IDENT, NUMBER, LPAREN, RPAREN, DOT, COMMA, PLUS, MINUS, MUL, DIV, END };
|
||||
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
size_t index;
|
||||
std::string_view value;
|
||||
|
||||
const char *kind_str()
|
||||
{
|
||||
const char *names[] = {"IDENT",
|
||||
"NUMBER",
|
||||
"LPAREN",
|
||||
"RPAREN",
|
||||
"DOT",
|
||||
"COMMA",
|
||||
"PLUS",
|
||||
"MINUS",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"END"};
|
||||
return names[static_cast<size_t>(kind)];
|
||||
}
|
||||
};
|
||||
|
||||
struct LexerError {
|
||||
size_t index;
|
||||
char c;
|
||||
const char *message;
|
||||
|
||||
LexerError(size_t index, char c, const char *message) : index(index), c(c), message(message) {}
|
||||
};
|
||||
|
||||
class Lexer {
|
||||
std::string_view text;
|
||||
size_t index;
|
||||
size_t start;
|
||||
|
||||
public:
|
||||
void init(std::string_view text)
|
||||
{
|
||||
this->text = text;
|
||||
index = 0;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
Token next_token()
|
||||
{
|
||||
if (end()) {
|
||||
start = index;
|
||||
return make_token(TokenKind::END);
|
||||
}
|
||||
|
||||
start = index;
|
||||
char c = next();
|
||||
|
||||
if (isspace(c)) {
|
||||
return next_token();
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '(':
|
||||
return make_token(TokenKind::LPAREN);
|
||||
break;
|
||||
case ')':
|
||||
return make_token(TokenKind::RPAREN);
|
||||
break;
|
||||
case '.':
|
||||
if (!std::isdigit(peek())) {
|
||||
return make_token(TokenKind::DOT);
|
||||
}
|
||||
|
||||
break;
|
||||
case ',':
|
||||
return make_token(TokenKind::COMMA);
|
||||
break;
|
||||
case '+':
|
||||
return make_token(TokenKind::PLUS);
|
||||
break;
|
||||
case '-':
|
||||
return make_token(TokenKind::MINUS);
|
||||
break;
|
||||
case '*':
|
||||
return make_token(TokenKind::MUL);
|
||||
break;
|
||||
case '/':
|
||||
return make_token(TokenKind::DIV);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '_' || isalpha(c)) {
|
||||
return parse_identifier();
|
||||
}
|
||||
|
||||
if (c == '.' || isdigit(c)) {
|
||||
return parse_number(c);
|
||||
}
|
||||
|
||||
throw make_error("unexpected character");
|
||||
}
|
||||
|
||||
private:
|
||||
Token parse_number(char c)
|
||||
{
|
||||
if (c == '.') {
|
||||
if (end() || !isdigit(peek())) {
|
||||
throw make_error("expected digit");
|
||||
}
|
||||
|
||||
consume_number();
|
||||
}
|
||||
else {
|
||||
consume_number();
|
||||
|
||||
if (match('.')) {
|
||||
consume_number();
|
||||
}
|
||||
}
|
||||
|
||||
return make_token(TokenKind::NUMBER);
|
||||
}
|
||||
|
||||
void consume_number()
|
||||
{
|
||||
while (!end()) {
|
||||
char c = peek();
|
||||
|
||||
if (isdigit(c)) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Token parse_identifier()
|
||||
{
|
||||
while (!end()) {
|
||||
char c = peek();
|
||||
|
||||
if (c == '_' || isalnum(c)) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return make_token(TokenKind::IDENT);
|
||||
}
|
||||
|
||||
LexerError make_error(const char *message)
|
||||
{
|
||||
return LexerError(index - 1, text[index - 1], message);
|
||||
}
|
||||
|
||||
Token make_token(TokenKind kind)
|
||||
{
|
||||
return Token{kind, start, text.substr(start, index - start)};
|
||||
}
|
||||
|
||||
bool match(char c)
|
||||
{
|
||||
if (!end() && peek() == c) {
|
||||
next();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool end()
|
||||
{
|
||||
return index >= text.size();
|
||||
}
|
||||
|
||||
char next()
|
||||
{
|
||||
return text[index++];
|
||||
}
|
||||
|
||||
char peek()
|
||||
{
|
||||
return text[index];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,182 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "DNA_node_types.h"
|
||||
#include "UI_interface.hh"
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "evaluation_context.hh"
|
||||
#include "expression.hh"
|
||||
#include "lexer.hh"
|
||||
#include "parser.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
NODE_STORAGE_FUNCS(NodeGeometryMathExpression)
|
||||
|
||||
static void node_declare(blender::nodes::NodeDeclarationBuilder &b)
|
||||
{
|
||||
auto node = b.node_or_null();
|
||||
|
||||
if (node == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeGeometryMathExpression *storage = static_cast<NodeGeometryMathExpression *>(node->storage);
|
||||
|
||||
b.is_function_node();
|
||||
|
||||
Set<std::string_view> vars;
|
||||
|
||||
parse_var_names(storage->variables, [&b, &vars](std::string_view name) {
|
||||
if (vars.contains(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name[0] == 'f') {
|
||||
b.add_input<decl::Float>(name);
|
||||
}
|
||||
else if (name[0] == 'v') {
|
||||
b.add_input<decl::Vector>(name);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
vars.add(name);
|
||||
});
|
||||
|
||||
if (storage->output_type == GEO_NODE_MATH_EXPRESSION_OUTPUT_FLOAT) {
|
||||
b.add_output<decl::Float>("Value");
|
||||
}
|
||||
else if (storage->output_type == GEO_NODE_MATH_EXPRESSION_OUTPUT_VECTOR) {
|
||||
b.add_output<decl::Vector>("Value");
|
||||
}
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
SCOPED_TIMER(__func__);
|
||||
// Most of the stuff here only needs to be done once after the expression text has changed.
|
||||
// The list of operations should be cached somewhere to avoid reparsing every time this function
|
||||
// is called.
|
||||
|
||||
const NodeGeometryMathExpression &storage = node_storage(params.node());
|
||||
Map<std::string_view, fn::GField> vars;
|
||||
|
||||
parse_var_names(storage.variables, [&vars, ¶ms](std::string_view name) {
|
||||
if (vars.contains(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name[0] == 'f') {
|
||||
vars.add(name, params.extract_input<Field<float>>(name));
|
||||
}
|
||||
else if (name[0] == 'v') {
|
||||
vars.add(name, params.extract_input<Field<float3>>(name));
|
||||
}
|
||||
});
|
||||
|
||||
Parser parser;
|
||||
std::unique_ptr<Expression> expr;
|
||||
|
||||
try {
|
||||
expr = parser.parse(storage.expression);
|
||||
}
|
||||
catch (LexerError err) {
|
||||
params.error_message_add(
|
||||
NodeWarningType::Error,
|
||||
fmt::format("{}: col:{} '{}'", err.message, err.index + 1, err.c).c_str());
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
catch (ParserError err) {
|
||||
params.error_message_add(
|
||||
NodeWarningType::Error,
|
||||
fmt::format(TIP_("{}: col:{} '{}'"), err.message, err.token.index + 1, err.token.value)
|
||||
.c_str());
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
EvaluationContext ctx([¶ms, &vars](std::string_view name) -> fn::GField {
|
||||
if (!vars.contains(name)) {
|
||||
throw "variable does not exist";
|
||||
}
|
||||
|
||||
return vars.lookup(name);
|
||||
});
|
||||
|
||||
try {
|
||||
fn::GField field = expr->compile(ctx);
|
||||
|
||||
if (field.cpp_type().is<float>() &&
|
||||
storage.output_type != GEO_NODE_MATH_EXPRESSION_OUTPUT_FLOAT) {
|
||||
params.error_message_add(
|
||||
NodeWarningType::Error,
|
||||
TIP_("The result of the expression (Float) does not match the ouput type of the node"));
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (field.cpp_type().is<float3>() &&
|
||||
storage.output_type != GEO_NODE_MATH_EXPRESSION_OUTPUT_VECTOR) {
|
||||
params.error_message_add(
|
||||
NodeWarningType::Error,
|
||||
TIP_("The result of the expression (Vector) does not match the ouput type of the node"));
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
params.set_output("Value", field);
|
||||
}
|
||||
catch (EvaluationError err) {
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
fmt::format("{}: col:{} '{}'",
|
||||
err.message,
|
||||
err.expression->get_token().index + 1,
|
||||
err.expression->get_token().value)
|
||||
.c_str());
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*c*/, PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "variables", UI_ITEM_NONE, "Variables", ICON_NONE);
|
||||
uiItemR(layout, ptr, "output_type", UI_ITEM_NONE, "Output Type", ICON_NONE);
|
||||
uiItemR(layout, ptr, "expression", UI_ITEM_NONE, "Expression", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
{
|
||||
node->storage = MEM_callocN(sizeof(NodeGeometryMathExpression), __func__);
|
||||
NodeGeometryMathExpression &storage = node_storage(*node);
|
||||
storage.output_type = GEO_NODE_MATH_EXPRESSION_OUTPUT_VECTOR;
|
||||
strcpy(storage.variables, "v1, fa, fb, fc");
|
||||
strcpy(storage.expression, "vec(fa, fb, fc) + v1");
|
||||
}
|
||||
|
||||
inline void node_register()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_geo_math_expression_cc;
|
||||
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_MATH_EXPRESSION, "Math Expression", NODE_CLASS_CONVERTER);
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.initfunc = file_ns::node_init;
|
||||
ntype.geometry_node_execute = file_ns::node_geo_exec;
|
||||
ntype.draw_buttons = file_ns::node_layout;
|
||||
|
||||
node_type_storage(&ntype,
|
||||
"NodeGeometryMathExpression",
|
||||
node_free_standard_storage,
|
||||
node_copy_standard_storage);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,246 @@
|
|||
#include <cmath>
|
||||
|
||||
#include "expression.hh"
|
||||
#include "parser.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse(std::string_view text)
|
||||
{
|
||||
peeked = false;
|
||||
lexer.init(text);
|
||||
auto expr = parse_expression();
|
||||
|
||||
expect(TokenKind::END, "expected end of input");
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_expression()
|
||||
{
|
||||
return parse_addition();
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_addition()
|
||||
{
|
||||
auto expr = parse_multiplication();
|
||||
|
||||
Token token;
|
||||
while (match({TokenKind::PLUS, TokenKind::MINUS}, token)) {
|
||||
expr = std::make_unique<BinaryExpression>(std::move(expr), parse_multiplication(), token);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_multiplication()
|
||||
{
|
||||
auto expr = parse_unary();
|
||||
Token token;
|
||||
|
||||
while (match({TokenKind::MUL, TokenKind::DIV}, token)) {
|
||||
expr = std::make_unique<BinaryExpression>(std::move(expr), parse_unary(), token);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_unary()
|
||||
{
|
||||
Token token;
|
||||
|
||||
if (match({TokenKind::MINUS}, token)) {
|
||||
return std::make_unique<UnaryExpression>(parse_unary(), token);
|
||||
}
|
||||
|
||||
return parse_ufcs();
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_ufcs()
|
||||
{
|
||||
auto expr = parse_primary();
|
||||
Token token;
|
||||
|
||||
while (match({TokenKind::DOT}, token)) {
|
||||
Token ident = expect(TokenKind::IDENT, "expected identifier");
|
||||
Vector<std::unique_ptr<Expression>> args;
|
||||
args.append(std::move(expr));
|
||||
|
||||
if (match(TokenKind::LPAREN)) {
|
||||
expr = parse_call(ident, std::move(args));
|
||||
}
|
||||
else {
|
||||
expr = make_call_expression(std::move(args), ident);
|
||||
}
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_primary()
|
||||
{
|
||||
Token token = next();
|
||||
|
||||
if (token.kind == TokenKind::IDENT) {
|
||||
if (match(TokenKind::LPAREN)) {
|
||||
return parse_call(token);
|
||||
}
|
||||
|
||||
return parse_identifier(token);
|
||||
}
|
||||
|
||||
if (token.kind == TokenKind::NUMBER) {
|
||||
return std::make_unique<NumberExpression>(parse_number(token.value), token);
|
||||
}
|
||||
|
||||
if (token.kind == TokenKind::LPAREN) {
|
||||
auto expr = std::make_unique<GroupExpression>(parse_expression(), token);
|
||||
expect(TokenKind::RPAREN, "expected closing paren");
|
||||
return expr;
|
||||
}
|
||||
|
||||
if (token.kind == TokenKind::END) {
|
||||
throw ParserError(token, "unexpected end of expression");
|
||||
}
|
||||
else {
|
||||
throw ParserError(token, "unexpected token");
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_call(Token token,
|
||||
Vector<std::unique_ptr<Expression>> args)
|
||||
{
|
||||
if (!check(TokenKind::RPAREN)) {
|
||||
do {
|
||||
args.append(parse_expression());
|
||||
} while (match(TokenKind::COMMA));
|
||||
}
|
||||
|
||||
expect(TokenKind::RPAREN, "expected closing paren");
|
||||
return make_call_expression(std::move(args), token);
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::parse_identifier(Token token)
|
||||
{
|
||||
if (token.value == "pi") {
|
||||
return std::make_unique<NumberExpression>(M_PI, token);
|
||||
}
|
||||
else if (token.value == "tau") {
|
||||
return std::make_unique<NumberExpression>(M_PI * 2, token);
|
||||
}
|
||||
else if (token.value == "e") {
|
||||
return std::make_unique<NumberExpression>(M_E, token);
|
||||
}
|
||||
|
||||
return std::make_unique<VariableExpression>(token);
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::make_call_expression(Vector<std::unique_ptr<Expression>> args,
|
||||
Token token)
|
||||
{
|
||||
auto def = lookup_function(token.value);
|
||||
|
||||
if (!def.has_value()) {
|
||||
throw ParserError(token, "invalid function");
|
||||
}
|
||||
|
||||
if (args.size() != def->arg_count) {
|
||||
throw ParserError(token, "incorrect number of arguments");
|
||||
}
|
||||
|
||||
return std::make_unique<CallExpression>(def->name, std::move(args), token);
|
||||
}
|
||||
|
||||
float Parser::parse_number(std::string_view text)
|
||||
{
|
||||
double d;
|
||||
auto result = std::from_chars(text.data(), text.data() + text.size(), d);
|
||||
|
||||
if (result.ec != std::errc()) {
|
||||
// The number has already been parsed and is valid.
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
[[maybe_unused]] Token Parser::expect(TokenKind kind, const char *message)
|
||||
{
|
||||
if (peek().kind != kind) {
|
||||
throw ParserError(peek(), message);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
template<size_t N> bool Parser::match(const TokenKind (&kind)[N], Token &r_token)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (peek().kind == kind[i]) {
|
||||
r_token = next();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Parser::match(const TokenKind kind)
|
||||
{
|
||||
if (peek().kind == kind) {
|
||||
next();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Parser::check(const TokenKind kind)
|
||||
{
|
||||
if (peek().kind == kind) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Token Parser::next()
|
||||
{
|
||||
Token token = peek();
|
||||
peeked = false;
|
||||
return token;
|
||||
}
|
||||
|
||||
Token Parser::peek()
|
||||
{
|
||||
if (peeked) {
|
||||
return peeked_token;
|
||||
}
|
||||
|
||||
peeked_token = lexer.next_token();
|
||||
peeked = true;
|
||||
return peeked_token;
|
||||
}
|
||||
|
||||
void parse_var_names(std::string_view vars, std::function<void(std::string_view)> cb)
|
||||
{
|
||||
size_t start = vars.size();
|
||||
|
||||
for (size_t i = 0; i < vars.size(); i++) {
|
||||
char c = vars[i];
|
||||
|
||||
if (start == vars.size() && (c == '_' || isalpha(c))) {
|
||||
start = i;
|
||||
}
|
||||
else if (start != vars.size() && (c != '_' && !isalnum(c))) {
|
||||
cb(vars.substr(start, i - start));
|
||||
start = vars.size();
|
||||
}
|
||||
|
||||
if (start != vars.size() && i == vars.size() - 1) {
|
||||
cb(vars.substr(start, i + 1 - start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace blender::nodes::node_geo_math_expression_cc
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "functions.hh"
|
||||
#include "lexer.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_math_expression_cc {
|
||||
|
||||
class Expression;
|
||||
|
||||
struct ParserError {
|
||||
Token token;
|
||||
const char *message;
|
||||
|
||||
ParserError(Token token, const char *message) : token(token), message(message) {}
|
||||
};
|
||||
|
||||
class Parser {
|
||||
Lexer lexer;
|
||||
Token peeked_token;
|
||||
bool peeked;
|
||||
|
||||
public:
|
||||
std::unique_ptr<Expression> parse(std::string_view text);
|
||||
std::unique_ptr<Expression> parse_expression();
|
||||
std::unique_ptr<Expression> parse_addition();
|
||||
std::unique_ptr<Expression> parse_multiplication();
|
||||
std::unique_ptr<Expression> parse_unary();
|
||||
std::unique_ptr<Expression> parse_ufcs();
|
||||
std::unique_ptr<Expression> parse_primary();
|
||||
|
||||
std::unique_ptr<Expression> parse_call(
|
||||
Token token,
|
||||
Vector<std::unique_ptr<Expression>> args = Vector<std::unique_ptr<Expression>>());
|
||||
|
||||
std::unique_ptr<Expression> parse_identifier(Token token);
|
||||
|
||||
std::unique_ptr<Expression> make_call_expression(Vector<std::unique_ptr<Expression>> args,
|
||||
Token token);
|
||||
|
||||
float parse_number(std::string_view text);
|
||||
|
||||
[[maybe_unused]] Token expect(TokenKind kind, const char *message);
|
||||
template<size_t N> bool match(const TokenKind (&kind)[N], Token &r_token);
|
||||
bool match(const TokenKind kind);
|
||||
bool check(const TokenKind kind);
|
||||
Token next();
|
||||
Token peek();
|
||||
};
|
||||
|
||||
void parse_var_names(std::string_view vars, std::function<void(std::string_view)> cb);
|
||||
|
||||
} // namespace blender::nodes::node_geo_math_expression_cc
|
Loading…
Reference in New Issue