Compare commits

...

8 Commits

Author SHA1 Message Date
Maddie Busig
193aaad305 Change demo to flower output 2025-05-07 02:47:40 -07:00
Maddie Busig
6c309a0265 Implement generation 2025-05-07 02:06:15 -07:00
Maddie Busig
faf9f7af3b Add scale to generator 2025-05-07 02:04:59 -07:00
Maddie Busig
513d000e11 Add generator usage and move large data to EWRAM 2025-05-07 01:26:04 -07:00
Maddie Busig
32e0b9280f Add beginning of generation 2025-05-07 01:25:24 -07:00
Maddie Busig
ba5f3206bf Add stack usage diagonstic options 2025-05-07 01:24:30 -07:00
Maddie Busig
22f87a9c9e Remove old disabled implementation 2025-05-07 00:21:47 -07:00
Maddie Busig
924bd2b94c Add parsing ruleset 2025-05-07 00:11:12 -07:00
4 changed files with 336 additions and 417 deletions

View File

@ -17,6 +17,9 @@ if (EXCEPTIONS STREQUAL OFF)
endif() endif()
add_compile_definitions(ETL_THROW_EXCEPTIONS) add_compile_definitions(ETL_THROW_EXCEPTIONS)
add_compile_options("-fstack-usage")
add_compile_options("-fcallgraph-info=su")
add_compile_options("-Wstack-usage=1000")
add_link_options("-Wl,--print-memory-usage") add_link_options("-Wl,--print-memory-usage")
add_executable(${PROJECT_NAME}) add_executable(${PROJECT_NAME})

View File

