- Rough First Pass implementation for format.h

- Started 2D Transform Component and relevant math extensions
 - Switched sequence to use pointers instead of arrays
This commit is contained in:
2025-09-23 18:07:54 -04:00
parent 1a9a80e37f
commit f636feb4f1
18 changed files with 866 additions and 234 deletions

View File

@@ -244,6 +244,8 @@ add_library(fennec STATIC
# EXTRA SOURCES ======================================================================================================== # EXTRA SOURCES ========================================================================================================
${FENNEC_EXTRA_SOURCES} ${FENNEC_EXTRA_SOURCES}
include/fennec/langproc/strings/format.h
include/fennec/scene/components/transform2d.h
) )
add_dependencies(fennec metaprogramming fennec-dependencies) add_dependencies(fennec metaprogramming fennec-dependencies)

View File

@@ -524,6 +524,65 @@ class BinTreePrinter:
return self.Iterator(self.tree, self.root, self.capacity) 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 ================================================================================================================ # Graph ================================================================================================================
class GraphPrinter: class GraphPrinter:
@@ -611,7 +670,7 @@ def register_printers():
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter) pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter) pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter) 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::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter)
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter) pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp return pp

View File

@@ -38,6 +38,8 @@
#include <fennec/math/exponential.h> #include <fennec/math/exponential.h>
#include <fennec/memory/allocator.h> #include <fennec/memory/allocator.h>
// TODO: this should probably be refactored to use pointers over tables
namespace fennec namespace fennec
{ {
@@ -579,8 +581,8 @@ public:
}; };
struct in_order { struct in_order {
size_t head;
list<size_t> visit; list<size_t> visit;
size_t head;
constexpr size_t operator()(const bintree& tree, size_t start) { constexpr size_t operator()(const bintree& tree, size_t start) {
head = start; head = start;
@@ -683,8 +685,9 @@ public:
constexpr iterator(bintree* tree, size_t root, size_t node) constexpr iterator(bintree* tree, size_t root, size_t node)
: _tree(tree) : _tree(tree)
, _order(root) , _order()
, _n(node) { , _n(node) {
_order.head = root;
} }
size_t index() const { size_t index() const {

View File

@@ -445,10 +445,7 @@ public:
// prefix operator // prefix operator
constexpr friend iterator& operator++(iterator& rhs) { constexpr friend iterator& operator++(iterator& rhs) {
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) { rhs._n = rhs._list->_next(rhs._n);
return rhs;
}
rhs._n = npos;
return rhs; return rhs;
} }

View File

@@ -36,6 +36,9 @@
#include <fennec/containers/sequence.h> #include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h> #include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h> #include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h>
#include <fennec/lang/compare.h> #include <fennec/lang/compare.h>
#include <fennec/memory/allocator.h> #include <fennec/memory/allocator.h>
@@ -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 // 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. // 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 namespace fennec
{ {
@@ -74,50 +80,138 @@ namespace fennec
/// \tparam CompareT Function for comparing two values /// \tparam CompareT Function for comparing two values
/// \tparam AllocT An allocator class /// \tparam AllocT An allocator class
template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>> template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
struct sequence : protected bintree<pair<TypeT, bool>, AllocT> { struct sequence {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
protected: protected:
struct node; struct _node;
public: public:
using value_t = TypeT; using value_t = TypeT;
using node_t = pair<TypeT, bool>; using node_t = pair<TypeT, bool>;
using alloc_t = allocator_traits<AllocT>::template rebind<node>; using alloc_t = allocator_traits<AllocT>::template rebind<_node>;
using base_t = bintree<pair<TypeT, bool>, AllocT>;
using compare_t = CompareT; using compare_t = CompareT;
static constexpr size_t npos = -1;
enum color_ : bool { enum color_ : bool {
black = false, black = false,
red = true, red = true,
}; };
enum dir_ : bool {
dir_left = false,
dir_right = true,
};
class iterator; class iterator;
protected: protected:
using typename base_t::in_order; using node = _node*;
using base_t::left; struct _node {
using base_t::right; node parent;
using base_t::child; node child[2];
using base_t::direction; value_t key;
using base_t::parent; bool color;
using base_t::grandparent;
using base_t::sibling;
using base_t::left_most; template<typename...ArgsT>
using base_t::right_most; constexpr _node(ArgsT&&...args)
: parent(nullptr)
, child { nullptr, nullptr }
, key(fennec::forward<ArgsT>(args)...)
, color(red) {
}
using base_t::emplace_left; template<typename...ArgsT>
using base_t::emplace_right; constexpr _node(node p, node l, node r, ArgsT&&...args)
: parent(p)
, child { l, r }
, key(fennec::forward<ArgsT>(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; // Member Access Helpers
using base_t::_root;
using base_t::_size; 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 ========================================================================================== // Constructors & Destructors ==========================================================================================
public: public:
@@ -126,7 +220,9 @@ public:
/// ///
/// \brief Default Constructor, initializes an empty sequence /// \brief Default Constructor, initializes an empty sequence
constexpr sequence() = default; constexpr sequence()
: _root(nullptr), _size(0) {
}
/// ///
/// \brief Move Constructor, takes ownership of a sequence /// \brief Move Constructor, takes ownership of a sequence
@@ -154,11 +250,11 @@ public:
/// \param val The value to find /// \param val The value to find
/// \returns An iterator at the value /// \returns An iterator at the value
constexpr iterator find(const value_t& val) { constexpr iterator find(const value_t& val) {
size_t node = _root; node node = _root;
while (node != npos) { while (node) {
if (_compare(val, _value(node))) { if (_compare(val, _key(node))) {
node = _left(node); node = _left(node);
} else if (_compare(_value(node), val)) { } else if (_compare(_key(node), val)) {
node = _right(node); node = _right(node);
} else { } else {
return sequence::iterator(this, _root, node); return sequence::iterator(this, _root, node);
@@ -185,15 +281,15 @@ public:
/// ///
/// \returns The number of elements in the sequence /// \returns The number of elements in the sequence
using base_t::size; constexpr size_t size() const {
return _size;
/// }
/// \returns The capacity of the underlying allocation
using base_t::capacity;
/// ///
/// \returns `true` when there are no elements in the sequence, `false` otherwise. /// \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 /// \brief Move Insertion, moves `val` into the sequence
/// \param val The value to insert /// \param val The value to insert
constexpr void insert(value_t&& val) { constexpr void insert(value_t&& val) {
size_t i = _insert_bst(fennec::forward<value_t>(val)); node i = _insert_bst(fennec::forward<value_t>(val));
_fix_insert(i); _fix_insert(i);
} }
@@ -214,7 +310,7 @@ public:
/// \brief Copy Insertion, inserts a copy of `val` into the sequence /// \brief Copy Insertion, inserts a copy of `val` into the sequence
/// \param val The value to insert /// \param val The value to insert
constexpr void insert(const value_t& val) { constexpr void insert(const value_t& val) {
size_t i = _insert_bst(val); node i = _insert_bst(val);
_fix_insert(i); _fix_insert(i);
} }
@@ -224,24 +320,25 @@ public:
/// \param args The arguments to construct with /// \param args The arguments to construct with
template<typename...ArgsT> template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) { constexpr void emplace(ArgsT&&...args) {
size_t i = _insert_bst(fennec::forward<ArgsT>(args)...); node i = _insert_bst(fennec::forward<ArgsT>(args)...);
_fix_insert(i); _fix_insert(i);
} }
constexpr void erase(const value_t& val) { 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 /// \brief Destructs all elements, *in-order*, contained in the sequence
constexpr void clear() { constexpr void clear() {
in_order order; list<node> visit;
size_t node = order(*this, _root); for (iterator it = begin(); it != end(); ++it) {
while (node != npos) { visit.push_back(it._node);
size_t erase = node;
node = order[*this, node, traversal_control_continue];
fennec::destruct(&_table[erase]);
} }
for (node n : visit) {
_free_node(n);
}
_root = nullptr;
_size = 0; _size = 0;
} }
@@ -258,67 +355,127 @@ public:
/// ///
/// \returns An iterator after the largest element in the sequence /// \returns An iterator after the largest element in the sequence
constexpr iterator end() { constexpr iterator end() {
return sequence::iterator(this, _root, npos); return sequence::iterator(this, _root, nullptr);
} }
class iterator : public base_t::iterator { class iterator {
protected: protected:
using base_t::iterator::_n; sequence* _seq;
using base_t::iterator::_tree; node _head;
node _node;
list<node> _visit;
public: public:
using base_t::iterator::iterator; constexpr iterator(sequence* seq, node start)
: _seq(seq)
, _head(start)
, _node(seq->leftmost(start)) {
}
value_t& operator*() { constexpr iterator(sequence* seq, node root, node start)
return base_t::iterator::operator*().first; : _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 { const value_t& operator*() const {
return base_t::iterator::operator*().first; return _node->key;
}
value_t* operator->() {
return &base_t::iterator::operator*().first;
} }
const value_t* operator->() const { 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 ============================================================================================================== // Fields ==============================================================================================================
protected: protected:
alloc_t _alloc;
node _root;
compare_t _compare; compare_t _compare;
inline static bool color_sink = red; size_t _size;
inline static value_t value_sink;
// Helpers ============================================================================================================= // Helpers =============================================================================================================
protected: protected:
using base_t::_left;
using base_t::_right;
using base_t::_parent;
using base_t::_child;
constexpr value_t& _value(size_t i) { template<typename...ArgsT>
return _table[i].value.first; constexpr node _make_node(ArgsT&&...args) {
node res = _alloc.allocate(1);
fennec::construct(res, fennec::forward<ArgsT>(args)...);
return res;
} }
constexpr const value_t& _value(size_t i) const { constexpr void _free_node(node n) {
return _table[i].value.first; fennec::destruct(n);
_alloc.deallocate(n);
} }
constexpr bool& _color(size_t i) { constexpr node _rotate(node sub, bool dir) {
return _table[i].value.second; 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 { constexpr void _recolor(node n) {
return i == npos ? false : _table[i].value.second; bool c = color(n) == black;
}
constexpr void _recolor(size_t n) {
bool c = !color(n);
if (n == _root) { // Only recolor if not the root node if (n == _root) { // Only recolor if not the root node
_color(n) = c; _color(n) = c;
} }
@@ -328,45 +485,53 @@ protected:
// run-of-the-mill bst insert // run-of-the-mill bst insert
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t _insert_bst(ArgsT&&...args) { constexpr node _insert_bst(ArgsT&&...args) {
value_t val(fennec::forward<ArgsT>(args)...); node res = _make_node(fennec::forward<ArgsT>(args)...);
if (_root == npos) { if (_root == nullptr) {
return _root = base_t::emplace(npos, false, fennec::move(val), red); ++_size;
_color(res) = black;
return _root = res;
} }
size_t i = _root; node i = _root;
size_t p = npos; node p = nullptr;
while (i != npos) { bool d = dir_left;
while (i != nullptr) {
p = i; p = i;
if (_compare(val, _value(i))) { if (_compare(_key(res), _key(i))) {
i = _left(i); i = _left(i);
} else if (_compare(_value(i), val)) { d = dir_left;
} else if (_compare(_key(i), _key(res))) {
i = _right(i); i = _right(i);
d = dir_right;
} else { } else {
return npos; _free_node(res);
return nullptr;
} }
} }
bool d = _compare(val, _value(p)); ++_size;
return base_t::emplace(p, !d, fennec::move(val), red); _child(p, d) = res;
_parent(res) = p;
return res;
} }
// This makes some cheats given that the structure is modified only by internal functions // 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 // If such is the case, ONLY LL, LR, RL, and RR will show up
// Then we just need to handle splitting a 4-node // Then we just need to handle splitting a 4-node
constexpr void _fix_insert(size_t n) { constexpr void _fix_insert(node n) {
if (n == npos) { if (n == nullptr) {
return; return;
} }
size_t p = _parent(n); node p = _parent(n);
while (n != _root && color(n) == red && color(p) == red) { while (n != _root && color(n) == red && color(p) == red) {
size_t g = _parent(p); node g = _parent(p);
bool d = n == _right(p); bool d = n == _right(p);
bool r = p == _right(g); bool r = p == _right(g);
size_t u = _child(g, !r); node u = _child(g, !r);
if (color(u) == red) { if (color(u) == red) {
_recolor(g); _recolor(g);
@@ -376,12 +541,12 @@ protected:
} }
if (d != r) { if (d != r) {
rotate(p, r); _rotate(p, r);
n = p; n = p;
p = _parent(n); p = _parent(n);
} }
rotate(g, not r); _rotate(g, not r);
fennec::swap(_color(p), _color(g)); fennec::swap(_color(p), _color(g));
n = p; n = p;
p = _parent(n); p = _parent(n);
@@ -389,13 +554,13 @@ protected:
_color(_root) = black; _color(_root) = black;
} }
constexpr void _swap_val(size_t a, size_t b) { constexpr void _swap_val(node a, node b) {
fennec::swap(_value(a), _value(b)); fennec::swap(_key(a), _key(b));
} }
constexpr size_t _red_child(size_t x) { constexpr node _red_child(node x) {
size_t l = _left(x); node l = _left(x);
size_t r = _right(x); node r = _right(x);
if (color(l) == red) { if (color(l) == red) {
return l; return l;
@@ -405,32 +570,32 @@ protected:
return r; return r;
} }
return npos; return nullptr;
} }
constexpr void _fix_erase(size_t n) { constexpr void _fix_erase(node n) {
if (n == npos) { if (n == nullptr) {
return; return;
} }
if (n == _root) { if (n == _root) {
_root = npos; _root = nullptr;
return; return;
} }
size_t o = n; node o = n;
size_t p = _parent(n); node p = _parent(n);
if (p == npos) { if (p == nullptr) {
_root = npos; _root = nullptr;
return; return;
} }
bool d = n == _right(p); bool d = n == _right(p);
size_t c = _red_child(n); node c = _red_child(n);
size_t s = npos; node s = nullptr;
if (_color(n) == red || c != npos) { if (_color(n) == red || c != nullptr) {
_child(p, d) = c; _child(p, d) = c;
if (c != npos) { if (c != nullptr) {
_parent(c) = p; _parent(c) = p;
} }
_color(c) = black; _color(c) = black;
@@ -442,19 +607,19 @@ protected:
d = n == _right(p); d = n == _right(p);
s = _child(p, !d); s = _child(p, !d);
if (s == npos) { if (s == nullptr) {
break; break;
} }
if (_color(s) == red) { if (_color(s) == red) {
_color(s) = black; _color(s) = black;
_color(p) = red; _color(p) = red;
rotate(p, d); _rotate(p, d);
continue; continue;
} }
size_t nc = _child(s, d); node nc = _child(s, d);
size_t nf = _child(s, !d); node nf = _child(s, !d);
if (color(nc) == black && color(nf) == black) { if (color(nc) == black && color(nf) == black) {
_color(s) = red; _color(s) = red;
@@ -469,62 +634,61 @@ protected:
if (color(nf) == black) { if (color(nf) == black) {
_color(nc) = black; _color(nc) = black;
_color(s) = red; _color(s) = red;
rotate(s, !d); _rotate(s, !d);
s = nc; s = nc;
nf = s; nf = s;
} }
_color(s) = _color(p); _color(s) = _color(p);
_color(p) = black; _color(p) = black;
_color(nf) = black; _color(nf) = black;
rotate(p, d); _rotate(p, d);
break; break;
} }
p = parent(o); p = parent(o);
if (p != npos) { if (p != nullptr) {
if (o == _left(p)) { if (o == _left(p)) {
_left(p) = npos; _left(p) = nullptr;
} else { } else {
_right(p) = npos; _right(p) = nullptr;
} }
_color(_root) = black; _color(_root) = black;
} else { } else {
_root = npos; _root = nullptr;
} }
} }
constexpr void _erase(size_t n) { constexpr void _erase(node n) {
if (n == npos) { if (n == nullptr) {
return; return;
} }
size_t l = _left(n); node l = _left(n);
size_t r = _right(n); node r = _right(n);
// 2 children // 2 children
if (l != npos && r != npos) { if (l != nullptr && r != nullptr) {
size_t s = left_most(r); node s = leftmost(r);
_swap_val(n, s); _swap_val(n, s);
n = s; n = s;
l = _left(n); l = _left(n);
r = _right(n); r = _right(n);
} }
size_t p = _parent(n); node p = _parent(n);
bool d = n == right(p); bool d = n == right(p);
size_t c = l != npos ? l : r; node c = l != nullptr ? l : r;
// Single child // Single child
if (c != npos) { if (c != nullptr) {
_parent(c) = p; _parent(c) = p;
} }
// Handles root cases // Handles root cases
if (p == npos) { if (p == nullptr) {
_root = c; _root = c;
if (c == npos) { if (c == nullptr) {
fennec::destruct(&_table[n]); _free_node(n);
_freed.push_back(n);
--_size; --_size;
return; return;
} else { } else {
@@ -533,19 +697,17 @@ protected:
} }
// Single Child, Red, and Root cases // Single Child, Red, and Root cases
if (p == npos || c != npos || _color(n) == red) { if (p == nullptr || c != nullptr || _color(n) == red) {
if (p != npos) { if (p != nullptr) {
_child(p, d) = c; _child(p, d) = c;
} }
fennec::destruct(&_table[n]); _free_node(n);
_freed.push_back(n);
--_size; --_size;
return; return;
} }
_fix_erase(n); _fix_erase(n);
fennec::destruct(&_table[n]); _free_node(n);
_freed.push_back(n);
--_size; --_size;
} }
}; };

View File

@@ -27,20 +27,33 @@ namespace fennec
struct event; struct event;
///
/// \brief Class outlining the interface for an object that listens for events
class event_listener { class event_listener {
public: public:
virtual ~event_listener() = default; virtual ~event_listener() = default;
virtual void handle_event(event* event) = 0; 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<event> {
// Delete default constructor to enforce type scheme
event() = delete; event() = delete;
///
/// \brief Event Constructor, stores the event type
/// \tparam EventT The type of the event
template<typename EventT> template<typename EventT>
event() : type(typeuuid<EventT>()) { } event(EventT* type)
: typed(type) {
}
///
/// \brief Registers a listener for the event type
/// \tparam EventT
/// \param listener
template<typename EventT> template<typename EventT>
static void add_listener(event_listener* listener) { static void add_listener(event_listener* listener) {
event::add_listener(listener, typeuuid<EventT, event>()); event::add_listener(listener, typeuuid<EventT, event>());

View File

@@ -32,8 +32,6 @@ constexpr uint64_t nullid = 0;
template<typename TypeT, typename RootT = void> template<typename TypeT, typename RootT = void>
FENNEC_NO_INLINE uint64_t typeuuid() { FENNEC_NO_INLINE uint64_t typeuuid() {
assertf(not is_constant_evaluated(), "Type UUIDs Cannot Be Obtained at Compile Time");
static bool init = false; static bool init = false;
static uint64_t id = nullid; static uint64_t id = nullid;

View File

@@ -67,26 +67,40 @@ struct tokenizer {
string brackets; // characters that mark brackets string brackets; // characters that mark brackets
string quotes; // characters that mark a string sequence, entire string sequence is treated as one token 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 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 { enum token_ : uint8_t {
token_string = 0, token_text = 0,
token_newline = 1, token_integer,
token_escaped = 2, token_string,
token_operator = 3, token_newline,
token_bracket = 4, token_escaped,
token_quoted = 5, token_operator,
token_bracket,
token_quoted,
num_token_types
}; };
using token = pair<string, uint8_t>; using token = pair<string, uint8_t>;
private: private:
// list<token> operator()(const string& line) { static constexpr uint8_t token_delimiter = num_token_types;
// list<token> res;
// constexpr list<token> operator()(const string& line) {
// for (size_t i = 0; i < ) list<token> res;
// priority_queue<pair<size_t, uint8_t>> idx;
// return res;
// } for (size_t i = 0; i < line.size(); ++i) {
for (char c : delimiter) {
idx.emplace()
}
}
return res;
}
private: private:
}; };

View File

@@ -48,6 +48,12 @@ using ::ispunct;
using ::tolower; using ::tolower;
using ::toupper; using ::toupper;
struct string_view {
const char* str;
size_t len;
};
/// ///
/// \brief This struct wraps c-style strings /// \brief This struct wraps c-style strings
/// ///
@@ -82,7 +88,9 @@ public:
: _str(str) : _str(str)
, _size(n - 1) , _size(n - 1)
, _const(false) { , _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) : _str(str)
, _size(n - 1) , _size(n - 1)
, _const(false) { , _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) : _cstr(str)
, _size(n - 1) , _size(n - 1)
, _const(true) { , _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) : _cstr(str)
, _size(n - 1) , _size(n - 1)
, _const(true) { , _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'` /// \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'` /// \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 { constexpr bool empty() const {
return _cstr == nullptr || _size == 0; return _cstr == nullptr || _size == 0;

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \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 <fennec/containers/pair.h>
#include <fennec/langproc/strings/cstring.h>
#include <fennec/langproc/strings/string.h>
namespace fennec
{
template<typename TypeT>
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<cstring> {
constexpr string operator()(const string_view&, const cstring& str) {
return string(str);
}
};
template<size_t N>
struct formatter<const char (&)[N]> {
constexpr string operator()(const string_view&, const char (&str)[N]) {
return string(str);
}
};
template<size_t N, typename...ArgsT>
struct format_string {
// Definitions =========================================================================================================
private:
static constexpr size_t npos = -1;
enum token_ : uint8_t {
token_text = 0,
token_param,
};
using token = pair<uint8_t, string_view>;
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<ArgsT>(args)...);
}
private:
array<token, N> _tokens;
size_t _num_tokens;
string _format(size_t) const {
return string{""};
}
template<typename HeadT, typename...RestT>
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<HeadT> fmt;
res += fmt(token.second, head);
res += this->_format(i + 1, fennec::forward<RestT>(rest)...);
break;
}
res += token.second;
}
return res;
}
};
template<size_t N, typename...ArgsT>
string _format(const format_string<N, ArgsT...>& str, ArgsT&&...args) {
return str.format(fennec::forward<ArgsT>(args)...);
}
template<size_t N, typename...ArgsT>
string format(const char (&str)[N], ArgsT&&...args) {
return fennec::_format<N, ArgsT...>(format_string<N, ArgsT...>(str), fennec::forward<ArgsT>(args)...);
}
}
#endif // FENNEC_LANGPROC_STRINGS_FORMAT_H

View File

@@ -108,6 +108,10 @@ public:
} }
} }
constexpr _string(const string_view& view)
: _string(view.str, view.len) {
}
/// ///
/// \brief String Copy Constructor /// \brief String Copy Constructor
/// \param str the string to copy /// \param str the string to copy

View File

@@ -32,7 +32,7 @@ namespace fennec
/// \param x a vector containing the position /// \param x a vector containing the position
/// \returns An identity matrix with the last column set to x /// \returns An identity matrix with the last column set to x
template<typename genType> template<typename genType>
constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) { constexpr mat<genType, 3, 3> translation(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>( return mat<genType, 3, 3>(
1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0,
@@ -45,7 +45,7 @@ constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) {
/// \param x a vector containing the position /// \param x a vector containing the position
/// \returns An identity matrix with the last column set to x /// \returns An identity matrix with the last column set to x
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) { constexpr mat<genType, 4, 4> translation(const vector<genType, 0, 1, 2>& x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
1, 0, 0, 0, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0,
@@ -59,7 +59,7 @@ constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) {
/// \param x a vector containing the scale for each axis /// \param x a vector containing the scale for each axis
/// \returns A diagonal matrix with the terms of x /// \returns A diagonal matrix with the terms of x
template<typename genType> template<typename genType>
constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) { constexpr mat<genType, 3, 3> scaling(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>( return mat<genType, 3, 3>(
x.x, 0, 0, x.x, 0, 0,
0, x.y, 0, 0, x.y, 0,
@@ -72,7 +72,7 @@ constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) {
/// \param x a vector containing the scale for each axis /// \param x a vector containing the scale for each axis
/// \returns A diagonal matrix with the terms of x /// \returns A diagonal matrix with the terms of x
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> scaling(const vec<genType, 3>& x) { constexpr mat<genType, 4, 4> scaling(const vector<genType, 0, 1, 2>& x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
x.x, 0, 0, 0, x.x, 0, 0, 0,
0, x.y, 0, 0, 0, x.y, 0, 0,
@@ -81,6 +81,15 @@ constexpr mat<genType, 4, 4> scaling(const vec<genType, 3>& x) {
); );
} }
template<typename genType>
constexpr mat<genType, 3, 3> shear(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>(
1, x.x, 0,
x.y, 1, 0,
0, 0, 1
);
}
/// ///
/// \brief Create a 2d rotation matrix /// \brief Create a 2d rotation matrix
/// \param a the angle of the rotation in radians /// \param a the angle of the rotation in radians
@@ -106,7 +115,7 @@ constexpr mat<genType, 3, 3> rotation(const genType a) {
/// \param a The rotation about the axis in radians /// \param a The rotation about the axis in radians
/// \returns A rotation matrix about A /// \returns A rotation matrix about A
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) { constexpr mat<genType, 4, 4> rotation(const vector<genType, 0, 1, 2>& A, genType a) {
// https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle // https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
// Calculate sin and cos terms // Calculate sin and cos terms
@@ -114,8 +123,8 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) {
const genType s = fennec::sin(a); const genType s = fennec::sin(a);
// Calculate axis term // Calculate axis term
const vec<genType, 3> u = (fennec::one<genType>() - c) * A; const vector<genType, 0, 1, 2> u = (fennec::one<genType>() - c) * A;
const vec<genType, 3> v = s * A; const vector<genType, 0, 1, 2> v = s * A;
// Calculate the Matrix // Calculate the Matrix
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
@@ -126,20 +135,20 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) {
); );
} }
template<typename genType> // template<typename genType>
constexpr qua<genType> rotation(const vec<genType, 3>& axis, genType angle) { // constexpr qua<genType> rotation(const vector<genType, 0, 1, 2>& axis, genType angle) {
vec<genType, 3> a = fennec::normalize(axis); // vector<genType, 0, 1, 2> a = fennec::normalize(axis);
const genType s = fennec::sin(angle); // const genType s = fennec::sin(angle);
//
return qua<genType>(fennec::cos(angle * genType(0.5)), s * a); // return qua<genType>(fennec::cos(angle * genType(0.5)), s * a);
} // }
/// \brief enum to denote the unit-axis of rotation /// \brief enum to denote the unit-axis of rotation
enum rot_ enum rotation_
{ {
rot_x = 0 rotation_x = 0
, rot_y , rotation_y
, rot_z , rotation_z
}; };
/// ///
@@ -147,7 +156,7 @@ enum rot_
/// \tparam axis the unit axis to rotate around /// \tparam axis the unit axis to rotate around
/// \param a the angle, in radians /// \param a the angle, in radians
/// \returns a matrix that rotates vectors around a unit axis /// \returns a matrix that rotates vectors around a unit axis
template<typename genType, rot_ axis> template<typename genType, rotation_ axis>
constexpr mat<genType, 4, 4> rotation(genType a) { constexpr mat<genType, 4, 4> rotation(genType a) {
// https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations // https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations
@@ -156,7 +165,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
const genType s = fennec::sin(a); const genType s = fennec::sin(a);
// Calculate the matrix // Calculate the matrix
if constexpr(axis == rot_x) { if constexpr(axis == rotation_x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
1, 0, 0, 0, 1, 0, 0, 0,
0, c, -s, 0, 0, c, -s, 0,
@@ -164,7 +173,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
0, 0, 0, 1 0, 0, 0, 1
); );
} }
else if constexpr(axis == rot_y) { else if constexpr(axis == rotation_y) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
c, 0, s, 0, c, 0, s, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -172,7 +181,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
0, 0, 0, 1 0, 0, 0, 1
); );
} }
else if constexpr(axis == rot_z) { else if constexpr(axis == rotation_z) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
c, -s, 0, 0, c, -s, 0, 0,
s, c, 0, 0, s, c, 0, 0,
@@ -202,7 +211,7 @@ enum order_
/// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll) /// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll)
/// \returns a matrix that rotates vectors /// \returns a matrix that rotates vectors
template<typename genType, order_ order = order_zxy> template<typename genType, order_ order = order_zxy>
constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) { constexpr mat<genType, 4, 4> rotation(const vector<genType, 0, 1, 2>& E) {
// expanded using a CAS // expanded using a CAS
// Calculate sin and cos terms // Calculate sin and cos terms
@@ -221,40 +230,35 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) {
sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0, sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_xzy) {
else if constexpr(order == order_xzy) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, -sg, cg*sb, 0, cb*cg, -sg, cg*sb, 0,
ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*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, cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_yxz) {
else if constexpr(order == order_yxz) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0, cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0,
ca*sg, ca*cg, -sa, 0, ca*sg, ca*cg, -sa, 0,
cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0, cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_yzx) {
else if constexpr(order == order_yzx) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0, cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0,
sg, ca*cg, -cg*sa, 0, sg, ca*cg, -cg*sa, 0,
-cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0, -cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_zxy) {
else if constexpr(order == order_zxy) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0, 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, cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0,
-ca*sb, sa, ca*cb, 0, -ca*sb, sa, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_zyx) {
else if constexpr(order == order_zyx) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0, 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, cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0,

View File

@@ -251,7 +251,6 @@ struct matrix
/// \param mat matrix to copy /// \param mat matrix to copy
constexpr matrix(const matrix_t& mat) constexpr matrix(const matrix_t& mat)
: data{ mat.data } { : data{ mat.data } {
} }
/// ///
@@ -261,7 +260,26 @@ struct matrix
/// \param mat matrix to move /// \param mat matrix to move
constexpr matrix(matrix_t&& mat) noexcept constexpr matrix(matrix_t&& mat) noexcept
: data{ mat.data } { : data{ mat.data } {
}
template<typename OScalarT>
constexpr matrix(const matrix<OScalarT, RowsV, ColIndicesV...>& 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<typename OScalarT>
constexpr matrix(matrix<OScalarT, RowsV, ColIndicesV...>&& 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<size_t ORowsV, size_t...OColIndicesV> requires(columns == ORowsV)
constexpr matrix<scalar_t, RowsV, OColIndicesV...>& operator*=(const matrix<scalar_t, ORowsV, OColIndicesV...>& rhs) {
return *this = *this * rhs;
}
/// @} /// @}

View File

@@ -1086,22 +1086,22 @@ private:
} }
template<size_t OffsetV> template<size_t OffsetV>
constexpr void _insert(ScalarT& x) { constexpr void _insert(const ScalarT& x) {
data[OffsetV] = x; data[OffsetV] = x;
} }
template<size_t OffsetV, typename OScalarT> template<size_t OffsetV, typename OScalarT>
constexpr void _insert(OScalarT& x) { constexpr void _insert(const OScalarT& x) {
data[OffsetV] = ScalarT(x); data[OffsetV] = ScalarT(x);
} }
template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV> template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV>
constexpr void _insert(vector<OScalarT, OIndicesV...>& vec) { constexpr void _insert(const vector<OScalarT, OIndicesV...>& vec) {
((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...); ((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...);
} }
template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV> template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV>
constexpr void _insert(swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) { constexpr void _insert(const swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) {
size_t i = 0; size_t i = 0;
((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...); ((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...);
} }

View File

@@ -28,7 +28,7 @@
namespace fennec namespace fennec
{ {
class component { class component : typed<component> {
public: public:
// TYPEDEFS & CONSTANTS ================================================================================================ // TYPEDEFS & CONSTANTS ================================================================================================
@@ -196,8 +196,8 @@ public:
const size_t node; const size_t node;
template<typename ComponentT> template<typename ComponentT>
component(size_t node) component(ComponentT* type, size_t node)
: type(type_list()[uuid<ComponentT>()]) : typed(type)
, node(node) { , node(node) {
} }
}; };

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \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 <fennec/math/matrix.h>
#include <fennec/math/ext/transform.h>
#include <fennec/scene/component.h>
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

View File

@@ -25,8 +25,19 @@
namespace fennec 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<string> { class scene : public rdtree<string> {
public: 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 { size_t operator[](const cstring& name) const {
list<size_t> parse; list<size_t> parse;
parse.push_back(root); parse.push_back(root);
@@ -34,13 +45,20 @@ public:
if (*_data[parse.front()].value == name) { if (*_data[parse.front()].value == name) {
return parse.front(); 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(); parse.pop_front();
} }
return npos; 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 { size_t operator[](const string& name) const {
list<size_t> parse; list<size_t> parse;
parse.push_back(root); parse.push_back(root);
@@ -48,8 +66,10 @@ public:
if (*_data[parse.front()].value == name) { if (*_data[parse.front()].value == name) {
return parse.front(); 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(); parse.pop_front();
} }
return npos; return npos;

View File

@@ -30,7 +30,7 @@
#ifndef FENNEC_TEST_LANGPROC_FORMAT_H #ifndef FENNEC_TEST_LANGPROC_FORMAT_H
#define FENNEC_TEST_LANGPROC_FORMAT_H #define FENNEC_TEST_LANGPROC_FORMAT_H
#include <fennec/langproc/compile/tokenizer.h> #include <fennec/langproc/strings/format.h>
namespace fennec namespace fennec
{ {
@@ -40,20 +40,7 @@ namespace test
inline void fennec_test_langproc_format() { inline void fennec_test_langproc_format() {
// tokenizer math = { fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!"));
// .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 << ", ";
// }
} }
} }