diff options
-rw-r--r-- | compat/unreachable.hpp | 16 | ||||
-rw-r--r-- | main/editor.cpp | 78 | ||||
-rw-r--r-- | main/editor.hpp | 19 | ||||
-rw-r--r-- | main/menu.cpp | 2 | ||||
-rw-r--r-- | src/random.cpp | 45 | ||||
-rw-r--r-- | src/random.hpp | 32 |
6 files changed, 191 insertions, 1 deletions
diff --git a/compat/unreachable.hpp b/compat/unreachable.hpp new file mode 100644 index 00000000..bb029b6d --- /dev/null +++ b/compat/unreachable.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace floormat { + +[[noreturn]] inline void unreachable() +{ +#if defined __GNUC__ + __builtin_unreachable(); +#elif defined _MSC_VER + __assume(false); +#else + *(volatile int*)0 = 0; +#endif +} + +} // namespace floormat diff --git a/main/editor.cpp b/main/editor.cpp index 7759b596..58b1e14a 100644 --- a/main/editor.cpp +++ b/main/editor.cpp @@ -2,7 +2,9 @@ #include "serialize/json-helper.hpp" #include "serialize/tile-atlas.hpp" #include "src/loader.hpp" +#include "random.hpp" #include "compat/assert.hpp" +#include "compat/unreachable.hpp" #include <filesystem> #include <vector> @@ -23,6 +25,7 @@ void tile_type::load_atlases() Containers::StringView name = atlas->name(); if (auto x = name.findLast('.'); x) name = name.prefix(x.data()); + std::get<1>(_permutation).reserve((std::size_t)atlas->num_tiles().product()); _atlases[name] = std::move(atlas); } } @@ -43,6 +46,81 @@ std::shared_ptr<tile_atlas> tile_type::atlas(Containers::StringView str) ABORT("no such atlas: %s", str.cbegin()); } +void tile_type::clear_selection() +{ + _selected_tile = {}; + _permutation = {}; + _selection_mode = sel_none; +} + +void tile_type::select_tile(const std::shared_ptr<tile_atlas>& atlas, std::uint8_t variant) +{ + ASSERT(atlas); + clear_selection(); + _selection_mode = sel_tile; + _selected_tile = { atlas, variant }; +} + +void tile_type::select_tile_permutation(const std::shared_ptr<tile_atlas>& atlas) +{ + ASSERT(atlas); + clear_selection(); + _selection_mode = sel_perm; + _permutation = { atlas, {} }; +} + +bool tile_type::is_tile_selected(const std::shared_ptr<tile_atlas>& atlas, std::uint8_t variant) +{ + ASSERT(atlas); + return _selection_mode == sel_tile && _selected_tile == std::make_tuple(atlas, variant); +} + +bool tile_type::is_permutation_selected(const std::shared_ptr<tile_atlas>& atlas) +{ + ASSERT(atlas); + return _selection_mode == sel_perm && std::get<0>(_permutation) == atlas; +} + +template<std::random_access_iterator T> +void fisher_yates(T begin, T end) +{ + const auto N = std::distance(begin, end); + for (auto i = N-1; i >= 1; i--) + { + const auto j = random(i+1); + using std::swap; + swap(begin[i], begin[j]); + } +} + +std::tuple<std::shared_ptr<tile_atlas>, std::uint8_t> tile_type::get_selected_perm() +{ + auto& [atlas, vec] = _permutation; + const std::size_t N = atlas->num_tiles().product(); + if (N == 0) + return {}; + if (vec.empty()) + { + for (std::uint8_t i = 0; i < N; i++) + vec.push_back(i); + fisher_yates(vec.begin(), vec.end()); + } + const auto idx = vec.back(); + vec.pop_back(); + return {atlas, idx}; +} + +std::optional<std::tuple<std::shared_ptr<tile_atlas>, std::uint8_t>> tile_type::get_selected() +{ + switch (_selection_mode) + { + case sel_none: return std::nullopt; + case sel_tile: return _selected_tile; + case sel_perm: return get_selected_perm(); + default: unreachable(); + } +} + editor_state::editor_state() { } diff --git a/main/editor.hpp b/main/editor.hpp index 53ecf13a..1f0a0f72 100644 --- a/main/editor.hpp +++ b/main/editor.hpp @@ -3,6 +3,7 @@ #include <map> #include <memory> #include <tuple> +#include <optional> #include <Corrade/Containers/StringView.h> namespace floormat { @@ -25,13 +26,31 @@ struct tile_type final Containers::StringView name() const { return _name; } editor_mode mode() const { return _mode; } + void clear_selection(); + void select_tile(const std::shared_ptr<tile_atlas>& atlas, std::uint8_t variant); + void select_tile_permutation(const std::shared_ptr<tile_atlas>& atlas); + bool is_tile_selected(const std::shared_ptr<tile_atlas>& atlas, std::uint8_t variant); + bool is_permutation_selected(const std::shared_ptr<tile_atlas>& atlas); + std::optional<std::tuple<std::shared_ptr<tile_atlas>, std::uint8_t>> get_selected(); + private: + enum selection_mode : std::uint8_t { + sel_none, sel_tile, sel_perm, + }; + enum rotation : std::uint8_t { + rot_N, rot_W, + }; + std::string _name; std::map<std::string, std::shared_ptr<tile_atlas>> _atlases; std::tuple<std::shared_ptr<tile_atlas>, std::uint32_t> _selected_tile; + std::tuple<std::shared_ptr<tile_atlas>, std::vector<std::uint8_t>> _permutation; + selection_mode _selection_mode = sel_none; editor_mode _mode; + rotation _rotation{}; void load_atlases(); + std::tuple<std::shared_ptr<tile_atlas>, std::uint8_t> get_selected_perm(); }; struct editor_state final diff --git a/main/menu.cpp b/main/menu.cpp index 3357aa6e..e5f291af 100644 --- a/main/menu.cpp +++ b/main/menu.cpp @@ -152,7 +152,7 @@ void app::draw_menu_(tile_type& type, float main_menu_height) const auto& k_ = k; const auto& v_ = v; const auto click_event = [&] { - if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && ImGui::GetIO().MouseClicked[1]) + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { Debug{} << "shuffle" << k_.data(); } diff --git a/src/random.cpp b/src/random.cpp new file mode 100644 index 00000000..82c47ff9 --- /dev/null +++ b/src/random.cpp @@ -0,0 +1,45 @@ +#include "random.hpp" +#include <random> +#include <limits> + +namespace floormat { + +static thread_local auto g = std::independent_bits_engine<decltype(std::ranlux48{}), 32, std::uint32_t>{std::ranlux48{}}; + +struct random_engine_impl final : random_engine { + std::size_t operator()() override; + float operator()(float min, float max) override; +}; + +std::size_t random_engine_impl::operator()() +{ + if constexpr(sizeof(std::size_t) > sizeof(std::uint32_t)) + { + constexpr std::size_t N = (sizeof(std::size_t) + sizeof(std::uint32_t)-1) / sizeof(std::uint32_t); + static_assert(N >= 2); + union { + std::size_t x; + std::uint32_t a[N]; + } ret; +#pragma omp unroll full + for (std::size_t i = 0; i < N; i++) + ret.a[i] = g(); + return ret.x; + } + else + return (std::size_t)g(); +} + +float random_engine_impl::operator()(float min, float max) +{ + std::uniform_real_distribution<float> dist{min, max}; + return dist(g); +} + +static random_engine& make_random_impl() { + static random_engine_impl ret; + return ret; +} +random_engine& random = make_random_impl(); + +} // namespace floormat diff --git a/src/random.hpp b/src/random.hpp new file mode 100644 index 00000000..3616aabf --- /dev/null +++ b/src/random.hpp @@ -0,0 +1,32 @@ +#pragma once +#include <cstddef> +#include <concepts> +#include <type_traits> + +namespace floormat { + +struct random_engine +{ + virtual inline ~random_engine(); + virtual std::common_type_t<std::size_t, std::uintptr_t, std::ptrdiff_t> operator()() = 0; + + template<std::integral T> + requires (sizeof(T) <= sizeof(std::size_t)) + T operator()(T max) { + return static_cast<T>(operator()() % static_cast<std::size_t>(max)); + } + + template<std::integral T> + requires (sizeof(T) <= sizeof(std::size_t)) + T operator()(T min, T max) { + return min + operator()(max-min); + } + + virtual float operator()(float min, float max) = 0; +}; + +random_engine::~random_engine() = default; + +[[maybe_unused]] extern random_engine& random; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace floormat |