mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 07:58:59 +02:00
Initial version of AnimExpression class with minimal tokenizer
This commit is contained in:
parent
f4cc8c4c2e
commit
340096d457
4 changed files with 354 additions and 6 deletions
228
libraries/animation/src/AnimExpression.cpp
Normal file
228
libraries/animation/src/AnimExpression.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
//
|
||||||
|
// AnimExpression.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 11/1/15.
|
||||||
|
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
#include <QRegExp>
|
||||||
|
|
||||||
|
#include "AnimExpression.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void AnimExpression::Token::dump() {
|
||||||
|
switch (type) {
|
||||||
|
case End:
|
||||||
|
qCDebug(animation) << " End";
|
||||||
|
break;
|
||||||
|
case Identifier:
|
||||||
|
qCDebug(animation) << " Identifer =" << strVal;
|
||||||
|
break;
|
||||||
|
case LiteralInt:
|
||||||
|
qCDebug(animation) << " LiteralInt =" << intVal;
|
||||||
|
break;
|
||||||
|
case LiteralFloat:
|
||||||
|
qCDebug(animation) << " LiteralFloat" << floatVal;
|
||||||
|
break;
|
||||||
|
case LiteralVec3:
|
||||||
|
qCDebug(animation) << " LiteralVec3" << vec3Val;
|
||||||
|
break;
|
||||||
|
case LiteralVec4:
|
||||||
|
qCDebug(animation) << " LiteralVec4" << vec4Val;
|
||||||
|
break;
|
||||||
|
case LiteralQuat:
|
||||||
|
qCDebug(animation) << " LiteralQuat" << quatVal;
|
||||||
|
break;
|
||||||
|
case And:
|
||||||
|
qCDebug(animation) << " And";
|
||||||
|
break;
|
||||||
|
case Or:
|
||||||
|
qCDebug(animation) << " Or";
|
||||||
|
break;
|
||||||
|
case GreaterThan:
|
||||||
|
qCDebug(animation) << " GreaterThan";
|
||||||
|
break;
|
||||||
|
case GreaterThanEqual:
|
||||||
|
qCDebug(animation) << " GreaterThanEqual";
|
||||||
|
break;
|
||||||
|
case LessThan:
|
||||||
|
qCDebug(animation) << " LessThan";
|
||||||
|
break;
|
||||||
|
case LessThanEqual:
|
||||||
|
qCDebug(animation) << " LessThanEqual";
|
||||||
|
break;
|
||||||
|
case Equal:
|
||||||
|
qCDebug(animation) << " Equal";
|
||||||
|
break;
|
||||||
|
case NotEqual:
|
||||||
|
qCDebug(animation) << " NotEqual";
|
||||||
|
break;
|
||||||
|
case LeftParen:
|
||||||
|
qCDebug(animation) << " LeftParen";
|
||||||
|
break;
|
||||||
|
case RightParen:
|
||||||
|
qCDebug(animation) << " RightParen";
|
||||||
|
break;
|
||||||
|
case Not:
|
||||||
|
qCDebug(animation) << " Not";
|
||||||
|
break;
|
||||||
|
case Minus:
|
||||||
|
qCDebug(animation) << " Minus";
|
||||||
|
break;
|
||||||
|
case Plus:
|
||||||
|
qCDebug(animation) << " Plus";
|
||||||
|
break;
|
||||||
|
case Multiply:
|
||||||
|
qCDebug(animation) << " Multiply";
|
||||||
|
break;
|
||||||
|
case Modulus:
|
||||||
|
qCDebug(animation) << " Modulus";
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
qCDebug(animation) << " Error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AnimExpression::AnimExpression(const QString& str) :
|
||||||
|
_expression(str) {
|
||||||
|
parseExpression(_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimExpression::parseExpression(const QString& str) {
|
||||||
|
Token token(Token::End);
|
||||||
|
auto iter = str.begin();
|
||||||
|
do {
|
||||||
|
token = consumeToken(str, iter);
|
||||||
|
switch(token.type) {
|
||||||
|
case Token::Error:
|
||||||
|
case Token::End:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while(iter != str.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
while (iter != str.end()) {
|
||||||
|
if (iter->isSpace()) {
|
||||||
|
++iter;
|
||||||
|
} else if (iter->isLetter()) {
|
||||||
|
return consumeIdentifier(str, iter);
|
||||||
|
} else if (iter->isDigit()) {
|
||||||
|
return consumeNumber(str, iter);
|
||||||
|
} else {
|
||||||
|
switch (iter->unicode()) {
|
||||||
|
case '&': return consumeAnd(str, iter);
|
||||||
|
case '|': return consumeOr(str, iter);
|
||||||
|
case '>': return consumeGreaterThan(str, iter);
|
||||||
|
case '<': return consumeLessThan(str, iter);
|
||||||
|
case '(': ++iter; return Token(Token::LeftParen);
|
||||||
|
case ')': ++iter; return Token(Token::RightParen);
|
||||||
|
case '!': return consumeNot(str, iter);
|
||||||
|
case '-': ++iter; return Token(Token::Minus);
|
||||||
|
case '+': ++iter; return Token(Token::Plus);
|
||||||
|
case '*': ++iter; return Token(Token::Multiply);
|
||||||
|
case '%': ++iter; return Token(Token::Modulus);
|
||||||
|
default:
|
||||||
|
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||||
|
return Token(Token::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Token(Token::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->isLetter());
|
||||||
|
auto begin = iter;
|
||||||
|
while (iter->isLetter() && iter != str.end()) {
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
int pos = (int)(begin - str.begin());
|
||||||
|
int len = (int)(iter - begin);
|
||||||
|
return Token(QStringRef(const_cast<const QString*>(&str), pos, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->isDigit());
|
||||||
|
auto begin = iter;
|
||||||
|
while (iter->isDigit() && iter != str.end()) {
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
int pos = (int)(begin - str.begin());
|
||||||
|
int len = (int)(iter - begin);
|
||||||
|
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
|
||||||
|
return Token(sub.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->unicode() == '&');
|
||||||
|
iter++;
|
||||||
|
if (iter->unicode() == '&') {
|
||||||
|
iter++;
|
||||||
|
return Token(Token::And);
|
||||||
|
} else {
|
||||||
|
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||||
|
return Token(Token::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->unicode() == '|');
|
||||||
|
iter++;
|
||||||
|
if (iter->unicode() == '|') {
|
||||||
|
iter++;
|
||||||
|
return Token(Token::Or);
|
||||||
|
} else {
|
||||||
|
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
|
||||||
|
return Token(Token::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->unicode() == '>');
|
||||||
|
iter++;
|
||||||
|
if (iter->unicode() == '=') {
|
||||||
|
iter++;
|
||||||
|
return Token(Token::GreaterThanEqual);
|
||||||
|
} else {
|
||||||
|
return Token(Token::GreaterThan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->unicode() == '<');
|
||||||
|
iter++;
|
||||||
|
if (iter->unicode() == '=') {
|
||||||
|
iter++;
|
||||||
|
return Token(Token::LessThanEqual);
|
||||||
|
} else {
|
||||||
|
return Token(Token::LessThan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const {
|
||||||
|
assert(iter != str.end());
|
||||||
|
assert(iter->unicode() == '!');
|
||||||
|
iter++;
|
||||||
|
if (iter->unicode() == '=') {
|
||||||
|
iter++;
|
||||||
|
return Token(Token::NotEqual);
|
||||||
|
} else {
|
||||||
|
return Token(Token::Not);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
78
libraries/animation/src/AnimExpression.h
Normal file
78
libraries/animation/src/AnimExpression.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// AnimExpression.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 11/1/15.
|
||||||
|
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AnimExpression
|
||||||
|
#define hifi_AnimExpression
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
class AnimExpression {
|
||||||
|
public:
|
||||||
|
friend class AnimTests;
|
||||||
|
AnimExpression(const QString& str);
|
||||||
|
protected:
|
||||||
|
struct Token {
|
||||||
|
enum Type {
|
||||||
|
End = 0,
|
||||||
|
Identifier,
|
||||||
|
LiteralInt,
|
||||||
|
LiteralFloat,
|
||||||
|
LiteralVec3,
|
||||||
|
LiteralVec4,
|
||||||
|
LiteralQuat,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
GreaterThan,
|
||||||
|
GreaterThanEqual,
|
||||||
|
LessThan,
|
||||||
|
LessThanEqual,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
LeftParen,
|
||||||
|
RightParen,
|
||||||
|
Not,
|
||||||
|
Minus,
|
||||||
|
Plus,
|
||||||
|
Multiply,
|
||||||
|
Modulus,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
Token(Type type) : type(type) {}
|
||||||
|
Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {}
|
||||||
|
Token(int val) : type(Type::LiteralInt), intVal(val) {}
|
||||||
|
Type type = End;
|
||||||
|
QString strVal;
|
||||||
|
int intVal;
|
||||||
|
float floatVal;
|
||||||
|
glm::vec3 vec3Val;
|
||||||
|
glm::vec4 vec4Val;
|
||||||
|
glm::quat quatVal;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void dump();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
bool parseExpression(const QString& str);
|
||||||
|
Token consumeToken(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeNumber(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeAnd(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeOr(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeLessThan(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
Token consumeNot(const QString& str, QString::const_iterator& iter) const;
|
||||||
|
|
||||||
|
QString _expression;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "AnimTests.h"
|
#include "AnimTests.h"
|
||||||
#include "AnimNodeLoader.h"
|
#include <AnimNodeLoader.h>
|
||||||
#include "AnimClip.h"
|
#include <AnimClip.h>
|
||||||
#include "AnimBlendLinear.h"
|
#include <AnimBlendLinear.h>
|
||||||
#include "AnimationLogging.h"
|
#include <AnimationLogging.h>
|
||||||
#include "AnimVariant.h"
|
#include <AnimVariant.h>
|
||||||
#include "AnimUtil.h"
|
#include <AnimExpression.h>
|
||||||
|
#include <AnimUtil.h>
|
||||||
|
|
||||||
#include <../QTestExtensions.h>
|
#include <../QTestExtensions.h>
|
||||||
|
|
||||||
|
@ -322,4 +323,44 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
|
||||||
QVERIFY(resultFrame == startFrame + 0.5f);
|
QVERIFY(resultFrame == startFrame + 0.5f);
|
||||||
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
|
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
|
||||||
triggers.clear();
|
triggers.clear();
|
||||||
|
|
||||||
|
void AnimTests::testTokenizer() {
|
||||||
|
QString str = "(10 + x) >= 20 && (y != !z)";
|
||||||
|
AnimExpression e("");
|
||||||
|
auto iter = str.cbegin();
|
||||||
|
AnimExpression::Token token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::LeftParen);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::LiteralInt);
|
||||||
|
QVERIFY(token.intVal == 10);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::Plus);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||||
|
QVERIFY(token.strVal == "x");
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::RightParen);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::LiteralInt);
|
||||||
|
QVERIFY(token.intVal == 20);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::And);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::LeftParen);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||||
|
QVERIFY(token.strVal == "y");
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::NotEqual);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::Not);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::Identifier);
|
||||||
|
QVERIFY(token.strVal == "z");
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
|
QVERIFY(token.type == AnimExpression::Token::RightParen);
|
||||||
|
token = e.consumeToken(str, iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ private slots:
|
||||||
void testLoader();
|
void testLoader();
|
||||||
void testVariant();
|
void testVariant();
|
||||||
void testAccumulateTime();
|
void testAccumulateTime();
|
||||||
|
void testTokenizer();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AnimTests_h
|
#endif // hifi_AnimTests_h
|
||||||
|
|
Loading…
Reference in a new issue