@ -21,6 +21,8 @@ constexpr size_t g_max_cgroup_tokens = 16;
constexpr size_t g_max_wgroup_weights = 8; constexpr size_t g_max_wgroup_weights = 8;
constexpr size_t g_max_match_groups = 8; constexpr size_t g_max_match_groups = 8;
constexpr size_t g_max_match_wgroups = 4; constexpr size_t g_max_match_wgroups = 4;
constexpr size_t g_max_leafs = 128;
constexpr size_t g_max_wleafs = 128;
using token_id_t = uint32_t; using token_id_t = uint32_t;
using group_id_t = uint32_t; using group_id_t = uint32_t;
@ -251,12 +253,46 @@ class generator_t {
etl::vector<weighted_group_t, g_max_wgroups> m_weighted_groups; etl::vector<weighted_group_t, g_max_wgroups> m_weighted_groups;
etl::vector<group_output_t, g_max_groups> m_group_outputs; etl::vector<group_output_t, g_max_groups> m_group_outputs;
etl::vector<token_match_t, g_max_tokens> m_token_matches; etl::vector<token_match_t, g_max_tokens> m_token_matches;
group_id_t m_axiom;
uint32_t m_gen_num; bool m_has_ruleset = false;
group_id_t m_axiom;
mtl::fixed m_scale = 1;
etl::vector<leaf_t, g_max_leafs> m_leaf_buf1;
etl::vector<leaf_t, g_max_leafs> m_leaf_buf2;
etl::vector<weighted_leaf_t, g_max_leafs> m_wleaf_buf1;
etl::vector<weighted_leaf_t, g_max_leafs> m_wleaf_buf2;
// Need to use pointers betwen etl::ivector does not implement swap :(
etl::ivector<leaf_t>* m_leafs_prev = &m_leaf_buf1;
etl::ivector<leaf_t>* m_leafs_cur = &m_leaf_buf2;
etl::ivector<weighted_leaf_t>* m_wleafs_prev = &m_wleaf_buf1;
etl::ivector<weighted_leaf_t>* m_wleafs_cur = &m_wleaf_buf2;
uint32_t m_gen_num = 0;
void parse_group_characteristic(group_id_t gid, group_output_t& output, const ruleset_t& ruleset);
void swap_buffers();
void clear_current_buffers();
void clear_previous_buffers();
void clear_all_buffers();
bool generate_group(group_id_t gid, mtl::vec2 pos, mtl::mat<2, 2> orient, etl::ivector<marker_t>& out_markers);
bool generate_axiom(etl::ivector<marker_t>& out_markers);
bool generate_leafs(etl::ivector<marker_t>& out_markers);
public: public:
generator_t(const ruleset_t& ruleset); generator_t() : m_axiom(0) { }
generator_t(const ruleset_t& ruleset) : m_axiom(0) {
parse_ruleset(ruleset);
}
void set_scale(mtl::fixed scale) { m_scale = scale; }
void parse_ruleset(const ruleset_t& ruleset);
/** /**
* @brief Steps one generation, outputting generated markers * @brief Steps one generation, outputting generated markers
@ -273,229 +309,11 @@ public:
* the current generation to @p out_markers. If there is not enough room * the current generation to @p out_markers. If there is not enough room
* for all of the child markers, @p out_markers is unmodified. * for all of the child markers, @p out_markers is unmodified.
*/ */
template <typename CONTAINER_T> bool step_generation(etl::ivector<marker_t>& out_markers);
bool step_generation(CONTAINER_T& out_markers);
uint32_t generation_num() const { return m_gen_num; } uint32_t generation_num() const { return m_gen_num; }
}; };
#if 0
class generator_t {
private:
static constexpr size_t g_max_groups = 32;
static constexpr size_t g_max_weighted_groups = 32;
static constexpr size_t g_max_tokens = 32;
static constexpr size_t g_max_leafs_per_gen = 1024; // Maximum leafs per generation
static constexpr size_t g_max_rules_basic = 16;
static constexpr size_t g_max_rules_marking = 4;
// We are creating two buffers are leafs. One for the current generation
// and one for the next generation. These will be accessed through current/next
// ivectors that will point to different buffers as they are swapped.
etl::vector<leaf_t, g_max_leafs_per_gen> m_leaf_buf1;
etl::vector<leaf_t, g_max_leafs_per_gen> m_leaf_buf2;
etl::vector<token_t, g_max_tokens> m_tokens;
etl::vector<group_characteristic_t, g_max_groups> m_group_characteristics;
etl::vector<group_t, g_max_groups> m_groups;
etl::vector<weighted_group_t, g_max_weighted_groups> m_weighted_groups;
etl::vector<branch_rule_basic_t, g_max_rules_basic> m_basic_branch_rules;
etl::vector<branch_rule_marking_t, g_max_rules_marking> m_marking_branch_rules;
etl::ivector<leaf_t>* m_leafs_cur;
etl::ivector<leaf_t>* m_leafs_next;
bool m_processed;
uint32_t m_generation;
mtl::fixed m_scale_factor;
weighted_group_id_t m_axiom;
mtl::vec2 m_init_position;
mtl::fixed m_init_orientation;
public:
/**
* Default constructor
*/
generator_t() noexcept :
m_leafs_cur(&m_leaf_buf1),
m_leafs_next(&m_leaf_buf2),
m_axiom(0),
m_generation(0),
m_scale_factor(1),
m_processed(false) {}
/**
* @brief Add new token variable that can be used in this generator's group characteristics.
*
* @param type Type of the token to add
* @param value Value of the token. Unused if @p type is @c token_type_e::generate. Defaults to 0
*
* @ret @c token_id_t of the newly added token, local to this generator.
*
* @pre preprocess() has not been called on this generator.
*
* @exception mtl::system_error If the generator has already been preprocessed.
* @exception mtl::invalid_argument If the given token type is invalid.
* @exception mtl::length_error If the maximum number of tokens is reached.
*/
token_id_t add_token(token_type_e type, mtl::fixed value = 0);
/**
* @brief Add new group charateristic.
*
* After all tokens, groups, and rules have been added, the generator's groups
* must be processed by calling preprocess(). Once this is done, no new
* groups may be added. If an exception occurs, no data is modified.
*
* @param factor The number of times the group is repeated. Treats the group as if @p tokens was repeated @p factor number of times.
* @param tokens @c etl::ivector of @token_id_t that describe how the group functions. Processed in order, front to back.
*
* @ret @c group_id_t of the newly added group, local to this generator.
*
* @pre preprocess() has not been called on this generator.
*
* @exception mtl::system_error If the generator has already been preprocessed.
* @exception mtl::invalid_argument If an invalid token was encountered.
* @exception mtl::length_error If the maximum number of groups is reached.
*/
group_id_t add_group_characteristic(uint32_t factor, const etl::ivector<token_id_t>& tokens);
/**
* @overload
*
* Convience overload, defaulting to a group with factor 1.
*/
group_id_t add_group_characteristic(const etl::ivector<token_id_t>& tokens);
/**
* @brief Add a weighted group to the generator.
*
* Adds weighted group of groups, where each subgroup has a
* <tt>(weight) / (total weight)</tt> chance of being selected. Weights
* are represented as @c uint32_t. If an exception occurs, no data is modified.
*
* @param weights @c etl::ivector of @c etl::pair s of @c group_id_it and their corresponding weights.
*
* @ret @c weighted_group_id_t of the newly added group, local to this generator.
*
* @pre preprocess() has not been called on this generator.
*
* @exception mtl::invalid_argument If no groups were supplied, or an invalid group was encountered.
* @exception mtl::system_error If the generator has already been preprocessed.
* @exception mtl::length_error If the maximum number of weighted groups is reached.
*/
weighted_group_id_t add_weighted_group(const etl::ivector<etl::pair<group_id_t, uint32_t>>& group_weights);
/**
* @overload
*
* Convenience overload. Creates a weighted group for a group with only
* one possibility.
*/
weighted_group_id_t add_weighted_group(group_id_t group);
/**
* @brief Add a basic (non-marking) branch rule to the generator.
*
* Basic branch rules describe how this generator will create new leafs
* each generation.
*
* @param match Token to match and generate the group in place of.
* @param wgroup Weighted group to generate. Only one subgroup will be selected to generate.
*
* @ret @c branch_rule_basic_id_t of the newly added branch rule, local to this generator.
*
* @pre preprocess() has not been called on this generator.
*
* @exception mtl::invalid_argument If no groups were supplied, or an invalid group was encountered.
* @exception mtl::system_error If the generator has already been preprocessed.
* @exception mtl::length_error If the maximum number of basic branch rules is reached.
*/
branch_rule_basic_id_t add_basic_branch_rule(token_id_t match, weighted_group_id_t wgroup);
/**
* @brief Add a marking branch rule to the generator.
*
* Marking branch rules describe what points (markers) this generator
* will output to the caller each generator.
*
* @param match Token to match and mark.
* @param marker_id ID to mark the point with.
*
* @ret @c branch_rule_marking_id_t of the newly added branch rule, local to this generator.
*
* @pre preprocess() has not been called on this generator.
*
* @exception mtl::invalid_argument If an invalid token was supplied.
* @exception mtl::system_error If the generator has already been preprocessed.
* @exception mtl::length_error If the maximum number of marking branch rules is reached.
*/
branch_rule_marking_id_t add_marking_branch_rule(token_id_t match, uint32_t marker_id);
/**
* @brief Set the axiom used in this generator.
*
* The "axiom" is used to generate the very first generation of leafs.
* Only one subgroup from the weighted group is selected.
*
* @exception mtl::invalid If an invalid weighted group ID was suppied.
*/
void set_axiom(weighted_group_id_t wgroup);
/**
* @brief Perform preprocessing on the supplied group characteristics.
*
* Transforms each group, producing a list of child leafs and markers
* that should be generated for each group characteristic. Has no effect
* if called more than once.
*
* @pre All tokens, groups characteristics, weighted groups, and branch
* rules have been added.
*
* @exception mtl::length_error If the number of resulting child leafs
* or markers exceeds the maximum allowed for one group.
*/
void preprocess();
/**
* @brief Set the scale factor used each generation.
*
* Each time the generation is stepped, the length of each walk is scaled
* by @p scale_factor. Can be set to a new value at any time.
*
* @param scale_factor The new scale factor to use
*/
void set_scale_factor(mtl::fixed scale_factor) noexcept;
/**
* @brief Set the initial position the axiom is generated in.
*
* @param position The initial position to use.
*/
void set_initial_position(mtl::vec2 position) noexcept;
/**
* @brief Set the initial orientation the axiom is generated in.
*
* @param orientation The initial orientation to use. Given in radians.
*/
void set_initial_orientation(mtl::fixed orientation) noexcept;
/**
* @brief Steps one generation in the fractal, generating a vector of markers.
*
* The vector of markers will NOT be cleared at the beginning of generation.
*
* @param out_markers An @c etl::ivector of @c marker_t. Each marker
* encountered will be appended to this list in the order encountered.
*
* @ret true if the maximum number of markers was reached, otherwise false.
*/
bool step_generation(const etl::ivector<marker_t>& out_markers) noexcept;
};
#endif
template <typename CONTGROUP_T, typename CONTWGROUP_T> template <typename CONTGROUP_T, typename CONTWGROUP_T>
void ruleset_t::find_token_matches(token_id_t token, CONTGROUP_T& out_groups, CONTWGROUP_T& out_wgroups) const { void ruleset_t::find_token_matches(token_id_t token, CONTGROUP_T& out_groups, CONTWGROUP_T& out_wgroups) const {
for (auto rule : m_branch_rules) { for (auto rule : m_branch_rules) {

View File

@ -110,200 +110,223 @@ branch_rule_id_t ruleset_t::add_branch_rule_weighted(token_id_t match, weighted_
return m_branch_rules.size() - 1; return m_branch_rules.size() - 1;
} }
// ---------- GENERATOR ----------
#if 0 void generator_t::parse_ruleset(const ruleset_t& ruleset) {
token_id_t generator_t::add_token(token_type_e type, mtl::fixed value) { mlog::debug << "Parsing ruleset" << endl;
log::debug << "Adding token with type " << (int)type << endl;
if (m_processed) { clear_all_buffers();
log::error << "Failed to add token, already preprocessed" << endl; m_has_ruleset = true;
throw mtl::system_error(); m_axiom = ruleset.get_axiom();
for (weighted_group_id_t wgid = 0; wgid < ruleset.num_weighted_groups(); ++wgid) {
m_weighted_groups.push_back(ruleset.get_weighted_group(wgid));
} }
if (m_tokens.full()) { mlog::debug << "Copied " << m_weighted_groups.size() << " weighted groups" << endl;
log::error << "Failed to add token, tokens full" << endl;
throw mtl::length_error(); for (token_id_t tok = 0; tok < ruleset.num_tokens(); ++tok) {
m_token_matches.push_back(token_match_t());
token_match_t& cur_match = m_token_matches.back();
ruleset.find_token_matches(tok, cur_match.m_groups, cur_match.m_wgroups);
} }
switch (type) { for (group_id_t gid = 0; gid < ruleset.num_groups(); ++gid) {
m_group_outputs.push_back(group_output_t());
parse_group_characteristic(gid, m_group_outputs.back(), ruleset);
}
}
void generator_t::parse_group_characteristic(group_id_t id, group_output_t& output, const ruleset_t& ruleset) {
mlog::debug << "Parsing group characteristic for group " << id << endl;
vec2 cur_pos(0, 0);
mat<2, 2> cur_orient = create_rotation(fixed(1), fixed(0));
const group_characteristic_t& charac = ruleset.get_group_characteristic(id);
for (uint32_t i = 0; i < charac.m_factor; ++i) {
for (token_id_t tokid : charac.m_token_ids) {
const token_t& tok = ruleset.get_token(tokid);
// Process token marking
if (tok.m_mark != 0) {
marker_t marker { cur_pos, tok.m_mark };
output.m_child_markers.push_back(marker);
mlog::debug << "Group " << id << " has marker ID: " << tok.m_mark << endl;
mlog::debug << " at position " << cur_pos << ", cos(angle) = " << cur_orient.e[0][0] << ", sin(angle) = " << cur_orient.e[1][0] << endl;
}
// Process token groups
const token_match_t& match = m_token_matches.at(tokid);
// Regular groups
for (group_id_t groupid : match.m_groups) {
leaf_t leaf;
leaf.m_group_id = groupid;
leaf.m_orientation = cur_orient;
leaf.m_position = cur_pos;
output.m_child_leafs.push_back(leaf);
mlog::debug << "Group " << id << " has child leaf w/ group id " << groupid << endl;
mlog::debug << " at position " << cur_pos << ", cos(angle) = " << cur_orient.e[0][0] << ", sin(angle) = " << cur_orient.e[1][0] << endl;
}
// Weighted groups
for (weighted_group_id_t wgroupid : match.m_wgroups) {
weighted_leaf_t wleaf;
wleaf.m_weighted_group_id = wgroupid;
wleaf.m_orientation = cur_orient;
wleaf.m_position = cur_pos;
output.m_child_wleafs.push_back(wleaf);
mlog::debug << "Group " << id << " has weighted child leaf w/ wgroup " << wgroupid << endl;
mlog::debug << " at position " << cur_pos << ", cos(angle) = " << cur_orient.e[0][0] << ", sin(angle) = " << cur_orient.e[1][0] << endl;
}
// Apply the token's transformation
switch (tok.m_type) {
case token_type_e::walk: case token_type_e::walk:
cur_pos = cur_pos + cur_orient * vec2(0, tok.m_value);
mlog::debug << "Transforming with walk of " << tok.m_value << endl;
break;
case token_type_e::rotate: case token_type_e::rotate:
case token_type_e::generate: cur_orient = create_rotation(tok.m_value, tok.m_value2) * cur_orient;
m_tokens.push_back(token_t{type, value}); mlog::debug << "Transforming with rotation of " << tok.m_value << endl;
return m_tokens.size() - 1; // IDs are simply index into vector break;
default: default:;
log::error << "Failed to add token, invalid token type" << endl; }
throw mtl::invalid_argument(); }
} }
} }
group_id_t generator_t::add_group_characteristic(uint32_t factor, const etl::ivector<token_id_t>& tokens) { void generator_t::swap_buffers() {
if (m_processed) { std::swap(m_leafs_cur, m_leafs_prev);
log::error << "Failed to add characteristic, already preprocessed" << endl; std::swap(m_wleafs_cur, m_wleafs_prev);
throw mtl::system_error();
}
if (m_group_characteristics.full()) {
log::error << "Failed to add characteristic, vector full" << endl;
throw mtl::length_error();
}
// We need to look for invalid tokens AOT, because we only modify the
// generator if the supplied data is valid.
size_t num_tokens = m_tokens.size();
for (token_id_t token : tokens) {
if (token >= num_tokens) {
log::error << "Failed to add characteristic, invalid token found: " << token << " >= " << num_tokens << endl;
throw mtl::invalid_argument();
}
}
m_group_characteristics.push_back(group_characteristic_t());
group_characteristic_t& characteristic = m_group_characteristics.back();
characteristic.m_factor = factor;
characteristic.m_token_ids.assign(tokens.begin(), tokens.end());
return m_group_characteristics.size() - 1; // IDs are just the index into vector
} }
group_id_t generator_t::add_group_characteristic(const etl::ivector<token_id_t>& tokens) { void generator_t::clear_current_buffers() {
return add_group_characteristic(1, tokens); m_leafs_cur->clear();
m_wleafs_cur->clear();
} }
weighted_group_id_t generator_t::add_weighted_group(const etl::ivector<etl::pair<group_id_t, uint32_t>>& group_weights) { void generator_t::clear_previous_buffers() {
if (m_processed) { m_leafs_prev->clear();
log::error << "Failed to add weighted group, already preprocessed" << endl; m_wleafs_prev->clear();
throw mtl::system_error();
}
if (m_weighted_groups.full()) {
log::error << "Failed to add weighted group, vector full" << endl;
throw mtl::length_error();
}
size_t num_groups = m_group_characteristics.size();
for (etl::pair<group_id_t, uint32_t> pair : group_weights) {
if (pair.first >= num_groups) {
log::error << "Failed to add weighted group, invalid group found: " << pair.first << " >= " << num_groups << endl;
throw mtl::invalid_argument();
}
}
m_weighted_groups.push_back(weighted_group_t());
weighted_group_t& wgroup = m_weighted_groups.back();
uint32_t weight_total = 0;
for (etl::pair<group_id_t, uint32_t> pair : group_weights) {
wgroup.m_groups.push_back(pair.first);
wgroup.m_weights.push_back(pair.second);
wgroup.m_weight_total += pair.second;
}
wgroup.m_weight_total = weight_total;
return m_weighted_groups.size() - 1;
} }
weighted_group_id_t generator_t::add_weighted_group(group_id_t group) { void generator_t::clear_all_buffers() {
if (m_processed) { clear_current_buffers();
log::error << "Failed to add weighted group, already preprocessed" << endl; clear_previous_buffers();
throw mtl::system_error();
}
if (m_weighted_groups.full()) {
log::error << "Failed to add weighted group, vector full" << endl;
throw mtl::length_error();
}
if (group >= m_group_characteristics.size()) {
log::error << "Failed to add weighted group, invalid group encountered" << endl;
throw mtl::invalid_argument();
}
m_weighted_groups.push_back(weighted_group_t());
weighted_group_t& wgroup = m_weighted_groups.back();
wgroup.m_groups.push_back(group);
wgroup.m_weights.push_back(1); // We still need to supply a weight
wgroup.m_weight_total = 1;
return m_weighted_groups.size() - 1;
} }
branch_rule_basic_id_t generator_t::add_basic_branch_rule(token_id_t match, weighted_group_id_t wgroup) { bool generator_t::generate_axiom(etl::ivector<marker_t>& out_markers) {
if (m_processed) { const group_output_t& axiom_output = m_group_outputs[m_axiom];
log::error << "Failed to add basic branch rule, already preprocessed" << endl;
throw mtl::system_error(); if (m_leafs_cur->capacity() < axiom_output.m_child_leafs.size() ||
m_wleafs_cur->capacity() < axiom_output.m_child_wleafs.size() ||
out_markers.capacity() < axiom_output.m_child_markers.size()) {
mlog::debug << "Reached limit" << endl;
return false;
} }
if (m_basic_branch_rules.full()) { for (const leaf_t& leaf : axiom_output.m_child_leafs) {
log::error << "Failed to add basic branch rule, vector full" << endl; m_leafs_cur->push_back(leaf);
throw mtl::length_error(); }
for (const weighted_leaf_t& wleaf : axiom_output.m_child_wleafs) {
m_wleafs_cur->push_back(wleaf);
}
for (const marker_t& mark : axiom_output.m_child_markers) {
out_markers.push_back(mark);
} }
if (match >= m_tokens.size()) { return true;
log::error << "Failed to add basic branch rule, invalid match token" << endl; }
throw mtl::invalid_argument();
bool generator_t::generate_group(group_id_t gid, vec2 pos, mat<2, 2> orient, etl::ivector<marker_t>& out_markers) {
const group_output_t& group_output = m_group_outputs[gid];
if (m_leafs_cur->capacity() < group_output.m_child_leafs.size() ||
m_wleafs_cur->capacity() < group_output.m_child_wleafs.size() ||
out_markers.capacity() < group_output.m_child_markers.size()) {
mlog::debug << "Reached limit" << endl;
return false;
} }
if (wgroup >= m_weighted_groups.size()) { fixed cur_scale = 1;
log::error << "Failed to add basic branch rule, invalid weighted group" << endl; for (uint32_t i = 1; i < m_gen_num; ++i) { // start @ 1, don't scale axiom
throw mtl::invalid_argument(); cur_scale *= m_scale;
} }
m_basic_branch_rules.push_back(branch_rule_basic_t{match, wgroup}); for (const leaf_t& leaf : group_output.m_child_leafs) {
leaf_t child;
return m_basic_branch_rules.size() - 1; child.m_group_id = leaf.m_group_id;
} child.m_position = pos + orient * cur_scale * leaf.m_position;
child.m_orientation = leaf.m_orientation * orient;
branch_rule_marking_id_t generator_t::add_marking_branch_rule(token_id_t match, uint32_t marker_id) { m_leafs_cur->push_back(child);
if (m_processed) { }
log::error << "Failed to add marking branch rule, already preprocessed" << endl; for (const weighted_leaf_t& wleaf : group_output.m_child_wleafs) {
throw mtl::system_error(); weighted_leaf_t child;
child.m_weighted_group_id = wleaf.m_weighted_group_id;
child.m_position = pos + orient * cur_scale * m_gen_num * wleaf.m_position;
child.m_orientation = wleaf.m_orientation * orient;
m_wleafs_cur->push_back(child);
}
for (const marker_t& mark : group_output.m_child_markers) {
marker_t child;
child.m_id = mark.m_id;
child.m_pos = orient * cur_scale * mark.m_pos;
out_markers.push_back(child);
} }
if (m_basic_branch_rules.full()) { return true;
log::error << "Failed to add marking branch rule, vector full" << endl; }
throw mtl::length_error();
bool generator_t::generate_leafs(etl::ivector<marker_t>& out_markers) {
bool good = true;
for (const leaf_t& leaf : *m_leafs_prev) {
good = good && generate_group(leaf.m_group_id, leaf.m_position, leaf.m_orientation, out_markers);
if (!good) {
mlog::debug << "Failed to generate leafs" << endl;
return false;
}
} }
if (match >= m_tokens.size()) { return true;
log::error << "Failed to add marking branch rule, invalid match token" << endl; }
throw mtl::invalid_argument();
bool generator_t::step_generation(etl::ivector<marker_t>& out_markers) {
clear_current_buffers();
bool good = true;
if (m_leafs_prev->empty() && m_leafs_cur->empty()) {
good = generate_axiom(out_markers);
} else {
good = generate_leafs(out_markers);
} }
m_marking_branch_rules.push_back(branch_rule_marking_t{match, marker_id}); swap_buffers();
return m_marking_branch_rules.size() - 1; if (good) {
} ++m_gen_num;
void generator_t::set_axiom(weighted_group_id_t wgroup) {
if (wgroup >= m_weighted_groups.size()) {
log::error << "Failed to set axiom, invalid weighted group" << endl;
throw mtl::invalid_argument();
} }
m_axiom = wgroup; return good;
} }
void generator_t::preprocess() {
}
void generator_t::set_scale_factor(mtl::fixed scale_factor) noexcept {
}
void generator_t::set_initial_position(mtl::vec2 position) noexcept {
}
void generator_t::set_initial_orientation(mtl::fixed orientation) noexcept {
}
bool generator_t::step_generation(const etl::ivector<marker_t>& out_markers) noexcept {
}
#endif
} // namespace fractal } // namespace fractal

View File

@ -9,22 +9,38 @@
using namespace mtl; using namespace mtl;
using namespace fractal; using namespace fractal;
namespace mlog = mtl::log;
GBA_EWRAM_DATA ruleset_t rules;
GBA_EWRAM_DATA generator_t gen;
GBA_EWRAM_DATA etl::vector<marker_t, 100> output;
int main(void) { int main(void) {
REG_DISPCNT = DCNT_MODE3 | DCNT_BG2; REG_DISPCNT = DCNT_MODE3 | DCNT_BG2;
log::debug << "Hello world!" << mtl::endl; mlog::debug << "Hello world!" << mtl::flush;
constexpr float cos_PI_2 = std::cos(M_PI_2); constexpr float cos_PI_2 = std::cos(M_PI_2);
constexpr float cos_PI_3 = std::cos(M_PI / 3);
constexpr float cos_PI_4 = std::cos(M_PI_4); constexpr float cos_PI_4 = std::cos(M_PI_4);
constexpr float cos_PI_8 = std::cos(M_PI_4 / 2);
constexpr float cos_PI_16 = std::cos(M_PI_4 / 4);
constexpr float sin_PI_2 = std::sin(M_PI_2); constexpr float sin_PI_2 = std::sin(M_PI_2);
constexpr float sin_PI_3 = std::sin(M_PI / 3);
constexpr float sin_PI_4 = std::sin(M_PI_4); constexpr float sin_PI_4 = std::sin(M_PI_4);
constexpr float sin_PI_8 = std::sin(M_PI_4 / 2);
constexpr float sin_PI_16 = std::sin(M_PI_4 / 4);
fixed cos_PI_2_fx = cos_PI_2; fixed cos_PI_2_fx = cos_PI_2;
fixed cos_PI_3_fx = cos_PI_3;
fixed cos_PI_4_fx = cos_PI_4; fixed cos_PI_4_fx = cos_PI_4;
fixed cos_PI_8_fx = cos_PI_8;
fixed cos_PI_16_fx = cos_PI_16;
fixed sin_PI_2_fx = sin_PI_2; fixed sin_PI_2_fx = sin_PI_2;
fixed sin_PI_3_fx = sin_PI_3;
fixed sin_PI_4_fx = sin_PI_4; fixed sin_PI_4_fx = sin_PI_4;
fixed sin_PI_8_fx = sin_PI_8;
ruleset_t rules; fixed sin_PI_16_fx = sin_PI_16;
try { try {
fixed branch_angle = M_PI_4; fixed branch_angle = M_PI_4;
@ -32,18 +48,18 @@ int main(void) {
token_id_t walk_petal_length = rules.add_token(token_type_e::walk, 8); token_id_t walk_petal_length = rules.add_token(token_type_e::walk, 8);
token_id_t walk_petal_side = rules.add_token(token_type_e::walk, 2); token_id_t walk_petal_side = rules.add_token(token_type_e::walk, 2);
token_id_t rotate_axiom = rules.add_token(token_type_e::rotate, cos_PI_2_fx, sin_PI_2_fx); token_id_t rotate_axiom = rules.add_token(token_type_e::rotate, cos_PI_3_fx, sin_PI_3_fx);
token_id_t rotate_petal = rules.add_token(token_type_e::rotate, cos_PI_2_fx, -sin_PI_2_fx); token_id_t rotate_petal = rules.add_token(token_type_e::rotate, cos_PI_3_fx, -sin_PI_3_fx);
token_id_t rotate_branchr = rules.add_token(token_type_e::rotate, cos_PI_4_fx, -sin_PI_4_fx); token_id_t rotate_branchr = rules.add_token(token_type_e::rotate, cos_PI_8_fx, -sin_PI_8_fx);
token_id_t rotate_branchl = rules.add_token(token_type_e::rotate, cos_PI_2_fx, sin_PI_2_fx); token_id_t rotate_branchl = rules.add_token(token_type_e::rotate, cos_PI_4_fx, sin_PI_4_fx);
token_id_t branch = rules.add_token(token_type_e::empty); token_id_t branch = rules.add_token(token_type_e::empty);
token_id_t petal = rules.add_token(token_type_e::empty); token_id_t petal = rules.add_token(token_type_e::empty);
token_id_t mark = rules.add_token(token_type_e::empty, 0, 0, 1); token_id_t mark = rules.add_token(token_type_e::empty, 0, 0, 1);
log::info << "Added tokens" << endl; mlog::info << "Added tokens" << endl;
uint32_t axiom_factor = 4; uint32_t axiom_factor = 6;
etl::vector axiom_chr{ branch, rotate_axiom }; etl::vector axiom_chr{ branch, rotate_axiom };
group_id_t axiom_grp = rules.add_group_characteristic(axiom_chr, axiom_factor); group_id_t axiom_grp = rules.add_group_characteristic(axiom_chr, axiom_factor);
@ -54,27 +70,86 @@ int main(void) {
etl::vector petal_chr{ mark, walk_petal_length, mark, rotate_petal, walk_petal_side, mark }; etl::vector petal_chr{ mark, walk_petal_length, mark, rotate_petal, walk_petal_side, mark };
group_id_t petal_grp = rules.add_group_characteristic(petal_chr); group_id_t petal_grp = rules.add_group_characteristic(petal_chr);
log::info << "Added group characteristics" << endl; mlog::info << "Added group characteristics" << endl;
rules.add_branch_rule(branch, branch_grp); rules.add_branch_rule(branch, branch_grp);
rules.add_branch_rule(petal, petal_grp); rules.add_branch_rule(petal, petal_grp);
log::info << "Added rules" << endl; mlog::info << "Added rules" << endl;
log::info << "Finished configuring ruleset" << endl; mlog::info << "Finished configuring ruleset" << endl;
} catch(const mtl::exception&) { } catch(const mtl::exception&) {
log::info << "Failed to configure ruleset, caught exception" << endl; mlog::info << "Failed to configure ruleset, caught exception" << endl;
return 0; return 0;
} }
generator_t gen(rules); size_t gen_num = 0;
etl::vector<marker_t, 100> output;
while (gen.step_generation(output)) { try {
log::debug << "Stepped generation" << gen.generation_num() << endl; constexpr fixed scale = 0.8;
gen.parse_ruleset(rules);
gen.set_scale(scale);
for (const marker_t& m : output) { while (gen.step_generation(output) && gen.generation_num() < 6) {
log::debug << ' ' << m.m_id << ", " << m.m_pos << endl; mlog::debug << "Stepped generation" << gen.generation_num() << endl;
gen_num = gen.generation_num();
} }
} catch (etl::exception& e) {
mlog::error << "Caught ETL exception: " << e.what() << endl;
} catch (mtl::exception& e) {
mlog::error << "Caught MTL exception: " << e.id() << endl;
}
mlog::info << "Stepped " << gen.generation_num() << " generations" << mtl::endl;
mlog::debug << "End" << mtl::flush;
mlog::info << "Output " << output.size() << " markers: " << endl;
for (const marker_t& marker : output) {
mlog::info << " " << marker.m_pos << endl;
}
vec2 origin(120, 80);
int imgscale = 8;
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t a = 0;
COLOR color[3] = { CLR_FUCHSIA, CLR_CREAM, CLR_MAROON };
for (size_t i = 2; i < output.size(); i += 3) {
const marker_t& m1 = output[i-2];
const marker_t& m2 = output[i-1];
const marker_t& m3 = output[i-0];
r = 0xFF * 2 / i;
g = 0xFF * 2 / i;
b = 0x33;
a = 0xFF;
//color = (a << 24) | (r << 16) | (g << 8) | b;
clr_rotate(color, 3, 1);
vec2 v1 = origin + imgscale * m1.m_pos;
vec2 v2 = origin + imgscale * m2.m_pos;
vec2 v3 = origin + imgscale * m3.m_pos;
mlog::debug << "Vertices: " << v1 << ", " << v2 << ", " << v3 << endl;
int32_t iv1_x = v1.x.raw() / 256;
int32_t iv1_y = v1.y.raw() / 256;
int32_t iv2_x = v2.x.raw() / 256;
int32_t iv2_y = v2.y.raw() / 256;
int32_t iv3_x = v3.x.raw() / 256;
int32_t iv3_y = v3.y.raw() / 256;
mlog::debug << " (" << iv1_x << ", " << iv1_y << ")" << endl;
mlog::debug << " (" << iv2_x << ", " << iv2_y << ")" << endl;
mlog::debug << " (" << iv3_x << ", " << iv3_y << ")" << endl;
bmp16_line(iv1_x, iv1_y, iv2_x, iv2_y, color[0], vid_mem, 480);
bmp16_line(iv1_x, iv1_y, iv3_x, iv3_y, color[0], vid_mem, 480);
bmp16_line(iv2_x, iv2_y, iv3_x, iv3_y, color[0], vid_mem, 480);
} }
while (true) { while (true) {