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:
bodymovin
2025-02-21 08:42:23 +00:00
parent d27fada007
commit a7752f3a09
49 changed files with 1620 additions and 3 deletions

View File

@@ -1 +1 @@
5153dac481f44db86b07528b35d31051b4118fff
cb0f89f200fcf0a969fdc748c5240aeb17313b67

View File

@@ -0,0 +1,8 @@
{
"name": "DataConverterFormula",
"key": {
"int": 536,
"string": "dataconverterformula"
},
"extends": "data_bind/converters/data_converter.json"
}

View 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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "FormulaTokenInput",
"key": {
"int": 545,
"string": "formulatokeninput"
},
"extends": "data_bind/converters/formula/formula_token.json"
}

View File

@@ -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"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "FormulaTokenParenthesis",
"key": {
"int": 539,
"string": "formulatokenparenthesis"
},
"extends": "data_bind/converters/formula/formula_token.json"
}

View File

@@ -0,0 +1,8 @@
{
"name": "FormulaTokenParenthesisClose",
"key": {
"int": 540,
"string": "formulatokenparenthesisclose"
},
"extends": "data_bind/converters/formula/formula_token_parenthesis.json"
}

View File

@@ -0,0 +1,8 @@
{
"name": "FormulaTokenParenthesisOpen",
"key": {
"int": 544,
"string": "formulatokenparenthesisopen"
},
"extends": "data_bind/converters/formula/formula_token_parenthesis.json"
}

View File

@@ -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
}
}
}

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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();
}
}

View 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);
}

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}