summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compat/unreachable.hpp16
-rw-r--r--main/editor.cpp78
-rw-r--r--main/editor.hpp19
-rw-r--r--main/menu.cpp2
-rw-r--r--src/random.cpp45
-rw-r--r--src/random.hpp32
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