mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 04:07:11 +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 "AnimNodeLoader.h"
|
||||
#include "AnimClip.h"
|
||||
#include "AnimBlendLinear.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimVariant.h"
|
||||
#include "AnimUtil.h"
|
||||
#include <AnimNodeLoader.h>
|
||||
#include <AnimClip.h>
|
||||
#include <AnimBlendLinear.h>
|
||||
#include <AnimationLogging.h>
|
||||
#include <AnimVariant.h>
|
||||
#include <AnimExpression.h>
|
||||
#include <AnimUtil.h>
|
||||
|
||||
#include <../QTestExtensions.h>
|
||||
|
||||
|
@ -322,4 +323,44 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
|
|||
QVERIFY(resultFrame == startFrame + 0.5f);
|
||||
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
|
||||
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 testVariant();
|
||||
void testAccumulateTime();
|
||||
void testTokenizer();
|
||||
};
|
||||
|
||||
#endif // hifi_AnimTests_h
|
||||
|
|
Loading…
Reference in a new issue