mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Math formula converter
Adds a math formula converter. It includes support for formula input parsing in the editor UI the cpp side will be added on the next PR Diffs= cb0f89f200 Math formula converter (#8952) Co-authored-by: hernan <hernan@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
||||
5153dac481f44db86b07528b35d31051b4118fff
|
||||
cb0f89f200fcf0a969fdc748c5240aeb17313b67
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "DataConverterFormula",
|
||||
"key": {
|
||||
"int": 536,
|
||||
"string": "dataconverterformula"
|
||||
},
|
||||
"extends": "data_bind/converters/data_converter.json"
|
||||
}
|
||||
29
dev/defs/data_bind/converters/formula/formula_token.json
Normal file
29
dev/defs/data_bind/converters/formula/formula_token.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "FormulaToken",
|
||||
"key": {
|
||||
"int": 537,
|
||||
"string": "formulatoken"
|
||||
},
|
||||
"properties": {
|
||||
"order": {
|
||||
"type": "FractionalIndex",
|
||||
"initialValue": "FractionalIndex.invalid",
|
||||
"key": {
|
||||
"int": 772,
|
||||
"string": "order"
|
||||
},
|
||||
"description": "Order of the token in the overall formula.",
|
||||
"runtime": false
|
||||
},
|
||||
"formulaId": {
|
||||
"type": "Id",
|
||||
"initialValue": "Core.missingId",
|
||||
"key": {
|
||||
"int": 773,
|
||||
"string": "formulaid"
|
||||
},
|
||||
"description": "Id of the formula this token belongs to.",
|
||||
"runtime": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "FormulaTokenArgumentSeparator",
|
||||
"key": {
|
||||
"int": 538,
|
||||
"string": "formulatokenargumentseparator"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token.json",
|
||||
"properties": {
|
||||
"functionId": {
|
||||
"type": "Id",
|
||||
"initialValue": "Core.missingId",
|
||||
"key": {
|
||||
"int": 774,
|
||||
"string": "functionid"
|
||||
},
|
||||
"description": "Id of the function this separator belongs to",
|
||||
"runtime": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "FormulaTokenFunction",
|
||||
"key": {
|
||||
"int": 542,
|
||||
"string": "formulatokenfunction"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token_parenthesis.json",
|
||||
"properties": {
|
||||
"functionType": {
|
||||
"type": "uint",
|
||||
"initialValue": "0",
|
||||
"key": {
|
||||
"int": 776,
|
||||
"string": "functiontype"
|
||||
},
|
||||
"description": "Type of function"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "FormulaTokenInput",
|
||||
"key": {
|
||||
"int": 545,
|
||||
"string": "formulatokeninput"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token.json"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "FormulaTokenOperation",
|
||||
"key": {
|
||||
"int": 541,
|
||||
"string": "formulatokenoperation"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token.json",
|
||||
"properties": {
|
||||
"operationType": {
|
||||
"type": "uint",
|
||||
"initialValue": "0",
|
||||
"key": {
|
||||
"int": 775,
|
||||
"string": "operationtype"
|
||||
},
|
||||
"description": "Operation token"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "FormulaTokenParenthesis",
|
||||
"key": {
|
||||
"int": 539,
|
||||
"string": "formulatokenparenthesis"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token.json"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "FormulaTokenParenthesisClose",
|
||||
"key": {
|
||||
"int": 540,
|
||||
"string": "formulatokenparenthesisclose"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token_parenthesis.json"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "FormulaTokenParenthesisOpen",
|
||||
"key": {
|
||||
"int": 544,
|
||||
"string": "formulatokenparenthesisopen"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token_parenthesis.json"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "FormulaTokenValue",
|
||||
"key": {
|
||||
"int": 543,
|
||||
"string": "formulatokenvalue"
|
||||
},
|
||||
"extends": "data_bind/converters/formula/formula_token.json",
|
||||
"properties": {
|
||||
"operationValue": {
|
||||
"type": "double",
|
||||
"initialValue": "1",
|
||||
"key": {
|
||||
"int": 777,
|
||||
"string": "operationvalue"
|
||||
},
|
||||
"description": "Number token",
|
||||
"bindable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
45
include/rive/data_bind/converters/data_converter_formula.hpp
Normal file
45
include/rive/data_bind/converters/data_converter_formula.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef _RIVE_DATA_CONVERTER_FORMULA_HPP_
|
||||
#define _RIVE_DATA_CONVERTER_FORMULA_HPP_
|
||||
#include "rive/generated/data_bind/converters/data_converter_formula_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
#include "rive/data_bind/data_bind.hpp"
|
||||
#include "rive/data_bind/data_values/data_value_number.hpp"
|
||||
#include <stdio.h>
|
||||
#include <unordered_map>
|
||||
namespace rive
|
||||
{
|
||||
|
||||
class DataConverterFormula : public DataConverterFormulaBase
|
||||
{
|
||||
public:
|
||||
~DataConverterFormula();
|
||||
DataType outputType() override { return DataType::number; };
|
||||
void addToken(FormulaToken*);
|
||||
void addOutputToken(FormulaToken*, int);
|
||||
void initialize();
|
||||
void isInstance(bool value) { m_isInstance = value; }
|
||||
|
||||
protected:
|
||||
DataValue* convert(DataValue* value, DataBind* dataBind) override;
|
||||
DataValue* reverseConvert(DataValue* value, DataBind* dataBind) override;
|
||||
DataValueNumber m_output;
|
||||
Core* clone() const override;
|
||||
void bindFromContext(DataContext* dataContext, DataBind* dataBind) override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
int getPrecedence(FormulaToken*);
|
||||
float getRandom(int);
|
||||
float applyOperation(float left, float right, int operationType);
|
||||
float applyFunction(std::vector<float>& stack,
|
||||
int functionTypeIndex,
|
||||
int totalArguments);
|
||||
std::vector<FormulaToken*> m_tokens;
|
||||
std::vector<FormulaToken*> m_outputQueue;
|
||||
std::vector<float> m_randoms;
|
||||
std::unordered_map<FormulaToken*, int> m_argumentsCount;
|
||||
bool m_isInstance = false;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
27
include/rive/data_bind/converters/formula/formula_token.hpp
Normal file
27
include/rive/data_bind/converters/formula/formula_token.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_base.hpp"
|
||||
#include "rive/data_bind/data_values/data_value.hpp"
|
||||
#include "rive/data_bind/data_context.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaToken : public FormulaTokenBase
|
||||
{
|
||||
public:
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
|
||||
virtual void bindFromContext(DataContext* dataContext, DataBind* dataBind);
|
||||
virtual void update();
|
||||
void markDirty();
|
||||
void addDataBind(DataBind* dataBind);
|
||||
void copy(const FormulaTokenBase& object);
|
||||
std::vector<DataBind*> dataBinds() const { return m_dataBinds; }
|
||||
|
||||
private:
|
||||
std::vector<DataBind*> m_dataBinds;
|
||||
DataBind* m_parentDataBind;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_ARGUMENT_SEPARATOR_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_ARGUMENT_SEPARATOR_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_argument_separator_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenArgumentSeparator : public FormulaTokenArgumentSeparatorBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_FUNCTION_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_FUNCTION_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_function_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenFunction : public FormulaTokenFunctionBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_INPUT_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_INPUT_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_input_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenInput : public FormulaTokenInputBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_OPERATION_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_OPERATION_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_operation_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenOperation : public FormulaTokenOperationBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesis : public FormulaTokenParenthesisBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_CLOSE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_CLOSE_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_close_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesisClose : public FormulaTokenParenthesisCloseBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_OPEN_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_OPEN_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_open_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesisOpen : public FormulaTokenParenthesisOpenBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_VALUE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_VALUE_HPP_
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_value_base.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenValue : public FormulaTokenValueBase
|
||||
{
|
||||
public:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
28
include/rive/function_type.hpp
Normal file
28
include/rive/function_type.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _RIVE_FUNCTION_TYPE_HPP_
|
||||
#define _RIVE_FUNCTION_TYPE_HPP_
|
||||
|
||||
namespace rive
|
||||
{
|
||||
enum class FunctionType : int
|
||||
{
|
||||
min = 0,
|
||||
max = 1,
|
||||
round = 2,
|
||||
ceil = 3,
|
||||
floor = 4,
|
||||
sqrt = 5,
|
||||
pow = 6,
|
||||
exp = 7,
|
||||
log = 8,
|
||||
cosine = 9,
|
||||
sine = 10,
|
||||
tangent = 11,
|
||||
acosine = 12,
|
||||
asine = 13,
|
||||
atangent = 14,
|
||||
atangent2 = 15,
|
||||
random = 16,
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -132,6 +132,7 @@
|
||||
#include "rive/data_bind/bindable_property_trigger.hpp"
|
||||
#include "rive/data_bind/converters/data_converter.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_boolean_negate.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_formula.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_group.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_group_item.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_interpolator.hpp"
|
||||
@@ -147,6 +148,15 @@
|
||||
#include "rive/data_bind/converters/data_converter_system_normalizer.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_to_string.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_trigger.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_argument_separator.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_function.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_input.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_operation.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_close.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_open.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_value.hpp"
|
||||
#include "rive/data_bind/data_bind.hpp"
|
||||
#include "rive/data_bind/data_bind_context.hpp"
|
||||
#include "rive/draw_rules.hpp"
|
||||
@@ -543,6 +553,8 @@ public:
|
||||
return new BindablePropertyBoolean();
|
||||
case DataBindBase::typeKey:
|
||||
return new DataBind();
|
||||
case DataConverterFormulaBase::typeKey:
|
||||
return new DataConverterFormula();
|
||||
case DataConverterOperationBase::typeKey:
|
||||
return new DataConverterOperation();
|
||||
case DataConverterOperationValueBase::typeKey:
|
||||
@@ -569,6 +581,24 @@ public:
|
||||
return new DataConverterTrigger();
|
||||
case DataConverterStringTrimBase::typeKey:
|
||||
return new DataConverterStringTrim();
|
||||
case FormulaTokenBase::typeKey:
|
||||
return new FormulaToken();
|
||||
case FormulaTokenArgumentSeparatorBase::typeKey:
|
||||
return new FormulaTokenArgumentSeparator();
|
||||
case FormulaTokenParenthesisBase::typeKey:
|
||||
return new FormulaTokenParenthesis();
|
||||
case FormulaTokenParenthesisCloseBase::typeKey:
|
||||
return new FormulaTokenParenthesisClose();
|
||||
case FormulaTokenOperationBase::typeKey:
|
||||
return new FormulaTokenOperation();
|
||||
case FormulaTokenFunctionBase::typeKey:
|
||||
return new FormulaTokenFunction();
|
||||
case FormulaTokenValueBase::typeKey:
|
||||
return new FormulaTokenValue();
|
||||
case FormulaTokenParenthesisOpenBase::typeKey:
|
||||
return new FormulaTokenParenthesisOpen();
|
||||
case FormulaTokenInputBase::typeKey:
|
||||
return new FormulaTokenInput();
|
||||
case DataConverterOperationViewModelBase::typeKey:
|
||||
return new DataConverterOperationViewModel();
|
||||
case DataConverterBooleanNegateBase::typeKey:
|
||||
@@ -1291,6 +1321,12 @@ public:
|
||||
case DataConverterStringTrimBase::trimTypePropertyKey:
|
||||
object->as<DataConverterStringTrimBase>()->trimType(value);
|
||||
break;
|
||||
case FormulaTokenOperationBase::operationTypePropertyKey:
|
||||
object->as<FormulaTokenOperationBase>()->operationType(value);
|
||||
break;
|
||||
case FormulaTokenFunctionBase::functionTypePropertyKey:
|
||||
object->as<FormulaTokenFunctionBase>()->functionType(value);
|
||||
break;
|
||||
case DataConverterToStringBase::flagsPropertyKey:
|
||||
object->as<DataConverterToStringBase>()->flags(value);
|
||||
break;
|
||||
@@ -1932,6 +1968,9 @@ public:
|
||||
case DataConverterInterpolatorBase::durationPropertyKey:
|
||||
object->as<DataConverterInterpolatorBase>()->duration(value);
|
||||
break;
|
||||
case FormulaTokenValueBase::operationValuePropertyKey:
|
||||
object->as<FormulaTokenValueBase>()->operationValue(value);
|
||||
break;
|
||||
case BindablePropertyNumberBase::propertyValuePropertyKey:
|
||||
object->as<BindablePropertyNumberBase>()->propertyValue(value);
|
||||
break;
|
||||
@@ -2558,6 +2597,10 @@ public:
|
||||
return object->as<DataConverterStringPadBase>()->padType();
|
||||
case DataConverterStringTrimBase::trimTypePropertyKey:
|
||||
return object->as<DataConverterStringTrimBase>()->trimType();
|
||||
case FormulaTokenOperationBase::operationTypePropertyKey:
|
||||
return object->as<FormulaTokenOperationBase>()->operationType();
|
||||
case FormulaTokenFunctionBase::functionTypePropertyKey:
|
||||
return object->as<FormulaTokenFunctionBase>()->functionType();
|
||||
case DataConverterToStringBase::flagsPropertyKey:
|
||||
return object->as<DataConverterToStringBase>()->flags();
|
||||
case DataConverterToStringBase::decimalsPropertyKey:
|
||||
@@ -3007,6 +3050,8 @@ public:
|
||||
return object->as<DataConverterRangeMapperBase>()->maxOutput();
|
||||
case DataConverterInterpolatorBase::durationPropertyKey:
|
||||
return object->as<DataConverterInterpolatorBase>()->duration();
|
||||
case FormulaTokenValueBase::operationValuePropertyKey:
|
||||
return object->as<FormulaTokenValueBase>()->operationValue();
|
||||
case BindablePropertyNumberBase::propertyValuePropertyKey:
|
||||
return object->as<BindablePropertyNumberBase>()
|
||||
->propertyValue();
|
||||
@@ -3304,6 +3349,8 @@ public:
|
||||
case DataConverterStringPadBase::lengthPropertyKey:
|
||||
case DataConverterStringPadBase::padTypePropertyKey:
|
||||
case DataConverterStringTrimBase::trimTypePropertyKey:
|
||||
case FormulaTokenOperationBase::operationTypePropertyKey:
|
||||
case FormulaTokenFunctionBase::functionTypePropertyKey:
|
||||
case DataConverterToStringBase::flagsPropertyKey:
|
||||
case DataConverterToStringBase::decimalsPropertyKey:
|
||||
case BindablePropertyEnumBase::propertyValuePropertyKey:
|
||||
@@ -3514,6 +3561,7 @@ public:
|
||||
case DataConverterRangeMapperBase::minOutputPropertyKey:
|
||||
case DataConverterRangeMapperBase::maxOutputPropertyKey:
|
||||
case DataConverterInterpolatorBase::durationPropertyKey:
|
||||
case FormulaTokenValueBase::operationValuePropertyKey:
|
||||
case BindablePropertyNumberBase::propertyValuePropertyKey:
|
||||
case NestedArtboardLeafBase::alignmentXPropertyKey:
|
||||
case NestedArtboardLeafBase::alignmentYPropertyKey:
|
||||
@@ -3980,6 +4028,10 @@ public:
|
||||
return object->is<DataConverterStringPadBase>();
|
||||
case DataConverterStringTrimBase::trimTypePropertyKey:
|
||||
return object->is<DataConverterStringTrimBase>();
|
||||
case FormulaTokenOperationBase::operationTypePropertyKey:
|
||||
return object->is<FormulaTokenOperationBase>();
|
||||
case FormulaTokenFunctionBase::functionTypePropertyKey:
|
||||
return object->is<FormulaTokenFunctionBase>();
|
||||
case DataConverterToStringBase::flagsPropertyKey:
|
||||
return object->is<DataConverterToStringBase>();
|
||||
case DataConverterToStringBase::decimalsPropertyKey:
|
||||
@@ -4392,6 +4444,8 @@ public:
|
||||
return object->is<DataConverterRangeMapperBase>();
|
||||
case DataConverterInterpolatorBase::durationPropertyKey:
|
||||
return object->is<DataConverterInterpolatorBase>();
|
||||
case FormulaTokenValueBase::operationValuePropertyKey:
|
||||
return object->is<FormulaTokenValueBase>();
|
||||
case BindablePropertyNumberBase::propertyValuePropertyKey:
|
||||
return object->is<BindablePropertyNumberBase>();
|
||||
case NestedArtboardLeafBase::alignmentXPropertyKey:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _RIVE_DATA_CONVERTER_FORMULA_BASE_HPP_
|
||||
#define _RIVE_DATA_CONVERTER_FORMULA_BASE_HPP_
|
||||
#include "rive/data_bind/converters/data_converter.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class DataConverterFormulaBase : public DataConverter
|
||||
{
|
||||
protected:
|
||||
typedef DataConverter Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 536;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case DataConverterFormulaBase::typeKey:
|
||||
case DataConverterBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_ARGUMENT_SEPARATOR_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_ARGUMENT_SEPARATOR_BASE_HPP_
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenArgumentSeparatorBase : public FormulaToken
|
||||
{
|
||||
protected:
|
||||
typedef FormulaToken Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 538;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenArgumentSeparatorBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_BASE_HPP_
|
||||
#include "rive/core.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenBase : public Core
|
||||
{
|
||||
protected:
|
||||
typedef Core Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 537;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const FormulaTokenBase& object) {}
|
||||
|
||||
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_FUNCTION_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_FUNCTION_BASE_HPP_
|
||||
#include "rive/core/field_types/core_uint_type.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenFunctionBase : public FormulaTokenParenthesis
|
||||
{
|
||||
protected:
|
||||
typedef FormulaTokenParenthesis Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 542;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenFunctionBase::typeKey:
|
||||
case FormulaTokenParenthesisBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
static const uint16_t functionTypePropertyKey = 776;
|
||||
|
||||
protected:
|
||||
uint32_t m_FunctionType = 0;
|
||||
|
||||
public:
|
||||
inline uint32_t functionType() const { return m_FunctionType; }
|
||||
void functionType(uint32_t value)
|
||||
{
|
||||
if (m_FunctionType == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_FunctionType = value;
|
||||
functionTypeChanged();
|
||||
}
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const FormulaTokenFunctionBase& object)
|
||||
{
|
||||
m_FunctionType = object.m_FunctionType;
|
||||
FormulaTokenParenthesis::copy(object);
|
||||
}
|
||||
|
||||
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
|
||||
{
|
||||
switch (propertyKey)
|
||||
{
|
||||
case functionTypePropertyKey:
|
||||
m_FunctionType = CoreUintType::deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
return FormulaTokenParenthesis::deserialize(propertyKey, reader);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void functionTypeChanged() {}
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_INPUT_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_INPUT_BASE_HPP_
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenInputBase : public FormulaToken
|
||||
{
|
||||
protected:
|
||||
typedef FormulaToken Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 545;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenInputBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_OPERATION_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_OPERATION_BASE_HPP_
|
||||
#include "rive/core/field_types/core_uint_type.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenOperationBase : public FormulaToken
|
||||
{
|
||||
protected:
|
||||
typedef FormulaToken Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 541;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenOperationBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
static const uint16_t operationTypePropertyKey = 775;
|
||||
|
||||
protected:
|
||||
uint32_t m_OperationType = 0;
|
||||
|
||||
public:
|
||||
inline uint32_t operationType() const { return m_OperationType; }
|
||||
void operationType(uint32_t value)
|
||||
{
|
||||
if (m_OperationType == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_OperationType = value;
|
||||
operationTypeChanged();
|
||||
}
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const FormulaTokenOperationBase& object)
|
||||
{
|
||||
m_OperationType = object.m_OperationType;
|
||||
FormulaToken::copy(object);
|
||||
}
|
||||
|
||||
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
|
||||
{
|
||||
switch (propertyKey)
|
||||
{
|
||||
case operationTypePropertyKey:
|
||||
m_OperationType = CoreUintType::deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
return FormulaToken::deserialize(propertyKey, reader);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void operationTypeChanged() {}
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_BASE_HPP_
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesisBase : public FormulaToken
|
||||
{
|
||||
protected:
|
||||
typedef FormulaToken Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 539;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenParenthesisBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_CLOSE_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_CLOSE_BASE_HPP_
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesisCloseBase : public FormulaTokenParenthesis
|
||||
{
|
||||
protected:
|
||||
typedef FormulaTokenParenthesis Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 540;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenParenthesisCloseBase::typeKey:
|
||||
case FormulaTokenParenthesisBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_PARENTHESIS_OPEN_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_PARENTHESIS_OPEN_BASE_HPP_
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenParenthesisOpenBase : public FormulaTokenParenthesis
|
||||
{
|
||||
protected:
|
||||
typedef FormulaTokenParenthesis Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 544;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenParenthesisOpenBase::typeKey:
|
||||
case FormulaTokenParenthesisBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
Core* clone() const override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef _RIVE_FORMULA_TOKEN_VALUE_BASE_HPP_
|
||||
#define _RIVE_FORMULA_TOKEN_VALUE_BASE_HPP_
|
||||
#include "rive/core/field_types/core_double_type.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class FormulaTokenValueBase : public FormulaToken
|
||||
{
|
||||
protected:
|
||||
typedef FormulaToken Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 543;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case FormulaTokenValueBase::typeKey:
|
||||
case FormulaTokenBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
static const uint16_t operationValuePropertyKey = 777;
|
||||
|
||||
protected:
|
||||
float m_OperationValue = 1.0f;
|
||||
|
||||
public:
|
||||
inline float operationValue() const { return m_OperationValue; }
|
||||
void operationValue(float value)
|
||||
{
|
||||
if (m_OperationValue == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_OperationValue = value;
|
||||
operationValueChanged();
|
||||
}
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const FormulaTokenValueBase& object)
|
||||
{
|
||||
m_OperationValue = object.m_OperationValue;
|
||||
FormulaToken::copy(object);
|
||||
}
|
||||
|
||||
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
|
||||
{
|
||||
switch (propertyKey)
|
||||
{
|
||||
case operationValuePropertyKey:
|
||||
m_OperationValue = CoreDoubleType::deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
return FormulaToken::deserialize(propertyKey, reader);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void operationValueChanged() {}
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
21
include/rive/importers/data_converter_formula_importer.hpp
Normal file
21
include/rive/importers/data_converter_formula_importer.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _RIVE_DATA_CONVERTER_FORMULA_IMPORTER_HPP_
|
||||
#define _RIVE_DATA_CONVERTER_FORMULA_IMPORTER_HPP_
|
||||
|
||||
#include "rive/importers/import_stack.hpp"
|
||||
|
||||
namespace rive
|
||||
{
|
||||
class Core;
|
||||
class DataConverterFormula;
|
||||
class DataConverterFormulaImporter : public ImportStackObject
|
||||
{
|
||||
private:
|
||||
DataConverterFormula* m_dataConverterFormula;
|
||||
|
||||
public:
|
||||
DataConverterFormulaImporter(DataConverterFormula* formula);
|
||||
DataConverterFormula* formula() { return m_dataConverterFormula; }
|
||||
StatusCode resolve() override;
|
||||
};
|
||||
} // namespace rive
|
||||
#endif
|
||||
500
src/data_bind/converters/data_converter_formula.cpp
Normal file
500
src/data_bind/converters/data_converter_formula.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include "rive/data_bind/converters/data_converter_formula.hpp"
|
||||
#include "rive/data_bind/data_values/data_value_number.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_value.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_argument_separator.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_input.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_operation.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_function.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_open.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_close.hpp"
|
||||
#include "rive/animation/arithmetic_operation.hpp"
|
||||
#include "rive/function_type.hpp"
|
||||
#include <cmath>
|
||||
|
||||
using namespace rive;
|
||||
|
||||
DataConverterFormula::~DataConverterFormula()
|
||||
{
|
||||
if (m_isInstance)
|
||||
{
|
||||
|
||||
for (auto& token : m_outputQueue)
|
||||
{
|
||||
delete token;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (auto& token : m_tokens)
|
||||
{
|
||||
delete token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DataConverterFormula::getPrecedence(FormulaToken* token)
|
||||
{
|
||||
if (token->is<FormulaTokenParenthesis>())
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
if (token->is<FormulaTokenFunction>())
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
if (token->is<FormulaTokenOperation>())
|
||||
{
|
||||
auto operationToken = token->as<FormulaTokenOperation>();
|
||||
auto operationType =
|
||||
(ArithmeticOperation)operationToken->operationType();
|
||||
if (operationType == ArithmeticOperation::add)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (operationType == ArithmeticOperation::subtract)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (operationType == ArithmeticOperation::multiply)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if (operationType == ArithmeticOperation::divide)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DataConverterFormula::initialize()
|
||||
{
|
||||
// convert the formula to Reverse Polish Notation using a version of
|
||||
// the Shunting yard algorithm
|
||||
// Some optimizations could be done here by precomputing constant parts
|
||||
// of the equation
|
||||
std::vector<FormulaToken*> operationsStack;
|
||||
int tokenIndex = 0;
|
||||
for (auto& token : m_tokens)
|
||||
{
|
||||
if (token->is<FormulaTokenValue>() || token->is<FormulaTokenInput>())
|
||||
{
|
||||
m_outputQueue.push_back(token);
|
||||
}
|
||||
else if (token->is<FormulaTokenOperation>())
|
||||
{
|
||||
while (operationsStack.size() > 0 &&
|
||||
!operationsStack.back()->is<FormulaTokenParenthesisOpen>() &&
|
||||
getPrecedence(operationsStack.back()) >=
|
||||
getPrecedence(token))
|
||||
{
|
||||
FormulaToken* higherToken = operationsStack.back();
|
||||
operationsStack.pop_back();
|
||||
m_outputQueue.push_back(higherToken);
|
||||
}
|
||||
operationsStack.push_back(token);
|
||||
}
|
||||
else if (token->is<FormulaTokenParenthesisOpen>() ||
|
||||
token->is<FormulaTokenFunction>())
|
||||
{
|
||||
|
||||
// Peeking into the next token to identify whether the current
|
||||
// function has no arguments. Is there a better way to reliably
|
||||
// count the number of arguments?
|
||||
FormulaToken* nextToken = tokenIndex == m_tokens.size() - 1
|
||||
? nullptr
|
||||
: m_tokens[tokenIndex + 1];
|
||||
m_argumentsCount[token] =
|
||||
(nextToken != nullptr &&
|
||||
nextToken->is<FormulaTokenParenthesisClose>())
|
||||
? 0
|
||||
: 1;
|
||||
operationsStack.push_back(token);
|
||||
}
|
||||
else if (token->is<FormulaTokenParenthesisClose>())
|
||||
{
|
||||
|
||||
while (
|
||||
operationsStack.size() > 0 &&
|
||||
(!operationsStack.back()->is<FormulaTokenParenthesisOpen>() &&
|
||||
!operationsStack.back()->is<FormulaTokenFunction>()))
|
||||
{
|
||||
auto higherToken = operationsStack.back();
|
||||
operationsStack.pop_back();
|
||||
m_outputQueue.push_back(higherToken);
|
||||
}
|
||||
if (operationsStack.size() == 0)
|
||||
{
|
||||
// mismatched parenthesis
|
||||
}
|
||||
else
|
||||
{
|
||||
auto openingToken = operationsStack.back();
|
||||
operationsStack.pop_back();
|
||||
if (openingToken->is<FormulaTokenFunction>())
|
||||
{
|
||||
m_outputQueue.push_back(openingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discarding open parenthesis
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token->is<FormulaTokenArgumentSeparator>())
|
||||
{
|
||||
int index = operationsStack.size() - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
auto operationTokenCandidate = operationsStack[index];
|
||||
auto argumentsCount =
|
||||
m_argumentsCount.find(operationTokenCandidate);
|
||||
if (argumentsCount != m_argumentsCount.end())
|
||||
{
|
||||
|
||||
int count = argumentsCount->second;
|
||||
m_argumentsCount[operationTokenCandidate] = count + 1;
|
||||
break;
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
}
|
||||
tokenIndex++;
|
||||
}
|
||||
while (operationsStack.size() > 0)
|
||||
{
|
||||
auto operation = operationsStack.back();
|
||||
operationsStack.pop_back();
|
||||
if (operation->is<FormulaTokenParenthesisOpen>())
|
||||
{
|
||||
// mismatched parenthesis
|
||||
}
|
||||
else
|
||||
{
|
||||
m_outputQueue.push_back(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float DataConverterFormula::applyOperation(float left,
|
||||
float right,
|
||||
int operationTypeInt)
|
||||
{
|
||||
auto operationType = (ArithmeticOperation)operationTypeInt;
|
||||
switch (operationType)
|
||||
{
|
||||
case ArithmeticOperation::add:
|
||||
return left + right;
|
||||
case ArithmeticOperation::subtract:
|
||||
return left - right;
|
||||
case ArithmeticOperation::multiply:
|
||||
return left * right;
|
||||
case ArithmeticOperation::divide:
|
||||
return left / right;
|
||||
case ArithmeticOperation::modulo:
|
||||
return fmodf(left, right);
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float DataConverterFormula::getRandom(int randomIndex)
|
||||
{
|
||||
while (m_randoms.size() <= randomIndex)
|
||||
{
|
||||
m_randoms.push_back((float)rand() / float(RAND_MAX));
|
||||
}
|
||||
return m_randoms[randomIndex];
|
||||
}
|
||||
|
||||
float DataConverterFormula::applyFunction(std::vector<float>& stack,
|
||||
int functionTypeIndex,
|
||||
int totalArguments)
|
||||
{
|
||||
|
||||
std::vector<float> functionArguments;
|
||||
int index = 0;
|
||||
int currentRandom = 0;
|
||||
while (index < totalArguments)
|
||||
{
|
||||
auto functionArgument = stack.back();
|
||||
stack.pop_back();
|
||||
functionArguments.push_back(functionArgument);
|
||||
index++;
|
||||
}
|
||||
auto functionType = (FunctionType)functionTypeIndex;
|
||||
switch (functionType)
|
||||
{
|
||||
case FunctionType::min:
|
||||
{
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
float minValue = functionArguments[0];
|
||||
for (auto i = 1; i < functionArguments.size(); i++)
|
||||
{
|
||||
if (functionArguments[i] < minValue)
|
||||
{
|
||||
minValue = functionArguments[i];
|
||||
}
|
||||
}
|
||||
return minValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FunctionType::max:
|
||||
{
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
float maxValue = functionArguments[0];
|
||||
for (auto i = 1; i < functionArguments.size(); i++)
|
||||
{
|
||||
if (functionArguments[i] > maxValue)
|
||||
{
|
||||
maxValue = functionArguments[i];
|
||||
}
|
||||
}
|
||||
return maxValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FunctionType::round:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return roundf(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::ceil:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return ceilf(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::floor:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return floorf(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::sqrt:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return sqrtf(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::pow:
|
||||
{
|
||||
if (functionArguments.size() > 1)
|
||||
{
|
||||
auto exponent = functionArguments[functionArguments.size() - 2];
|
||||
auto x = functionArguments.back();
|
||||
return powf(x, exponent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FunctionType::exp:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return exp(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::log:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return log(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::cosine:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return cos(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::sine:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return sin(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::tangent:
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return tan(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::acosine:
|
||||
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return acos(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::asine:
|
||||
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return asin(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::atangent:
|
||||
|
||||
if (functionArguments.size() > 0)
|
||||
{
|
||||
return atan(functionArguments.back());
|
||||
}
|
||||
break;
|
||||
case FunctionType::atangent2:
|
||||
{
|
||||
if (functionArguments.size() > 1)
|
||||
{
|
||||
auto argument1 = functionArguments.back();
|
||||
auto argument2 =
|
||||
functionArguments[functionArguments.size() - 2];
|
||||
return atan2(argument1, argument2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FunctionType::random:
|
||||
{
|
||||
float randomValue = getRandom(currentRandom++);
|
||||
float lowerBound = 0;
|
||||
float upperBound = 1;
|
||||
if (functionArguments.size() == 1)
|
||||
{
|
||||
upperBound = functionArguments.back();
|
||||
}
|
||||
else if (functionArguments.size() > 1)
|
||||
{
|
||||
lowerBound = functionArguments.back();
|
||||
upperBound = functionArguments[functionArguments.size() - 2];
|
||||
}
|
||||
return lowerBound + (upperBound - lowerBound) * randomValue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataValue* DataConverterFormula::convert(DataValue* value, DataBind* dataBind)
|
||||
{
|
||||
if (value->is<DataValueNumber>())
|
||||
{
|
||||
float inputValue = value->as<DataValueNumber>()->value();
|
||||
float resultValue = inputValue;
|
||||
|
||||
std::vector<float> stack;
|
||||
for (auto& token : m_outputQueue)
|
||||
{
|
||||
if (token->is<FormulaTokenOperation>())
|
||||
{
|
||||
if (stack.size() > 1)
|
||||
{
|
||||
auto right = stack.back();
|
||||
stack.pop_back();
|
||||
auto left = stack.back();
|
||||
stack.pop_back();
|
||||
float operationResult = applyOperation(
|
||||
left,
|
||||
right,
|
||||
token->as<FormulaTokenOperation>()->operationType());
|
||||
stack.push_back(operationResult);
|
||||
}
|
||||
}
|
||||
else if (token->is<FormulaTokenFunction>())
|
||||
{
|
||||
auto argumentsCount = m_argumentsCount.find(token);
|
||||
float operationResult = applyFunction(
|
||||
stack,
|
||||
token->as<FormulaTokenFunction>()->functionType(),
|
||||
argumentsCount == m_argumentsCount.end()
|
||||
? 0
|
||||
: argumentsCount->second);
|
||||
stack.push_back(operationResult);
|
||||
}
|
||||
else if (token->is<FormulaTokenInput>())
|
||||
{
|
||||
stack.push_back(inputValue);
|
||||
}
|
||||
else if (token->is<FormulaTokenValue>())
|
||||
{
|
||||
stack.push_back(
|
||||
token->as<FormulaTokenValue>()->operationValue());
|
||||
}
|
||||
}
|
||||
|
||||
// If the formula is well formed, the stack at the end has to be of size
|
||||
// 1
|
||||
if (stack.size() == 1)
|
||||
{
|
||||
resultValue = stack.back();
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
m_output.value(resultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_output.value(DataValueNumber::defaultValue);
|
||||
}
|
||||
return &m_output;
|
||||
}
|
||||
|
||||
DataValue* DataConverterFormula::reverseConvert(DataValue* value,
|
||||
DataBind* dataBind)
|
||||
{
|
||||
return convert(value, dataBind);
|
||||
}
|
||||
|
||||
void DataConverterFormula::addToken(FormulaToken* token)
|
||||
{
|
||||
m_tokens.push_back(token);
|
||||
}
|
||||
|
||||
void DataConverterFormula::addOutputToken(FormulaToken* token,
|
||||
int argumentsCount)
|
||||
{
|
||||
m_outputQueue.push_back(token);
|
||||
m_argumentsCount[token] = argumentsCount;
|
||||
}
|
||||
|
||||
// Warning! this clone override is not making a clean copy of the core object.
|
||||
// It creates a limited instance to avoid recalculating the output stack every
|
||||
// time
|
||||
Core* DataConverterFormula::clone() const
|
||||
{
|
||||
auto cloned = DataConverterFormulaBase::clone()->as<DataConverterFormula>();
|
||||
// Instead of cloning all tookens, we can clone the processed tokens that
|
||||
// will be used during conversion
|
||||
for (auto& token : m_outputQueue)
|
||||
{
|
||||
auto tokenArgumentsCountSearch = m_argumentsCount.find(token);
|
||||
auto argumentsCount =
|
||||
tokenArgumentsCountSearch == m_argumentsCount.end()
|
||||
? 0
|
||||
: tokenArgumentsCountSearch->second;
|
||||
auto clonedToken = token->clone()->as<FormulaToken>();
|
||||
cloned->addOutputToken(clonedToken, argumentsCount);
|
||||
}
|
||||
cloned->isInstance(true);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
void DataConverterFormula::bindFromContext(DataContext* dataContext,
|
||||
DataBind* dataBind)
|
||||
{
|
||||
for (auto& token : m_outputQueue)
|
||||
{
|
||||
token->bindFromContext(dataContext, dataBind);
|
||||
}
|
||||
}
|
||||
|
||||
void DataConverterFormula::update()
|
||||
{
|
||||
for (auto& token : m_outputQueue)
|
||||
{
|
||||
token->update();
|
||||
}
|
||||
}
|
||||
71
src/data_bind/converters/formula/formula_token.cpp
Normal file
71
src/data_bind/converters/formula/formula_token.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_formula.hpp"
|
||||
#include "rive/importers/data_converter_formula_importer.hpp"
|
||||
#include "rive/data_bind/data_bind_context.hpp"
|
||||
#include <cmath>
|
||||
|
||||
using namespace rive;
|
||||
|
||||
StatusCode FormulaToken::import(ImportStack& importStack)
|
||||
{
|
||||
auto dataConveterFormulaImporter =
|
||||
importStack.latest<DataConverterFormulaImporter>(
|
||||
DataConverterFormulaBase::typeKey);
|
||||
if (dataConveterFormulaImporter == nullptr)
|
||||
{
|
||||
return StatusCode::MissingObject;
|
||||
}
|
||||
dataConveterFormulaImporter->formula()->addToken(this);
|
||||
return Super::import(importStack);
|
||||
}
|
||||
|
||||
void FormulaToken::addDataBind(DataBind* dataBind)
|
||||
{
|
||||
m_dataBinds.push_back(dataBind);
|
||||
}
|
||||
|
||||
void FormulaToken::markDirty()
|
||||
{
|
||||
m_parentDataBind->addDirt(ComponentDirt::Dependents |
|
||||
ComponentDirt::Bindings,
|
||||
false);
|
||||
}
|
||||
|
||||
void FormulaToken::bindFromContext(DataContext* dataContext, DataBind* dataBind)
|
||||
{
|
||||
m_parentDataBind = dataBind;
|
||||
for (auto dataBind : m_dataBinds)
|
||||
{
|
||||
if (dataBind->is<DataBindContext>())
|
||||
{
|
||||
|
||||
dataBind->as<DataBindContext>()->bindFromContext(dataContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FormulaToken::update()
|
||||
{
|
||||
for (auto dataBind : m_dataBinds)
|
||||
{
|
||||
auto d = dataBind->dirt();
|
||||
if (d == ComponentDirt::None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
dataBind->dirt(ComponentDirt::None);
|
||||
dataBind->update(d);
|
||||
}
|
||||
}
|
||||
|
||||
void FormulaToken::copy(const FormulaTokenBase& object)
|
||||
{
|
||||
auto dataBinds = object.as<FormulaToken>()->dataBinds();
|
||||
for (auto dataBind : dataBinds)
|
||||
{
|
||||
auto dataBindClone = dataBind->clone()->as<DataBind>();
|
||||
dataBindClone->target(this);
|
||||
addDataBind(dataBindClone);
|
||||
}
|
||||
FormulaTokenBase::copy(object);
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "rive/data_bind/context/context_value_trigger.hpp"
|
||||
#include "rive/data_bind/data_values/data_type.hpp"
|
||||
#include "rive/data_bind/converters/data_converter.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
#include "rive/animation/transition_viewmodel_condition.hpp"
|
||||
#include "rive/animation/state_machine.hpp"
|
||||
#include "rive/importers/artboard_importer.hpp"
|
||||
@@ -52,6 +53,10 @@ StatusCode DataBind::import(ImportStack& importStack)
|
||||
{
|
||||
target()->as<DataConverter>()->addDataBind(this);
|
||||
}
|
||||
else if (target()->is<FormulaToken>())
|
||||
{
|
||||
target()->as<FormulaToken>()->addDataBind(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (target()->coreType())
|
||||
@@ -240,9 +245,18 @@ bool DataBind::addDirt(ComponentDirt value, bool recurse)
|
||||
m_changedCallback();
|
||||
}
|
||||
#endif
|
||||
if (target() != nullptr && target()->is<DataConverter>())
|
||||
if (target() != nullptr)
|
||||
{
|
||||
target()->as<DataConverter>()->markConverterDirty();
|
||||
if (target()->is<DataConverter>())
|
||||
{
|
||||
|
||||
target()->as<DataConverter>()->markConverterDirty();
|
||||
}
|
||||
else if (target()->is<FormulaToken>())
|
||||
{
|
||||
|
||||
target()->as<FormulaToken>()->markDirty();
|
||||
}
|
||||
}
|
||||
if ((m_Dirt & ComponentDirt::Dependents) != 0 && m_ContextValue != nullptr)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "rive/importers/backboard_importer.hpp"
|
||||
#include "rive/importers/bindable_property_importer.hpp"
|
||||
#include "rive/importers/data_converter_group_importer.hpp"
|
||||
#include "rive/importers/data_converter_formula_importer.hpp"
|
||||
#include "rive/importers/enum_importer.hpp"
|
||||
#include "rive/importers/file_asset_importer.hpp"
|
||||
#include "rive/importers/import_stack.hpp"
|
||||
@@ -420,6 +421,12 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
|
||||
object->as<DataConverterGroup>());
|
||||
stackType = DataConverterGroupBase::typeKey;
|
||||
break;
|
||||
case DataConverterFormulaBase::typeKey:
|
||||
stackObject =
|
||||
rivestd::make_unique<DataConverterFormulaImporter>(
|
||||
object->as<DataConverterFormula>());
|
||||
stackType = DataConverterFormulaBase::typeKey;
|
||||
break;
|
||||
}
|
||||
if (importStack.makeLatest(stackType, std::move(stackObject)) !=
|
||||
StatusCode::Ok)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/data_converter_formula_base.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_formula.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* DataConverterFormulaBase::clone() const
|
||||
{
|
||||
auto cloned = new DataConverterFormula();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_argument_separator_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_argument_separator.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenArgumentSeparatorBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenArgumentSeparator();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaToken();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_function_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_function.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenFunctionBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenFunction();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_input_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_input.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenInputBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenInput();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_operation_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_operation.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenOperationBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenOperation();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenParenthesisBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenParenthesis();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_close_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_close.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenParenthesisCloseBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenParenthesisClose();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_parenthesis_open_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_parenthesis_open.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenParenthesisOpenBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenParenthesisOpen();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/data_bind/converters/formula/formula_token_value_base.hpp"
|
||||
#include "rive/data_bind/converters/formula/formula_token_value.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* FormulaTokenValueBase::clone() const
|
||||
{
|
||||
auto cloned = new FormulaTokenValue();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
16
src/importers/data_converter_formula_importer.cpp
Normal file
16
src/importers/data_converter_formula_importer.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "rive/artboard.hpp"
|
||||
#include "rive/importers/data_converter_formula_importer.hpp"
|
||||
#include "rive/data_bind/converters/data_converter_formula.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
DataConverterFormulaImporter::DataConverterFormulaImporter(
|
||||
DataConverterFormula* formula) :
|
||||
m_dataConverterFormula(formula)
|
||||
{}
|
||||
|
||||
StatusCode DataConverterFormulaImporter::resolve()
|
||||
{
|
||||
m_dataConverterFormula->initialize();
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
Reference in New Issue
Block a user