Expression token caching.
parent
1ec7598ac0
commit
22bc36c9f2
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue