mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-04-18 08:08:35 -05:00
Add math helpers
This commit is contained in:
parent
b28c2b28b2
commit
4a3339c09c
|
|
@ -252,6 +252,8 @@ target_sources(
|
|||
src/utils/file-selection.hpp
|
||||
src/utils/macro-list.cpp
|
||||
src/utils/macro-list.hpp
|
||||
src/utils/math-helpers.cpp
|
||||
src/utils/math-helpers.hpp
|
||||
src/utils/mouse-wheel-guard.cpp
|
||||
src/utils/mouse-wheel-guard.hpp
|
||||
src/utils/name-dialog.cpp
|
||||
|
|
|
|||
|
|
@ -933,6 +933,11 @@ AdvSceneSwitcher.process.addArgumentDescription="Add new argument:"
|
|||
AdvSceneSwitcher.process.entry="Run{{filePath}}{{advancedSettings}}"
|
||||
AdvSceneSwitcher.process.entry.workingDirectory="Working directory:{{workingDirectory}}"
|
||||
|
||||
AdvSceneSwitcher.math.notANumber="not a valid number"
|
||||
AdvSceneSwitcher.math.expressionFail="failed evaluate expression"
|
||||
AdvSceneSwitcher.math.expressionFailParentheses="failed evaluate expression (missing Parentheses?)"
|
||||
AdvSceneSwitcher.math.notFullyResolved="not all operands were used"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
|
|
|||
|
|
@ -36,15 +36,13 @@ const static std::map<MacroConditionVariable::Type, std::string>
|
|||
|
||||
static bool isNumber(const Variable &var)
|
||||
{
|
||||
double _;
|
||||
return var.DoubleValue(_);
|
||||
return var.DoubleValue().has_value();
|
||||
}
|
||||
|
||||
static bool compareNumber(const Variable &var, double value, bool less)
|
||||
{
|
||||
double varValue;
|
||||
|
||||
if (!var.DoubleValue(varValue)) {
|
||||
auto varValue = var.DoubleValue();
|
||||
if (!varValue.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (less) {
|
||||
|
|
@ -84,9 +82,9 @@ bool MacroConditionVariable::CompareVariables()
|
|||
return false;
|
||||
}
|
||||
|
||||
double val1, val2;
|
||||
bool validNumbers = var1->DoubleValue(val1);
|
||||
validNumbers = validNumbers && var2->DoubleValue(val2);
|
||||
auto val1 = var1->DoubleValue();
|
||||
auto val2 = var2->DoubleValue();
|
||||
bool validNumbers = val1.has_value() && val2.has_value();
|
||||
|
||||
switch (_type) {
|
||||
case MacroConditionVariable::Type::EQUALS_VARIABLE:
|
||||
|
|
|
|||
256
src/utils/math-helpers.cpp
Normal file
256
src/utils/math-helpers.cpp
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#include "math-helpers.hpp"
|
||||
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cmath>
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
static std::vector<std::string_view> nonOperatorSeparators = {
|
||||
" ",
|
||||
"\t",
|
||||
};
|
||||
|
||||
static std::vector<std::string_view> operators = {
|
||||
"+", "-", "*", "/", "(", ")",
|
||||
};
|
||||
|
||||
static std::vector<std::string> splitStringIntoTokens(const std::string &input)
|
||||
{
|
||||
auto separators = nonOperatorSeparators;
|
||||
separators.insert(separators.end(), operators.begin(), operators.end());
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while (start < input.length()) {
|
||||
std::string::size_type min_pos = std::string::npos;
|
||||
std::string separator;
|
||||
|
||||
// Find the next occurrence of each separator
|
||||
for (const auto &sep : separators) {
|
||||
std::string::size_type pos = input.find(sep, start);
|
||||
if (pos != std::string::npos && pos < min_pos) {
|
||||
min_pos = pos;
|
||||
separator = sep;
|
||||
}
|
||||
}
|
||||
|
||||
// If a separator was found, add the token before it to the vector
|
||||
if (min_pos != std::string::npos) {
|
||||
tokens.push_back(input.substr(start, min_pos - start));
|
||||
start = min_pos + separator.length();
|
||||
|
||||
// If the separator itself was an operator, add it as a
|
||||
// token to the vector
|
||||
if (std::find(operators.begin(), operators.end(),
|
||||
separator) != operators.end()) {
|
||||
tokens.push_back(separator);
|
||||
}
|
||||
}
|
||||
// Otherwise, add the remaining string to the vector and exit the
|
||||
// loop
|
||||
else {
|
||||
tokens.push_back(input.substr(start));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static int precedence(const std::string &op)
|
||||
{
|
||||
if (op == "*" || op == "/") {
|
||||
return 2;
|
||||
} else if (op == "+" || op == "-") {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getErrorMsg(const std::string &expr, const std::string &msg)
|
||||
{
|
||||
return std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.expressionFail")) +
|
||||
" \"" + expr + "\":\n" + msg;
|
||||
}
|
||||
|
||||
static std::string getErrorMsg(const std::string &expr,
|
||||
std::stack<std::string> operators,
|
||||
std::stack<double> operands,
|
||||
const std::string &msg)
|
||||
{
|
||||
std::string errMsg = getErrorMsg(expr, msg);
|
||||
errMsg += "\n\n ---" +
|
||||
std::string(
|
||||
obs_module_text("AdvSceneSwitcher.math.operators")) +
|
||||
" ---\n";
|
||||
while (!operators.empty()) {
|
||||
errMsg += operators.top() + "\n";
|
||||
operators.pop();
|
||||
}
|
||||
|
||||
errMsg +=
|
||||
"\n\n --- " +
|
||||
std::string(obs_module_text("AdvSceneSwitcher.math.operands")) +
|
||||
" ---\n";
|
||||
while (!operands.empty()) {
|
||||
errMsg += std::to_string(operands.top()) + "\n";
|
||||
operands.pop();
|
||||
}
|
||||
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
static void evalHelper(std::stack<std::string> &operators,
|
||||
std::stack<double> &operands)
|
||||
{
|
||||
std::string op = operators.top();
|
||||
operators.pop();
|
||||
double op2 = operands.top();
|
||||
operands.pop();
|
||||
double op1 = operands.top();
|
||||
operands.pop();
|
||||
double result;
|
||||
if (op == "+") {
|
||||
result = op1 + op2;
|
||||
} else if (op == "-") {
|
||||
result = op1 - op2;
|
||||
} else if (op == "*") {
|
||||
result = op1 * op2;
|
||||
} else if (op == "/") {
|
||||
result = op1 / op2;
|
||||
}
|
||||
operands.push(result);
|
||||
}
|
||||
|
||||
std::variant<double, std::string>
|
||||
EvalMathExpression(const std::string &expression)
|
||||
{
|
||||
// Create a stack to store operands and operators
|
||||
std::stack<double> operands;
|
||||
std::stack<std::string> operators;
|
||||
|
||||
auto tokens = splitStringIntoTokens(expression);
|
||||
|
||||
// Loop through each token in the expression
|
||||
for (const auto &token : tokens) {
|
||||
// If the token is a number, push it onto the operand stack
|
||||
if (isdigit(token[0])) {
|
||||
auto operand = GetDouble(token);
|
||||
if (!operand.has_value()) {
|
||||
return getErrorMsg(
|
||||
expression,
|
||||
"\"" + token + "\" " +
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.math.notANumber"));
|
||||
}
|
||||
operands.push(*operand);
|
||||
}
|
||||
// If the token is an operator, evaluate higher-precedence
|
||||
// operators first
|
||||
else if (token == "+" || token == "-" || token == "*" ||
|
||||
token == "/") {
|
||||
while (!operators.empty() &&
|
||||
precedence(operators.top()) >=
|
||||
precedence(token)) {
|
||||
if (operators.empty() || operands.size() < 2) {
|
||||
return getErrorMsg(expression,
|
||||
operators, operands,
|
||||
"");
|
||||
}
|
||||
evalHelper(operators, operands);
|
||||
}
|
||||
operators.push(token);
|
||||
}
|
||||
// If the token is an opening bracket, push it onto the
|
||||
// operator stack
|
||||
else if (token == "(") {
|
||||
operators.push(token);
|
||||
}
|
||||
// If the token is a closing bracket, evaluate the expression
|
||||
// inside the brackets
|
||||
else if (token == ")") {
|
||||
while (operators.top() != "(") {
|
||||
if (operators.empty() || operands.size() < 2) {
|
||||
return getErrorMsg(expression,
|
||||
operators, operands,
|
||||
"");
|
||||
}
|
||||
evalHelper(operators, operands);
|
||||
if (operators.empty()) {
|
||||
return getErrorMsg(
|
||||
expression, operators, operands,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.math.expressionFailParentheses"));
|
||||
}
|
||||
}
|
||||
operators.pop(); // Pop the opening bracket
|
||||
|
||||
}
|
||||
|
||||
// TODO: Add function "operators"
|
||||
//
|
||||
// If the token is a function, pop the top operand and apply
|
||||
// the function
|
||||
// else if (token == "sin" || token == "cos" || token == "tan" ||
|
||||
// token == "sqrt") {
|
||||
// double op = operands.top();
|
||||
// operands.pop();
|
||||
// double result;
|
||||
// if (token == "sin") {
|
||||
// result = sin(op);
|
||||
// } else if (token == "cos") {
|
||||
// result = cos(op);
|
||||
// } else if (token == "tan") {
|
||||
// result = tan(op);
|
||||
// } else if (token == "sqrt") {
|
||||
// result = sqrt(op);
|
||||
// }
|
||||
// operands.push(result);
|
||||
// }
|
||||
|
||||
else if (!token.empty()) {
|
||||
return getErrorMsg(
|
||||
expression,
|
||||
std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.invalidToken")) +
|
||||
" \"" + token + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate any remaining operators in the stack
|
||||
while (!operators.empty()) {
|
||||
if (operators.empty() || operands.size() < 2) {
|
||||
return getErrorMsg(expression, operators, operands, "");
|
||||
}
|
||||
evalHelper(operators, operands);
|
||||
}
|
||||
|
||||
// The result is the top operand on the stack
|
||||
if (operands.size() != 1) {
|
||||
return getErrorMsg(
|
||||
expression, operators, operands,
|
||||
std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.notFullyResolved")));
|
||||
}
|
||||
return operands.top();
|
||||
}
|
||||
|
||||
bool IsValidNumber(const std::string &str)
|
||||
{
|
||||
return GetDouble(str).has_value();
|
||||
}
|
||||
|
||||
std::optional<double> GetDouble(const std::string &str)
|
||||
{
|
||||
char *end = nullptr;
|
||||
double value = std::strtod(str.c_str(), &end);
|
||||
if (end != str.c_str() && *end == '\0' && value != HUGE_VAL) {
|
||||
return value;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
9
src/utils/math-helpers.hpp
Normal file
9
src/utils/math-helpers.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
std::variant<double, std::string>
|
||||
EvalMathExpression(const std::string &expression);
|
||||
bool IsValidNumber(const std::string &);
|
||||
std::optional<double> GetDouble(const std::string &);
|
||||
|
|
@ -51,11 +51,8 @@ template<typename T> T NumberVariable<T>::GetValue() const
|
|||
if (!var) {
|
||||
return {};
|
||||
}
|
||||
double value;
|
||||
if (!var->DoubleValue(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return value;
|
||||
auto value = var->DoubleValue();
|
||||
return value.value_or(0.0);
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "variable.hpp"
|
||||
#include "math-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <switcher-data-structs.hpp>
|
||||
|
|
@ -46,11 +47,9 @@ void Variable::Save(obs_data_t *obj) const
|
|||
obs_data_set_string(obj, "defaultValue", _defaultValue.c_str());
|
||||
}
|
||||
|
||||
bool Variable::DoubleValue(double &value) const
|
||||
std::optional<double> Variable::DoubleValue() const
|
||||
{
|
||||
char *end = nullptr;
|
||||
value = strtod(_value.c_str(), &end);
|
||||
return end != _value.c_str() && *end == '\0' && value != HUGE_VAL;
|
||||
return GetDouble(_value);
|
||||
}
|
||||
|
||||
void Variable::SetValue(const std::string &val)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "resizing-text-edit.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <QStringList>
|
||||
#include <obs.hpp>
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ public:
|
|||
void Load(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj) const;
|
||||
std::string Value() const { return _value; }
|
||||
bool DoubleValue(double &) const;
|
||||
std::optional<double> DoubleValue() const;
|
||||
void SetValue(const std::string &val);
|
||||
void SetValue(double);
|
||||
static std::shared_ptr<Item> Create()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user