Expression token caching.

master
Auri 2021-08-20 18:28:13 -07:00
parent 1ec7598ac0
commit 22bc36c9f2
6 changed files with 118 additions and 106 deletions

View File

@ -18,7 +18,9 @@ void Gui::Element::setStyle(StyleRule style, const std::any& value) {
}
ivec2 Gui::Element::getComputedSize() {
let size = getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, glm::max(layoutSize, 0));
let size = getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1));
if (size.x == -1) size.x = std::max(layoutSize.x, 0);
if (size.y == -1) size.y = std::max(layoutSize.y, 0);
return size;
}
@ -132,7 +134,7 @@ void Gui::Element::layoutChildren() {
let selfSize = getComputedContentSize();
i32 explicitSize = gap * (children.size() - 1);
usize implicitCount = 0;
i32 implicitCount = 0;
for (const let& child : children) {
let childExplicitSize = child->getExplicitSize();
@ -141,7 +143,7 @@ void Gui::Element::layoutChildren() {
let childMargin = child->getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
explicitSize += childMargin[primary] + childMargin[primary + 2];
}
/**
* The cumulative layout offset of the children across the x and y axis.
*/
@ -154,9 +156,10 @@ void Gui::Element::layoutChildren() {
/**
* The amount of size each implicitly sized element should occupy.
*/
// std::cout << selfSize << ": " << (selfSize[primary] - explicitSize) << std::endl;
i32 implicitElemSize = floor((selfSize[primary] - explicitSize) /
(std::max)(implicitCount, static_cast<usize>(1)));
i32 implicitElemSize = floor((selfSize[primary] - explicitSize) / (std::max)(implicitCount, 1));
ivec2 selfOffset = getComputedPos() + parentOffset;

View File

@ -5,8 +5,8 @@
#include "client/gui/Expression.h"
#include "util/Util.h"
#include "Gui.h"
#include "util/Util.h"
Gui::Expression::Expression(const string& exp) {
setExpression(exp);
@ -22,25 +22,25 @@ void Gui::Expression::setExpression(string exp) {
exp.erase(std::remove_if(exp.begin(), exp.end(), isspace), exp.end());
// Process Infix into Postfix (RPN)
infix = {};
std::queue<string> queue {};
std::stack<char> operators {};
bool nextOperatorIsUnary = true;
String temp = {};
string temp = {};
while (exp.size()) {
let& c = exp[0];
// Number or Unit or Keyword
if ((c >= '0' && c <= '9') || c == '.' || (c >= 97 && c <= 122) ||
(nextOperatorIsUnary && (c == '+' || c == '-'))) {
temp.v += c;
temp += c;
nextOperatorIsUnary = false;
}
// Binary Operator
else if (!nextOperatorIsUnary && (c == '+' || c == '-' || c == '*' || c == '/' || c == '^')) {
if (temp.v.size()) {
infix.emplace(temp);
if (temp.size()) {
queue.emplace(temp);
temp = {};
}
@ -48,7 +48,7 @@ void Gui::Expression::setExpression(string exp) {
((c != '^' && PRECEDENCE.at(operators.top()) >= PRECEDENCE.at(c)) ||
PRECEDENCE.at(operators.top()) > PRECEDENCE.at(c))) {
infix.emplace(string(1, operators.top()));
queue.emplace(string(1, operators.top()));
operators.pop();
}
@ -57,8 +57,8 @@ void Gui::Expression::setExpression(string exp) {
}
// Opening Parentheses
else if (c == '(') {
if (temp.v.size()) {
infix.emplace(temp);
if (temp.size()) {
queue.emplace(temp);
temp = {};
}
@ -67,13 +67,13 @@ void Gui::Expression::setExpression(string exp) {
}
// Closing Parentheses
else if (c == ')') {
if (!temp.v.size()) throw std::logic_error("Empty or mismatched parentheses.");
infix.emplace(temp);
if (!temp.size()) throw std::logic_error("Empty or mismatched parentheses.");
queue.emplace(temp);
temp = {};
if (!operators.size()) throw std::logic_error("Mismatched parentheses.");
while (operators.top() != '(') {
infix.emplace(string(1, operators.top()));
queue.emplace(string(1, operators.top()));
operators.pop();
if (!operators.size()) throw std::logic_error("Mismatched parentheses.");
}
@ -85,25 +85,31 @@ void Gui::Expression::setExpression(string exp) {
exp.erase(0, 1);
}
if (temp.v.size()) {
infix.push(temp);
if (temp.size()) {
queue.push(temp);
temp = {};
}
while (operators.size()) {
if (operators.top() == '(') throw std::logic_error("Mismatched parentheses.");
infix.emplace(string(1, operators.top()));
queue.emplace(string(1, operators.top()));
operators.pop();
}
expression.clear();
expression.reserve(queue.size());
while (queue.size()) {
expression.push_back(Token(queue.front()));
queue.pop();
}
}
f32 Gui::Expression::eval() {
let infix = this->infix;
std::stack<String> eval {};
std::stack<Token> eval {};
while (infix.size()) {
let& t = infix.front();
infix.pop();
for (usize i = 0; i < expression.size(); i++) {
let& t = expression[i];
if (!t.isOperator()) {
eval.emplace(t);
@ -111,33 +117,35 @@ f32 Gui::Expression::eval() {
else {
if (eval.size() < 2) throw std::runtime_error("Eval stack has < 2 items! This is an engine error!");
String b = eval.top();
let b = eval.top();
eval.pop();
String a = eval.top();
let a = eval.top();
eval.pop();
switch (t.v[0]) {
case '+':
eval.emplace(std::to_string(a.eval() + b.eval()));
switch (t.unit) {
default:
throw std::logic_error("Tried to operate with a non-operator token.");
case UnitOrOperator::ADD:
eval.emplace(a.evalValue() + b.evalValue(), UnitOrOperator::REAL_PIXEL);
break;
case '-':
eval.emplace(std::to_string(a.eval() - b.eval()));
case UnitOrOperator::SUBTRACT:
eval.emplace(a.evalValue() - b.evalValue(), UnitOrOperator::REAL_PIXEL);
break;
case '*':
eval.emplace(std::to_string(a.eval() * b.eval()));
case UnitOrOperator::MULTIPLY:
eval.emplace(a.evalValue() * b.evalValue(), UnitOrOperator::REAL_PIXEL);
break;
case '/':
eval.emplace(std::to_string(a.eval() / b.eval()));
case UnitOrOperator::DIVIDE:
eval.emplace(a.evalValue() / b.evalValue(), UnitOrOperator::REAL_PIXEL);
break;
case '^':
eval.emplace(std::to_string(pow(a.eval(), b.eval())));
case UnitOrOperator::EXPONENT:
eval.emplace(pow(a.evalValue(), b.evalValue()), UnitOrOperator::REAL_PIXEL);
break;
}
}
}
if (!eval.size()) throw std::runtime_error("Eval stack is empty! This is an engine error!");
return eval.top().eval();
return eval.top().evalValue();
}
const std::unordered_map<char, u8> Gui::Expression::PRECEDENCE {
@ -148,28 +156,45 @@ const std::unordered_map<char, u8> Gui::Expression::PRECEDENCE {
{ '-', 2 }
};
bool Gui::Expression::String::isOperator() {
return v.size() == 1 && (v[0] == '+' || v[0] == '-' || v[0] == '*' || v[0] == '/' || v[0] == '^');
}
f32 Gui::Expression::String::eval() {
Gui::Expression::Token::Token(const string& str) {
// Operator
if (str.size() == 1) {
switch (str[0]) {
default: break;
case '+': unit = UnitOrOperator::ADD; return;
case '-': unit = UnitOrOperator::SUBTRACT; return;
case '*': unit = UnitOrOperator::MULTIPLY; return;
case '/': unit = UnitOrOperator::DIVIDE; return;
case '^': unit = UnitOrOperator::EXPONENT; return;
}
}
// Value
usize unitInd = -1;
val = std::stof(str, &unitInd);
string unitStr = str.substr(unitInd);
f32 value = std::stof(v, &unitInd);
string unit = v.substr(unitInd);
switch (Util::hash(unit.data())) {
default:
throw std::logic_error("Unknown unit '" + unit + "'.");
case Util::hash("dp"):
return value * Gui::PX_SCALE;
// Unit
switch (Util::hash(unitStr.data())) {
default: throw std::logic_error("Unknown unit '" + unitStr + "'.");
case Util::hash("deg"):
return value * M_PI / 180.f;
case Util::hash(""):
case Util::hash("px"):
return value;
case Util::hash("dp"): unit = UnitOrOperator::DISPLAY_PIXEL; return;
case Util::hash(""):
case Util::hash("px"): unit = UnitOrOperator::REAL_PIXEL; return;
case Util::hash("deg"): unit = UnitOrOperator::DEGREE; return;
}
}
bool Gui::Expression::Token::isOperator() {
return static_cast<u8>(unit) >= 128;
}
f32 Gui::Expression::Token::evalValue() {
switch (unit) {
default: throw std::logic_error("Tried to evalValue() on an Operator token.");
case UnitOrOperator::DISPLAY_PIXEL: return val * Gui::PX_SCALE;
case UnitOrOperator::REAL_PIXEL: return val;
case UnitOrOperator::DEGREE: return val * M_PI / 180.f;
}
}

View File

@ -19,28 +19,19 @@ namespace Gui {
};
class Expression {
struct String {
String() = default;
explicit String(const string& v): v(v) {}
string v {};
bool isOperator();
f32 eval();
};
struct Token {
Token() = default;
explicit Token(f32 val, UnitOrOperator unit): val(val), unit(unit);
explicit Token(const string& str);
Token(f32 val, UnitOrOperator unit): val(val), unit(unit) {};
bool isOperator();
f32 evalValue();
f32 val;
f32 val = 0;
UnitOrOperator unit;
};
public:
Expression() = default;
Expression(const string& exp);
@ -48,9 +39,11 @@ namespace Gui {
void setExpression(string exp);
f32 eval();
private:
usize hash = 0;
std::queue<String> infix {};
std::vector<Token> expression;
const static std::unordered_map<char, u8> PRECEDENCE;
};

View File

@ -13,14 +13,14 @@ Gui::Root::Root(Window& window, TextureAtlas& atlas) :
body->setProps({
.id = "body",
.styles = {{
{ StyleRule::SIZE, array<string, 2> {
std::to_string(size.x) + "px", std::to_string(size.y) + "px" } }
{ StyleRule::SIZE, array<Expression, 2> {
Expression(std::to_string(size.x)), Expression(std::to_string(size.y)) }}
}}
});
lock = window.onResize([&](ivec2 size) {
body->setStyle(StyleRule::SIZE, array<string, 2> {
std::to_string(size.x) + "px", std::to_string(size.y) + "px" });
body->setStyle(StyleRule::SIZE, array<Expression, 2> {
Expression(std::to_string(size.x)), Expression(std::to_string(size.y)) });
Timer t("Resize UI");
body->updateElement();
t.printElapsedMs();

View File

@ -142,9 +142,9 @@ namespace Gui {
L == ValueType::LENGTH, bool> = true>
optional<N> get(StyleRule rule) const {
let raw = get<string>(rule);
let raw = get<Gui::Expression>(rule);
if (!raw) return std::nullopt;
return Gui::Expression(*raw).eval();
return raw->eval();
}
/**
@ -157,14 +157,13 @@ namespace Gui {
std::is_same_v<VN, glm::vec<VN::length(), typename VN::value_type>> &&
L == ValueType::LENGTH, bool> = true>
optional<VN> get(StyleRule rule) const {
let raw = get<array<string, VN::length()>>(rule);
if (!raw) return std::nullopt;
VN vec;
for (usize i = 0; i < VN::length(); i++)
vec[i] = Gui::Expression((*raw)[i]).eval();
return vec;
}
optional<VN> get(StyleRule rule) const {
let raw = get<array<Gui::Expression, VN::length()>>(rule);
if (!raw) return std::nullopt;
VN vec;
for (usize i = 0; i < VN::length(); i++) vec[i] = (*raw)[i].eval();
return vec;
}
/**
* Returns the specified Rule's value as a V,

View File

@ -30,18 +30,18 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
{ Gui::StyleRule::V_ALIGN, string("center") }
}}},
{ "navigation", {{
{ Gui::StyleRule::SIZE, array<string, 2> { "-1", "18dp" } }
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("-1"), Gui::Expression("18dp") } }
}}},
{ "navigationWrap", {{
{ Gui::StyleRule::DIRECTION, string("row") },
{ Gui::StyleRule::POS, array<string, 2> { "0", "0" } }
{ Gui::StyleRule::POS, array<Gui::Expression, 2> { Gui::Expression("0"), Gui::Expression("0") } }
}}},
{ "navigationBackground", {{
{ Gui::StyleRule::SIZE, array<string, 2> { "64dp", "18dp" } },
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("64dp"), Gui::Expression("18dp") } },
{ Gui::StyleRule::BACKGROUND, string("menu_bar_bg") }
}}},
{ "navigationButton", {{
{ Gui::StyleRule::SIZE, array<string, 2> { "16dp", "16dp" } },
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("16dp"), Gui::Expression("16dp") } },
{ Gui::StyleRule::CURSOR, string("pointer") }
}}}
});
@ -56,8 +56,9 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
let navigationList = navigation->append<Gui::BoxElement>({
.classes = { "navigationWrap" },
.styles = {{
{ Gui::StyleRule::PADDING, array<string, 4> { "1dp", "1dp", "1dp", "1dp" } },
{ Gui::StyleRule::GAP, array<string, 2> { "1dp", "1dp" } }
{ Gui::StyleRule::PADDING, array<Gui::Expression, 4>
{ Gui::Expression("1dp"), Gui::Expression("1dp"), Gui::Expression("1dp"), Gui::Expression("1dp") } },
{ Gui::StyleRule::GAP, array<Gui::Expression, 2> { Gui::Expression("1dp"), Gui::Expression("1dp") } }
}}
});
@ -80,8 +81,9 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
navigationList->append<Gui::BoxElement>({
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("#fff5") },
{ Gui::StyleRule::SIZE, array<string, 2> { "1dp", "10dp" } },
{ Gui::StyleRule::MARGIN, array<string, 4> { "2dp", "3dp", "2dp", "3dp" } }
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("1dp"), Gui::Expression("10dp") } },
{ Gui::StyleRule::MARGIN, array<Gui::Expression, 4>
{ Gui::Expression("2dp"), Gui::Expression("3dp"), Gui::Expression("2dp"), Gui::Expression("3dp") } }
}}
});
@ -109,12 +111,7 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
// sandbox.load(*selectedSubgame);
}
navigationList->append<Gui::BoxElement>({
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("#f006") },
{ Gui::StyleRule::SIZE, array<string, 2> { "-1", "16dp" } }
}}
});
navigationList->append<Gui::BoxElement>();
let settingsButton = navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" },
@ -139,11 +136,6 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
// if (!down) return;
// client.scene.setScene(make_unique<ConnectScene>(client, Address{ "127.0.0.1" }));
// });
// auto divider = make_shared<GuiRect>("divider");
// divider->create({ GS, GS * 10 }, {}, { 1, 1, 1, 0.3 });
// divider->setPos({ GS * 2 + GS * 18 * 2, GS * 4 });
// navigationBarIcons->add(divider);
}
void MainMenuScene::findSubgames() {