From f636feb4f1cd56f78898406996b94d242836a2a1 Mon Sep 17 00:00:00 2001 From: Medusa Slockbower Date: Tue, 23 Sep 2025 18:07:54 -0400 Subject: [PATCH] - Rough First Pass implementation for format.h - Started 2D Transform Component and relevant math extensions - Switched sequence to use pointers instead of arrays --- CMakeLists.txt | 2 + gdb/fennec/containers.py | 61 ++- include/fennec/containers/bintree.h | 7 +- include/fennec/containers/list.h | 5 +- include/fennec/containers/sequence.h | 448 ++++++++++++------ include/fennec/core/event.h | 19 +- include/fennec/lang/typed.h | 2 - include/fennec/langproc/compile/tokenizer.h | 40 +- include/fennec/langproc/strings/cstring.h | 30 +- include/fennec/langproc/strings/format.h | 174 +++++++ include/fennec/langproc/strings/string.h | 4 + include/fennec/math/ext/transform.h | 70 +-- include/fennec/math/matrix.h | 25 +- include/fennec/math/vector.h | 8 +- include/fennec/scene/component.h | 6 +- include/fennec/scene/components/transform2d.h | 154 ++++++ include/fennec/scene/scene.h | 28 +- test/tests/langproc/test_format.h | 17 +- 18 files changed, 866 insertions(+), 234 deletions(-) create mode 100644 include/fennec/langproc/strings/format.h create mode 100644 include/fennec/scene/components/transform2d.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3514866..5e51ba6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,6 +244,8 @@ add_library(fennec STATIC # EXTRA SOURCES ======================================================================================================== ${FENNEC_EXTRA_SOURCES} + include/fennec/langproc/strings/format.h + include/fennec/scene/components/transform2d.h ) add_dependencies(fennec metaprogramming fennec-dependencies) diff --git a/gdb/fennec/containers.py b/gdb/fennec/containers.py index eac8f1f..5f7e8c2 100644 --- a/gdb/fennec/containers.py +++ b/gdb/fennec/containers.py @@ -524,6 +524,65 @@ class BinTreePrinter: return self.Iterator(self.tree, self.root, self.capacity) +# SEQUENCE ============================================================================================================= + +class SequencePrinter: + """Print a fennec::sequence""" + + class Iterator: + def __init__(self, node): + self.visit = deque() + + if node is not None: + self.visit.append((node, 0, 0)) + + def __iter__(self): + return self + + def __next__(self): + if len(self.visit) == 0: + raise StopIteration + + node = self.visit[0][0] + i = self.visit[0][1] + depth = self.visit[0][2] + self.visit.popleft() + + value = node['key'] + left = node['child'][0] + right = node['child'][1] + print("it: ", node, " ", left, " ", right); + + if right != 0: + self.visit.appendleft((right, 1, depth + 1)) + if left != 0: + self.visit.appendleft((left, 0, depth + 1)) + + index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers + if i == 0: + index += '├' + else: + index += '└' + + index += '─' + index += '[{}]'.format(node) + return index, value + + + def __init__(self, val): + self.size = val['_size'] + self.root = val['_root'] + + def to_string(self): + if self.size == 0: + return "{ empty }" + return "{ size = " + str(self.size) + " }" + + def children(self): + print("root: ", self.root) + return self.Iterator(self.root) + + # Graph ================================================================================================================ class GraphPrinter: @@ -611,7 +670,7 @@ def register_printers(): pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter) pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter) pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter) - pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', BinTreePrinter) + pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', SequencePrinter) pp.add_printer('fennec::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter) pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter) return pp diff --git a/include/fennec/containers/bintree.h b/include/fennec/containers/bintree.h index 4ae74c9..840afef 100644 --- a/include/fennec/containers/bintree.h +++ b/include/fennec/containers/bintree.h @@ -38,6 +38,8 @@ #include #include +// TODO: this should probably be refactored to use pointers over tables + namespace fennec { @@ -579,8 +581,8 @@ public: }; struct in_order { - size_t head; list visit; + size_t head; constexpr size_t operator()(const bintree& tree, size_t start) { head = start; @@ -683,8 +685,9 @@ public: constexpr iterator(bintree* tree, size_t root, size_t node) : _tree(tree) - , _order(root) + , _order() , _n(node) { + _order.head = root; } size_t index() const { diff --git a/include/fennec/containers/list.h b/include/fennec/containers/list.h index 7009f3d..884b18f 100644 --- a/include/fennec/containers/list.h +++ b/include/fennec/containers/list.h @@ -445,10 +445,7 @@ public: // prefix operator constexpr friend iterator& operator++(iterator& rhs) { - if (rhs._list->_next(rhs._n) < rhs._list->capacity()) { - return rhs; - } - rhs._n = npos; + rhs._n = rhs._list->_next(rhs._n); return rhs; } diff --git a/include/fennec/containers/sequence.h b/include/fennec/containers/sequence.h index d8e662c..b335544 100644 --- a/include/fennec/containers/sequence.h +++ b/include/fennec/containers/sequence.h @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include #include @@ -47,6 +50,9 @@ // but now in the higher end it remains consistent. Something I was doing was disturbing both the rb-tree and bst tree // properties, now that is fixed. I'll see about optimizing more in the future. +// I realized that the way bintree is setup makes some insert calls O(n + log n) = O(n), so I switched to a pointer based model. +// This increased performance overall maintaining O(log n). + namespace fennec { @@ -74,50 +80,138 @@ namespace fennec /// \tparam CompareT Function for comparing two values /// \tparam AllocT An allocator class template, class AllocT = allocator>> -struct sequence : protected bintree, AllocT> { +struct sequence { // Definitions ========================================================================================================= protected: - struct node; + struct _node; public: using value_t = TypeT; using node_t = pair; - using alloc_t = allocator_traits::template rebind; - using base_t = bintree, AllocT>; + using alloc_t = allocator_traits::template rebind<_node>; using compare_t = CompareT; - static constexpr size_t npos = -1; enum color_ : bool { black = false, red = true, }; + enum dir_ : bool { + dir_left = false, + dir_right = true, + }; + class iterator; protected: - using typename base_t::in_order; + using node = _node*; - using base_t::left; - using base_t::right; - using base_t::child; - using base_t::direction; - using base_t::parent; - using base_t::grandparent; - using base_t::sibling; + struct _node { + node parent; + node child[2]; + value_t key; + bool color; - using base_t::left_most; - using base_t::right_most; + template + constexpr _node(ArgsT&&...args) + : parent(nullptr) + , child { nullptr, nullptr } + , key(fennec::forward(args)...) + , color(red) { + } - using base_t::emplace_left; - using base_t::emplace_right; + template + constexpr _node(node p, node l, node r, ArgsT&&...args) + : parent(p) + , child { l, r } + , key(fennec::forward(args)...) + , color(red) { + } - using base_t::rotate; + constexpr ~_node() { + parent = nullptr; + child[0] = nullptr; + child[1] = nullptr; + } + }; - using base_t::_table; - using base_t::_freed; - using base_t::_root; - using base_t::_size; + +// Member Access Helpers + + constexpr value_t& _key(node n) { + return n->key; + } + + constexpr bool& _color(node n) { + return n->color; + } + + constexpr node& _parent(node n) { + return n->parent; + } + + constexpr node& _child(node n, bool dir) { + return n->child[dir]; + } + + constexpr node& _left(node n) { + return n->child[dir_left]; + } + + constexpr node& _right(node n) { + return n->child[dir_right]; + } + + +// Safe Member Access Helpers + + + constexpr const value_t& key(node n) const { + return n->key; + } + + constexpr bool color(node n) { + return n ? n->color : (bool)black; + } + + constexpr node parent(node n) { + return n ? n->parent : nullptr; + } + + constexpr node child(node n, bool dir) { + return n ? n->child[dir] : nullptr; + } + + constexpr node left(node n) { + return n ? n->child[dir_left] : nullptr; + } + + constexpr node right(node n) { + return n ? n->child[dir_right] : nullptr; + } + + constexpr node leftmost(node n) { + if (n == nullptr) { + return nullptr; + } + + while (this->_left(n)) { + n = this->_left(n); + } + return n; + } + + constexpr node rightmost(node n) { + if (n == nullptr) { + return nullptr; + } + + while (this->_right(n)) { + n = this->_right(n); + } + return n; + } // Constructors & Destructors ========================================================================================== public: @@ -126,7 +220,9 @@ public: /// /// \brief Default Constructor, initializes an empty sequence - constexpr sequence() = default; + constexpr sequence() + : _root(nullptr), _size(0) { + } /// /// \brief Move Constructor, takes ownership of a sequence @@ -154,11 +250,11 @@ public: /// \param val The value to find /// \returns An iterator at the value constexpr iterator find(const value_t& val) { - size_t node = _root; - while (node != npos) { - if (_compare(val, _value(node))) { + node node = _root; + while (node) { + if (_compare(val, _key(node))) { node = _left(node); - } else if (_compare(_value(node), val)) { + } else if (_compare(_key(node), val)) { node = _right(node); } else { return sequence::iterator(this, _root, node); @@ -185,15 +281,15 @@ public: /// /// \returns The number of elements in the sequence - using base_t::size; - - /// - /// \returns The capacity of the underlying allocation - using base_t::capacity; + constexpr size_t size() const { + return _size; + } /// /// \returns `true` when there are no elements in the sequence, `false` otherwise. - using base_t::empty; + constexpr bool empty() const { + return _size == 0; + } /// @} @@ -206,7 +302,7 @@ public: /// \brief Move Insertion, moves `val` into the sequence /// \param val The value to insert constexpr void insert(value_t&& val) { - size_t i = _insert_bst(fennec::forward(val)); + node i = _insert_bst(fennec::forward(val)); _fix_insert(i); } @@ -214,7 +310,7 @@ public: /// \brief Copy Insertion, inserts a copy of `val` into the sequence /// \param val The value to insert constexpr void insert(const value_t& val) { - size_t i = _insert_bst(val); + node i = _insert_bst(val); _fix_insert(i); } @@ -224,24 +320,25 @@ public: /// \param args The arguments to construct with template constexpr void emplace(ArgsT&&...args) { - size_t i = _insert_bst(fennec::forward(args)...); + node i = _insert_bst(fennec::forward(args)...); _fix_insert(i); } constexpr void erase(const value_t& val) { - _erase(find(val).index()); + _erase(find(val)._node); } /// /// \brief Destructs all elements, *in-order*, contained in the sequence constexpr void clear() { - in_order order; - size_t node = order(*this, _root); - while (node != npos) { - size_t erase = node; - node = order[*this, node, traversal_control_continue]; - fennec::destruct(&_table[erase]); + list visit; + for (iterator it = begin(); it != end(); ++it) { + visit.push_back(it._node); } + for (node n : visit) { + _free_node(n); + } + _root = nullptr; _size = 0; } @@ -258,67 +355,127 @@ public: /// /// \returns An iterator after the largest element in the sequence constexpr iterator end() { - return sequence::iterator(this, _root, npos); + return sequence::iterator(this, _root, nullptr); } - class iterator : public base_t::iterator { + class iterator { protected: - using base_t::iterator::_n; - using base_t::iterator::_tree; + sequence* _seq; + node _head; + node _node; + list _visit; public: - using base_t::iterator::iterator; + constexpr iterator(sequence* seq, node start) + : _seq(seq) + , _head(start) + , _node(seq->leftmost(start)) { + } - value_t& operator*() { - return base_t::iterator::operator*().first; + constexpr iterator(sequence* seq, node root, node start) + : _seq(seq) + , _head(root) + , _node(start) { + } + + iterator& operator++() { + if (_node == nullptr) { + return *this; + } + + node parent = _seq->_parent(_node); + node pright = _seq->right(parent); + node next = _seq->leftmost(_seq->right(_node)); + + if (_node != pright && parent != nullptr) { + _visit.push_front(parent); + } + + if (next != nullptr) { + _visit.push_front(next); + } + + if (not _visit.empty()) { + _node = _visit.front(); + _visit.pop_front(); + } else { + _node = nullptr; + } + + return *this; + } + + iterator operator++(int) { + iterator prev = *this; + this->operator++(); + return prev; } const value_t& operator*() const { - return base_t::iterator::operator*().first; - } - - value_t* operator->() { - return &base_t::iterator::operator*().first; + return _node->key; } const value_t* operator->() const { - return &base_t::iterator::operator*().firstf; + return &_node->key; } + + constexpr friend bool operator==(const iterator& lhs, const iterator& rhs) { + return lhs._node == rhs._node; + } + + friend struct sequence; }; // Fields ============================================================================================================== protected: + alloc_t _alloc; + node _root; compare_t _compare; - inline static bool color_sink = red; - inline static value_t value_sink; + size_t _size; // Helpers ============================================================================================================= protected: - using base_t::_left; - using base_t::_right; - using base_t::_parent; - using base_t::_child; - constexpr value_t& _value(size_t i) { - return _table[i].value.first; + template + constexpr node _make_node(ArgsT&&...args) { + node res = _alloc.allocate(1); + fennec::construct(res, fennec::forward(args)...); + return res; } - constexpr const value_t& _value(size_t i) const { - return _table[i].value.first; + constexpr void _free_node(node n) { + fennec::destruct(n); + _alloc.deallocate(n); } - constexpr bool& _color(size_t i) { - return _table[i].value.second; + constexpr node _rotate(node sub, bool dir) { + if (sub == nullptr) { + return nullptr; + } + node sub_parent = _parent(sub); + node new_root = _child(sub, not dir); + node new_child = _child(new_root, dir); + + _child(sub, not dir) = new_child; + if (new_child != nullptr) { + _parent(new_child) = sub; + } + _child(new_root, dir) = sub; + + _parent(new_root) = sub_parent; + _parent(sub) = new_root; + if (sub_parent != nullptr) { + _child(sub_parent, sub == _right(sub_parent)) = new_root; + } else { + _root = new_root; + } + return new_root; } - constexpr bool color(size_t i) const { - return i == npos ? false : _table[i].value.second; - } - - constexpr void _recolor(size_t n) { - bool c = !color(n); + constexpr void _recolor(node n) { + bool c = color(n) == black; if (n == _root) { // Only recolor if not the root node _color(n) = c; } @@ -328,45 +485,53 @@ protected: // run-of-the-mill bst insert template - constexpr size_t _insert_bst(ArgsT&&...args) { - value_t val(fennec::forward(args)...); + constexpr node _insert_bst(ArgsT&&...args) { + node res = _make_node(fennec::forward(args)...); - if (_root == npos) { - return _root = base_t::emplace(npos, false, fennec::move(val), red); + if (_root == nullptr) { + ++_size; + _color(res) = black; + return _root = res; } - size_t i = _root; - size_t p = npos; - while (i != npos) { + node i = _root; + node p = nullptr; + bool d = dir_left; + while (i != nullptr) { p = i; - if (_compare(val, _value(i))) { + if (_compare(_key(res), _key(i))) { i = _left(i); - } else if (_compare(_value(i), val)) { + d = dir_left; + } else if (_compare(_key(i), _key(res))) { i = _right(i); + d = dir_right; } else { - return npos; + _free_node(res); + return nullptr; } } - bool d = _compare(val, _value(p)); - return base_t::emplace(p, !d, fennec::move(val), red); + ++_size; + _child(p, d) = res; + _parent(res) = p; + return res; } // This makes some cheats given that the structure is modified only by internal functions // If such is the case, ONLY LL, LR, RL, and RR will show up // Then we just need to handle splitting a 4-node - constexpr void _fix_insert(size_t n) { - if (n == npos) { + constexpr void _fix_insert(node n) { + if (n == nullptr) { return; } - size_t p = _parent(n); + node p = _parent(n); while (n != _root && color(n) == red && color(p) == red) { - size_t g = _parent(p); - bool d = n == _right(p); - bool r = p == _right(g); - size_t u = _child(g, !r); + node g = _parent(p); + bool d = n == _right(p); + bool r = p == _right(g); + node u = _child(g, !r); if (color(u) == red) { _recolor(g); @@ -376,12 +541,12 @@ protected: } if (d != r) { - rotate(p, r); + _rotate(p, r); n = p; p = _parent(n); } - rotate(g, not r); + _rotate(g, not r); fennec::swap(_color(p), _color(g)); n = p; p = _parent(n); @@ -389,13 +554,13 @@ protected: _color(_root) = black; } - constexpr void _swap_val(size_t a, size_t b) { - fennec::swap(_value(a), _value(b)); + constexpr void _swap_val(node a, node b) { + fennec::swap(_key(a), _key(b)); } - constexpr size_t _red_child(size_t x) { - size_t l = _left(x); - size_t r = _right(x); + constexpr node _red_child(node x) { + node l = _left(x); + node r = _right(x); if (color(l) == red) { return l; @@ -405,32 +570,32 @@ protected: return r; } - return npos; + return nullptr; } - constexpr void _fix_erase(size_t n) { - if (n == npos) { + constexpr void _fix_erase(node n) { + if (n == nullptr) { return; } if (n == _root) { - _root = npos; + _root = nullptr; return; } - size_t o = n; - size_t p = _parent(n); - if (p == npos) { - _root = npos; + node o = n; + node p = _parent(n); + if (p == nullptr) { + _root = nullptr; return; } - bool d = n == _right(p); - size_t c = _red_child(n); - size_t s = npos; - if (_color(n) == red || c != npos) { + bool d = n == _right(p); + node c = _red_child(n); + node s = nullptr; + if (_color(n) == red || c != nullptr) { _child(p, d) = c; - if (c != npos) { + if (c != nullptr) { _parent(c) = p; } _color(c) = black; @@ -442,19 +607,19 @@ protected: d = n == _right(p); s = _child(p, !d); - if (s == npos) { + if (s == nullptr) { break; } if (_color(s) == red) { _color(s) = black; _color(p) = red; - rotate(p, d); + _rotate(p, d); continue; } - size_t nc = _child(s, d); - size_t nf = _child(s, !d); + node nc = _child(s, d); + node nf = _child(s, !d); if (color(nc) == black && color(nf) == black) { _color(s) = red; @@ -469,62 +634,61 @@ protected: if (color(nf) == black) { _color(nc) = black; _color(s) = red; - rotate(s, !d); + _rotate(s, !d); s = nc; nf = s; } _color(s) = _color(p); _color(p) = black; _color(nf) = black; - rotate(p, d); + _rotate(p, d); break; } p = parent(o); - if (p != npos) { + if (p != nullptr) { if (o == _left(p)) { - _left(p) = npos; + _left(p) = nullptr; } else { - _right(p) = npos; + _right(p) = nullptr; } _color(_root) = black; } else { - _root = npos; + _root = nullptr; } } - constexpr void _erase(size_t n) { - if (n == npos) { + constexpr void _erase(node n) { + if (n == nullptr) { return; } - size_t l = _left(n); - size_t r = _right(n); + node l = _left(n); + node r = _right(n); // 2 children - if (l != npos && r != npos) { - size_t s = left_most(r); + if (l != nullptr && r != nullptr) { + node s = leftmost(r); _swap_val(n, s); n = s; l = _left(n); r = _right(n); } - size_t p = _parent(n); - bool d = n == right(p); - size_t c = l != npos ? l : r; + node p = _parent(n); + bool d = n == right(p); + node c = l != nullptr ? l : r; // Single child - if (c != npos) { + if (c != nullptr) { _parent(c) = p; } // Handles root cases - if (p == npos) { + if (p == nullptr) { _root = c; - if (c == npos) { - fennec::destruct(&_table[n]); - _freed.push_back(n); + if (c == nullptr) { + _free_node(n); --_size; return; } else { @@ -533,19 +697,17 @@ protected: } // Single Child, Red, and Root cases - if (p == npos || c != npos || _color(n) == red) { - if (p != npos) { + if (p == nullptr || c != nullptr || _color(n) == red) { + if (p != nullptr) { _child(p, d) = c; } - fennec::destruct(&_table[n]); - _freed.push_back(n); + _free_node(n); --_size; return; } _fix_erase(n); - fennec::destruct(&_table[n]); - _freed.push_back(n); + _free_node(n); --_size; } }; diff --git a/include/fennec/core/event.h b/include/fennec/core/event.h index 070cd25..4ce443a 100644 --- a/include/fennec/core/event.h +++ b/include/fennec/core/event.h @@ -27,20 +27,33 @@ namespace fennec struct event; +/// +/// \brief Class outlining the interface for an object that listens for events class event_listener { public: virtual ~event_listener() = default; virtual void handle_event(event* event) = 0; }; -struct event { - const uint64_t type; +/// +/// \brief Main event interface, includes static methods for registering listeners and dispatching events +struct event : typed { + // Delete default constructor to enforce type scheme event() = delete; + /// + /// \brief Event Constructor, stores the event type + /// \tparam EventT The type of the event template - event() : type(typeuuid()) { } + event(EventT* type) + : typed(type) { + } + /// + /// \brief Registers a listener for the event type + /// \tparam EventT + /// \param listener template static void add_listener(event_listener* listener) { event::add_listener(listener, typeuuid()); diff --git a/include/fennec/lang/typed.h b/include/fennec/lang/typed.h index 636e8dc..c79d60c 100644 --- a/include/fennec/lang/typed.h +++ b/include/fennec/lang/typed.h @@ -32,8 +32,6 @@ constexpr uint64_t nullid = 0; template FENNEC_NO_INLINE uint64_t typeuuid() { - assertf(not is_constant_evaluated(), "Type UUIDs Cannot Be Obtained at Compile Time"); - static bool init = false; static uint64_t id = nullid; diff --git a/include/fennec/langproc/compile/tokenizer.h b/include/fennec/langproc/compile/tokenizer.h index bbcefaf..fd44438 100644 --- a/include/fennec/langproc/compile/tokenizer.h +++ b/include/fennec/langproc/compile/tokenizer.h @@ -67,26 +67,40 @@ struct tokenizer { string brackets; // characters that mark brackets string quotes; // characters that mark a string sequence, entire string sequence is treated as one token escmap escapes; // characters that mark the start of an escape sequence and validate them + bool numbers; // Anything that resembles a number enum token_ : uint8_t { - token_string = 0, - token_newline = 1, - token_escaped = 2, - token_operator = 3, - token_bracket = 4, - token_quoted = 5, + token_text = 0, + token_integer, + token_string, + token_newline, + token_escaped, + token_operator, + token_bracket, + token_quoted, + + num_token_types }; using token = pair; private: -// list operator()(const string& line) { -// list res; -// -// for (size_t i = 0; i < ) -// -// return res; -// } + static constexpr uint8_t token_delimiter = num_token_types; + + constexpr list operator()(const string& line) { + list res; + priority_queue> idx; + + for (size_t i = 0; i < line.size(); ++i) { + + for (char c : delimiter) { + idx.emplace() + } + + } + + return res; + } private: }; diff --git a/include/fennec/langproc/strings/cstring.h b/include/fennec/langproc/strings/cstring.h index 2f34022..4edf8cc 100644 --- a/include/fennec/langproc/strings/cstring.h +++ b/include/fennec/langproc/strings/cstring.h @@ -48,6 +48,12 @@ using ::ispunct; using ::tolower; using ::toupper; + +struct string_view { + const char* str; + size_t len; +}; + /// /// \brief This struct wraps c-style strings /// @@ -82,7 +88,9 @@ public: : _str(str) , _size(n - 1) , _const(false) { - assert(_str[n - 1] == '\0', "Invalid NTBS."); + if constexpr(not is_constant_evaluated()) { + assert(_str[n - 1] == '\0', "Invalid NTBS."); + } } /// @@ -94,7 +102,9 @@ public: : _str(str) , _size(n - 1) , _const(false) { - assert(_str[n - 1] == '\0', "Invalid NTBS."); + if constexpr(not is_constant_evaluated()) { + assert(_str[n - 1] == '\0', "Invalid NTBS."); + } } /// @@ -105,7 +115,9 @@ public: : _cstr(str) , _size(n - 1) , _const(true) { - assert(_cstr[n - 1] == '\0', "Invalid NTBS."); + if constexpr(not is_constant_evaluated()) { + assert(_str[n - 1] == '\0', "Invalid NTBS."); + } } /// @@ -117,7 +129,9 @@ public: : _cstr(str) , _size(n - 1) , _const(true) { - assert(_cstr[n - 1] == '\0', "Invalid NTBS."); + if constexpr(not is_constant_evaluated()) { + assert(_str[n - 1] == '\0', "Invalid NTBS."); + } } /// @@ -170,11 +184,15 @@ public: /// /// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'` - constexpr size_t size() const { return _size; } + constexpr size_t size() const { + return _size; + } /// /// \returns the size of the string including its null terminator, i.e. `(*str)[capacity() - 1] == '\0'` - constexpr size_t capacity() const { return _size + 1; } + constexpr size_t capacity() const { + return _size + 1; + } constexpr bool empty() const { return _cstr == nullptr || _size == 0; diff --git a/include/fennec/langproc/strings/format.h b/include/fennec/langproc/strings/format.h new file mode 100644 index 0000000..4dee0c9 --- /dev/null +++ b/include/fennec/langproc/strings/format.h @@ -0,0 +1,174 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +/// +/// \file format.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_LANGPROC_STRINGS_FORMAT_H +#define FENNEC_LANGPROC_STRINGS_FORMAT_H +#include +#include +#include + +namespace fennec +{ + +template +struct formatter { + constexpr string operator()(const string_view&, const TypeT&) { + static_assert(false, "Formatter not implemented for the provided type."); + return string{""}; + } +}; + +template<> +struct formatter { + constexpr string operator()(const string_view&, const cstring& str) { + return string(str); + } +}; + +template +struct formatter { + constexpr string operator()(const string_view&, const char (&str)[N]) { + return string(str); + } +}; + +template +struct format_string { + +// Definitions ========================================================================================================= +private: + static constexpr size_t npos = -1; + + enum token_ : uint8_t { + token_text = 0, + token_param, + }; + + using token = pair; + + constexpr const char* find(const char* start, char c) { + while (*start != c && *start != '\0') { + ++start; + } + return start; + } + + +// Constructors & Destructor =========================================================================================== +public: + format_string() = delete; + + format_string(const char (&str)[N]) + : _tokens(), _num_tokens(0) { + static_assert(is_constant_evaluated(), "Runtime format strings are not supported."); + + // TODO: Numbered Parameters + + size_t params = 0; + for (size_t i = 0; i < N; ++i) { + size_t n = this->find(str + i, '{') - str; + + // Push the current token + if (n - i > 0) { + _tokens[_num_tokens++] = { token_text, { &str[i], n - i } }; + } + + // no more braces, break + if (n >= N) { + break; + } + + // escaped brace + if (str[n + 1] == '{') { + _tokens[_num_tokens++] = { token_text, { &str[n], 1 } }; + i = n + 1; + continue; + } + + size_t e = this->find(str + n, '}') - str; + //static_assert(e >= N, "Malformed format string, mismatched brace"); + + _tokens[_num_tokens++] = { token_param, { &str[n], e - n + 1 } }; + ++params; + i = e; + } + + //static_assert(params == sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count."); + } + + constexpr format_string(const format_string&) = default; + constexpr format_string(format_string&&) noexcept = default; + + ~format_string() = default; + + string format(ArgsT&&...args) const { + return this->_format(0, fennec::forward(args)...); + } + +private: + array _tokens; + size_t _num_tokens; + + string _format(size_t) const { + return string{""}; + } + + template + string _format(size_t i, HeadT&& head, RestT&&...rest) const { + string res; + for (; i < _num_tokens; ++i) { + const auto& token = _tokens[i]; + + if (token.first == token_param) { + formatter fmt; + res += fmt(token.second, head); + res += this->_format(i + 1, fennec::forward(rest)...); + break; + } + + res += token.second; + } + return res; + } +}; + +template +string _format(const format_string& str, ArgsT&&...args) { + return str.format(fennec::forward(args)...); +} + +template +string format(const char (&str)[N], ArgsT&&...args) { + return fennec::_format(format_string(str), fennec::forward(args)...); +} + +} + +#endif // FENNEC_LANGPROC_STRINGS_FORMAT_H \ No newline at end of file diff --git a/include/fennec/langproc/strings/string.h b/include/fennec/langproc/strings/string.h index 884b0ca..1098ca4 100644 --- a/include/fennec/langproc/strings/string.h +++ b/include/fennec/langproc/strings/string.h @@ -108,6 +108,10 @@ public: } } + constexpr _string(const string_view& view) + : _string(view.str, view.len) { + } + /// /// \brief String Copy Constructor /// \param str the string to copy diff --git a/include/fennec/math/ext/transform.h b/include/fennec/math/ext/transform.h index bdd59c7..187c14f 100644 --- a/include/fennec/math/ext/transform.h +++ b/include/fennec/math/ext/transform.h @@ -32,7 +32,7 @@ namespace fennec /// \param x a vector containing the position /// \returns An identity matrix with the last column set to x template -constexpr mat translation(const vec& x) { +constexpr mat translation(const vector& x) { return mat( 1, 0, 0, 0, 1, 0, @@ -45,7 +45,7 @@ constexpr mat translation(const vec& x) { /// \param x a vector containing the position /// \returns An identity matrix with the last column set to x template -constexpr mat translation(const vec& x) { +constexpr mat translation(const vector& x) { return mat( 1, 0, 0, 0, 0, 1, 0, 0, @@ -59,7 +59,7 @@ constexpr mat translation(const vec& x) { /// \param x a vector containing the scale for each axis /// \returns A diagonal matrix with the terms of x template -constexpr mat scaling(const vec& x) { +constexpr mat scaling(const vector& x) { return mat( x.x, 0, 0, 0, x.y, 0, @@ -72,7 +72,7 @@ constexpr mat scaling(const vec& x) { /// \param x a vector containing the scale for each axis /// \returns A diagonal matrix with the terms of x template -constexpr mat scaling(const vec& x) { +constexpr mat scaling(const vector& x) { return mat( x.x, 0, 0, 0, 0, x.y, 0, 0, @@ -81,6 +81,15 @@ constexpr mat scaling(const vec& x) { ); } +template +constexpr mat shear(const vector& x) { + return mat( + 1, x.x, 0, + x.y, 1, 0, + 0, 0, 1 + ); +} + /// /// \brief Create a 2d rotation matrix /// \param a the angle of the rotation in radians @@ -106,7 +115,7 @@ constexpr mat rotation(const genType a) { /// \param a The rotation about the axis in radians /// \returns A rotation matrix about A template -constexpr mat rotation(const vec& A, genType a) { +constexpr mat rotation(const vector& A, genType a) { // https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle // Calculate sin and cos terms @@ -114,8 +123,8 @@ constexpr mat rotation(const vec& A, genType a) { const genType s = fennec::sin(a); // Calculate axis term - const vec u = (fennec::one() - c) * A; - const vec v = s * A; + const vector u = (fennec::one() - c) * A; + const vector v = s * A; // Calculate the Matrix return mat( @@ -126,20 +135,20 @@ constexpr mat rotation(const vec& A, genType a) { ); } -template -constexpr qua rotation(const vec& axis, genType angle) { - vec a = fennec::normalize(axis); - const genType s = fennec::sin(angle); - - return qua(fennec::cos(angle * genType(0.5)), s * a); -} +// template +// constexpr qua rotation(const vector& axis, genType angle) { +// vector a = fennec::normalize(axis); +// const genType s = fennec::sin(angle); +// +// return qua(fennec::cos(angle * genType(0.5)), s * a); +// } /// \brief enum to denote the unit-axis of rotation -enum rot_ +enum rotation_ { - rot_x = 0 -, rot_y -, rot_z + rotation_x = 0 +, rotation_y +, rotation_z }; /// @@ -147,7 +156,7 @@ enum rot_ /// \tparam axis the unit axis to rotate around /// \param a the angle, in radians /// \returns a matrix that rotates vectors around a unit axis -template +template constexpr mat rotation(genType a) { // https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations @@ -156,7 +165,7 @@ constexpr mat rotation(genType a) { const genType s = fennec::sin(a); // Calculate the matrix - if constexpr(axis == rot_x) { + if constexpr(axis == rotation_x) { return mat( 1, 0, 0, 0, 0, c, -s, 0, @@ -164,7 +173,7 @@ constexpr mat rotation(genType a) { 0, 0, 0, 1 ); } - else if constexpr(axis == rot_y) { + else if constexpr(axis == rotation_y) { return mat( c, 0, s, 0, 0, 0, 0, 0, @@ -172,7 +181,7 @@ constexpr mat rotation(genType a) { 0, 0, 0, 1 ); } - else if constexpr(axis == rot_z) { + else if constexpr(axis == rotation_z) { return mat( c, -s, 0, 0, s, c, 0, 0, @@ -202,7 +211,7 @@ enum order_ /// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll) /// \returns a matrix that rotates vectors template -constexpr mat rotation(const vec& E) { +constexpr mat rotation(const vector& E) { // expanded using a CAS // Calculate sin and cos terms @@ -221,40 +230,35 @@ constexpr mat rotation(const vec& E) { sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0, 0, 0, 0, 1 ); - } - else if constexpr(order == order_xzy) { + } else if constexpr(order == order_xzy) { return mat( cb*cg, -sg, cg*sb, 0, ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*sb, 0, cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0, 0, 0, 0, 1 ); - } - else if constexpr(order == order_yxz) { + } else if constexpr(order == order_yxz) { return mat( cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0, ca*sg, ca*cg, -sa, 0, cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0, 0, 0, 0, 1 ); - } - else if constexpr(order == order_yzx) { + } else if constexpr(order == order_yzx) { return mat( cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0, sg, ca*cg, -cg*sa, 0, -cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0, 0, 0, 0, 1 ); - } - else if constexpr(order == order_zxy) { + } else if constexpr(order == order_zxy) { return mat( cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0, cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0, -ca*sb, sa, ca*cb, 0, 0, 0, 0, 1 ); - } - else if constexpr(order == order_zyx) { + } else if constexpr(order == order_zyx) { return mat( cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0, cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0, diff --git a/include/fennec/math/matrix.h b/include/fennec/math/matrix.h index 60a50cb..872c0ff 100644 --- a/include/fennec/math/matrix.h +++ b/include/fennec/math/matrix.h @@ -251,7 +251,6 @@ struct matrix /// \param mat matrix to copy constexpr matrix(const matrix_t& mat) : data{ mat.data } { - } /// @@ -261,7 +260,26 @@ struct matrix /// \param mat matrix to move constexpr matrix(matrix_t&& mat) noexcept : data{ mat.data } { + } + template + constexpr matrix(const matrix& mat) + : matrix() { + for (size_t i = 0; i < columns; ++i) { + for (size_t j = 0; j < rows; ++j) { + data[i][j] = scalar_t(mat[i][j]); + } + } + } + + template + constexpr matrix(matrix&& mat) noexcept + : matrix() { + for (size_t i = 0; i < columns; ++i) { + for (size_t j = 0; j < rows; ++j) { + data[i][j] = scalar_t(mat[i][j]); + } + } } @@ -629,6 +647,11 @@ struct matrix ); } + template requires(columns == ORowsV) + constexpr matrix& operator*=(const matrix& rhs) { + return *this = *this * rhs; + } + /// @} diff --git a/include/fennec/math/vector.h b/include/fennec/math/vector.h index dfc5867..d5d3eba 100644 --- a/include/fennec/math/vector.h +++ b/include/fennec/math/vector.h @@ -1086,22 +1086,22 @@ private: } template - constexpr void _insert(ScalarT& x) { + constexpr void _insert(const ScalarT& x) { data[OffsetV] = x; } template - constexpr void _insert(OScalarT& x) { + constexpr void _insert(const OScalarT& x) { data[OffsetV] = ScalarT(x); } template - constexpr void _insert(vector& vec) { + constexpr void _insert(const vector& vec) { ((data[OffsetV + OIndicesV] = fennec::forward(vec[OIndicesV])), ...); } template - constexpr void _insert(swizzle& vec) { + constexpr void _insert(const swizzle& vec) { size_t i = 0; ((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...); } diff --git a/include/fennec/scene/component.h b/include/fennec/scene/component.h index 1b7d2d9..d9b05f4 100644 --- a/include/fennec/scene/component.h +++ b/include/fennec/scene/component.h @@ -28,7 +28,7 @@ namespace fennec { -class component { +class component : typed { public: // TYPEDEFS & CONSTANTS ================================================================================================ @@ -196,8 +196,8 @@ public: const size_t node; template - component(size_t node) - : type(type_list()[uuid()]) + component(ComponentT* type, size_t node) + : typed(type) , node(node) { } }; diff --git a/include/fennec/scene/components/transform2d.h b/include/fennec/scene/components/transform2d.h new file mode 100644 index 0000000..f91a152 --- /dev/null +++ b/include/fennec/scene/components/transform2d.h @@ -0,0 +1,154 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +/// +/// \file transform2d.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_SCENE_COMPONENTS_TRANSFORM2D_H +#define FENNEC_SCENE_COMPONENTS_TRANSFORM2D_H + +#include +#include +#include + +namespace fennec +{ + +struct transform2d : component { +// Definitions ========================================================================================================= + + enum mobility_ : bool { + mobility_static = false, + mobility_free = true + }; + + +// Constructors & Destructor =========================================================================================== +public: + + /// + /// \brief Default Constructor, initializes an identity matrix. + transform2d(size_t node) + : component(this, node) + , _position(0, 0) + , _scale(1, 1) + , _shear(0, 0) + , _rotation(0) { + } + + /// + /// \brief Component Constructor, composes the internal matrix using the components. + /// \param pos Position as vec2 + /// \param scl Scale as vec2 + /// \param rot Rotation as degrees + /// \param skw Skew as vec2 + transform2d(size_t node, const vec2& pos, const vec2& scl, float rot, const vec2& skw = vec2(0, 0)) + : component(this, node) + , _position(pos) + , _scale(scl) + , _shear(skw) + , _rotation(rot) + , _dirty(true) + , _local(matrix()) { + } + + transform2d(const transform2d&) = default; + transform2d(transform2d&&) noexcept = default; + + ~transform2d() = default; + +// Access ============================================================================================================== + + constexpr const vec2& position() const { + return _position; + } + + constexpr const vec2& scale() const { + return _scale; + } + + constexpr float rotation() const { + return _rotation; + } + + constexpr const vec2& shear() const { + return _shear; + } + + +// Modifiers =========================================================================================================== + + constexpr void translate(const vec2& x) { + _position += x; + } + + constexpr void scale(const vec2& s) { + _scale *= s; + } + + constexpr void rotate(float r) { + _rotation += r; + } + + constexpr void shear(const vec2& s) { + _shear += s; + } + + constexpr void commit() { + if (not _dirty) { + return; + } + + _local = fennec::rotation(_rotation); + _local *= fennec::shear(_shear); + _local *= fennec::scaling(_scale); + _local *= fennec::translation(_position); + } + + constexpr const mat3& local() { + commit(); + return _local; + } + + constexpr const mat3& global() { + + } + +// Fields ============================================================================================================== +private: + vec2 _position; + vec2 _scale; + vec2 _shear; + float _rotation; + bool _dirty; + mat3 _local; +}; + +} + + +#endif // FENNEC_SCENE_COMPONENTS_TRANSFORM2D_H \ No newline at end of file diff --git a/include/fennec/scene/scene.h b/include/fennec/scene/scene.h index c15de22..ae158fb 100644 --- a/include/fennec/scene/scene.h +++ b/include/fennec/scene/scene.h @@ -25,8 +25,19 @@ namespace fennec { +/// +/// \brief Main Scene Hierarchy +/// \details This structure only contains the names of nodes and defers storage for components to +/// their respective systems. Simple components may be isolated, \see components.h for more info. +/// The hierarchy should be displayed using pre-order traversal. class scene : public rdtree { public: + + /// + /// \brief Find a node by name. + /// \details If multiple nodes have the same name, finds the first one in pre-order traversal. + /// \param name The name of the node to search for + /// \returns The id of the node, `npos` if not found. size_t operator[](const cstring& name) const { list parse; parse.push_back(root); @@ -34,13 +45,20 @@ public: if (*_data[parse.front()].value == name) { return parse.front(); } - parse.push_back(child(parse.front())); - parse.push_back(next(parse.front())); + + // Pre-Order traversal + parse.push_front(next(parse.front())); + parse.push_front(child(parse.front())); parse.pop_front(); } return npos; } + /// + /// \brief Find a node by name. + /// \details If multiple nodes have the same name, finds the first one in pre-order traversal. + /// \param name The name of the node to search for + /// \returns The id of the node, `npos` if not found. size_t operator[](const string& name) const { list parse; parse.push_back(root); @@ -48,8 +66,10 @@ public: if (*_data[parse.front()].value == name) { return parse.front(); } - parse.push_back(child(parse.front())); - parse.push_back(next(parse.front())); + + // Pre-Order traversal + parse.push_front(next(parse.front())); + parse.push_front(child(parse.front())); parse.pop_front(); } return npos; diff --git a/test/tests/langproc/test_format.h b/test/tests/langproc/test_format.h index 78e7090..6b3c241 100644 --- a/test/tests/langproc/test_format.h +++ b/test/tests/langproc/test_format.h @@ -30,7 +30,7 @@ #ifndef FENNEC_TEST_LANGPROC_FORMAT_H #define FENNEC_TEST_LANGPROC_FORMAT_H -#include +#include namespace fennec { @@ -40,20 +40,7 @@ namespace test inline void fennec_test_langproc_format() { -// tokenizer math = { -// .delimiter { " " }, -// .operators {"+-/*="}, -// .braces { "()" }, -// .escapes { "" }, -// .terminator { "" }, -// }; -// -// const auto res = math.parse("1 + 2 = 3"); -// fennec_test_run(res.size(), size_t(5)); -// -// for (const auto& token : res) { -// std::cout << token.second << ", "; -// } + fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!")); } }