diff --git a/include/fractal.hpp b/include/fractal.hpp index ae1c97c..82177ec 100644 --- a/include/fractal.hpp +++ b/include/fractal.hpp @@ -9,14 +9,6 @@ namespace fractal { -constexpr size_t g_max_groups = 32; -constexpr size_t g_max_weighted_groups = 32; -constexpr size_t g_max_variables = 32; -constexpr size_t g_max_concurrent_leafs = 1024; - -constexpr size_t g_max_rules_basic = 16; -constexpr size_t g_max_rules_marking = 4; - using token_id_t = uint32_t; using group_id_t = uint32_t; using weighted_group_id_t = uint32_t; @@ -85,5 +77,223 @@ struct branch_rule_marking_t { uint32_t m_marker_id; }; +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 m_leaf_buf1; + etl::vector m_leaf_buf2; + + etl::vector m_tokens; + + etl::vector m_group_characteristics; + etl::vector m_groups; + etl::vector m_weighted_groups; + + etl::vector m_basic_branch_rules; + etl::vector m_marking_branch_rules; + + etl::ivector* m_leafs_cur; + etl::ivector* 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() : + 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. + * + * @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 no tokens were supplied, or 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& tokens); + /** + * @overload + * + * Convience overload, defaulting to a group with factor 1. + */ + group_id_t add_group_characteristic(const etl::ivector& tokens); + + /** + * @brief Add a weighted group to the generator. + * + * Adds weighted group of groups, where each subgroup has a + * (weight) / (total weight) chance of being selected. Weights + * are represented as @c uint32_t. + * + * @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>& 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. + * + * @pre preprocess() has not been called on this generator. + * + * @exception mtl::system_error If the generator has already been preprocessed. + * @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); + /** + * @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); + + /** + * @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& out_markers) noexcept; +}; + } // namespace fractal