[Core] Add Boolean Functions to expressions (AND, OR, NOT) (#22506)
* [Core] Add Boolean Functions to expressions (AND, OR, BOOL, NOT) * [Core] Add `if` function to expressions to overcome ternary operator limitations * [Core] Update expressions grammar to recognize relational operations as arguments * [Core] The `if` function has been removed as no consensus was reached regarding its convenience or necessity. Its inclusion was considered potentially confusing or redundant. * [Core] Make boolean cast based on Confusion threshold.
This commit is contained in:
committed by
GitHub
parent
5d43908edc
commit
d4c38502d8
@@ -52,6 +52,7 @@
|
||||
#include <Base/RotationPy.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include <Base/Precision.h>
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
@@ -168,6 +169,15 @@ static inline T &&cast(App::any &&value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
inline bool asBool(double value) {
|
||||
return std::fabs(value) >= Base::Precision::Confusion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string unquote(const std::string & input)
|
||||
{
|
||||
assert(input.size() >= 4);
|
||||
@@ -1745,6 +1755,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
case TANH:
|
||||
case TRUNC:
|
||||
case VNORMALIZE:
|
||||
case NOT:
|
||||
if (args.size() != 1)
|
||||
ARGUMENT_THROW("exactly one required.");
|
||||
break;
|
||||
@@ -1810,6 +1821,8 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
case MIN:
|
||||
case STDDEV:
|
||||
case SUM:
|
||||
case AND:
|
||||
case OR:
|
||||
if (args.empty())
|
||||
ARGUMENT_THROW("at least one required.");
|
||||
break;
|
||||
@@ -1973,6 +1986,36 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class AndCollector : public Collector {
|
||||
public:
|
||||
void collect(Quantity value) override
|
||||
{
|
||||
if (first) {
|
||||
q = Quantity(asBool(value.getValue()) ? 1 : 0);
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
if (!asBool(value.getValue())) {
|
||||
q = Quantity(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OrCollector : public Collector {
|
||||
public:
|
||||
void collect(Quantity value) override
|
||||
{
|
||||
if (first) {
|
||||
q = Quantity(asBool(value.getValue()) ? 1 : 0);
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
if (asBool(value.getValue())) {
|
||||
q = Quantity(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Py::Object FunctionExpression::evalAggregate(
|
||||
const Expression *owner, int f, const std::vector<Expression*> &args)
|
||||
{
|
||||
@@ -1997,6 +2040,12 @@ Py::Object FunctionExpression::evalAggregate(
|
||||
case MAX:
|
||||
c = std::make_unique<MaxCollector>();
|
||||
break;
|
||||
case AND:
|
||||
c = std::make_unique<AndCollector>();
|
||||
break;
|
||||
case OR:
|
||||
c = std::make_unique<OrCollector>();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@@ -2499,6 +2548,9 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
if (v1.isDimensionlessOrUnit(Unit::Length) && v2.isDimensionlessOrUnit(Unit::Length) && v3.isDimensionlessOrUnit(Unit::Length))
|
||||
break;
|
||||
_EXPR_THROW("Translation units must be a length or dimensionless.", expr);
|
||||
case NOT:
|
||||
unit = Unit();
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
@@ -2590,6 +2642,9 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
value)));
|
||||
case TRANSLATIONM:
|
||||
return translationMatrix(v1.getValue(), v2.getValue(), v3.getValue());
|
||||
case NOT:
|
||||
output = asBool(value) ? 0 : 1;
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
@@ -2773,6 +2828,12 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << "stddev("; break;;
|
||||
case SUM:
|
||||
ss << "sum("; break;;
|
||||
case AND:
|
||||
ss << "and("; break;;
|
||||
case OR:
|
||||
ss << "or("; break;;
|
||||
case NOT:
|
||||
ss << "not("; break;;
|
||||
default:
|
||||
ss << fname << "("; break;;
|
||||
}
|
||||
@@ -3662,6 +3723,8 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["hiddenref"] = FunctionExpression::HIDDENREF;
|
||||
registered_functions["href"] = FunctionExpression::HREF;
|
||||
|
||||
registered_functions["not"] = FunctionExpression::NOT;
|
||||
|
||||
// Aggregates
|
||||
registered_functions["average"] = FunctionExpression::AVERAGE;
|
||||
registered_functions["count"] = FunctionExpression::COUNT;
|
||||
@@ -3669,6 +3732,8 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["min"] = FunctionExpression::MIN;
|
||||
registered_functions["stddev"] = FunctionExpression::STDDEV;
|
||||
registered_functions["sum"] = FunctionExpression::SUM;
|
||||
registered_functions["and"] = FunctionExpression::AND;
|
||||
registered_functions["or"] = FunctionExpression::OR;
|
||||
|
||||
has_registered_functions = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user