diff options
123 files changed, 3091 insertions, 2346 deletions
diff --git a/.gitmodules b/.gitmodules index 63a1a38..b5aa2c7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "thirdparty/imgui"] path = thirdparty/imgui url = https://github.com/ocornut/imgui +[submodule "thirdparty/lunasvg"] + path = thirdparty/lunasvg + url = https://github.com/sammycage/lunasvg diff --git a/Jamroot.jam b/Jamroot.jam index 8587aaa..8589861 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -12,6 +12,7 @@ pkg-config.import mxml : : <link>shared ; pkg-config.import assimp : : <link>shared ; lib pthread : : <link>shared ; lib OpenMeshCore : : <link>shared ; +lib boost_program_options : : <link>shared ; variant coverage : debug ; project i-like-trains : requirements @@ -46,15 +47,15 @@ project i-like-trains : requirements <toolset>tidy:<define>TIDY ; -exe iliketrains : - application/main.cpp - : - <library>ilt - ; +exe iliketrains : application/main.cpp : <library>ilt ; +exe resviewer : application/resviewer.cpp : <library>ilt <library>boost_program_options ; explicit main ; always main ; run iliketrains : -- : [ sequence.insertion-sort [ glob-tree-ex res : *.* ] ] : : main ; +explicit runresviewer ; +always runresviewer ; +run resviewer : -- : [ sequence.insertion-sort [ glob-tree-ex res : *.xml ] ] : : runresviewer ; lib ilt : [ glob-tree *.cpp *.?s *.t?s : application bin test thirdparty ] @@ -92,7 +93,6 @@ lib ilt : <library>thirdparty/<variant>release <implicit-dependency>thirdparty <library>sdl2 - <library>thirdparty//imguisdl2 <library>freetype2 <library>glib-2.0 <library>mxml diff --git a/application/main.cpp b/application/main.cpp index 723f3d2..a0c87c0 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -36,15 +36,14 @@ #include <ui/gameMainWindow.h> #include <ui/window.h> -static const int DISPLAY_WIDTH = 1280; -static const int DISPLAY_HEIGHT = 1024; +constexpr ScreenAbsCoord DEFAULT_WINDOW_SIZE {1280, 1024}; class DummyMainApplication : public GameState, public MainApplication { public: int run() { - windows.create<MainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT)->setContent<GameMainWindow>(); + windows.create<MainWindow>(DEFAULT_WINDOW_SIZE, "I Like Trains")->setContent<GameMainWindow>(); terrain = world.create<Terrain>(GeoData::loadFromAsciiGrid("test/fixtures/height/SD19.asc")); world.create<Water>(terrain); @@ -86,11 +85,11 @@ public: } const std::shared_ptr<Train> train = world.create<Train>(l3, 800000); - auto b47 = std::dynamic_pointer_cast<RailVehicleClass>(assets.at("brush-47")); + auto b47 = assets.at("brush-47").dynamicCast<RailVehicleClass>(); for (int N = 0; N < 6; N++) { train->create<RailVehicle>(b47); } - train->orders.removeAll(); + train->orders.clear(); train->orders.create<FreeRoam>(&train->orders); train->currentActivity = train->orders.current()->createActivity(); @@ -101,8 +100,9 @@ public: std::uniform_int_distribution<int> treeVariantDistribution {1, 4}; for (auto x = 311000000; x < 311830000; x += 5000) { for (auto y = 491100000; y < 491130000; y += 5000) { - world.create<Plant>(std::dynamic_pointer_cast<Foliage>(assets.at(std::format("Tree-{:#02}-{}", - treeDistribution(randomdev), treeVariantDistribution(randomdev)))), + world.create<Plant>(assets.at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev), + treeVariantDistribution(randomdev))) + .dynamicCast<Foliage>(), Location {terrain->positionAt({{x + positionOffsetDistribution(randomdev), y + positionOffsetDistribution(randomdev)}}), {0, rotationDistribution(randomdev), 0}}); @@ -112,7 +112,7 @@ public: mainLoop(); - world.objects.clear(); + world.clear(); return 0; } }; diff --git a/application/resviewer.cpp b/application/resviewer.cpp new file mode 100644 index 0000000..c82017b --- /dev/null +++ b/application/resviewer.cpp @@ -0,0 +1,227 @@ +#include <backends/imgui_impl_opengl3.h> +#include <backends/imgui_impl_sdl2.h> +#include <boost/program_options.hpp> +#include <game/environment.h> +#include <game/gamestate.h> +#include <game/terrain.h> +#include <gfx/gl/sceneProvider.h> +#include <gfx/gl/sceneRenderer.h> +#include <gfx/renderable.h> +#include <location.h> +#include <ui/applicationBase.h> +#include <ui/mainApplication.h> +#include <ui/mainWindow.h> + +constexpr ScreenAbsCoord DEFAULT_WINDOW_SIZE {800, 600}; +constexpr GlobalDistance TERRAIN_LIMIT = 1'000'000; +constexpr GlobalDistance TERRAIN_HEIGHT = 10'000; +constexpr RelativeDistance DEFAULT_CAMERA_DIST = 7'000; +constexpr GlobalDistance DEFAULT_CAMERA_HEIGHT = 5'000; +constexpr GlobalDistance DEFAULT_CAMERA_FOCUS = 3'000; +constexpr GlobalDistance MAX_CAMERA_HEIGHT = 10'000; +constexpr GlobalDistance MIN_CAMERA_DIST = 1'000; +constexpr GlobalDistance MAX_CAMERA_DIST = 30'000; + +class ViewerContent : public WindowContent, SceneRenderer, SceneProvider { +public: + ViewerContent(ScreenAbsCoord size, std::span<const std::filesystem::path> files) : + SceneRenderer {size, 0}, fileList(files) + { + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + gameState->terrain = std::make_shared<Terrain>( + GeoData::createFlat({-TERRAIN_LIMIT, -TERRAIN_LIMIT}, {TERRAIN_LIMIT, TERRAIN_LIMIT}, TERRAIN_HEIGHT)); + } + +private: + [[nodiscard]] + GlobalPosition3D + calcCameraPosition() const + { + return {sincos(cameraAngle) * cameraDistance, TERRAIN_HEIGHT + cameraHeight}; + } + + void + render() override + { + SceneRenderer::render(*this); + controls(); + } + + void + tick(TickDuration tick) override + { + if (autoRotate != 0) { + cameraAngle = normalize(cameraAngle + (autoRotate * tick.count())); + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + } + if (selectedFile) { + try { + if (const auto curmTime = std::filesystem::last_write_time(*selectedFile); curmTime != fileTime) { + location.reset(); + selectedAsset = nullptr; + gameState->assets = AssetFactory::loadXML(*selectedFile)->assets; + fileTime = curmTime; + if (!selectedAssetId.empty() && gameState->assets.contains(selectedAssetId)) { + auto asset = gameState->assets.at(selectedAssetId); + auto renderable = asset.getAs<const Renderable>(); + if (renderable) { + location = asset->createAt(position); + selectedAsset = renderable; + } + } + } + } + catch (...) { + } + } + } + + bool + handleInput(const SDL_Event & event) override + { + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { + SceneRenderer::resize({event.window.data1, event.window.data2}); + } + + return WindowContent::handleInput(event); + } + + void + controls() + { + if (ImGui::Begin("Resource view")) { + ImGui::SetWindowSize({}); + fileSelection(); + assetSelection(); + } + ImGui::End(); + + if (ImGui::Begin("Camera")) { + ImGui::SetWindowSize({}); + if (std::max({ImGui::SliderFloat("Camera position", &cameraAngle, -pi, pi), + ImGui::SliderInt("Camera focus", &cameraFocus, 1, MAX_CAMERA_HEIGHT), + ImGui::SliderInt("Camera height", &cameraHeight, 1, MAX_CAMERA_HEIGHT), + ImGui::SliderFloat("Camera distance", &cameraDistance, MIN_CAMERA_DIST, MAX_CAMERA_DIST)})) { + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + } + ImGui::SliderFloat("Auto rotate speed", &autoRotate, -1, 1); + } + ImGui::End(); + } + + void + fileSelection() + { + ImGui::BeginListBox("File"); + for (const auto & file : fileList) { + if (ImGui::Selectable(file.c_str(), &file == selectedFile)) { + location.reset(); + selectedAssetId.clear(); + selectedAsset = nullptr; + gameState->assets = AssetFactory::loadXML(file)->assets; + fileTime = std::filesystem::last_write_time(file); + selectedFile = &file; + } + } + ImGui::EndListBox(); + } + + void + assetSelection() + { + if (!gameState->assets.empty()) { + ImGui::BeginListBox("Asset"); + for (const auto & asset : gameState->assets) { + auto renderable = asset.second.getAs<const Renderable>(); + if (renderable) { + if (ImGui::Selectable(asset.first.c_str(), renderable == selectedAsset)) { + selectedAssetId = asset.first; + selectedAsset = renderable; + location = asset.second->createAt(position); + } + } + } + ImGui::EndListBox(); + } + } + + void + content(const SceneShader & sceneShader, const Frustum & frustum) const override + { + gameState->terrain->render(sceneShader, frustum); + if (selectedAsset) { + selectedAsset->render(sceneShader, frustum); + } + } + + void + lights(const SceneShader & sceneShader) const override + { + if (selectedAsset) { + selectedAsset->lights(sceneShader); + } + } + + void + shadows(const ShadowMapper & mapper, const Frustum & frustum) const override + { + gameState->terrain->shadows(mapper, frustum); + if (selectedAsset) { + selectedAsset->shadows(mapper, frustum); + } + } + + std::span<const std::filesystem::path> fileList; + std::filesystem::file_time_type fileTime; + const std::filesystem::path * selectedFile {}; + std::string selectedAssetId; + const Renderable * selectedAsset {}; + Location position {.pos = {0, 0, TERRAIN_HEIGHT}, .rot = {}}; + std::any location; + Angle cameraAngle {0.F}; + RelativeDistance cameraDistance {DEFAULT_CAMERA_DIST}; + GlobalDistance cameraHeight {DEFAULT_CAMERA_HEIGHT}; + GlobalDistance cameraFocus {DEFAULT_CAMERA_FOCUS}; + float autoRotate {0.F}; +}; + +int +main(int argc, char ** argv) +{ + class ResViewer : GameState, MainApplication { + public: + void + run(std::span<const std::filesystem::path> fileList) + { + windows.create<MainWindow>(DEFAULT_WINDOW_SIZE, "ILT - Resource Viewer") + ->setContent<ViewerContent>(fileList); + mainLoop(); + } + }; + + namespace po = boost::program_options; + po::options_description opts("ILT - Resource Viewer"); + std::vector<std::filesystem::path> resources; + // clang-format off + opts.add_options() + ("resource,r", po::value(&resources)->composing(), "Resource file") + ("help,h", po::value<bool>()->default_value(false)->zero_tokens(), "Help") + ; + // clang-format on + po::positional_options_description pod; + pod.add("resource", -1); + po::variables_map varmap; + po::store(po::command_line_parser(argc, argv).options(opts).positional(pod).run(), varmap); + po::notify(varmap); + + if (varmap.at("help").as<bool>()) { + std::cout << opts << '\n'; + return EXIT_SUCCESS; + } + + ResViewer {}.run(resources); + return EXIT_SUCCESS; +} diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp index e3f5feb..0254943 100644 --- a/assetFactory/asset.cpp +++ b/assetFactory/asset.cpp @@ -7,6 +7,12 @@ Asset::persist(Persistence::PersistenceStore & store) return STORE_MEMBER(id) && STORE_MEMBER(name); } +std::any +Asset::createAt(const Location &) const +{ + return {}; +} + Asset::TexturePtr Asset::getTexture() const { diff --git a/assetFactory/asset.h b/assetFactory/asset.h index 5bdd2f2..061a7c8 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -2,14 +2,22 @@ #include "factoryMesh.h" #include "persistence.h" +#include <any> +#include <manyPtr.h> #include <stdTypeDefs.h> class TextureAtlas; +class Renderable; +class Location; class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> { public: + using ManyPtr = ManySharedPtr<Asset, const Renderable>; using TexturePtr = std::shared_ptr<TextureAtlas>; + /// Used only for the asset viewer + [[nodiscard]] virtual std::any createAt(const Location &) const; + std::string id; std::string name; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 176e1f5..426fecc 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -5,6 +5,7 @@ #include "filesystem.h" #include "gfx/image.h" #include "gfx/models/texture.h" +#include "gfx/renderable.h" #include "object.h" #include "plane.h" #include "saxParse-persistence.h" @@ -146,7 +147,7 @@ bool AssetFactory::persist(Persistence::PersistenceStore & store) { using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>; - using MapAssets = Persistence::MapByMember<Assets>; + using MapAssets = Persistence::MapByMember<Assets, Asset::Ptr>; using MapTextureFragments = Persistence::MapByMember<TextureFragments>; using MapAssImp = Persistence::MapByMember<AssImps, std::shared_ptr<AssImp>, &AssImp::path>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 787f0a4..864e882 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -12,7 +12,7 @@ class Texture; class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map<std::string, Shape::Ptr, std::less<>>; - using Assets = std::map<std::string, Asset::Ptr, std::less<>>; + using Assets = std::map<std::string, Asset::ManyPtr, std::less<>>; using AssImps = std::map<std::string, AssImp::Ptr, std::less<>>; using TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>; using Colour = RGB; diff --git a/config/types.h b/config/types.h index c501f41..06825b5 100644 --- a/config/types.h +++ b/config/types.h @@ -42,6 +42,7 @@ using Normal3D = Normal<3>; using Rotation2D = Rotation<2>; using Rotation3D = Rotation<3>; using TextureRelCoord = glm::vec<2, float>; +using ImageDimensions = glm::vec<2, GLsizei>; using TextureDimensions = glm::vec<3, GLsizei>; using TextureRelRegion = glm::vec<4, float>; using TextureAbsCoord = glm::vec<2, GLsizei>; diff --git a/game/gamestate.h b/game/gamestate.h index 189417d..85cb5db 100644 --- a/game/gamestate.h +++ b/game/gamestate.h @@ -8,6 +8,7 @@ class WorldObject; class Terrain; class Environment; +class Renderable; class GameState { public: @@ -16,7 +17,7 @@ public: NO_MOVE(GameState); NO_COPY(GameState); - Collection<WorldObject> world; + SharedCollection<WorldObject, Renderable> world; std::shared_ptr<Terrain> terrain; std::shared_ptr<Environment> environment; AssetFactory::Assets assets; diff --git a/game/geoData.cpp b/game/geoData.cpp index 5cea4dd..b886efd 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -121,11 +121,9 @@ GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const walkUntil(PointFace {ray.start, face}, ray.start.xy() + (ray.direction.xy() * ::difference(extents.max.xy(), extents.min.xy())), [&out, &ray, this](const auto & step) { - BaryPosition bari {}; - RelativeDistance dist {}; const auto t = triangle<3>(step.current); - if (ray.intersectTriangle(t.x, t.y, t.z, bari, dist)) { - out.emplace(t * bari, step.current); + if (const auto inter = ray.intersectTriangle(t.x, t.y, t.z)) { + out.emplace(t * inter->bary, step.current); return true; } return false; @@ -286,9 +284,27 @@ GeoData::updateAllVertexNormals(const R & range) void GeoData::updateVertexNormal(VertexHandle vertex) { - Normal3D n; - calc_vertex_normal_correct(vertex, n); - set_normal(vertex, glm::normalize(n)); + Normal3D normal {}; + { // Lifted from calc_vertex_normal_correct in PolyMeshT_impl.hh but doesn't use Scalar to normalise + ConstVertexIHalfedgeIter cvih_it = this->cvih_iter(vertex); + if (!cvih_it.is_valid()) { // don't crash on isolated vertices + return; + } + Normal in_he_vec; + calc_edge_vector(*cvih_it, in_he_vec); + for (; cvih_it.is_valid(); ++cvih_it) { // calculates the sector normal defined by cvih_it and adds it to normal + if (this->is_boundary(*cvih_it)) { + continue; + } + HalfedgeHandle out_heh(this->next_halfedge_handle(*cvih_it)); + Normal out_he_vec; + calc_edge_vector(out_heh, out_he_vec); + normal += cross(in_he_vec, out_he_vec); // sector area is taken into account + in_he_vec = out_he_vec; + in_he_vec *= -1; // change the orientation + } + } // End lift + set_normal(vertex, glm::normalize(normal)); } OpenMesh::VertexHandle diff --git a/game/network/link.cpp b/game/network/link.cpp index c84524c..b8ffee2 100644 --- a/game/network/link.cpp +++ b/game/network/link.cpp @@ -5,37 +5,43 @@ #include <ray.h> #include <tuple> -Link::Link(End a, End b, float l) : ends {{std::move(a), std::move(b)}}, length {l} { } +Link::Link(End endA, End endB, float len) : ends {{std::move(endA), std::move(endB)}}, length {len} { } -LinkCurve::LinkCurve(GlobalPosition3D c, RelativeDistance r, Arc a) : centreBase {c}, radius {r}, arc {std::move(a)} { } +LinkCurve::LinkCurve(GlobalPosition3D centre, RelativeDistance radius, Arc arc) : + centreBase {centre}, radius {radius}, arc {std::move(arc)} +{ +} bool -operator<(const GlobalPosition3D & a, const GlobalPosition3D & b) +operator<(const GlobalPosition3D & left, const GlobalPosition3D & right) { // NOLINTNEXTLINE(hicpp-use-nullptr,modernize-use-nullptr) - return std::tie(a.x, a.y, a.z) < std::tie(b.x, b.y, b.z); + return std::tie(left.x, left.y, left.z) < std::tie(right.x, right.y, right.z); } bool -operator<(const Node & a, const Node & b) +operator<(const Node & left, const Node & right) { - return a.pos < b.pos; + return left.pos < right.pos; } Location LinkStraight::positionAt(RelativeDistance dist, unsigned char start) const { - const auto es {std::make_pair(ends[start].node.get(), ends[1 - start].node.get())}; - const RelativePosition3D diff {es.second->pos - es.first->pos}; - const auto dir {glm::normalize(diff)}; - return Location {es.first->pos + (vehiclePositionOffset() + dir * dist), {vector_pitch(dir), vector_yaw(dir), 0}}; + const auto endNodes = std::make_pair(ends[start].node.get(), ends[1 - start].node.get()); + const auto diff = ::difference(endNodes.second->pos, endNodes.first->pos); + const auto directionVector = glm::normalize(diff); + return Location { + .pos = endNodes.first->pos + (vehiclePositionOffset() + directionVector * dist), + .rot = {vector_pitch(directionVector), vector_yaw(directionVector), 0}, + }; } bool LinkStraight::intersectRay(const Ray<GlobalPosition3D> & ray) const { - return ray.passesCloseToEdges( - std::array {GlobalPosition3D {ends.front().node->pos}, GlobalPosition3D {ends.back().node->pos}}, 1000); + static constexpr auto PROXIMITY = 1'000; + return ray.passesCloseToEdges(std::array {ends.front().node->pos, ends.back().node->pos}, PROXIMITY); } std::vector<GlobalPosition3D> @@ -55,33 +61,36 @@ LinkStraight::getBase(RelativeDistance width) const Location LinkCurve::positionAt(float dist, unsigned char start) const { - static constexpr std::array<float, 2> dirOffset {half_pi, -half_pi}; - const auto frac {dist / length}; - const auto es {std::make_pair(ends[start].node.get(), ends[1 - start].node.get())}; - const auto as {std::make_pair(arc[start], arc[1 - start])}; - const auto ang {as.first + ((as.second - as.first) * frac)}; - const auto relPos {(sincos(ang) || 0.F) * radius}; - const auto relClimb {vehiclePositionOffset() + static constexpr std::array DIR_OFFSET {half_pi, -half_pi}; + const auto frac = dist / length; + const auto endNodes = std::make_pair(ends[start].node.get(), ends[1 - start].node.get()); + const auto arcEndAngles = std::make_pair(arc[start], arc[1 - start]); + const auto ang = glm::mix(arcEndAngles.first, arcEndAngles.second, frac); + const auto relPos = (sincos(ang) || 0.F) * radius; + const auto relClimb = vehiclePositionOffset() + RelativePosition3D {0, 0, - static_cast<RelativeDistance>(es.first->pos.z - centreBase.z) - + (static_cast<RelativeDistance>(es.second->pos.z - es.first->pos.z) * frac)}}; - const auto pitch {vector_pitch(difference(es.second->pos, es.first->pos) / length)}; - return Location {GlobalPosition3D(relPos + relClimb) + centreBase, {pitch, normalize(ang + dirOffset[start]), 0}}; + static_cast<RelativeDistance>(endNodes.first->pos.z - centreBase.z) + + (static_cast<RelativeDistance>(endNodes.second->pos.z - endNodes.first->pos.z) * frac)}; + const auto pitch {vector_pitch(difference(endNodes.second->pos, endNodes.first->pos) / length)}; + return Location { + .pos = GlobalPosition3D(relPos + relClimb) + centreBase, + .rot = {pitch, normalize(ang + DIR_OFFSET[start]), 0}, + }; } bool LinkCurve::intersectRay(const Ray<GlobalPosition3D> & ray) const { - const auto & e0p {ends[0].node->pos}; - const auto & e1p {ends[1].node->pos}; + const auto e0p = ends[0].node->pos.z; + const auto e1p = ends[1].node->pos.z; const auto slength = round_frac(length / 2.F, 5.F); const auto segs = std::round(15.F * slength / std::pow(radius, 0.7F)); - const auto step {glm::vec<2, RelativeDistance> {arc.length(), e1p.z - e0p.z} / segs}; + const auto step {glm::vec<2, RelativeDistance> {arc.length(), e1p - e0p} / segs}; auto segCount = static_cast<std::size_t>(std::lround(segs)) + 1; std::vector<GlobalPosition3D> points; points.reserve(segCount); - for (std::remove_const_t<decltype(step)> swing = {arc.first, centreBase.z - e0p.z}; segCount; + for (std::remove_const_t<decltype(step)> swing = {arc.first, centreBase.z - e0p}; segCount; swing += step, --segCount) { points.emplace_back(centreBase + ((sincos(swing.x) * radius) || swing.y)); } @@ -94,7 +103,7 @@ LinkCurve::getBase(RelativeDistance width) const const auto start = ends.front().node->pos; const auto end = ends.back().node->pos; const auto segs = std::ceil(std::sqrt(radius) * 0.02F * arc.length()); - const auto step {glm::vec<2, RelativeDistance> {arc.length(), end.z - start.z} / segs}; + const auto step = glm::vec<2, RelativeDistance> {arc.length(), end.z - start.z} / segs; auto segCount = static_cast<size_t>(segs) + 1; std::vector<GlobalPosition3D> out; diff --git a/game/network/link.h b/game/network/link.h index 59bbb65..0b58558 100644 --- a/game/network/link.h +++ b/game/network/link.h @@ -16,7 +16,7 @@ template<typename> class Ray; // it has location class Node : public StdTypeDefs<Node> { public: - explicit Node(GlobalPosition3D p) noexcept : pos(p) { }; + explicit Node(GlobalPosition3D position) noexcept : pos(position) { }; virtual ~Node() noexcept = default; NO_COPY(Node); NO_MOVE(Node); @@ -35,6 +35,7 @@ public: struct End { Node::Ptr node; float dir; + // NOLINTNEXTLINE(readability-redundant-member-init) don't require client to empty initialise this Nexts nexts {}; }; @@ -58,8 +59,8 @@ protected: } }; -bool operator<(const GlobalPosition3D & a, const GlobalPosition3D & b); -bool operator<(const Node & a, const Node & b); +bool operator<(const GlobalPosition3D &, const GlobalPosition3D &); +bool operator<(const Node &, const Node &); class LinkStraight : public virtual Link { public: diff --git a/game/network/network.cpp b/game/network/network.cpp index e67942f..c8482de 100644 --- a/game/network/network.cpp +++ b/game/network/network.cpp @@ -8,8 +8,8 @@ #include <stdexcept> #include <utility> -Network::Network(const std::string & tn) : - texture {std::make_shared<Texture>(tn, +Network::Network(const std::string & textureName) : + texture {std::make_shared<Texture>(textureName, TextureOptions { .minFilter = GL_NEAREST_MIPMAP_LINEAR, })} @@ -25,19 +25,18 @@ Network::nodeAt(GlobalPosition3D pos) Network::NodeInsertion Network::newNodeAt(GlobalPosition3D pos) { - if (auto [n, i] = candidateNodeAt(pos); i == NodeIs::NotInNetwork) { - return {*nodes.insert(std::move(n)).first, i}; - } - else { - return {std::move(n), NodeIs::InNetwork}; + auto [node, inNetwork] = candidateNodeAt(pos); + if (inNetwork == NodeIs::NotInNetwork) { + return {*nodes.insert(std::move(node)).first, inNetwork}; } + return {std::move(node), NodeIs::InNetwork}; } Node::Ptr Network::findNodeAt(GlobalPosition3D pos) const { - if (const auto n = nodes.find(pos); n != nodes.end()) { - return *n; + if (const auto node = nodes.find(pos); node != nodes.end()) { + return *node; } return {}; } @@ -45,8 +44,8 @@ Network::findNodeAt(GlobalPosition3D pos) const Network::NodeInsertion Network::candidateNodeAt(GlobalPosition3D pos) const { - if (const auto n = nodes.find(pos); n != nodes.end()) { - return {*n, NodeIs::InNetwork}; + if (const auto node = nodes.find(pos); node != nodes.end()) { + return {*node, NodeIs::InNetwork}; } return {std::make_shared<Node>(pos), NodeIs::NotInNetwork}; } @@ -54,12 +53,11 @@ Network::candidateNodeAt(GlobalPosition3D pos) const Node::Ptr Network::intersectRayNodes(const Ray<GlobalPosition3D> & ray) const { + static constexpr auto MIN_DISTANCE = 2000; // Click within 2m of a node if (const auto node = std::find_if(nodes.begin(), nodes.end(), [&ray](const Node::Ptr & node) { - GlobalPosition3D ipos; - Normal3D inorm; - return ray.intersectSphere(node->pos, 2000, ipos, inorm); + return ray.intersectSphere(node->pos, MIN_DISTANCE); }); node != nodes.end()) { return *node; @@ -68,14 +66,14 @@ Network::intersectRayNodes(const Ray<GlobalPosition3D> & ray) const } void -Network::joinLinks(const Link::Ptr & l, const Link::Ptr & ol) +Network::joinLinks(const Link::Ptr & link, const Link::Ptr & oldLink) { - if (l != ol) { - for (const auto oe : {0U, 1U}) { - for (const auto te : {0U, 1U}) { - if (l->ends[te].node == ol->ends[oe].node) { - l->ends[te].nexts.emplace_back(ol, oe); - ol->ends[oe].nexts.emplace_back(l, te); + if (link != oldLink) { + for (const auto oldLinkEnd : {0U, 1U}) { + for (const auto linkEnd : {0U, 1U}) { + if (link->ends[linkEnd].node == oldLink->ends[oldLinkEnd].node) { + link->ends[linkEnd].nexts.emplace_back(oldLink, oldLinkEnd); + oldLink->ends[oldLinkEnd].nexts.emplace_back(link, linkEnd); } } } @@ -93,7 +91,7 @@ Network::routeFromTo(const Link::End & start, GlobalPosition3D dest) const } Link::Nexts -Network::routeFromTo(const Link::End & end, const Node::Ptr & dest) const +Network::routeFromTo(const Link::End & end, const Node::Ptr & dest) { return RouteWalker().findRouteTo(end, dest); } @@ -102,11 +100,11 @@ GenCurveDef Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir) { const auto diff = difference(end, start); - const auto vy {vector_yaw(diff)}; + const auto yaw = vector_yaw(diff); const auto dir = pi + startDir; - const auto flatStart {start.xy()}, flatEnd {end.xy()}; - const auto n2ed {(vy * 2) - dir - pi}; - const auto centre {find_arc_centre(flatStart, dir, flatEnd, n2ed)}; + const auto flatStart = start.xy(), flatEnd = end.xy(); + const auto n2ed = (yaw * 2) - dir - pi; + const auto centre = find_arc_centre(flatStart, dir, flatEnd, n2ed); if (centre.second) { // right hand arc return {end, start, centre.first}; @@ -121,24 +119,23 @@ Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & en endDir += pi; const auto flatStart {start.xy()}, flatEnd {end.xy()}; auto midheight = [&](auto mid) { - const auto sm = ::distance<2>(flatStart, mid); - const auto em = ::distance<2>(flatEnd, mid); - return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em))); + const auto startToMid = ::distance<2>(flatStart, mid); + const auto endToMid = ::distance<2>(flatEnd, mid); + return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (startToMid / (startToMid + endToMid))); }; - if (const auto radii = find_arcs_radius(flatStart, startDir, flatEnd, endDir); radii.first < radii.second) { - const auto radius {radii.first}; - const auto c1 = flatStart + (sincos(startDir + half_pi) * radius); - const auto c2 = flatEnd + (sincos(endDir + half_pi) * radius); - const auto mid = (c1 + c2) / 2; - const auto midh = mid || midheight(mid); - return {{start, midh, c1}, {end, midh, c2}}; - } - else { - const auto radius {radii.second}; - const auto c1 = flatStart + (sincos(startDir - half_pi) * radius); - const auto c2 = flatEnd + (sincos(endDir - half_pi) * radius); - const auto mid = (c1 + c2) / 2; + const auto radii = find_arcs_radius(flatStart, startDir, flatEnd, endDir); + if (radii.first < radii.second) { + const auto radius = radii.first; + const auto centre1 = flatStart + (sincos(startDir + half_pi) * radius); + const auto centre2 = flatEnd + (sincos(endDir + half_pi) * radius); + const auto mid = (centre1 + centre2) / 2; const auto midh = mid || midheight(mid); - return {{midh, start, c1}, {midh, end, c2}}; + return {{start, midh, centre1}, {end, midh, centre2}}; } + const auto radius = radii.second; + const auto centre1 = flatStart + (sincos(startDir - half_pi) * radius); + const auto centre2 = flatEnd + (sincos(endDir - half_pi) * radius); + const auto mid = (centre1 + centre2) / 2; + const auto midh = mid || midheight(mid); + return {{midh, start, centre1}, {midh, end, centre2}}; } diff --git a/game/network/network.h b/game/network/network.h index 291c4ec..4f5d2b0 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -18,7 +18,7 @@ struct Surface; class GeoData; template<typename> class Ray; -template<size_t... n> using GenDef = std::tuple<glm::vec<n, GlobalDistance>...>; +template<size_t... N> using GenDef = std::tuple<glm::vec<N, GlobalDistance>...>; using GenCurveDef = GenDef<3, 3, 2>; class Network { @@ -30,7 +30,7 @@ public: [[nodiscard]] Node::Ptr findNodeAt(GlobalPosition3D) const; [[nodiscard]] Node::Ptr nodeAt(GlobalPosition3D); - enum class NodeIs { InNetwork, NotInNetwork }; + enum class NodeIs : uint8_t { InNetwork, NotInNetwork }; using NodeInsertion = std::pair<Node::Ptr, NodeIs>; [[nodiscard]] NodeInsertion newNodeAt(GlobalPosition3D); [[nodiscard]] NodeInsertion candidateNodeAt(GlobalPosition3D) const; @@ -38,7 +38,7 @@ public: [[nodiscard]] virtual Node::Ptr intersectRayNodes(const Ray<GlobalPosition3D> &) const; [[nodiscard]] Link::Nexts routeFromTo(const Link::End &, GlobalPosition3D) const; - [[nodiscard]] Link::Nexts routeFromTo(const Link::End &, const Node::Ptr &) const; + [[nodiscard]] static Link::Nexts routeFromTo(const Link::End &, const Node::Ptr &); virtual Link::CCollection candidateStraight(GlobalPosition3D, GlobalPosition3D) = 0; virtual Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) = 0; @@ -53,7 +53,7 @@ public: [[nodiscard]] virtual RelativeDistance getBaseWidth() const = 0; protected: - static void joinLinks(const Link::Ptr & l, const Link::Ptr & ol); + static void joinLinks(const Link::Ptr & link, const Link::Ptr & oldLink); static GenCurveDef genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir); static std::pair<GenCurveDef, GenCurveDef> genCurveDef( const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir, float endDir); @@ -77,37 +77,36 @@ class NetworkOf : public Network, public Renderable, public NetworkLinkHolder<Li protected: using Network::Network; - Collection<T> links; + SharedCollection<T> links; void joinLinks(const Link::Ptr &) const; -protected: [[nodiscard]] Link::Ptr intersectRayLinks(const Ray<GlobalPosition3D> &) const override; public: template<typename L, typename... Params> std::shared_ptr<L> - candidateLink(GlobalPosition3D a, GlobalPosition3D b, Params &&... params) + candidateLink(GlobalPosition3D positionA, GlobalPosition3D positionB, Params &&... params) requires std::is_base_of_v<T, L> { - const auto node1 = candidateNodeAt(a).first, node2 = candidateNodeAt(b).first; + const auto node1 = candidateNodeAt(positionA).first, node2 = candidateNodeAt(positionB).first; return std::make_shared<L>(*this, node1, node2, std::forward<Params>(params)...); } template<typename L, typename... Params> std::shared_ptr<L> - addLink(GlobalPosition3D a, GlobalPosition3D b, Params &&... params) + addLink(GlobalPosition3D positionA, GlobalPosition3D positionB, Params &&... params) requires std::is_base_of_v<T, L> { - const auto node1 = nodeAt(a), node2 = nodeAt(b); - auto l {links.template create<L>(*this, node1, node2, std::forward<Params>(params)...)}; - joinLinks(l); - return l; + const auto node1 = nodeAt(positionA), node2 = nodeAt(positionB); + auto newLink = links.template create<L>(*this, node1, node2, std::forward<Params>(params)...); + joinLinks(newLink); + return std::move(newLink); } - Link::CCollection candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) override; + Link::CCollection candidateStraight(GlobalPosition3D, GlobalPosition3D) override; Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) override; Link::CCollection candidateExtend(GlobalPosition3D, GlobalPosition3D) override; - Link::CCollection addStraight(const GeoData *, GlobalPosition3D n1, GlobalPosition3D n2) override; + Link::CCollection addStraight(const GeoData *, GlobalPosition3D, GlobalPosition3D) override; Link::CCollection addJoins(const GeoData *, GlobalPosition3D, GlobalPosition3D) override; Link::CCollection addExtend(const GeoData *, GlobalPosition3D, GlobalPosition3D) override; diff --git a/game/network/network.impl.h b/game/network/network.impl.h index 0a2f9ca..e339922 100644 --- a/game/network/network.impl.h +++ b/game/network/network.impl.h @@ -6,10 +6,10 @@ template<typename T, typename... Links> void -NetworkOf<T, Links...>::joinLinks(const Link::Ptr & l) const +NetworkOf<T, Links...>::joinLinks(const Link::Ptr & link) const { - for (const auto & ol : links.objects) { - Network::joinLinks(l, ol); + for (const auto & oldLink : links) { + Network::joinLinks(link, oldLink); } } @@ -18,11 +18,11 @@ Link::Ptr NetworkOf<T, Links...>::intersectRayLinks(const Ray<GlobalPosition3D> & ray) const { // Click link - if (const auto link = std::find_if(links.objects.begin(), links.objects.end(), + if (const auto link = std::find_if(links.begin(), links.end(), [&ray](const std::shared_ptr<T> & link) { return link->intersectRay(ray); }); - link != links.objects.end()) { + link != links.end()) { return *link; } return {}; @@ -32,11 +32,11 @@ template<typename T, typename... Links> float NetworkOf<T, Links...>::findNodeDirection(Node::AnyCPtr n) const { - for (const auto & l : links.objects) { - for (const auto & e : l->ends) { + for (const auto & link : links) { + for (const auto & end : link->ends) { // cppcheck-suppress useStlAlgorithm - if (e.node.get() == n.get()) { - return e.dir; + if (end.node.get() == n.get()) { + return end.dir; } } } @@ -45,16 +45,17 @@ NetworkOf<T, Links...>::findNodeDirection(Node::AnyCPtr n) const template<typename T, typename... Links> Link::CCollection -NetworkOf<T, Links...>::candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) +NetworkOf<T, Links...>::candidateStraight(GlobalPosition3D positionA, GlobalPosition3D positionB) { - return {candidateLink<typename T::StraightLink>(n1, n2)}; + return {candidateLink<typename T::StraightLink>(positionA, positionB)}; } template<typename T, typename... Links> Link::CCollection NetworkOf<T, Links...>::candidateJoins(GlobalPosition3D start, GlobalPosition3D end) { - if (::distance(start, end) < 2000.F) { + static constexpr auto MIN_DISTANCE = 2000.F; + if (::distance(start, end) < MIN_DISTANCE) { return {}; } const auto defs = genCurveDef( @@ -74,17 +75,17 @@ NetworkOf<T, Links...>::candidateExtend(GlobalPosition3D start, GlobalPosition3D template<typename T, typename... Links> Link::CCollection -NetworkOf<T, Links...>::addStraight(const GeoData * geoData, GlobalPosition3D n1, GlobalPosition3D n2) +NetworkOf<T, Links...>::addStraight(const GeoData * geoData, GlobalPosition3D positionA, GlobalPosition3D positionB) { Link::CCollection out; - geoData->walk(n1.xy(), n2, [geoData, &out, this, &n1](const GeoData::WalkStep & step) { + geoData->walk(positionA.xy(), positionB, [geoData, &out, this, &positionA](const GeoData::WalkStep & step) { if (step.previous.is_valid() && geoData->getSurface(step.current) != geoData->getSurface(step.previous)) { const auto surfaceEdgePosition = geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current)); - out.emplace_back(addLink<typename T::StraightLink>(n1, surfaceEdgePosition)); - n1 = surfaceEdgePosition; + out.emplace_back(addLink<typename T::StraightLink>(positionA, surfaceEdgePosition)); + positionA = surfaceEdgePosition; } }); - out.emplace_back(addLink<typename T::StraightLink>(n1, n2)); + out.emplace_back(addLink<typename T::StraightLink>(positionA, positionB)); return out; } @@ -92,6 +93,7 @@ template<typename T, typename... Links> Link::CCollection NetworkOf<T, Links...>::addCurve(const GeoData * geoData, const GenCurveDef & curve) { + static constexpr auto MIN_DISTANCE = 2000.F; auto [cstart, cend, centre] = curve; Link::CCollection out; std::set<GeoData::WalkStepCurve, SortedBy<&GeoData::WalkStepCurve::angle>> breaks; @@ -115,7 +117,7 @@ NetworkOf<T, Links...>::addCurve(const GeoData * geoData, const GenCurveDef & cu || geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current)).z; }); points.push_back(cend); - mergeClose(points, ::distance<3, GlobalDistance>, ::midpoint<3, GlobalDistance>, 2'000.F); + mergeClose(points, ::distance<3, GlobalDistance>, ::midpoint<3, GlobalDistance>, MIN_DISTANCE); std::ranges::transform(points | std::views::pairwise, std::back_inserter(out), [this, centre](const auto pair) { const auto [a, b] = pair; return this->addLink<typename T::CurveLink>(a, b, centre); @@ -127,7 +129,8 @@ template<typename T, typename... Links> Link::CCollection NetworkOf<T, Links...>::addJoins(const GeoData * geoData, GlobalPosition3D start, GlobalPosition3D end) { - if (::distance(start, end) < 2000.F) { + static constexpr auto MIN_DISTANCE = 2000.F; + if (::distance(start, end) < MIN_DISTANCE) { return {}; } const auto defs = genCurveDef(start, end, findNodeDirection(nodeAt(start)), findNodeDirection(nodeAt(end))); diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 2a18b9a..c0e597d 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -1,6 +1,5 @@ #include "rail.h" #include "game/gamestate.h" -#include "game/geoData.h" #include "network.h" #include <game/network/network.impl.h> // IWYU pragma: keep #include <gfx/gl/sceneShader.h> @@ -40,34 +39,33 @@ RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end) const auto flatStart {start.xy()}, flatEnd {end.xy()}; if (node2ins.second == NodeIs::InNetwork) { auto midheight = [&](auto mid) { - const auto sm = ::distance<2>(flatStart, mid); - const auto em = ::distance<2>(flatEnd, mid); - return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em))); + const auto startToMid = ::distance<2>(flatStart, mid); + const auto endToMid = ::distance<2>(flatEnd, mid); + return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (startToMid / (startToMid + endToMid))); }; const float dir2 = pi + findNodeDirection(node2ins.first); - if (const auto radii = find_arcs_radius(flatStart, dir, flatEnd, dir2); radii.first < radii.second) { - const auto radius {radii.first}; - const auto c1 = flatStart + (sincos(dir + half_pi) * radius); - const auto c2 = flatEnd + (sincos(dir2 + half_pi) * radius); - const auto mid = (c1 + c2) / 2; + const auto radii = find_arcs_radius(flatStart, dir, flatEnd, dir2); + if (radii.first < radii.second) { + const auto radius = radii.first; + const auto centre1 = flatStart + (sincos(dir + half_pi) * radius); + const auto centre2 = flatEnd + (sincos(dir2 + half_pi) * radius); + const auto mid = (centre1 + centre2) / 2; const auto midh = mid || midheight(mid); - addLink<RailLinkCurve>(start, midh, c1); - return addLink<RailLinkCurve>(end, midh, c2); - } - else { - const auto radius {radii.second}; - const auto c1 = flatStart + (sincos(dir - half_pi) * radius); - const auto c2 = flatEnd + (sincos(dir2 - half_pi) * radius); - const auto mid = (c1 + c2) / 2; - const auto midh = mid || midheight(mid); - addLink<RailLinkCurve>(midh, start, c1); - return addLink<RailLinkCurve>(midh, end, c2); + addLink<RailLinkCurve>(start, midh, centre1); + return addLink<RailLinkCurve>(end, midh, centre2); } + const auto radius = radii.second; + const auto centre1 = flatStart + (sincos(dir - half_pi) * radius); + const auto centre2 = flatEnd + (sincos(dir2 - half_pi) * radius); + const auto mid = (centre1 + centre2) / 2; + const auto midh = mid || midheight(mid); + addLink<RailLinkCurve>(midh, start, centre1); + return addLink<RailLinkCurve>(midh, end, centre2); } const auto diff = difference(end, start); - const auto vy {vector_yaw(diff)}; - const auto n2ed {(vy * 2) - dir - pi}; - const auto centre {find_arc_centre(flatStart, dir, flatEnd, n2ed)}; + const auto yaw = vector_yaw(diff); + const auto n2ed = (yaw * 2) - dir - pi; + const auto centre = find_arc_centre(flatStart, dir, flatEnd, n2ed); if (centre.second) { // right hand arc std::swap(start, end); @@ -102,31 +100,34 @@ namespace { } } -RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, const Node::Ptr & a, - const Node::Ptr & b) : RailLinkStraight(instances, a, b, b->pos - a->pos) +RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, const Node::Ptr & nodeA, + const Node::Ptr & nodeB) : RailLinkStraight(instances, nodeA, nodeB, nodeB->pos - nodeA->pos) { } -RailLinkStraight::RailLinkStraight( - NetworkLinkHolder<RailLinkStraight> & instances, Node::Ptr a, Node::Ptr b, const RelativePosition3D & diff) : - Link({std::move(a), vector_yaw(diff)}, {std::move(b), vector_yaw(-diff)}, glm::length(diff)), +RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, Node::Ptr nodeA, Node::Ptr nodeB, + const RelativePosition3D & diff) : + Link({.node = std::move(nodeA), .dir = vector_yaw(diff)}, {.node = std::move(nodeB), .dir = vector_yaw(-diff)}, + glm::length(diff)), instance {instances.vertices.acquire( ends[0].node->pos, ends[1].node->pos, flat_orientation(diff), roundSleepers(length))} { } -RailLinkCurve::RailLinkCurve( - NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & a, const Node::Ptr & b, GlobalPosition2D c) : - RailLinkCurve(instances, a, b, c || a->pos.z, ::distance<2>(a->pos.xy(), c), {c, a->pos, b->pos}) +RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & nodeA, + const Node::Ptr & nodeB, GlobalPosition2D centre) : + RailLinkCurve(instances, nodeA, nodeB, centre || nodeA->pos.z, ::distance<2>(nodeA->pos.xy(), centre), + {centre, nodeA->pos, nodeB->pos}) { } -RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & a, const Node::Ptr & b, - GlobalPosition3D c, RelativeDistance radius, const Arc arc) : - Link({a, normalize(arc.first + half_pi)}, {b, normalize(arc.second - half_pi)}, - glm::length(RelativePosition2D {radius * arc.length(), difference(a->pos, b->pos).z})), - LinkCurve {c, radius, arc}, instance {instances.vertices.acquire(ends[0].node->pos, ends[1].node->pos, c, - roundSleepers(length), half_pi - arc.first, half_pi - arc.second, radius)} +RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & nodeA, + const Node::Ptr & nodeB, GlobalPosition3D centre, RelativeDistance radius, const Arc arc) : + Link({.node = nodeA, .dir = normalize(arc.first + half_pi)}, + {.node = nodeB, .dir = normalize(arc.second - half_pi)}, + glm::length(RelativePosition2D {radius * arc.length(), difference(nodeA->pos, nodeB->pos).z})), + LinkCurve {centre, radius, arc}, instance {instances.vertices.acquire(ends[0].node->pos, ends[1].node->pos, centre, + roundSleepers(length), half_pi - arc.first, half_pi - arc.second, radius)} { } @@ -155,11 +156,11 @@ template<> NetworkLinkHolder<RailLinkCurve>::NetworkLinkHolder() namespace { template<typename LinkType> void - renderType(const NetworkLinkHolder<LinkType> & n, auto & s) + renderType(const NetworkLinkHolder<LinkType> & networkLinks, auto & shader) { - if (auto count = n.vertices.size()) { - s.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS); - glBindVertexArray(n.vao); + if (auto count = networkLinks.vertices.size()) { + shader.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS); + glBindVertexArray(networkLinks.vao); glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count)); } }; @@ -168,7 +169,7 @@ namespace { void RailLinks::render(const SceneShader & shader, const Frustum &) const { - if (!links.objects.empty()) { + if (!links.empty()) { texture->bind(); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1, 0); @@ -182,11 +183,12 @@ RailLinks::render(const SceneShader & shader, const Frustum &) const const Surface * RailLinks::getBaseSurface() const { - return std::dynamic_pointer_cast<Surface>(gameState->assets.at("terrain.surface.gravel")).get(); + return gameState->assets.at("terrain.surface.gravel").dynamicCast<const Surface>().get(); } RelativeDistance RailLinks::getBaseWidth() const { - return 5'700; + static constexpr auto BASE_WIDTH = 5'700; + return BASE_WIDTH; } diff --git a/game/orders.h b/game/orders.h index ca5cfdb..840aa3c 100644 --- a/game/orders.h +++ b/game/orders.h @@ -5,7 +5,7 @@ class Objective; -class Orders : public Collection<Objective> { +class Orders : public SharedCollection<Objective> { public: [[nodiscard]] Objective * current() const; Objective * next(); diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index 159a078..140c4e5 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -2,6 +2,16 @@ #include "gfx/gl/sceneShader.h" #include "gfx/gl/shadowMapper.h" #include "gfx/gl/vertexArrayObject.h" +#include <location.h> + +static_assert(std::is_constructible_v<Foliage>); + +std::any +Foliage::createAt(const Location & position) const +{ + return std::make_shared<InstanceVertices<LocationVertex>::InstanceProxy>( + instances.acquire(position.getRotationTransform(), position.rot.y, position.pos)); +} bool Foliage::persist(Persistence::PersistenceStore & store) diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h index 71bc734..d15a8b0 100644 --- a/game/scenary/foliage.h +++ b/game/scenary/foliage.h @@ -17,6 +17,8 @@ class Foliage : public Asset, public Renderable, public StdTypeDefs<Foliage> { glVertexArray instancePointVAO; public: + [[nodiscard]] std::any createAt(const Location &) const override; + struct LocationVertex { glm::mat3 rotation; float yaw; diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp index f1a02b2..d8e4c4e 100644 --- a/game/scenary/illuminator.cpp +++ b/game/scenary/illuminator.cpp @@ -2,6 +2,16 @@ #include "gfx/gl/sceneShader.h" #include "gfx/gl/vertexArrayObject.h" #include "gfx/models/texture.h" // IWYU pragma: keep +#include <location.h> + +static_assert(std::is_constructible_v<Illuminator>); + +std::any +Illuminator::createAt(const Location & position) const +{ + return std::make_shared<InstanceVertices<LocationVertex>::InstanceProxy>( + instances.acquire(position.getRotationTransform(), position.pos)); +} bool Illuminator::SpotLight::persist(Persistence::PersistenceStore & store) diff --git a/game/scenary/illuminator.h b/game/scenary/illuminator.h index 47ce337..200ba40 100644 --- a/game/scenary/illuminator.h +++ b/game/scenary/illuminator.h @@ -15,6 +15,8 @@ class Illuminator : public Asset, public Renderable, public StdTypeDefs<Illumina std::optional<glVertexArray> instancesSpotLightVAO, instancesPointLightVAO; public: + [[nodiscard]] std::any createAt(const Location &) const override; + struct LightCommonVertex { RelativePosition3D position; RGB colour; diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp index 59d1e83..4a0a22d 100644 --- a/game/vehicles/railVehicle.cpp +++ b/game/vehicles/railVehicle.cpp @@ -72,7 +72,12 @@ RailVehicle::intersectRay(const Ray<GlobalPosition3D> & ray, BaryPosition & bary }}; return std::any_of( triangles.begin(), triangles.end(), [&cornerVertices, &ray, &baryPos, &distance](const auto & idx) { - return ray.intersectTriangle( - cornerVertices[idx[0]], cornerVertices[idx[1]], cornerVertices[idx[2]], baryPos, distance); + if (const auto inter = ray.intersectTriangle( + cornerVertices[idx[0]], cornerVertices[idx[1]], cornerVertices[idx[2]])) { + baryPos = inter->bary; + distance = inter->distance; + return true; + }; + return false; }); } diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 162a29a..21f01c8 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -17,6 +17,19 @@ RailVehicleClass::persist(Persistence::PersistenceStore & store) && STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store); } +std::any +RailVehicleClass::createAt(const Location & position) const +{ + return std::make_shared<InstanceVertices<LocationVertex>::InstanceProxy>(instances.acquire(LocationVertex { + .body = position.getRotationTransform(), + .front = position.getRotationTransform(), + .back = position.getRotationTransform(), + .bodyPos = position.pos, + .frontPos = {sincos(position.rot.x) * wheelBase * 0.5F, position.pos.z}, + .backPos = {sincos(position.rot.x) * wheelBase * -0.5F, position.pos.z}, + })); +} + void RailVehicleClass::postLoad() { diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index 6eb4ca5..ccff3e2 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -17,6 +17,8 @@ public: void render(const SceneShader & shader, const Frustum &) const override; void shadows(const ShadowMapper & shadowMapper, const Frustum &) const override; + [[nodiscard]] std::any createAt(const Location &) const override; + struct LocationVertex { glm::mat3 body, front, back; GlobalPosition3D bodyPos, frontPos, backPos; diff --git a/game/vehicles/train.h b/game/vehicles/train.h index 4933347..88e30f9 100644 --- a/game/vehicles/train.h +++ b/game/vehicles/train.h @@ -15,7 +15,7 @@ class SceneShader; class ShadowMapper; template<typename> class Ray; -class Train : public Vehicle, public Collection<RailVehicle, false>, public Can<Go>, public Can<Idle> { +class Train : public Vehicle, public UniqueCollection<RailVehicle>, public Can<Go>, public Can<Idle> { public: explicit Train(const Link::CPtr & link, float linkDist = 0) : Vehicle {link, linkDist} { } diff --git a/game/water.h b/game/water.h index f9fe080..07d9ae1 100644 --- a/game/water.h +++ b/game/water.h @@ -29,6 +29,6 @@ private: void generateMeshes(); std::shared_ptr<GeoData> geoData; - Collection<MeshT<Vertex>, false> meshes; + UniqueCollection<MeshT<Vertex>> meshes; Texture::Ptr water; }; diff --git a/gfx/gl/sceneProvider.cpp b/gfx/gl/sceneProvider.cpp index e01532e..3681c60 100644 --- a/gfx/gl/sceneProvider.cpp +++ b/gfx/gl/sceneProvider.cpp @@ -5,7 +5,7 @@ void SceneProvider::environment(const SceneShader &, const SceneRenderer & renderer) const { renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); - renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {{-quarter_pi, -quarter_pi}}, *this); + renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {{-quarter_pi, quarter_pi}}, *this); } void diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index 5d35d01..9a4d153 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -71,6 +71,7 @@ SceneRenderer::resize(ScreenAbsCoord newSize) configuregdata(gIllumination, GL_RGB8, GL_RGB); glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.x, size.y); + shader.setViewPort({0, 0, size.x, size.y}); } void diff --git a/gfx/gl/shaders/uiShader.fs b/gfx/gl/shaders/uiShader.fs deleted file mode 100644 index c5f4e92..0000000 --- a/gfx/gl/shaders/uiShader.fs +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 core - -in vec2 texCoord0; - -uniform sampler2D sampler; - -void -main() -{ - gl_FragColor = texture(sampler, texCoord0); -} diff --git a/gfx/gl/shaders/uiShader.vs b/gfx/gl/shaders/uiShader.vs deleted file mode 100644 index e9e4373..0000000 --- a/gfx/gl/shaders/uiShader.vs +++ /dev/null @@ -1,13 +0,0 @@ -#version 330 core - -in vec4 position; - -out vec2 texCoord0; -uniform mat4 uiProjection; - -void -main() -{ - gl_Position = uiProjection * vec4(position.xy, 0.0, 1.0); - texCoord0 = position.zw; -} diff --git a/gfx/gl/shaders/uiShaderFont.fs b/gfx/gl/shaders/uiShaderFont.fs deleted file mode 100644 index a1ef6ef..0000000 --- a/gfx/gl/shaders/uiShaderFont.fs +++ /dev/null @@ -1,12 +0,0 @@ -#version 330 core - -in vec2 texCoord0; - -uniform sampler2D sampler; -uniform vec3 colour; - -void -main() -{ - gl_FragColor = vec4(colour, texture(sampler, texCoord0).r); -} diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 6525f76..3cb73f7 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -88,7 +88,7 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co shadowStenciller.setLightDirection(dir); for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { + if (const auto r = asset.getAs<const Renderable>()) { r->updateStencil(shadowStenciller); } } diff --git a/gfx/gl/uiShader.cpp b/gfx/gl/uiShader.cpp deleted file mode 100644 index 23da9dc..0000000 --- a/gfx/gl/uiShader.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "uiShader.h" -#include "gl_traits.h" -#include <gfx/gl/program.h> -#include <gfx/gl/shader.h> -#include <gfx/gl/shaders/fs-uiShader.h> -#include <gfx/gl/shaders/fs-uiShaderFont.h> -#include <gfx/gl/shaders/vs-uiShader.h> -#include <glm/glm.hpp> -#include <glm/gtc/type_ptr.hpp> - -UIShader::IconProgram::IconProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShader_fs} { } - -UIShader::TextProgram::TextProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShaderFont_fs} { } - -UIShader::UIShader(size_t width, size_t height) : - UIShader {glm::ortho<float>(0, static_cast<float>(width), 0, static_cast<float>(height))} -{ -} - -UIShader::UIShader(const glm::mat4 & viewProjection) : icon {viewProjection}, text {viewProjection} { } - -void -UIShader::TextProgram::use(const RGB & colour) const -{ - Program::use(); - glUniform(colorLoc, colour); -} diff --git a/gfx/gl/uiShader.h b/gfx/gl/uiShader.h deleted file mode 100644 index 6d00166..0000000 --- a/gfx/gl/uiShader.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "config/types.h" -#include "gl_traits.h" -#include "program.h" -#include <cstddef> -#include <glad/gl.h> -#include <glm/glm.hpp> -#include <glm/gtc/type_ptr.hpp> - -class UIShader { -public: - UIShader(std::size_t width, std::size_t height); - -private: - explicit UIShader(const glm::mat4 & viewProjection); - - class UIProgram : public Program { - public: - template<typename... S> - explicit UIProgram(const glm::mat4 & vp, S &&... srcs) : Program {std::forward<S>(srcs)...} - { - const RequiredUniformLocation uiProjectionLoc {*this, "uiProjection"}; - glUseProgram(*this); - glUniform(uiProjectionLoc, vp); - } - }; - - class IconProgram : public UIProgram { - public: - explicit IconProgram(const glm::mat4 & vp); - using Program::use; - }; - - class TextProgram : public UIProgram { - public: - explicit TextProgram(const glm::mat4 & vp); - void use(const RGB & colour) const; - - private: - RequiredUniformLocation colorLoc {*this, "colour"}; - }; - -public: - IconProgram icon; - TextProgram text; -}; diff --git a/lib/collection.h b/lib/collection.h index 6802bcb..1c77e1c 100644 --- a/lib/collection.h +++ b/lib/collection.h @@ -3,29 +3,62 @@ #include <algorithm> #include <functional> #include <memory> +#include <special_members.h> #include <type_traits> #include <vector> -template<typename Object, bool shared = true> class Collection { +template<typename Ptr, typename... Others> class Collection { public: + Collection() = default; virtual ~Collection() = default; - using Ptr = std::conditional_t<shared, std::shared_ptr<Object>, std::unique_ptr<Object>>; + DEFAULT_MOVE_NO_COPY(Collection); + + using Object = Ptr::element_type; using Objects = std::vector<Ptr>; - Objects objects; + template<typename T> using OtherObjects = std::vector<T *>; + + Collection & + operator=(Objects && other) + { + objects = std::move(other); + ((std::get<OtherObjects<Others>>(otherObjects).clear()), ...); + for (const auto & other : objects) { + addOthersPtr(other.get()); + } + return *this; + } + + const Ptr & + operator[](size_t idx) const + { + return objects[idx]; + } + + template<typename T = Object> + requires(std::is_same_v<T, Object> || (std::is_base_of_v<Others, T> || ...)) + [[nodiscard]] auto + size() const noexcept + { + return containerFor<T>().size(); + } template<typename T = Object, typename... Params> auto create(Params &&... params) requires std::is_base_of_v<Object, T> { - if constexpr (shared) { + if constexpr (requires(Ptr ptr) { ptr = std::make_shared<T>(std::forward<Params>(params)...); }) { auto obj = std::make_shared<T>(std::forward<Params>(params)...); objects.emplace_back(obj); + addOthersType<T>(obj.get()); return obj; } else { - return static_cast<T *>(objects.emplace_back(std::make_unique<T>(std::forward<Params>(params)...)).get()); + auto obj = static_cast<T *>( + objects.emplace_back(std::make_unique<T>(std::forward<Params>(params)...)).get()); + addOthersType<T>(obj); + return obj; } } @@ -33,12 +66,19 @@ public: T * find() { - if (auto i = std::find_if(objects.begin(), objects.end(), - [](auto && o) { - return (dynamic_cast<T *>(o.get())); - }); - i != objects.end()) { - return static_cast<T *>(i->get()); + const auto & srcObjects = containerFor<T>(); + if constexpr (std::is_convertible_v<typename std::remove_reference_t<decltype(srcObjects)>::value_type, T *>) { + if (srcObjects.empty()) { + return nullptr; + } + return srcObjects.front(); + } + else if (auto i = std::find_if(srcObjects.begin(), srcObjects.end(), + [](auto && o) { + return dynamic_cast<T *>(std::to_address(o)) != nullptr; + }); + i != srcObjects.end()) { + return static_cast<T *>(std::to_address(*i)); } return nullptr; } @@ -58,74 +98,229 @@ public: auto apply(const auto & m, Params &&... params) const { - return apply_internal<T>(objects.begin(), objects.end(), m, std::forward<Params>(params)...); + const auto & srcObjects = containerFor<T>(); + return apply_internal<T>(srcObjects.begin(), srcObjects.end(), m, std::forward<Params>(params)...); } template<typename T = Object, typename... Params> auto rapply(const auto & m, Params &&... params) const { - return apply_internal<T>(objects.rbegin(), objects.rend(), m, std::forward<Params>(params)...); + const auto & srcObjects = containerFor<T>(); + return apply_internal<T>(srcObjects.rbegin(), srcObjects.rend(), m, std::forward<Params>(params)...); } template<typename T = Object, typename... Params> auto applyOne(const auto & m, Params &&... params) const { - return applyOne_internal<T>(objects.begin(), objects.end(), m, std::forward<Params>(params)...); + const auto & srcObjects = containerFor<T>(); + return applyOne_internal<T>(srcObjects.begin(), srcObjects.end(), m, std::forward<Params>(params)...); } template<typename T = Object, typename... Params> auto rapplyOne(const auto & m, Params &&... params) const { - return applyOne_internal<T>(objects.rbegin(), objects.rend(), m, std::forward<Params>(params)...); + const auto & srcObjects = containerFor<T>(); + return applyOne_internal<T>(srcObjects.rbegin(), srcObjects.rend(), m, std::forward<Params>(params)...); } - template<typename T = Object> + template<typename T> + requires std::is_base_of_v<Object, T> auto removeAll() { - return std::erase_if(objects, [](auto && op) { - return dynamic_cast<T *>(op.get()); - }); + auto removeAllFrom = [](auto & container) { + if constexpr (std::is_base_of_v<T, std::decay_t<decltype(*container.front())>>) { + const auto size = container.size(); + container.clear(); + return size; + } + else { + return std::erase_if(container, [](auto && objPtr) -> bool { + return dynamic_cast<const T *>(std::to_address(objPtr)); + }); + } + }; + (removeAllFrom(std::get<OtherObjects<Others>>(otherObjects)), ...); + return removeAllFrom(objects); + } + + void + clear() + { + ((std::get<OtherObjects<Others>>(otherObjects).clear()), ...); + objects.clear(); } - auto + [[nodiscard]] auto + begin() const + { + return objects.begin(); + } + + [[nodiscard]] auto end() const { return objects.end(); } - auto + [[nodiscard]] auto + rbegin() const + { + return objects.rbegin(); + } + + [[nodiscard]] auto rend() const { return objects.rend(); } + [[nodiscard]] bool + empty() const + { + return objects.empty(); + } + + decltype(auto) + emplace(Ptr && ptr) + { + const auto & object = objects.emplace_back(std::move(ptr)); + addOthersPtr(object.get()); + return object; + } + protected: + Objects objects; + std::tuple<OtherObjects<Others>...> otherObjects; + + template<typename T> + void + addOthersType(T * obj) + { + applyToOthersType<T>( + [](auto & others, auto ptr) { + others.emplace_back(ptr); + }, + obj); + } + + void + addOthersPtr(Object * obj) + { + applyToOthersPtr( + [](auto & others, auto ptr) { + others.emplace_back(ptr); + }, + obj); + } + + template<typename T, typename... Params> + requires(sizeof...(Others) == 0) + void + applyToOthersType(const auto &, Params...) + { + } + + void + applyToOthersPtr(const auto &, Object *) + requires(sizeof...(Others) == 0) + { + } + + template<typename T, typename... Params> + requires(sizeof...(Others) > 0) + void + applyToOthersType(const auto & func, Params &&... params) + { + ( + [&]() { + if constexpr (std::is_convertible_v<T *, Others *>) { + std::invoke( + func, std::get<OtherObjects<Others>>(otherObjects), std::forward<Params>(params)...); + } + }(), + ...); + } + + void + applyToOthersPtr(const auto & func, Object * obj) + requires(sizeof...(Others) > 0) + { + ( + [&]() { + if (auto ptr = dynamic_cast<Others *>(obj)) { + std::invoke(func, std::get<OtherObjects<Others>>(otherObjects), ptr); + } + }(), + ...); + } + + template<typename T> + requires((std::is_base_of_v<Others, T> || ...)) + [[nodiscard]] consteval static size_t + idx() + { + size_t typeIdx = 0; + auto found = ((++typeIdx && std::is_base_of_v<Others, T>) || ...); + return typeIdx - found; + } + + template<typename T> + [[nodiscard]] + constexpr const auto & + containerFor() const + { + if constexpr ((std::is_base_of_v<Others, T> || ...)) { + return std::get<idx<T>()>(otherObjects); + } + else { + return objects; + } + } + template<typename T = Object, typename... Params> auto apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { - return std::count_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast<T *>(op.get())) { - std::invoke(m, o, std::forward<Params>(params)...); - return true; - } - return false; - }); + if constexpr (std::is_convertible_v<decltype(std::to_address(*begin)), T *>) { + std::for_each(begin, end, [&m, ¶ms...](auto && op) { + std::invoke(m, op, std::forward<Params>(params)...); + }); + return std::distance(begin, end); + } + else { + return std::count_if(begin, end, [&m, ¶ms...](auto && op) { + if (auto o = dynamic_cast<T *>(std::to_address(op))) { + std::invoke(m, o, std::forward<Params>(params)...); + return true; + } + return false; + }); + } } template<typename T = Object, typename... Params> auto applyOne_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { - return std::find_if(begin, end, [&m, ¶ms...](auto && op) { - if (auto o = dynamic_cast<T *>(op.get())) { - return std::invoke(m, o, std::forward<Params>(params)...); - } - return false; - }); + if constexpr (std::is_convertible_v<decltype(std::to_address(*begin)), T *>) { + return std::find_if(begin, end, [&m, ¶ms...](auto && op) { + return std::invoke(m, op, std::forward<Params>(params)...); + }); + } + else { + return std::find_if(begin, end, [&m, ¶ms...](auto && op) { + if (auto o = dynamic_cast<T *>(std::to_address(op))) { + return std::invoke(m, o, std::forward<Params>(params)...); + } + return false; + }); + } } }; + +template<typename T, typename... Others> using SharedCollection = Collection<std::shared_ptr<T>, Others...>; +template<typename T, typename... Others> using UniqueCollection = Collection<std::unique_ptr<T>, Others...>; diff --git a/lib/enumDetails.h b/lib/enumDetails.h index dfae082..49906d4 100644 --- a/lib/enumDetails.h +++ b/lib/enumDetails.h @@ -9,11 +9,11 @@ /// EnumDetailsBase // Shared helpers struct EnumDetailsBase { - template<size_t len> + template<size_t Len> constexpr static auto strArr(auto input, auto start, auto end) { - std::array<char, len> out; + std::array<char, Len> out; input.copy(out.begin(), end - start, start); return out; } @@ -33,22 +33,22 @@ protected: return std::string_view {__PRETTY_FUNCTION__}; }; - constexpr static auto typeNameStart {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()}; - constexpr static auto typeNameEnd {typeraw().find_first_of("];", typeNameStart)}; - constexpr static auto typeNameLen {typeNameEnd - typeNameStart}; - constexpr static auto typeNameArr {strArr<typeNameLen>(typeraw(), typeNameStart, typeNameEnd)}; + constexpr static auto TYPE_NAME_START {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()}; + constexpr static auto TYPE_NAME_END {typeraw().find_first_of("];", TYPE_NAME_START)}; + constexpr static auto TYPE_NAME_LEN {TYPE_NAME_END - TYPE_NAME_START}; + constexpr static auto TYPE_NAME_ARR {strArr<TYPE_NAME_LEN>(typeraw(), TYPE_NAME_START, TYPE_NAME_END)}; public: - constexpr static std::string_view typeName {typeNameArr.data(), typeNameArr.size()}; + constexpr static std::string_view TYPE_NAME {TYPE_NAME_ARR.data(), TYPE_NAME_ARR.size()}; }; /// EnumValueDetails // Extracts the value name and constructs string_views of the parts -template<auto value> struct EnumValueDetails : public EnumTypeDetails<decltype(value)> { +template<auto Value> struct EnumValueDetails : public EnumTypeDetails<decltype(Value)> { #ifndef ENUM_PROBE private: #endif - using T = EnumTypeDetails<decltype(value)>; + using T = EnumTypeDetails<decltype(Value)>; constexpr static auto raw() @@ -56,26 +56,23 @@ private: return std::string_view {__PRETTY_FUNCTION__}; }; - constexpr static auto nameStart {raw().find_last_of(": ") + 1}; - constexpr static auto nameEnd {raw().find_first_of("];", nameStart)}; - constexpr static auto nameLen {nameEnd - nameStart}; - constexpr static auto nameArr {EnumValueDetails::template strArr<nameLen>(raw(), nameStart, nameEnd)}; + constexpr static auto NAME_START {raw().find_last_of(": ") + 1}; + constexpr static auto NAME_END {raw().find_first_of("];", NAME_START)}; + constexpr static auto NAME_LEN {NAME_END - NAME_START}; + constexpr static auto NAME_ARR {EnumValueDetails::template strArr<NAME_LEN>(raw(), NAME_START, NAME_END)}; public: - constexpr static std::string_view valueName {nameArr.data(), nameArr.size()}; - constexpr static auto valid {valueName.back() < '0' || valueName.back() > '9'}; -#pragma GCC diagnostic push -#ifdef __clang__ -# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" -#endif - constexpr static auto raw_value {value}; -#pragma GCC diagnostic pop + constexpr static std::string_view VALUE_NAME {NAME_ARR.data(), NAME_ARR.size()}; + constexpr static auto VALID {VALUE_NAME.back() < '0' || VALUE_NAME.back() > '9'}; + constexpr static auto RAW_VALUE {Value}; }; /// EnumValueCollection // Customisation point for specifying the range of underlying values your enum can have template<typename E> struct EnumValueCollection { - using Vs = std::make_integer_sequence<int, 256>; + using Underlying = std::underlying_type_t<E>; + static constexpr Underlying UPPER = std::numeric_limits<Underlying>::max(); + using Vs = std::make_integer_sequence<Underlying, UPPER>; }; /// EnumDetails @@ -84,45 +81,36 @@ template<typename E> struct EnumDetails { #ifndef ENUM_PROBE private: #endif - template<auto... n> + using Underlying = std::underlying_type_t<E>; + + template<auto... N> constexpr static auto - get_valids(std::integer_sequence<int, n...>) + getValids(std::integer_sequence<Underlying, N...>) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#ifdef __clang__ -# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" -#endif - return std::array {EnumValueDetails<static_cast<E>(n)>::valid...}; -#pragma GCC diagnostic pop + return std::array {EnumValueDetails<static_cast<E>(N)>::VALID...}; } - template<auto... n> + template<auto... N> constexpr static auto - get_values(std::integer_sequence<int, n...>) + getValues(std::integer_sequence<Underlying, N...>) { -#pragma GCC diagnostic push -#ifdef __clang__ -# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" -#endif - return std::array {EnumValueDetails<static_cast<E>(n)>::raw_value...}; -#pragma GCC diagnostic pop + return std::array {EnumValueDetails<static_cast<E>(N)>::RAW_VALUE...}; } - template<auto... n> + template<auto... N> constexpr static auto - get_valueNames(std::integer_sequence<int, n...>) + getValueNames(std::integer_sequence<int, N...>) { - return std::array {EnumValueDetails<values[n]>::valueName...}; + return std::array {EnumValueDetails<VALUES[N]>::VALUE_NAME...}; } using EVC = EnumValueCollection<E>; - constexpr static auto valid_flags {get_valids(typename EVC::Vs {})}; - constexpr static auto valid_count {std::count_if(valid_flags.begin(), valid_flags.end(), std::identity {})}; + constexpr static auto VALID_FLAGS {getValids(typename EVC::Vs {})}; + constexpr static auto VALID_COUNT {std::count_if(VALID_FLAGS.begin(), VALID_FLAGS.end(), std::identity {})}; constexpr static auto - lookup(const auto key, const auto & search, - const auto & out) -> std::optional<typename std::decay_t<decltype(out)>::value_type> + lookup(const auto key, const auto & search, const auto & out) + -> std::optional<typename std::decay_t<decltype(out)>::value_type> { if (const auto itr = std::find(search.begin(), search.end(), key); itr != search.end()) { return out[static_cast<std::size_t>(std::distance(search.begin(), itr))]; @@ -131,32 +119,32 @@ private: } public: - constexpr static auto values {[]() { - constexpr auto values {get_values(typename EVC::Vs {})}; - static_assert(std::is_sorted(values.begin(), values.end()), "Candidate values must be sorted"); - std::array<E, valid_count> out; - std::copy_if(values.begin(), values.end(), out.begin(), [valid = valid_flags.begin()](auto) mutable { + constexpr static auto VALUES {[]() { + constexpr auto VALUES {getValues(typename EVC::Vs {})}; + static_assert(std::ranges::is_sorted(VALUES), "Candidate values must be sorted"); + std::array<E, VALID_COUNT> out; + std::copy_if(VALUES.begin(), VALUES.end(), out.begin(), [valid = VALID_FLAGS.begin()](auto) mutable { return *valid++; }); return out; }()}; - constexpr static auto names {get_valueNames(std::make_integer_sequence<int, valid_count> {})}; + constexpr static auto NAMES {getValueNames(std::make_integer_sequence<int, VALID_COUNT> {})}; constexpr static bool - is_valid(E value) noexcept + isValid(E value) noexcept { - return std::binary_search(values.begin(), values.end(), value); + return std::binary_search(VALUES.begin(), VALUES.end(), value); } constexpr static std::optional<E> parse(std::string_view name) noexcept { - return lookup(name, names, values); + return lookup(name, NAMES, VALUES); } constexpr static std::optional<std::string_view> - to_string(E value) noexcept + toString(E value) noexcept { - return lookup(value, values, names); + return lookup(value, VALUES, NAMES); } }; diff --git a/lib/glArrays.h b/lib/glArrays.h index 787ea17..842a593 100644 --- a/lib/glArrays.h +++ b/lib/glArrays.h @@ -6,6 +6,7 @@ #include <glad/gl.h> #include <special_members.h> +// NOLINTNEXTLINE(readability-identifier-naming) template<size_t N> class glArraysBase { static_assert(N > 0); @@ -15,20 +16,30 @@ public: CUSTOM_MOVE(glArraysBase); // NOLINTNEXTLINE(hicpp-explicit-conversions) - inline operator GLuint() const + requires(N == 1) { - static_assert(N == 1, "Implicit cast only if N == 1"); return ids.front(); } - inline auto + GLuint + operator*() const + requires(N == 1) + { + return ids.front(); + } + + const auto & operator[](size_t n) const { return ids[n]; } - constexpr static auto size {N}; + constexpr static auto + size() + { + return N; + } protected: glArraysBase() noexcept = default; @@ -49,6 +60,7 @@ glArraysBase<N>::operator=(glArraysBase<N> && src) noexcept return *this; } +// NOLINTNEXTLINE(readability-identifier-naming) template<size_t N, auto Gen, auto Del> class glArrays : public glArraysBase<N> { public: using glArraysBase<N>::glArraysBase; @@ -56,12 +68,12 @@ public: DEFAULT_MOVE_COPY(glArrays); - inline glArrays() noexcept + glArrays() noexcept { (*Gen)(N, this->ids.data()); } - inline ~glArrays() noexcept + ~glArrays() noexcept { if (this->ids.front()) { (*Del)(N, this->ids.data()); @@ -69,6 +81,7 @@ public: } }; +// NOLINTBEGIN(readability-identifier-naming) template<size_t N> using glVertexArrays = glArrays<N, &glGenVertexArrays, &glDeleteVertexArrays>; using glVertexArray = glVertexArrays<1>; template<size_t N> using glBuffers = glArrays<N, &glGenBuffers, &glDeleteBuffers>; @@ -79,3 +92,4 @@ template<size_t N> using glFrameBuffers = glArrays<N, &glGenFramebuffers, &glDel using glFrameBuffer = glFrameBuffers<1>; template<size_t N> using glRenderBuffers = glArrays<N, &glGenRenderbuffers, &glDeleteRenderbuffers>; using glRenderBuffer = glRenderBuffers<1>; +// NOLINTEND(readability-identifier-naming) diff --git a/lib/manyPtr.h b/lib/manyPtr.h new file mode 100644 index 0000000..9e08452 --- /dev/null +++ b/lib/manyPtr.h @@ -0,0 +1,86 @@ +#pragma once + +#include <memory> +#include <tuple> + +template<typename Primary, typename... Others> class ManyPtr : Primary { +public: + using element_type = typename Primary::element_type; + + template<typename... Params> ManyPtr(Params &&... params) : Primary {std::forward<Params>(params)...} + { + updatePtrs(); + } + + using Primary::operator->; + using Primary::operator*; + using Primary::operator bool; + using Primary::get; + + template<typename... Params> + void + reset(Params &&... params) + { + Primary::reset(std::forward<Params>(params)...); + updatePtrs(); + } + + template<typename Other> + [[nodiscard]] consteval static bool + couldBe() + { + return (std::is_convertible_v<Others *, Other *> || ...); + } + + template<typename Other> + requires(couldBe<Other>()) + [[nodiscard]] auto + getAs() const + { + return std::get<idx<Other>()>(others); + } + + template<typename Other> + requires(!couldBe<Other>() && requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); }) + [[nodiscard]] auto + dynamicCast() const + { + return std::dynamic_pointer_cast<Other>(*this); + } + + template<typename Other> + requires(!couldBe<Other>() && !requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); }) + [[nodiscard]] auto + dynamicCast() const + { + return dynamic_cast<Other *>(get()); + } + +private: + using OtherPtrs = std::tuple<Others *...>; + + template<typename Other> + requires(couldBe<Other>()) + [[nodiscard]] consteval static bool + idx() + { + size_t typeIdx = 0; + return ((typeIdx++ && std::is_convertible_v<Others *, Other *>) || ...); + } + + void + updatePtrs() + { + if (*this) { + others = {dynamic_cast<Others *>(get())...}; + } + else { + others = {}; + } + } + + OtherPtrs others; +}; + +template<typename Primary, typename... Others> using ManySharedPtr = ManyPtr<std::shared_ptr<Primary>, Others...>; +template<typename Primary, typename... Others> using ManyUniquePtr = ManyPtr<std::unique_ptr<Primary>, Others...>; diff --git a/lib/maths.cpp b/lib/maths.cpp index 3a9bf9b..12e0681 100644 --- a/lib/maths.cpp +++ b/lib/maths.cpp @@ -32,13 +32,13 @@ static_assert(pow(3, 2) == 9); static_assert(pow(pi, 3) == 31.006278991699219F); float -operator"" _mph(const long double v) +operator""_mph(const long double v) { return static_cast<float>(mph_to_ms(v)); } float -operator"" _kph(const long double v) +operator""_kph(const long double v) { return static_cast<float>(kph_to_ms(v)); } diff --git a/lib/maths.h b/lib/maths.h index 0afce98..43d6dcd 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -504,11 +504,11 @@ kph_to_ms(T v) } // ... literals are handy for now, probably go away when we load stuff externally -float operator"" _mph(const long double v); -float operator"" _kph(const long double v); +float operator""_mph(const long double v); +float operator""_kph(const long double v); constexpr float -operator"" _degrees(long double degrees) +operator""_degrees(long double degrees) { return static_cast<float>(degrees) * degreesToRads; } diff --git a/lib/persistence.h b/lib/persistence.h index e385b20..75bcea6 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -1,5 +1,6 @@ #pragma once +#include "manyPtr.h" #include <charconv> #include <format> #include <functional> @@ -200,7 +201,7 @@ namespace Persistence { } [[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0; - virtual void selHandler() {}; + virtual void selHandler() { }; virtual void setType(const std::string_view, const Persistable *) = 0; SelectionPtr sel {}; @@ -53,34 +53,55 @@ public: } } - bool - intersectTriangle(const PositionType t0, const PositionType t1, const PositionType t2, BaryPosition & bary, - RelativeDistance & distance) const + struct IntersectTriangleResult { + BaryPosition bary; + RelativeDistance distance; + }; + + std::optional<IntersectTriangleResult> + intersectTriangle(const PositionType t0, const PositionType t1, const PositionType t2) const { + IntersectTriangleResult out; if constexpr (std::is_floating_point_v<typename PositionType::value_type>) { - return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance) && distance >= 0.F; + if (glm::intersectRayTriangle(start, direction, t0, t1, t2, out.bary, out.distance) + && out.distance >= 0.F) { + return out; + } } else { const RelativePosition3D t0r = t0 - start, t1r = t1 - start, t2r = t2 - start; - return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance) && distance >= 0.F; + if (glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, out.bary, out.distance) + && out.distance >= 0.F) { + return out; + } } + return std::nullopt; } - bool - intersectSphere(const PositionType centre, const PositionType::value_type size, PositionType & position, - Normal3D & normal) const + struct IntersectSphereResult { + PositionType position; + Normal3D normal; + }; + + std::optional<IntersectSphereResult> + intersectSphere(const PositionType centre, const PositionType::value_type size) const { + IntersectSphereResult out; if constexpr (std::is_floating_point_v<typename PositionType::value_type>) { - return glm::intersectRaySphere(start, direction, centre, size, position, normal); + if (glm::intersectRaySphere(start, direction, centre, size, out.position, out.normal)) { + return out; + } } else { const RelativePosition3D cr = centre - start; RelativePosition3D positionF {}; - const auto r = glm::intersectRaySphere( - {}, direction, cr, static_cast<RelativeDistance>(size), positionF, normal); - position = GlobalPosition3D(positionF) + start; - return r; + if (glm::intersectRaySphere( + {}, direction, cr, static_cast<RelativeDistance>(size), positionF, out.normal)) { + out.position = GlobalPosition3D(positionF) + start; + return out; + } } + return std::nullopt; } }; diff --git a/lib/stream_support.h b/lib/stream_support.h index 5f276fd..7f1df96 100644 --- a/lib/stream_support.h +++ b/lib/stream_support.h @@ -81,7 +81,7 @@ namespace std { inline std::ostream & operator<<(std::ostream & s, const E & e) { - return s << EnumTypeDetails<E>::typeName << "::" << EnumDetails<E>::to_string(e).value(); + return s << EnumTypeDetails<E>::TYPE_NAME << "::" << EnumDetails<E>::toString(e).value(); } template<typename T> diff --git a/res/ui/icon/magnifier.svg b/res/ui/icon/magnifier.svg new file mode 100644 index 0000000..97abe9f --- /dev/null +++ b/res/ui/icon/magnifier.svg @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Creator: CorelDRAW -->
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1.70666in" height="1.70666in" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 1707 1707"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <style type="text/css">
+ <![CDATA[
+ .fil3 {fill:url(#id0)}
+ .fil0 {fill:url(#id1)}
+ .fil2 {fill:url(#id2)}
+ .fil4 {fill:url(#id3)}
+ .fil1 {fill:url(#id4)}
+ .fil5 {fill:url(#id5)}
+ ]]>
+ </style>
+ <linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="647.772" y1="1185.78" x2="647.772" y2="109.768">
+ <stop offset="0" style="stop-opacity:1; stop-color:#AFAFAF"/>
+ <stop offset="1" style="stop-opacity:1; stop-color:#9C9C9C"/>
+ </linearGradient>
+ <linearGradient id="id1" gradientUnits="userSpaceOnUse" x1="1138.83" y1="1294.33" x2="1138.83" y2="983.327">
+ <stop offset="0" style="stop-opacity:1; stop-color:#949494"/>
+ <stop offset="1" style="stop-opacity:1; stop-color:#858585"/>
+ </linearGradient>
+ <linearGradient id="id2" gradientUnits="userSpaceOnUse" xlink:href="#id1" x1="647.772" y1="1295.55" x2="647.772" y2="0.00393701">
+ </linearGradient>
+ <linearGradient id="id3" gradientUnits="userSpaceOnUse" x1="647.772" y1="1143.81" x2="647.772" y2="151.736">
+ <stop offset="0" style="stop-opacity:1; stop-color:#D2D2D2"/>
+ <stop offset="1" style="stop-opacity:1; stop-color:#BBBBBB"/>
+ </linearGradient>
+ <linearGradient id="id4" gradientUnits="userSpaceOnUse" x1="1411.49" y1="1116.31" x2="1411.49" y2="1706.66">
+ <stop offset="0" style="stop-opacity:1; stop-color:#FF9911"/>
+ <stop offset="1" style="stop-opacity:1; stop-color:#FF6600"/>
+ </linearGradient>
+ <linearGradient id="id5" gradientUnits="userSpaceOnUse" x1="657.22" y1="496.535" x2="657.22" y2="322.63">
+ <stop offset="0" style="stop-opacity:1; stop-color:#E6E6E6"/>
+ <stop offset="1" style="stop-opacity:1; stop-color:#CCCCCC"/>
+ </linearGradient>
+ </defs>
+ <g id="Layer_x0020_1">
+ <metadata id="CorelCorpID_0Corel-Layer"/>
+ <path class="fil0" d="M1161 983l133 133 -178 178 -133 -133c36,-21 69,-47 100,-78 31,-31 57,-64 78,-100z"/>
+ <path class="fil1" d="M1116 1294l178 -178 376 376c49,49 49,129 0,178l0 0c-49,49 -129,49 -178,0l-376 -376z"/>
+ <path class="fil2" d="M190 190c253,-253 663,-253 916,0 253,253 253,663 0,916 -253,253 -663,253 -916,0 -253,-253 -253,-663 0,-916z"/>
+ <path class="fil3" d="M267 267c210,-210 551,-210 761,0 210,210 210,551 0,761 -210,210 -551,210 -761,0 -210,-210 -210,-551 0,-761z"/>
+ <path class="fil4" d="M297 297c194,-194 508,-194 702,0 193,194 193,508 0,702 -194,193 -508,193 -702,0 -194,-194 -194,-508 0,-702z"/>
+ <path class="fil5" d="M440 486c-14,14 -37,14 -52,0 -14,-15 -14,-38 0,-52 75,-74 172,-111 269,-111 97,0 195,37 269,111 14,14 14,38 0,52 -14,14 -37,14 -52,0 -60,-60 -138,-90 -217,-90 -79,0 -157,30 -217,90z"/>
+ </g>
+</svg>
diff --git a/res/ui/icon/network.png b/res/ui/icon/network.png Binary files differdeleted file mode 100644 index 7a091f3..0000000 --- a/res/ui/icon/network.png +++ /dev/null diff --git a/res/ui/icon/rails.svg b/res/ui/icon/rails.svg new file mode 100644 index 0000000..81e9b94 --- /dev/null +++ b/res/ui/icon/rails.svg @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Creator: CorelDRAW -->
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1.99999in" height="1.99999in" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 2000 2000"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <style type="text/css">
+ <![CDATA[
+ .fil0 {fill:none}
+ .fil1 {fill:#333333}
+ .fil2 {fill:#C06000}
+ .fil3 {fill:#CCCCCC}
+ ]]>
+ </style>
+ </defs>
+ <rect class="fil0" width="2000" height="2000"/>
+ <g id="Layer_x0020_1">
+ <metadata id="CorelCorpID_0Corel-Layer"/>
+ <path class="fil1" d="M1448 515l-61 0 29 126 108 0c30,0 55,23 63,59l38 168c8,37 -28,69 -63,69l-80 0 28 126 127 0c31,0 55,23 63,59l38 168c9,37 -27,69 -63,69l-98 0 28 126 146 0c31,0 55,22 63,58l38 169c8,37 -27,68 -63,68l-126 0c-8,20 -30,34 -51,34l-81 0c-21,0 -38,-13 -48,-34l-966 0c-10,21 -27,34 -48,34l-81 0c-21,0 -43,-14 -51,-34l-126 0c-36,0 -71,-31 -63,-68l38 -169c8,-36 32,-58 63,-58l146 0 29 -126 -99 0c-36,0 -72,-32 -63,-69l38 -168c8,-36 32,-59 63,-59l127 0 28 -126 -80 0c-35,0 -71,-32 -63,-69l38 -168c8,-36 32,-59 63,-59l109 0 28 -126c-40,0 -87,8 -113,-25 -10,-13 -14,-28 -11,-44 56,-248 29,-226 192,-226 10,-21 27,-34 49,-34l80 0c21,0 43,14 51,34l278 0c8,-20 30,-34 51,-34l81 0c21,0 39,13 48,34 163,0 136,-21 192,226 8,37 -28,69 -63,69zm-971 1239l341 -1516 0 -1c-1,-2 -5,-4 -8,-4l-80 0c-5,0 -8,10 -9,15l-338 1506c-2,7 -5,13 5,13l81 0c4,0 7,-8 8,-13zm108 -269l830 0 -28 -126 -773 0 -29 126zm95 -422l640 0 -28 -126 -584 0 -28 126zm95 -422l450 0 -28 -126 -394 0 -28 126zm-436 1093l46 -202 -136 0 0 0c-12,0 -16,17 -18,22l-37 168c-2,6 12,12 17,12l128 0zm95 -422l45 -202 -116 0 0 0c-12,0 -17,17 -18,22l-38 168c-1,7 13,12 18,12l109 0zm425 -1046l-45 202 373 0 -46 -202 -282 0zm-95 422l-45 202 562 0 -45 -202 -472 0zm-94 422l-46 202 752 0 -45 -202 -661 0zm-95 422l-46 202 942 0 -46 -202 -850 0zm760 -1035l-56 -251c-1,-5 -4,-13 -9,-13l-80 0c-3,0 -6,2 -8,4l341 1517c1,5 5,13 8,13l81 0c3,0 7,-2 8,-4l-285 -1266zm-4 -231l46 202 71 0c5,0 19,-5 17,-11l-37 -169c-2,-5 -6,-22 -18,-22l-79 0zm95 422l45 202 91 0c4,0 19,-5 17,-12l-38 -168c-1,-5 -6,-22 -17,-22l-98 0zm95 422l45 202 109 0c6,0 19,-5 18,-12l-38 -168c-1,-5 -6,-22 -17,-22l-117 0zm95 422l45 202 128 0c6,0 18,-6 18,-12l-38 -168c-1,-5 -6,-22 -18,-22l-135 0zm-1140 -844c-11,0 -16,17 -17,22l-38 168c-2,7 13,12 17,12l91 0 45 -202 -98 0zm114 -422c-12,0 -16,17 -17,22l-38 169c-2,6 12,11 17,11l72 0 45 -202 -79 0z"/>
+ <path class="fil2" d="M339 1734l46 -202 -136 0 0 0c-12,0 -16,17 -18,22l-37 168c-2,6 12,12 17,12l128 0z"/>
+ <path class="fil3" d="M721 248l-338 1506c-2,7 -5,13 5,13l81 0c4,0 7,-8 8,-13l341 -1516 0 -1c-1,-2 -5,-4 -8,-4l-80 0c-5,0 -8,10 -9,15z"/>
+ <polygon class="fil2" points="1425,1532 575,1532 529,1734 1471,1734 "/>
+ <path class="fil3" d="M1182 237l341 1517c1,5 5,13 8,13l81 0c3,0 7,-2 8,-4l-285 -1266 -56 -251c-1,-5 -4,-13 -9,-13l-80 0c-3,0 -6,2 -8,4z"/>
+ <path class="fil2" d="M1751 1532l-135 0 45 202 128 0c6,0 18,-6 18,-12l-38 -168c-1,-5 -6,-22 -18,-22z"/>
+ <path class="fil2" d="M1566 1312l109 0c6,0 19,-5 18,-12l-38 -168c-1,-5 -6,-22 -17,-22l-117 0 45 202z"/>
+ <polygon class="fil2" points="1376,1312 1331,1110 670,1110 624,1312 "/>
+ <path class="fil2" d="M434 1312l45 -202 -116 0 0 0c-12,0 -17,17 -18,22l-38 168c-1,7 13,12 18,12l109 0z"/>
+ <path class="fil2" d="M438 890l91 0 45 -202 -98 0c-11,0 -16,17 -17,22l-38 168c-2,7 13,12 17,12z"/>
+ <polygon class="fil2" points="764,688 719,890 1281,890 1236,688 "/>
+ <path class="fil2" d="M1426 688l45 202 91 0c4,0 19,-5 17,-12l-38 -168c-1,-5 -6,-22 -17,-22l-98 0z"/>
+ <path class="fil2" d="M1377 468l71 0c5,0 19,-5 17,-11l-37 -169c-2,-5 -6,-22 -18,-22l-79 0 46 202z"/>
+ <polygon class="fil2" points="1187,468 1141,266 859,266 814,468 "/>
+ <path class="fil2" d="M624 468l45 -202 -79 0c-12,0 -16,17 -17,22l-38 169c-2,6 12,11 17,11l72 0z"/>
+ </g>
+</svg>
diff --git a/res/ui/icon/road.svg b/res/ui/icon/road.svg new file mode 100644 index 0000000..3b9ae77 --- /dev/null +++ b/res/ui/icon/road.svg @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Creator: CorelDRAW -->
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="512px" height="512px" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 512 512"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <style type="text/css">
+ <![CDATA[
+ .fil0 {fill:#4D4D4D}
+ .fil1 {fill:#99A4AC}
+ .fil2 {fill:#D9F2F2}
+ ]]>
+ </style>
+ </defs>
+ <g id="Layer_x0020_1">
+ <metadata id="CorelCorpID_0Corel-Layer"/>
+ <path class="fil0" d="M491 512l-470 0c-5,0 -8,-4 -7,-8l82 -498c1,-3 4,-6 8,-6l308 0c4,0 7,2 7,6l79 498c1,3 -2,8 -7,8zm-406 -14l56 -484 -31 0 -81 484 56 0zm288 -484l54 484 56 0 -77 -484 -33 0zm40 484l-54 -484 -64 0 0 86c-1,4 -4,7 -8,7l-47 0c-4,0 -7,-3 -7,-7l0 -86 -77 0 -57 484 134 0 0 -86c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8l0 86 118 0zm-133 0l0 -79 -33 0 0 79 33 0zm7 -121l-47 0c-4,0 -7,-3 -7,-7l0 -93c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8l0 93c-1,4 -4,7 -8,7zm-40 -14l33 0 0 -79 -33 0 0 79zm40 -121l-47 0c-4,0 -7,-3 -7,-7l0 -93c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8l0 93c-1,4 -4,7 -8,7zm-40 -14l33 0 0 -79 -33 0 0 79zm0 -135l33 0 0 -79 -33 0 0 79z"/>
+ <path class="fil1" d="M295 498l118 0 -54 -484 -64 0 0 86c-1,4 -4,7 -8,7l-47 0c-4,0 -7,-3 -7,-7l0 -86 -77 0 -57 484 134 0 0 -86c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8l0 86zm0 -221l0 93c-1,4 -4,7 -8,7l-47 0c-4,0 -7,-3 -7,-7l0 -93c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8zm-8 -35l-47 0c-4,0 -7,-3 -7,-7l0 -93c0,-4 3,-8 7,-8l47 0c4,0 8,4 8,8l0 93c-1,4 -4,7 -8,7z"/>
+ <polygon class="fil2" points="247,228 280,228 280,149 247,149 "/>
+ <polygon class="fil2" points="280,284 247,284 247,363 280,363 "/>
+ <polygon class="fil2" points="280,419 247,419 247,498 280,498 "/>
+ <polygon class="fil2" points="247,93 280,93 280,14 247,14 "/>
+ <polygon class="fil1" points="373,14 427,498 483,498 406,14 "/>
+ <polygon class="fil1" points="85,498 141,14 110,14 29,498 "/>
+ </g>
+</svg>
diff --git a/test/.clang-tidy b/test/.clang-tidy new file mode 100644 index 0000000..6e9ebae --- /dev/null +++ b/test/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: '-readability-magic-numbers' + diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 5189add..bdaaa45 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -75,6 +75,7 @@ perfrun perf-instancing.cpp : : test-instancing ; run test-glContainer.cpp : : : <library>test ; run test-pack.cpp : : : <library>test ; run test-environment.cpp : : : <library>test ; +run test-ui.cpp : : : <library>test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; alias perf : perf-assetFactory perf-persistence perf-geoData perf-instancing perf-terrain ; diff --git a/test/enumDetailsData.h b/test/enumDetailsData.h index b7bd601..6383838 100644 --- a/test/enumDetailsData.h +++ b/test/enumDetailsData.h @@ -1,23 +1,24 @@ #pragma once +#include <cstdint> #include <enumDetails.h> -enum GlobalUnscoped { aa, b, c }; -enum class GlobalScoped { aa, b, c }; +enum GlobalUnscoped : uint8_t { Aa, B, C }; +enum class GlobalScoped : int8_t { Aa, B, C }; namespace ns { - enum Unscoped { aa, b, c }; - enum class Scoped { aa, b, c }; + enum Unscoped : int8_t { Aa, B, C }; + enum class Scoped : int8_t { Aa, B, C }; } namespace test1 { - enum class DefaultDense { a, bee, ci, de }; + enum class DefaultDense : int8_t { A, Bee, Ci, De }; } namespace test2 { - enum class NumberedSparse { a = 0, bee = 3, ci = -20, de = 100 }; + enum class NumberedSparse : int8_t { A = 0, Bee = 3, Ci = -20, De = 100 }; } template<> struct EnumValueCollection<test2::NumberedSparse> { // Any ordered integer_sequence which includes all enumeration values - using Vs = std::integer_sequence<int, -100, -20, 0, 3, 10, 100, 1000>; + using Vs = std::integer_sequence<int8_t, -100, -20, 0, 3, 10, 100, 110>; }; diff --git a/test/perf-assetFactory.cpp b/test/perf-assetFactory.cpp index 671713c..0f7895b 100644 --- a/test/perf-assetFactory.cpp +++ b/test/perf-assetFactory.cpp @@ -2,27 +2,29 @@ #include "testMainWindow.h" #include <benchmark/benchmark.h> -static void -brush47xml_load(benchmark::State & state) -{ - TestMainWindowAppBase window; +namespace { + void + brush47xmlLoad(benchmark::State & state) + { + TestMainWindowAppBase window; - for (auto _ : state) { - benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/brush47.xml")); + for (auto loop : state) { + benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/brush47.xml")); + } } -} -static void -foliagexml_load(benchmark::State & state) -{ - TestMainWindowAppBase window; + void + foliagexmlLoad(benchmark::State & state) + { + TestMainWindowAppBase window; - for (auto _ : state) { - benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/foliage.xml")); + for (auto loop : state) { + benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/foliage.xml")); + } } } -BENCHMARK(brush47xml_load); -BENCHMARK(foliagexml_load); +BENCHMARK(brush47xmlLoad); +BENCHMARK(foliagexmlLoad); BENCHMARK_MAIN(); diff --git a/test/perf-geoData.cpp b/test/perf-geoData.cpp index d9ea8c6..679f19f 100644 --- a/test/perf-geoData.cpp +++ b/test/perf-geoData.cpp @@ -2,56 +2,56 @@ #include <game/geoData.h> namespace { - const GeoData tm {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")}; + const GeoData GEO_DATA_FIXTURE {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")}; void - terrain_findPoint(benchmark::State & state) + terrainFindPoint(benchmark::State & state) { - for (auto _ : state) { - benchmark::DoNotOptimize(tm.findPoint({315555000, 495556000})); + for (auto loop : state) { + benchmark::DoNotOptimize(GEO_DATA_FIXTURE.findPoint({315555000, 495556000})); } } void - terrain_walk(benchmark::State & state) + terrainWalk(benchmark::State & state) { const glm::vec2 point {310001000, 490000000}; - const GeoData::PointFace start {point, tm.findPoint(point)}; - for (auto _ : state) { - tm.walk(start, {319999000, 500000000}, [](auto f) { - benchmark::DoNotOptimize(f); + const GeoData::PointFace start {point, GEO_DATA_FIXTURE.findPoint(point)}; + for (auto loop : state) { + GEO_DATA_FIXTURE.walk(start, {319999000, 500000000}, [](auto step) { + benchmark::DoNotOptimize(step); }); } } void - terrain_walkBoundary(benchmark::State & state) + terrainWalkBoundary(benchmark::State & state) { - for (auto _ : state) { - tm.boundaryWalk([](auto heh) { + for (auto loop : state) { + GEO_DATA_FIXTURE.boundaryWalk([](auto heh) { benchmark::DoNotOptimize(heh); }); } } void - terrain_deform(benchmark::State & state) + terrainDeform(benchmark::State & state) { std::array<GlobalPosition3D, 3> points {{ {315555000, 495556000, 0}, {315655000, 495556000, 0}, {315655000, 495557000, 0}, }}; - for (auto _ : state) { - auto geoData {tm}; + for (auto loop : state) { + auto geoData {GEO_DATA_FIXTURE}; benchmark::DoNotOptimize(geoData.setHeights(points, GeoData::SetHeightsOpts {.surface = nullptr})); } } } -BENCHMARK(terrain_findPoint); -BENCHMARK(terrain_walk); -BENCHMARK(terrain_walkBoundary); -BENCHMARK(terrain_deform); +BENCHMARK(terrainFindPoint); +BENCHMARK(terrainWalk); +BENCHMARK(terrainWalkBoundary); +BENCHMARK(terrainDeform); BENCHMARK_MAIN(); diff --git a/test/perf-instancing.cpp b/test/perf-instancing.cpp index 3638111..a56d60e 100644 --- a/test/perf-instancing.cpp +++ b/test/perf-instancing.cpp @@ -3,38 +3,41 @@ #include <benchmark/benchmark.h> #include <random> -struct Instance { - GlobalPosition3D pos; - glm::mat3 rot; -}; +namespace { + struct Instance { + GlobalPosition3D pos; + glm::mat3 rot; + }; -struct data { - explicit data(size_t n) - { - std::mt19937 gen(std::random_device {}()); - std::uniform_int_distribution<GlobalDistance> xy(0, 1000000); - std::uniform_int_distribution<GlobalDistance> z(0, 10000); - while (n--) { - proxies.emplace_back(instances.acquire(GlobalPosition3D {xy(gen), xy(gen), z(gen)}, glm::mat3 {})); + struct Data { + explicit Data(size_t n) + { + std::mt19937 gen(std::random_device {}()); + std::uniform_int_distribution<GlobalDistance> xyDistrib(0, 1000000); + std::uniform_int_distribution<GlobalDistance> zDistrib(0, 10000); + while (n--) { + proxies.emplace_back(instances.acquire( + GlobalPosition3D {xyDistrib(gen), xyDistrib(gen), zDistrib(gen)}, glm::mat3 {})); + } } - } - InstanceVertices<Instance> instances; - std::vector<InstanceVertices<Instance>::InstanceProxy> proxies; -}; + InstanceVertices<Instance> instances; + std::vector<InstanceVertices<Instance>::InstanceProxy> proxies; + }; -static void -partition(benchmark::State & state) -{ - TestMainWindowAppBase window; - data d(static_cast<size_t>(state.range())); - GlobalPosition2D pos {}; - for (auto _ : state) { - d.instances.partition([&pos](const auto & i) { - return std::abs(i.pos.x - pos.x) < 5 && std::abs(i.pos.y - pos.y) < 5; - }); - pos += GlobalPosition2D {33, 17}; - pos %= 1000000; + void + partition(benchmark::State & state) + { + TestMainWindowAppBase window; + Data data(static_cast<size_t>(state.range())); + GlobalPosition2D pos {}; + for (auto loop : state) { + data.instances.partition([&pos](const auto & instance) { + return std::abs(instance.pos.x - pos.x) < 5 && std::abs(instance.pos.y - pos.y) < 5; + }); + pos += GlobalPosition2D {33, 17}; + pos %= 1000000; + } } } diff --git a/test/perf-persistence.cpp b/test/perf-persistence.cpp index 2e099bf..a39b0ba 100644 --- a/test/perf-persistence.cpp +++ b/test/perf-persistence.cpp @@ -1,23 +1,24 @@ #include "lib/jsonParse-persistence.h" -#include "testMainWindow.h" #include "testStructures.h" #include <benchmark/benchmark.h> -template<typename T> -static void -parse_load_object(benchmark::State & state, T &&, const char * path) -{ - for (auto _ : state) { - std::ifstream in {path}; - benchmark::DoNotOptimize(Persistence::JsonParsePersistence {}.loadState<T>(in)); +namespace { + template<typename T> + void + parseLoadObject(benchmark::State & state, T &&, const char * path) + { + for (auto loop : state) { + std::ifstream inStrm {path}; + benchmark::DoNotOptimize(Persistence::JsonParsePersistence {}.loadState<T>(inStrm)); + } } } -BENCHMARK_CAPTURE(parse_load_object, load_object, std::unique_ptr<TestObject> {}, FIXTURESDIR "json/load_object.json"); -BENCHMARK_CAPTURE(parse_load_object, nested, std::unique_ptr<TestObject> {}, FIXTURESDIR "json/nested.json"); -BENCHMARK_CAPTURE(parse_load_object, shared_ptr_diff, std::unique_ptr<SharedTestObject> {}, +BENCHMARK_CAPTURE(parseLoadObject, load_object, std::unique_ptr<TestObject> {}, FIXTURESDIR "json/load_object.json"); +BENCHMARK_CAPTURE(parseLoadObject, nested, std::unique_ptr<TestObject> {}, FIXTURESDIR "json/nested.json"); +BENCHMARK_CAPTURE(parseLoadObject, shared_ptr_diff, std::unique_ptr<SharedTestObject> {}, FIXTURESDIR "json/shared_ptr_diff.json"); -BENCHMARK_CAPTURE(parse_load_object, shared_ptr_same, std::unique_ptr<SharedTestObject> {}, +BENCHMARK_CAPTURE(parseLoadObject, shared_ptr_same, std::unique_ptr<SharedTestObject> {}, FIXTURESDIR "json/shared_ptr_same.json"); BENCHMARK_MAIN(); diff --git a/test/perf-terrain.cpp b/test/perf-terrain.cpp index e75f80b..ed6a200 100644 --- a/test/perf-terrain.cpp +++ b/test/perf-terrain.cpp @@ -1,19 +1,18 @@ #include "game/terrain.h" #include "gfx/camera.h" -#include "gfx/frustum.h" #include "gfx/gl/sceneShader.h" #include "testMainWindow.h" #include <benchmark/benchmark.h> namespace { - const TestMainWindowAppBase window; + const TestMainWindowAppBase WINDOW; void terrainMeshgen(benchmark::State & state) { Terrain terrain {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")}; - for (auto _ : state) { + for (auto loop : state) { terrain.generateMeshes(); } } @@ -26,7 +25,7 @@ namespace { Camera cam {terrain.getExtents().min + GlobalPosition3D {0, 0, 10000}, 45.F, 1.F, 1, 10000}; cam.setForward(::north + ::east); - for (auto _ : state) { + for (auto loop : state) { terrain.render(shader, cam); } } diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 9bade82..ac7e04c 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -19,72 +19,77 @@ #include "gfx/renderable.h" #include "lib/collection.h" #include "lib/location.h" -#include "lib/stream_support.h" +#include "lib/stream_support.h" // IWYU pragma: keep #include "testMainWindow.h" +#include <special_members.h> BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -const std::filesystem::path TMP {"/tmp"}; - -class FactoryFixture : public TestRenderOutputSize<TextureAbsCoord {2048, 1024}>, public SceneProvider { -public: - FactoryFixture() : sceneRenderer {size, output} { } - - ~FactoryFixture() - { - auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); - std::filesystem::create_directories(outpath.parent_path()); - Texture::save(outImage, outpath.c_str()); - } - - void - content(const SceneShader & shader, const Frustum & frustum) const override - { - shader.basic.use(Location {{0, 0, 0}, {0, 0, 0}}); - objects.apply(&Renderable::render, shader, frustum); - } - - void - lights(const SceneShader & shader) const override - { - objects.apply(&Renderable::lights, shader); - } - - void - environment(const SceneShader &, const SceneRenderer & sceneRenderer) const override - { - sceneRenderer.setAmbientLight({.4, .4, .4}); - sceneRenderer.setDirectionalLight({.6, .6, .6}, {{0.9, 0.5}}, *this); - } - - void - shadows(const ShadowMapper & mapper, const Frustum & frustum) const override - { - mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); - objects.apply(&Renderable::shadows, mapper, frustum); - } - - void - render(float dist) - { - sceneRenderer.camera.setView({-dist, dist * 1.2f, dist * 1.2f}, south + east + down); - sceneRenderer.render(*this); - } - - Collection<const Renderable> objects; - -private: - SceneRenderer sceneRenderer; -}; - -BOOST_AUTO_TEST_CASE(surfaces, *boost::unit_test::timeout(5)) +namespace { + class FactoryFixture : public TestRenderOutputSize<TextureAbsCoord {2048, 1024}>, public SceneProvider { + public: + FactoryFixture() : sceneRenderer {size, output} { } + + NO_COPY(FactoryFixture); + NO_MOVE(FactoryFixture); + + ~FactoryFixture() override + { + auto outpath = (ANALYSIS_DIRECTORY / boost::unit_test::framework::current_test_case().full_name()) + .replace_extension(".tga"); + std::filesystem::create_directories(outpath.parent_path()); + Texture::save(outImage, outpath.c_str()); + } + + void + content(const SceneShader & shader, const Frustum & frustum) const override + { + shader.basic.use(Location {.pos = {0, 0, 0}, .rot = {0, 0, 0}}); + objects.apply(&Renderable::render, shader, frustum); + } + + void + lights(const SceneShader & shader) const override + { + objects.apply(&Renderable::lights, shader); + } + + void + environment(const SceneShader &, const SceneRenderer & sceneRenderer) const override + { + sceneRenderer.setAmbientLight({.4, .4, .4}); + sceneRenderer.setDirectionalLight({.6, .6, .6}, {{0.9, 0.5}}, *this); + } + + void + shadows(const ShadowMapper & mapper, const Frustum & frustum) const override + { + mapper.dynamicPoint.use(Location {.pos = {0, 0, 0}, .rot = {0, 0, 0}}); + objects.apply(&Renderable::shadows, mapper, frustum); + } + + void + render(float dist) + { + sceneRenderer.camera.setView({-dist, dist * 1.2F, dist * 1.2F}, south + east + down); + sceneRenderer.render(*this); + } + + SharedCollection<const Renderable> objects; + + private: + SceneRenderer sceneRenderer; + }; +} + +BOOST_AUTO_TEST_CASE(Surfaces, *boost::unit_test::timeout(5)) { - auto mf = AssetFactory::loadXML(RESDIR "/surfaces.xml"); - BOOST_REQUIRE(mf); - BOOST_CHECK_EQUAL(4, mf->assets.size()); - auto gravelAsset = mf->assets.at("terrain.surface.gravel"); + auto factory = AssetFactory::loadXML(RESDIR "/surfaces.xml"); + BOOST_REQUIRE(factory); + BOOST_CHECK_EQUAL(4, factory->assets.size()); + auto gravelAsset = factory->assets.at("terrain.surface.gravel"); BOOST_REQUIRE(gravelAsset); - auto gravel = std::dynamic_pointer_cast<Surface>(gravelAsset); + auto gravel = gravelAsset.dynamicCast<Surface>(); BOOST_REQUIRE(gravel); BOOST_REQUIRE_EQUAL(gravel->name, "Gravel"); BOOST_REQUIRE_EQUAL(gravel->colorBias, RGB {.9F}); @@ -93,87 +98,87 @@ BOOST_AUTO_TEST_CASE(surfaces, *boost::unit_test::timeout(5)) BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); -BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(Brush47xml, *boost::unit_test::timeout(5)) { - auto mf = AssetFactory::loadXML(RESDIR "/brush47.xml"); - BOOST_REQUIRE(mf); - gameState.assets = mf->assets; - BOOST_REQUIRE_GE(mf->shapes.size(), 6); - BOOST_CHECK(mf->shapes.at("plane")); - BOOST_CHECK(mf->shapes.at("cylinder")); - BOOST_CHECK(mf->shapes.at("cuboid")); - BOOST_CHECK(mf->shapes.at("wheel")); - BOOST_CHECK(mf->shapes.at("axel")); - auto bogie = mf->shapes.at("bogie"); + auto factory = AssetFactory::loadXML(RESDIR "/brush47.xml"); + BOOST_REQUIRE(factory); + gameState.assets = factory->assets; + BOOST_REQUIRE_GE(factory->shapes.size(), 6); + BOOST_CHECK(factory->shapes.at("plane")); + BOOST_CHECK(factory->shapes.at("cylinder")); + BOOST_CHECK(factory->shapes.at("cuboid")); + BOOST_CHECK(factory->shapes.at("wheel")); + BOOST_CHECK(factory->shapes.at("axel")); + auto bogie = factory->shapes.at("bogie"); BOOST_REQUIRE(bogie); auto bogieObj = std::dynamic_pointer_cast<const Object>(bogie); BOOST_CHECK_GE(bogieObj->uses.size(), 3); - BOOST_CHECK_EQUAL(1, mf->assets.size()); - auto brush47 = mf->assets.at("brush-47"); + BOOST_CHECK_EQUAL(1, factory->assets.size()); + auto brush47 = factory->assets.at("brush-47"); BOOST_REQUIRE(brush47); - auto brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>(brush47); + auto brush47rvc = brush47.dynamicCast<RailVehicleClass>(); BOOST_REQUIRE(brush47rvc); BOOST_REQUIRE(brush47rvc->bodyMesh); BOOST_REQUIRE(brush47rvc->bogies.front()); BOOST_REQUIRE(brush47rvc->bogies.back()); auto railVehicle = std::make_shared<RailVehicle>(brush47rvc); - objects.objects.push_back(brush47rvc); + objects.emplace(brush47rvc); render(10000); } -BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(Tree, *boost::unit_test::timeout(5)) { - auto mf = AssetFactory::loadXML(RESDIR "/foliage.xml"); - BOOST_REQUIRE(mf); - gameState.assets = mf->assets; - auto tree_01_1 = mf->assets.at("Tree-01-1"); - BOOST_REQUIRE(tree_01_1); - auto tree_01_1_f = std::dynamic_pointer_cast<Foliage>(tree_01_1); - BOOST_REQUIRE(tree_01_1_f); - - auto plant1 = std::make_shared<Plant>(tree_01_1_f, Location {{-2000, 2000, 0}, {0, 0, 0}}); - auto plant2 = std::make_shared<Plant>(tree_01_1_f, Location {{3000, -4000, 0}, {0, 1, 0}}); - auto plant3 = std::make_shared<Plant>(tree_01_1_f, Location {{-2000, -4000, 0}, {0, 2, 0}}); - auto plant4 = std::make_shared<Plant>(tree_01_1_f, Location {{3000, 2000, 0}, {0, 3, 0}}); - objects.objects.push_back(tree_01_1_f); + auto factory = AssetFactory::loadXML(RESDIR "/foliage.xml"); + BOOST_REQUIRE(factory); + gameState.assets = factory->assets; + auto tree011Asset = factory->assets.at("Tree-01-1"); + BOOST_REQUIRE(tree011Asset); + auto tree011 = tree011Asset.dynamicCast<Foliage>(); + BOOST_REQUIRE(tree011); + + auto plant1 = std::make_shared<Plant>(tree011, Location {.pos = {-2000, 2000, 0}, .rot = {0, 0, 0}}); + auto plant2 = std::make_shared<Plant>(tree011, Location {.pos = {3000, -4000, 0}, .rot = {0, 1, 0}}); + auto plant3 = std::make_shared<Plant>(tree011, Location {.pos = {-2000, -4000, 0}, .rot = {0, 2, 0}}); + auto plant4 = std::make_shared<Plant>(tree011, Location {.pos = {3000, 2000, 0}, .rot = {0, 3, 0}}); + objects.emplace(tree011); render(6000); } -BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(Lights, *boost::unit_test::timeout(5)) { - auto mf = AssetFactory::loadXML(RESDIR "/lights.xml"); - BOOST_REQUIRE(mf); - gameState.assets = mf->assets; - auto rlight = mf->assets.at("r-light"); + auto factory = AssetFactory::loadXML(RESDIR "/lights.xml"); + BOOST_REQUIRE(factory); + gameState.assets = factory->assets; + auto rlightAsset = factory->assets.at("r-light"); + BOOST_REQUIRE(rlightAsset); + auto oldlampAsset = factory->assets.at("old-lamp"); + BOOST_REQUIRE(oldlampAsset); + auto rlight = rlightAsset.dynamicCast<Illuminator>(); BOOST_REQUIRE(rlight); - auto oldlamp = mf->assets.at("old-lamp"); + auto oldlamp = oldlampAsset.dynamicCast<Illuminator>(); BOOST_REQUIRE(oldlamp); - auto rlight_f = std::dynamic_pointer_cast<Illuminator>(rlight); - BOOST_REQUIRE(rlight_f); - auto oldlamp_f = std::dynamic_pointer_cast<Illuminator>(oldlamp); - BOOST_REQUIRE(oldlamp_f); - - auto light1 = std::make_shared<Light>(oldlamp_f, Location {{0, 0, 0}, {0, 0, 0}}); - auto light2 = std::make_shared<Light>(rlight_f, Location {{-4000, 0, 0}, {0, 2, 0}}); - auto light3 = std::make_shared<Light>(rlight_f, Location {{-4000, -4000, 0}, {0, 1, 0}}); - auto light4 = std::make_shared<Light>(oldlamp_f, Location {{3000, 4600, 0}, {0, 2, 0}}); - objects.objects.push_back(rlight_f); - objects.objects.push_back(oldlamp_f); + + auto light1 = std::make_shared<Light>(oldlamp, Location {.pos = {0, 0, 0}, .rot = {0, 0, 0}}); + auto light2 = std::make_shared<Light>(rlight, Location {.pos = {-4000, 0, 0}, .rot = {0, 2, 0}}); + auto light3 = std::make_shared<Light>(rlight, Location {.pos = {-4000, -4000, 0}, .rot = {0, 1, 0}}); + auto light4 = std::make_shared<Light>(oldlamp, Location {.pos = {3000, 4600, 0}, .rot = {0, 2, 0}}); + objects.emplace(rlight); + objects.emplace(oldlamp); // yes I'm hacking some floor to light up as though its a bush - auto floorf = std::dynamic_pointer_cast<Foliage>(mf->assets.at("floor")); + auto floorf = factory->assets.at("floor").dynamicCast<Foliage>(); auto floor = std::make_shared<Plant>(floorf, Location {}); - objects.objects.push_back(floorf); + objects.emplace(floorf); render(6000); } BOOST_AUTO_TEST_SUITE_END(); -BOOST_AUTO_TEST_CASE(loadall) +BOOST_AUTO_TEST_CASE(Loadall) { const auto assets = AssetFactory::loadAll(RESDIR); BOOST_CHECK(assets.at("brush-47")); @@ -182,7 +187,7 @@ BOOST_AUTO_TEST_CASE(loadall) template<typename T> using InOut = std::tuple<T, T>; -BOOST_DATA_TEST_CASE(normalizeColourName, +BOOST_DATA_TEST_CASE(NormalizeColourName, boost::unit_test::data::make<InOut<std::string>>({ {"", ""}, {"black", "black"}, @@ -194,14 +199,14 @@ BOOST_DATA_TEST_CASE(normalizeColourName, {"BlAck ", "black"}, {"Bl Ack ", "black"}, }), - in_, exp) + input, exp) { - auto in {in_}; - BOOST_CHECK_NO_THROW(AssetFactory::normalizeColourName(in)); - BOOST_CHECK_EQUAL(in, exp); + auto toNormalize {input}; + BOOST_CHECK_NO_THROW(AssetFactory::normalizeColourName(toNormalize)); + BOOST_CHECK_EQUAL(toNormalize, exp); } -BOOST_AUTO_TEST_CASE(parseX11RGB, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(ParseX11RGB, *boost::unit_test::timeout(5)) { const auto parsedColours = AssetFactory::parseX11RGB(FIXTURESDIR "rgb.txt"); BOOST_REQUIRE_EQUAL(parsedColours.size(), 20); @@ -210,7 +215,7 @@ BOOST_AUTO_TEST_CASE(parseX11RGB, *boost::unit_test::timeout(5)) BOOST_CHECK_CLOSE_VEC(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(0.79, 0.88, 1)); } -BOOST_AUTO_TEST_CASE(texturePacker, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(TexturePackerSimple, *boost::unit_test::timeout(5)) { std::vector<TexturePacker::Image> input { {10, 10}, @@ -220,28 +225,28 @@ BOOST_AUTO_TEST_CASE(texturePacker, *boost::unit_test::timeout(5)) {10, 200}, {5, 5}, }; - TexturePacker tp {input}; - BOOST_CHECK_EQUAL(TexturePacker::Size(128, 256), tp.minSize()); - const auto result = tp.pack(); + TexturePacker packer {input}; + BOOST_CHECK_EQUAL(TexturePacker::Size(128, 256), packer.minSize()); + const auto result = packer.pack(); } -BOOST_AUTO_TEST_CASE(texturePacker_many, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(TexturePackerMany, *boost::unit_test::timeout(5)) { std::vector<TexturePacker::Image> images(256); std::fill(images.begin(), images.end(), TexturePacker::Image {32, 32}); - const auto totalSize = std::accumulate(images.begin(), images.end(), 0, [](auto t, const auto & i) { - return t + TexturePacker::area(i); + const auto totalSize = std::ranges::fold_left(images, 0, [](auto total, const auto & image) { + return total + TexturePacker::area(image); }); - TexturePacker tp {images}; - BOOST_CHECK_EQUAL(TexturePacker::Size(32, 32), tp.minSize()); - const auto result = tp.pack(); + TexturePacker packer {images}; + BOOST_CHECK_EQUAL(TexturePacker::Size(32, 32), packer.minSize()); + const auto result = packer.pack(); BOOST_CHECK_EQUAL(result.first.size(), images.size()); BOOST_CHECK_GE(TexturePacker::area(result.second), TexturePacker::area(images.front()) * static_cast<GLsizei>(images.size())); BOOST_CHECK_EQUAL(totalSize, TexturePacker::area(result.second)); } -BOOST_AUTO_TEST_CASE(texturePacker_many_random, *boost::unit_test::timeout(15)) +BOOST_AUTO_TEST_CASE(TexturePackerManyRandom, *boost::unit_test::timeout(15)) { std::vector<TexturePacker::Image> images(2048); std::mt19937 gen(std::random_device {}()); @@ -249,7 +254,7 @@ BOOST_AUTO_TEST_CASE(texturePacker_many_random, *boost::unit_test::timeout(15)) std::generate(images.begin(), images.end(), [&dim, &gen]() { return TexturePacker::Image {2 ^ dim(gen), 2 ^ dim(gen)}; }); - TexturePacker tp {images}; - const auto result = tp.pack(); + TexturePacker packer {images}; + const auto result = packer.pack(); BOOST_CHECK_EQUAL(result.first.size(), images.size()); } diff --git a/test/test-collection.cpp b/test/test-collection.cpp index 00204fc..45086a9 100644 --- a/test/test-collection.cpp +++ b/test/test-collection.cpp @@ -8,109 +8,394 @@ #include <special_members.h> #include <vector> -class Base { -public: - Base() = default; - virtual ~Base() = default; - DEFAULT_MOVE_COPY(Base); - - virtual bool - add() - { - total += 1; - return false; - } - - unsigned int total {0}; -}; - -class Sub : public Base { -public: - bool - add() override - { - total += 2; - return true; - } -}; - -using TestCollection = Collection<Base>; - -BOOST_TEST_DONT_PRINT_LOG_VALUE(Collection<Base>::Objects::const_iterator) -BOOST_TEST_DONT_PRINT_LOG_VALUE(Collection<Base>::Objects::const_reverse_iterator) +namespace { + class Base { + public: + Base() = default; + virtual ~Base() = default; + DEFAULT_MOVE_COPY(Base); + + virtual bool + add() + { + total += 1; + return false; + } + + [[nodiscard]] virtual bool + yes() const + { + return true; + } + + unsigned int total {0}; + }; + + class Sub : public Base { + public: + bool + add() override + { + total += 2; + return true; + } + }; + + class Sub1 : public Sub { }; + + class Sub2 : public Sub { }; + + class Base2 { + public: + Base2() = default; + virtual ~Base2() = default; + DEFAULT_MOVE_COPY(Base2); + }; + + class Multi : public Sub1, public Base2 { }; + + using TestCollection = SharedCollection<Base, Sub>; +} + +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::Objects::const_reverse_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::OtherObjects<Sub>::iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestCollection::OtherObjects<Sub>::const_iterator) BOOST_FIXTURE_TEST_SUITE(tc, TestCollection) -BOOST_AUTO_TEST_CASE(empty) +BOOST_AUTO_TEST_CASE(Empty) { + BOOST_CHECK(TestCollection::empty()); BOOST_REQUIRE(!apply(&Base::add)); - const auto i = applyOne(&Base::add); - BOOST_CHECK_EQUAL(i, end()); + const auto appliedTo = applyOne(&Base::add); + BOOST_CHECK_EQUAL(appliedTo, end()); + BOOST_CHECK(!find<Base>()); + BOOST_CHECK(!find<Sub>()); + BOOST_CHECK(!find<Sub1>()); } -BOOST_AUTO_TEST_CASE(a_base) +BOOST_AUTO_TEST_CASE(ABaseApply) { - auto b = create<Base>(); + auto base = create<Base>(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); BOOST_REQUIRE(apply(&Base::add)); - BOOST_CHECK_EQUAL(b->total, 1); - const auto i = applyOne(&Base::add); - BOOST_CHECK_EQUAL(i, end()); + BOOST_CHECK_EQUAL(base->total, 1); + const auto appliedTo = applyOne(&Base::add); + BOOST_CHECK_EQUAL(appliedTo, end()); + BOOST_CHECK_EQUAL(base.get(), find<Base>()); + BOOST_CHECK(!find<Sub>()); + BOOST_CHECK(!find<Sub1>()); } -BOOST_AUTO_TEST_CASE(a_rbase) +BOOST_AUTO_TEST_CASE(EmplaceOthers) { - auto b = create<Base>(); + auto base = emplace(std::make_shared<Base>()); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); + auto sub = emplace(std::make_shared<Sub>()); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get<OtherObjects<Sub>>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(base.get(), find<Base>()); + BOOST_CHECK_EQUAL(sub.get(), find<Sub>()); + BOOST_CHECK(!find<Sub1>()); +} + +BOOST_AUTO_TEST_CASE(ABaseRApply) +{ + auto base = create<Base>(); BOOST_REQUIRE(rapply(&Base::add)); - BOOST_CHECK_EQUAL(b->total, 1); - const auto i = rapplyOne(&Base::add); - BOOST_CHECK_EQUAL(i, rend()); + BOOST_CHECK_EQUAL(base->total, 1); + const auto appliedTo = rapplyOne(&Base::add); + BOOST_CHECK_EQUAL(appliedTo, rend()); } -BOOST_AUTO_TEST_CASE(a_sub) +BOOST_AUTO_TEST_CASE(ASubApply) { - auto s = create<Sub>(); + auto sub = create<Sub>(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get<OtherObjects<Sub>>(otherObjects).size(), 1); BOOST_REQUIRE(apply(&Base::add)); - BOOST_CHECK_EQUAL(s->total, 2); - const auto i = applyOne(&Base::add); - BOOST_CHECK_NE(i, end()); - BOOST_CHECK_EQUAL(*i, s); + BOOST_CHECK_EQUAL(sub->total, 2); + const auto appliedTo = applyOne(&Base::add); + BOOST_CHECK_NE(appliedTo, end()); + BOOST_CHECK_EQUAL(*appliedTo, sub); +} + +BOOST_AUTO_TEST_CASE(Filter) +{ + static_assert(TestCollection::idx<Sub>() == 0); + static_assert(TestCollection::idx<const Sub>() == 0); + static_assert(TestCollection::idx<Sub1>() == 0); + static_assert(TestCollection::idx<const Sub1>() == 0); + create<Base>(); + BOOST_CHECK_EQUAL(1, apply<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply<Sub1>(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne<Sub1>(&Base::yes)); + create<Sub>(); + BOOST_CHECK_EQUAL(2, apply<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(1, apply<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(0, apply<Sub1>(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin(), applyOne<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).end(), applyOne<Sub1>(&Base::yes)); + create<Sub1>(); + BOOST_CHECK_EQUAL(3, apply<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(2, apply<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(1, apply<Sub1>(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne<Base>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin(), applyOne<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).begin() + 1, applyOne<Sub1>(&Base::yes)); + + BOOST_CHECK_EQUAL(std::get<idx<Sub>()>(otherObjects).size(), 2); + BOOST_CHECK_EQUAL(std::get<idx<Sub1>()>(otherObjects).size(), 2); + + BOOST_CHECK_EQUAL(&objects, &containerFor<Base>()); + BOOST_CHECK_EQUAL(&std::get<0>(otherObjects), &containerFor<Sub>()); + BOOST_CHECK_EQUAL(&std::get<0>(otherObjects), &containerFor<Sub1>()); +} + +BOOST_AUTO_TEST_CASE(BeginEnd) +{ + BOOST_CHECK_EQUAL(0, std::distance(begin(), end())); + create<Sub>(); + create<Base>(); + BOOST_CHECK_EQUAL(2, std::distance(begin(), end())); +} + +BOOST_AUTO_TEST_CASE(RBeginREnd) +{ + BOOST_CHECK_EQUAL(0, std::distance(rbegin(), rend())); + create<Sub>(); + create<Base>(); + BOOST_CHECK_EQUAL(2, std::distance(rbegin(), rend())); +} + +BOOST_AUTO_TEST_CASE(CreateCreate) +{ + auto base1 = findOrCreate<Base>(); + BOOST_CHECK(base1); + auto base2 = findOrCreate<Base>(); + BOOST_CHECK_EQUAL(base1, base2); + auto sub1 = findOrCreate<Sub>(); + BOOST_CHECK_NE(sub1, base1); + auto sub2 = findOrCreate<Sub>(); + BOOST_CHECK_EQUAL(sub1, sub2); +} + +BOOST_AUTO_TEST_CASE(CreateCreateSub) +{ + auto sub = findOrCreate<Sub>(); + auto base = findOrCreate<Base>(); + BOOST_CHECK_EQUAL(sub, base); +} + +BOOST_AUTO_TEST_SUITE_END() + +using TestUniqueCollection = UniqueCollection<Base, Sub>; +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::const_reverse_iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::iterator) +BOOST_TEST_DONT_PRINT_LOG_VALUE(TestUniqueCollection::Objects::reverse_iterator) + +BOOST_FIXTURE_TEST_SUITE(utc, TestUniqueCollection) + +BOOST_AUTO_TEST_CASE(UniqueCreate) +{ + create<Base>(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); + create<Sub>(); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get<OtherObjects<Sub>>(otherObjects).size(), 1); +} + +BOOST_AUTO_TEST_CASE(MoveAssign) +{ + create<Base>(); + create<Sub>(); + + TestUniqueCollection::Objects other; + TestUniqueCollection::operator=(std::move(other)); + BOOST_CHECK(objects.empty()); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); + + other.push_back(std::make_unique<Sub>()); + other.push_back(std::make_unique<Base>()); + TestUniqueCollection::operator=(std::move(other)); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get<OtherObjects<Sub>>(otherObjects).size(), 1); + BOOST_CHECK(other.empty()); +} + +BOOST_AUTO_TEST_CASE(ClearAll) +{ + create<Base>(); + create<Sub>(); + emplace(std::make_unique<Base>()); + emplace(std::make_unique<Sub>()); + + clear(); + BOOST_CHECK(objects.empty()); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); +} + +BOOST_AUTO_TEST_CASE(RemoveAllOfSub) +{ + create<Base>(); + create<Sub>(); + emplace(std::make_unique<Base>()); + emplace(std::make_unique<Sub>()); + emplace(std::make_unique<Sub1>()); + + BOOST_CHECK_EQUAL(removeAll<Sub>(), 3); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK(std::get<OtherObjects<Sub>>(otherObjects).empty()); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_FIXTURE_TEST_SUITE(btc, UniqueCollection<Base>) + +BOOST_AUTO_TEST_CASE(NoOthers) +{ + create<Base>(); + create<Sub>(); + emplace(std::make_unique<Base>()); + emplace(std::make_unique<Sub>()); +} + +BOOST_AUTO_TEST_CASE(ApplyAll) +{ + create<Base>(); + BOOST_CHECK_EQUAL(0, apply<Sub>(&Base::add)); + BOOST_CHECK_EQUAL(1, apply<Base>(&Base::add)); + create<Sub>(); + BOOST_CHECK_EQUAL(1, apply<Sub>(&Base::add)); + BOOST_CHECK_EQUAL(2, apply<Base>(&Base::add)); +} + +BOOST_AUTO_TEST_CASE(ApplyOneType) +{ + create<Base>(); + BOOST_CHECK_EQUAL(objects.end(), applyOne<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne<Base>(&Base::yes)); + create<Sub>(); + BOOST_CHECK_EQUAL(objects.begin() + 1, applyOne<Sub>(&Base::yes)); + BOOST_CHECK_EQUAL(objects.begin(), applyOne<Base>(&Base::yes)); + create<Sub1>(); + BOOST_CHECK_EQUAL(objects.begin() + 2, applyOne<Sub1>(&Base::yes)); +} + +BOOST_AUTO_TEST_SUITE_END() + +using MultiCollection = Collection<std::unique_ptr<Base>, Multi, Sub, Base2>; + +BOOST_FIXTURE_TEST_SUITE(multi, MultiCollection) + +BOOST_AUTO_TEST_CASE(AddMulti) +{ + static_assert(MultiCollection::idx<Multi>() == 0); + static_assert(MultiCollection::idx<Sub>() == 1); + static_assert(MultiCollection::idx<Base2>() == 2); + create<Base>(); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create<Sub>(); + BOOST_CHECK_EQUAL(objects.size(), 2); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create<Sub1>(); + BOOST_CHECK_EQUAL(objects.size(), 3); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 2); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create<Sub2>(); + BOOST_CHECK_EQUAL(objects.size(), 4); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 3); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + create<Multi>(); + BOOST_CHECK_EQUAL(size(), 5); + BOOST_CHECK_EQUAL(size<Multi>(), 1); + BOOST_CHECK_EQUAL(size<Sub>(), 4); + BOOST_CHECK_EQUAL(size<Base2>(), 1); +} + +BOOST_AUTO_TEST_CASE(RemoveMulti) +{ + create<Base>(); + create<Sub>(); + create<Sub1>(); + create<Sub2>(); + create<Multi>(); + BOOST_CHECK_EQUAL(objects.size(), 5); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 1); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 4); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 1); + + BOOST_CHECK_EQUAL(removeAll<Multi>(), 1); + BOOST_CHECK_EQUAL(objects.size(), 4); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 3); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + + BOOST_CHECK_EQUAL(removeAll<Sub>(), 3); + BOOST_CHECK_EQUAL(objects.size(), 1); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); + + BOOST_CHECK_EQUAL(removeAll<Base>(), 1); + BOOST_CHECK_EQUAL(objects.size(), 0); + BOOST_CHECK_EQUAL(std::get<0>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<1>(otherObjects).size(), 0); + BOOST_CHECK_EQUAL(std::get<2>(otherObjects).size(), 0); } BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_CASE(wrapped_ptr_file_cons) +BOOST_AUTO_TEST_CASE(WrappedPtrFileCons) { using FilePtr = wrapped_ptr<FILE, &fclose>; - const FilePtr fp {fopen, "/dev/null", "r"}; - BOOST_REQUIRE(fp); - BOOST_CHECK_NO_THROW(fflush(fp)); + const FilePtr file {fopen, "/dev/null", "r"}; + BOOST_REQUIRE(file); + BOOST_CHECK_NO_THROW(fflush(file)); - BOOST_CHECK_EQUAL(fp.get(), fp.operator->()); - BOOST_CHECK_EQUAL(fp.get(), fp.operator FILE *()); + BOOST_CHECK_EQUAL(file.get(), file.operator->()); + BOOST_CHECK_EQUAL(file.get(), file.operator FILE *()); } -BOOST_AUTO_TEST_CASE(wrapped_ptr_file_move) +BOOST_AUTO_TEST_CASE(WrappedPtrFileMove) { using FilePtr = wrapped_ptr<FILE, &fclose>; - FilePtr fp {fopen, "/dev/null", "r"}; - BOOST_REQUIRE(fp); + FilePtr file {fopen, "/dev/null", "r"}; + BOOST_REQUIRE(file); - FilePtr fp2 {std::move(fp)}; - BOOST_REQUIRE(!fp); + FilePtr fp2 {std::move(file)}; + BOOST_REQUIRE(!file); BOOST_REQUIRE(fp2); - fp = std::move(fp2); - BOOST_REQUIRE(fp); + file = std::move(fp2); + BOOST_REQUIRE(file); BOOST_REQUIRE(!fp2); FilePtr fp3 {fopen, "/dev/null", "r"}; - fp = std::move(fp3); + file = std::move(fp3); } -BOOST_AUTO_TEST_CASE(wrapped_ptr_file_typed) +BOOST_AUTO_TEST_CASE(WrappedPtrFileTyped) { using FilePtr = wrapped_ptrt<FILE, &fopen, &fclose>; - const FilePtr fp {"/dev/null", "r"}; - BOOST_REQUIRE(fp); - BOOST_CHECK_NO_THROW(fflush(fp)); + const FilePtr file {"/dev/null", "r"}; + BOOST_REQUIRE(file); + BOOST_CHECK_NO_THROW(fflush(file)); } diff --git a/test/test-enumDetails.cpp b/test/test-enumDetails.cpp index 0e759e5..d4f4d2f 100644 --- a/test/test-enumDetails.cpp +++ b/test/test-enumDetails.cpp @@ -7,47 +7,47 @@ #include <enumDetails.h> #include <stream_support.h> -constexpr std::array INVALID_NAMES {"", "missing", "GlobalScoped::aa", "GlobalScoped", "ns::aa", "a", "bb"}; -constexpr std::array VALID_NAMES {"aa", "b", "c"}; -template<typename E> constexpr std::array VALID_VALUES {E::aa, E::b, E::c}; +constexpr std::array INVALID_NAMES {"", "missing", "GlobalScoped::Aa", "GlobalScoped", "ns::Aa", "A", "Bb"}; +constexpr std::array VALID_NAMES {"Aa", "B", "C"}; +template<typename E> constexpr std::array VALID_VALUES {E::Aa, E::B, E::C}; // Not a template, else Boost test framework throws printing the context constexpr std::array INVALID_VALUES {-1, 3, 20}; #define TESTS_FOR_TYPE(TYPE) \ - BOOST_DATA_TEST_CASE(invalid_check_##TYPE, INVALID_VALUES, in) \ + BOOST_DATA_TEST_CASE(InvalidCheck##TYPE, INVALID_VALUES, input) \ { \ - BOOST_CHECK(!EnumDetails<TYPE>::is_valid(static_cast<TYPE>(in))); \ + BOOST_CHECK(!EnumDetails<TYPE>::isValid(static_cast<TYPE>(input))); \ } \ - BOOST_DATA_TEST_CASE(invalid_parse_##TYPE, INVALID_NAMES, in) \ + BOOST_DATA_TEST_CASE(InvalidParse##TYPE, INVALID_NAMES, input) \ { \ - BOOST_CHECK(!EnumDetails<TYPE>::parse(in).has_value()); \ + BOOST_CHECK(!EnumDetails<TYPE>::parse(input).has_value()); \ } \ - BOOST_DATA_TEST_CASE(invalid_to_string_##TYPE, INVALID_VALUES, in) \ + BOOST_DATA_TEST_CASE(InvalidToString##TYPE, INVALID_VALUES, input) \ { \ - BOOST_CHECK(!EnumDetails<TYPE>::to_string(static_cast<TYPE>(in)).has_value()); \ + BOOST_CHECK(!EnumDetails<TYPE>::toString(static_cast<TYPE>(input)).has_value()); \ } \ - BOOST_DATA_TEST_CASE(valid_check_##TYPE, VALID_VALUES<TYPE>, in) \ + BOOST_DATA_TEST_CASE(ValidCheck##TYPE, VALID_VALUES<TYPE>, input) \ { \ - BOOST_CHECK(EnumDetails<TYPE>::is_valid(in)); \ + BOOST_CHECK(EnumDetails<TYPE>::isValid(input)); \ } \ - BOOST_DATA_TEST_CASE(valid_parse_##TYPE, VALID_NAMES ^ VALID_VALUES<TYPE>, in, out) \ + BOOST_DATA_TEST_CASE(ValidParse##TYPE, VALID_NAMES ^ VALID_VALUES<TYPE>, input, output) \ { \ - const auto v = EnumDetails<TYPE>::parse(in); \ - BOOST_CHECK_IF(vo, v.has_value()) { \ - BOOST_CHECK_EQUAL(v.value(), out); \ + const auto parsed = EnumDetails<TYPE>::parse(input); \ + BOOST_CHECK_IF(parsedOK, parsed.has_value()) { \ + BOOST_CHECK_EQUAL(parsed.value(), output); \ } \ } \ - BOOST_DATA_TEST_CASE(valid_to_string_##TYPE, VALID_VALUES<TYPE> ^ VALID_NAMES, in, out) \ + BOOST_DATA_TEST_CASE(ValidToString##TYPE, VALID_VALUES<TYPE> ^ VALID_NAMES, input, output) \ { \ - const auto v = EnumDetails<TYPE>::to_string(in); \ - BOOST_CHECK_IF(vo, v.has_value()) { \ - BOOST_CHECK_EQUAL(v.value(), out); \ + const auto parsed = EnumDetails<TYPE>::toString(input); \ + BOOST_CHECK_IF(parsedOK, parsed.has_value()) { \ + BOOST_CHECK_EQUAL(parsed.value(), output); \ } \ } TESTS_FOR_TYPE(GlobalScoped) TESTS_FOR_TYPE(GlobalUnscoped) -using ns_unscoped = ns::Unscoped; -using ns_scoped = ns::Scoped; -TESTS_FOR_TYPE(ns_unscoped) -TESTS_FOR_TYPE(ns_scoped) +using NsUnscoped = ns::Unscoped; +using NsScoped = ns::Scoped; +TESTS_FOR_TYPE(NsUnscoped) +TESTS_FOR_TYPE(NsScoped) diff --git a/test/test-environment.cpp b/test/test-environment.cpp index 8bd64be..a249192 100644 --- a/test/test-environment.cpp +++ b/test/test-environment.cpp @@ -2,7 +2,6 @@ #include "testHelpers.h" #include <boost/test/data/test_case.hpp> #include <boost/test/unit_test.hpp> -#include <cmath> #include <stream_support.h> #include <chronology.h> @@ -11,25 +10,27 @@ #include <gfx/lightDirection.h> #include <maths.h> -using sunPosTestData = std::tuple<Direction2D, time_t, Direction2D>; -using sunDirTestData = std::tuple<Direction2D, Direction3D, float, float>; -constexpr Direction2D Doncaster = {-1.1, 53.5}; -constexpr Direction2D NewYork = {74.0, 40.7}; -constexpr Direction2D Syndey = {-151.2, -33.9}; -constexpr Direction2D EqGM = {}; +namespace { + using SunPosTestData = std::tuple<Direction2D, time_t, Direction2D>; + using SunDirTestData = std::tuple<Direction2D, Direction3D, float, float>; + constexpr Direction2D DONCASTER = {-1.1, 53.5}; + constexpr Direction2D NEW_YORK = {74.0, 40.7}; + constexpr Direction2D SYNDEY = {-151.2, -33.9}; + constexpr Direction2D EQ_GM = {}; +} -BOOST_DATA_TEST_CASE(sun_position, - boost::unit_test::data::make<sunPosTestData>({ - {EqGM, "2024-01-02T00:00:00"_time_t, {181.52F, -66.86F}}, - {EqGM, "2024-01-02T06:00:00"_time_t, {113.12F, -0.85F}}, - {EqGM, "2024-01-02T06:30:00"_time_t, {113.12F, 6.05F}}, - {EqGM, "2024-01-02T12:00:00"_time_t, {177.82F, 66.97F}}, - {EqGM, "2024-01-02T18:00:00"_time_t, {246.99F, 0.90F}}, - {EqGM, "2024-01-03T00:00:00"_time_t, {181.52F, -67.04F}}, - {EqGM, "2024-06-29T12:00:00"_time_t, {2.1F, 66.80F}}, - {Doncaster, "2024-06-29T12:00:00"_time_t, {176.34F, 59.64F}}, - {NewYork, "2024-06-29T12:00:00"_time_t, {278.04F, 27.34F}}, - {Syndey, "2024-06-29T12:00:00"_time_t, {106.13F, -63.29F}}, +BOOST_DATA_TEST_CASE(SunPosition, + boost::unit_test::data::make<SunPosTestData>({ + {EQ_GM, "2024-01-02T00:00:00"_time_t, {181.52F, -66.86F}}, + {EQ_GM, "2024-01-02T06:00:00"_time_t, {113.12F, -0.85F}}, + {EQ_GM, "2024-01-02T06:30:00"_time_t, {113.12F, 6.05F}}, + {EQ_GM, "2024-01-02T12:00:00"_time_t, {177.82F, 66.97F}}, + {EQ_GM, "2024-01-02T18:00:00"_time_t, {246.99F, 0.90F}}, + {EQ_GM, "2024-01-03T00:00:00"_time_t, {181.52F, -67.04F}}, + {EQ_GM, "2024-06-29T12:00:00"_time_t, {2.1F, 66.80F}}, + {DONCASTER, "2024-06-29T12:00:00"_time_t, {176.34F, 59.64F}}, + {NEW_YORK, "2024-06-29T12:00:00"_time_t, {278.04F, 27.34F}}, + {SYNDEY, "2024-06-29T12:00:00"_time_t, {106.13F, -63.29F}}, }), position, timeOfYear, expSunPos) { @@ -38,8 +39,8 @@ BOOST_DATA_TEST_CASE(sun_position, BOOST_CHECK_CLOSE(sunPos.y, expSunPos.y, 1.F); } -BOOST_DATA_TEST_CASE(sun_direction, - boost::unit_test::data::make<sunDirTestData>({ +BOOST_DATA_TEST_CASE(SunDirection, + boost::unit_test::data::make<SunDirTestData>({ {{0.F, 0.F}, south, 0.314F, 0.0087F}, {{90.F, 0.F}, west, 0.314F, 0.0087F}, {{-90.F, 0.F}, east, 0.314F, 0.0087F}, @@ -55,9 +56,9 @@ BOOST_DATA_TEST_CASE(sun_direction, }), position, direction, amb, dir) { - const LightDirection ld {position * degreesToRads}; - BOOST_CHECK_CLOSE_VEC(ld.vector(), direction); - BOOST_CHECK_CLOSE(glm::length(ld.vector()), 1.F, 1); - BOOST_CHECK_CLOSE(ld.ambient(), amb, 5); - BOOST_CHECK_CLOSE(ld.directional(), dir, 5); + const LightDirection lightDir {position * degreesToRads}; + BOOST_CHECK_CLOSE_VEC(lightDir.vector(), direction); + BOOST_CHECK_CLOSE(glm::length(lightDir.vector()), 1.F, 1); + BOOST_CHECK_CLOSE(lightDir.ambient(), amb, 5); + BOOST_CHECK_CLOSE(lightDir.directional(), dir, 5); } diff --git a/test/test-geoData-counts.cpp b/test/test-geoData-counts.cpp index 446a68a..89f9633 100644 --- a/test/test-geoData-counts.cpp +++ b/test/test-geoData-counts.cpp @@ -11,7 +11,7 @@ BOOST_TEST_DONT_PRINT_LOG_VALUE(GeoMutation); BOOST_TEST_DECORATOR(*boost::unit_test::timeout(2)); -BOOST_DATA_TEST_CASE(deformLogical, +BOOST_DATA_TEST_CASE(DeformLogical, boost::unit_test::data::make<Something>({ {"nochange", [](GeoData &) {}, 16, 33, 18}, // No change base case {"simple", diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index e3ef9ad..a697578 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -1,77 +1,82 @@ #define BOOST_TEST_MODULE terrain -#include "game/terrain.h" -#include "test/testMainWindow.h" -#include "test/testRenderOutput.h" #include "testHelpers.h" -#include "ui/applicationBase.h" +#include "testMainWindow.h" +#include "testRenderOutput.h" #include <boost/test/data/test_case.hpp> #include <boost/test/unit_test.hpp> -#include <gfx/gl/sceneRenderer.h> #include <stream_support.h> +#include <game/terrain.h> +#include <gfx/gl/sceneRenderer.h> + #include <game/geoData.h> -class TestTerrainMesh : public GeoData { -public: - TestTerrainMesh() : GeoData {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")} { } -}; +namespace { + class TestTerrainMesh : public GeoData { + public: + TestTerrainMesh() : GeoData {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")} { } + }; -constexpr size_t ncols = 200, nrows = 200, xllcorner = 310000000, yllcorner = 490000000, cellsize = 50000; + constexpr size_t NCOLS = 200, NROWS = 200, XLLCORNER = 310000000, YLLCORNER = 490000000, CELLSIZE = 50000; + const TestTerrainMesh FIXED_TERRTAIN; +} + +BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); BOOST_FIXTURE_TEST_SUITE(ttm, TestTerrainMesh); -BOOST_AUTO_TEST_CASE(loadSuccess) +BOOST_AUTO_TEST_CASE(LoadSuccess) { - BOOST_CHECK_EQUAL(ncols * nrows, n_vertices()); - BOOST_CHECK_EQUAL(2 * (ncols - 1) * (nrows - 1), n_faces()); + BOOST_CHECK_EQUAL(NCOLS * NROWS, n_vertices()); + BOOST_CHECK_EQUAL(2 * (NCOLS - 1) * (NROWS - 1), n_faces()); const auto [lower, upper] = getExtents(); BOOST_CHECK_EQUAL(lower, GlobalPosition3D(310000000, 490000000, -2600)); BOOST_CHECK_EQUAL(upper, GlobalPosition3D(319950000, 499950000, 571600)); } -BOOST_AUTO_TEST_CASE(sanityCheck) +#ifndef NDEBUG +BOOST_AUTO_TEST_CASE(SanityCheck) { BOOST_CHECK_NO_THROW(sanityCheck()); } +#endif BOOST_AUTO_TEST_SUITE_END(); -static const TestTerrainMesh fixedTerrtain; - -BOOST_AUTO_TEST_CASE(locatePointFace) +BOOST_AUTO_TEST_CASE(LocatePointFace) { - const GeoData::PointFace pf {{310002000, 490003000}}; - BOOST_CHECK(!pf.isLocated()); - BOOST_CHECK(pf.face(&fixedTerrtain).is_valid()); - BOOST_CHECK_EQUAL(pf.face(&fixedTerrtain).idx(), 0); + const GeoData::PointFace pointFace {{310002000, 490003000}}; + BOOST_CHECK(!pointFace.isLocated()); + BOOST_CHECK(pointFace.face(&FIXED_TERRTAIN).is_valid()); + BOOST_CHECK_EQUAL(pointFace.face(&FIXED_TERRTAIN).idx(), 0); } -BOOST_AUTO_TEST_CASE(preLocatePointFace) +BOOST_AUTO_TEST_CASE(PreLocatePointFace) { - const GeoData::PointFace pf {{310002000, 490003000}, &fixedTerrtain}; - BOOST_CHECK(pf.isLocated()); - BOOST_CHECK_EQUAL(pf.face(&fixedTerrtain).idx(), 0); + const GeoData::PointFace pointFace {{310002000, 490003000}, &FIXED_TERRTAIN}; + BOOST_CHECK(pointFace.isLocated()); + BOOST_CHECK_EQUAL(pointFace.face(&FIXED_TERRTAIN).idx(), 0); } using FindPointData = std::tuple<GlobalPosition2D, int>; // No boundary cases as these can produce different valid results depending on starting point -BOOST_DATA_TEST_CASE(findPointOnTerrain, +BOOST_DATA_TEST_CASE(FindPointOnTerrain, boost::unit_test::data::make<FindPointData>({ - {{0, 0}, -1}, {{xllcorner, 0}, -1}, {{0, yllcorner}, -1}, {{xllcorner + 1, yllcorner + 2}, 0}, - {{xllcorner + (cellsize * (nrows - 1)) - 2, yllcorner + (cellsize * (ncols - 1)) - 1}, 79200}, + {{0, 0}, -1}, {{XLLCORNER, 0}, -1}, {{0, YLLCORNER}, -1}, {{XLLCORNER + 1, YLLCORNER + 2}, 0}, + {{XLLCORNER + (CELLSIZE * (NROWS - 1)) - 2, YLLCORNER + (CELLSIZE * (NCOLS - 1)) - 1}, 79200}, {{315555000, 495556000}, 44400}, // perf test target }) * boost::unit_test::data::make<int>( {0, 1, 2, 3, 4, 5, 6, 10, 100, 150, 200, 1000, 1234, 17439, 79201, 79200, 79199}), - p, fh, start) + point, exp, start) { - BOOST_CHECK_EQUAL(fh, fixedTerrtain.findPoint(p, GeoData::FaceHandle(start)).idx()); + BOOST_CHECK_EQUAL(exp, FIXED_TERRTAIN.findPoint(point, GeoData::FaceHandle(start)).idx()); } using FindPositionData = std::tuple<GlobalPosition2D, GlobalDistance>; -BOOST_DATA_TEST_CASE(findPositionAt, +BOOST_DATA_TEST_CASE(FindPositionAt, boost::unit_test::data::make<FindPositionData>({ // corners {{310000000, 490000000}, 32800}, @@ -86,36 +91,36 @@ BOOST_DATA_TEST_CASE(findPositionAt, // other {{310751000, 490152000}, 58326}, }), - p, h) + point, height) { - BOOST_CHECK_EQUAL(fixedTerrtain.positionAt(p), p || h); + BOOST_CHECK_EQUAL(FIXED_TERRTAIN.positionAt(point), point || height); } using FindRayIntersectData = std::tuple<GlobalPosition3D, Direction3D, GlobalPosition3D>; -BOOST_DATA_TEST_CASE(findRayIntersect, +BOOST_DATA_TEST_CASE(FindRayIntersect, boost::unit_test::data::make<FindRayIntersectData>({ {{310000000, 490000000, 50000}, {1, 1, -2}, {310008583, 490008583, 32834}}, {{310000000, 490000000, 50000}, {1, 1, -1}, {310017131, 490017131, 32869}}, }), - p, d, i) + point, direction, intersectionPoint) { - BOOST_CHECK_EQUAL(fixedTerrtain.intersectRay({p, d})->first, i); + BOOST_CHECK_EQUAL(FIXED_TERRTAIN.intersectRay({point, direction})->first, intersectionPoint); } -BOOST_AUTO_TEST_CASE(boundaryWalk) +BOOST_AUTO_TEST_CASE(BoundaryWalk) { size_t count {}; - fixedTerrtain.boundaryWalk([&count](auto heh) { - BOOST_CHECK(fixedTerrtain.is_boundary(heh)); + FIXED_TERRTAIN.boundaryWalk([&count](auto heh) { + BOOST_CHECK(FIXED_TERRTAIN.is_boundary(heh)); count++; }); - BOOST_CHECK_EQUAL(count, 2 * (ncols + nrows - 2)); + BOOST_CHECK_EQUAL(count, 2 * (NCOLS + NROWS - 2)); } using WalkTerrainData = std::tuple<GlobalPosition2D, GlobalPosition2D, std::vector<int>>; -BOOST_DATA_TEST_CASE(walkTerrain, +BOOST_DATA_TEST_CASE(WalkTerrain, boost::unit_test::data::make<WalkTerrainData>({ {{310002000, 490003000}, {310002000, 490003000}, {0}}, {{310003000, 490002000}, {310003000, 490002000}, {1}}, @@ -130,10 +135,10 @@ BOOST_DATA_TEST_CASE(walkTerrain, {{309999000, 490003000}, {310004000, 489997000}, {0, 1}}, {{310004000, 489997000}, {309999000, 490003000}, {1, 0}}, }), - from, to, visits) + fromPoint, toPoint, visits) { std::vector<int> visited; - BOOST_CHECK_NO_THROW(fixedTerrtain.walk(from, to, [&visited](auto step) { + BOOST_CHECK_NO_THROW(FIXED_TERRTAIN.walk(fromPoint, toPoint, [&visited](auto step) { if (!visited.empty()) { BOOST_CHECK_EQUAL(step.previous.idx(), visited.back()); } @@ -142,21 +147,21 @@ BOOST_DATA_TEST_CASE(walkTerrain, BOOST_CHECK_EQUAL_COLLECTIONS(visited.begin(), visited.end(), visits.begin(), visits.end()); } -BOOST_DATA_TEST_CASE(walkTerrainSetsFromFace, +BOOST_DATA_TEST_CASE(WalkTerrainSetsFromFace, boost::unit_test::data::make<WalkTerrainData>({ {{310002000, 490003000}, {310002000, 490003000}, {0}}, {{310003000, 490002000}, {310003000, 490002000}, {1}}, {{310002000, 490003000}, {310003000, 490002000}, {0, 1}}, {{310003000, 490002000}, {310002000, 490003000}, {1, 0}}, }), - from, to, visits) + fromPoint, toPoint, visits) { - GeoData::PointFace pf {from}; - BOOST_CHECK_NO_THROW(fixedTerrtain.walk(pf, to, [](auto) { })); - BOOST_CHECK_EQUAL(pf.face(&fixedTerrtain).idx(), visits.front()); + GeoData::PointFace pointFace {fromPoint}; + BOOST_CHECK_NO_THROW(FIXED_TERRTAIN.walk(pointFace, toPoint, [](auto) { })); + BOOST_CHECK_EQUAL(pointFace.face(&FIXED_TERRTAIN).idx(), visits.front()); } -BOOST_DATA_TEST_CASE(walkTerrainUntil, +BOOST_DATA_TEST_CASE(WalkTerrainUntil, boost::unit_test::data::make<WalkTerrainData>({ {{310002000, 490003000}, {310002000, 490003000}, {0}}, {{310003000, 490002000}, {310003000, 490002000}, {1}}, @@ -166,10 +171,10 @@ BOOST_DATA_TEST_CASE(walkTerrainUntil, {{310202000, 490003000}, {310002000, 490003000}, {8, 7, 6, 5, 4}}, {{310002000, 490003000}, {310002000, 490203000}, {0, 399, 398, 797, 796}}, }), - from, to, visits) + fromPoint, toPoint, visits) { std::vector<int> visited; - BOOST_CHECK_NO_THROW(fixedTerrtain.walkUntil(from, to, [&visited](const auto & step) { + BOOST_CHECK_NO_THROW(FIXED_TERRTAIN.walkUntil(fromPoint, toPoint, [&visited](const auto & step) { visited.emplace_back(step.current.idx()); return visited.size() >= 5; })); @@ -181,7 +186,7 @@ using WalkTerrainCurveData = std::tuple<GlobalPosition2D, GlobalPosition2D, Glob BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1)) -BOOST_DATA_TEST_CASE(walkTerrainCurveSetsFromFace, +BOOST_DATA_TEST_CASE(WalkTerrainCurveSetsFromFace, boost::unit_test::data::make<WalkTerrainCurveData>({ {{310002000, 490003000}, {310002000, 490003000}, {310002000, 490003000}, {0}, {}}, {{310003000, 490002000}, {310003000, 490002000}, {310003000, 490002000}, {1}, {}}, @@ -207,13 +212,13 @@ BOOST_DATA_TEST_CASE(walkTerrainCurveSetsFromFace, {311000000, 490203001}, }}, }), - from, to, centre, visits, exits) + fromPoint, toPoint, centre, visits, exits) { BOOST_REQUIRE_EQUAL(visits.size(), exits.size() + 1); std::vector<int> visited; std::vector<GlobalPosition2D> exited; - BOOST_CHECK_NO_THROW(fixedTerrtain.walk(from, to, centre, [&](const auto & step) { + BOOST_CHECK_NO_THROW(FIXED_TERRTAIN.walk(fromPoint, toPoint, centre, [&](const auto & step) { visited.emplace_back(step.current.idx()); BOOST_REQUIRE(!std::ranges::contains(exited, step.exitPosition)); exited.emplace_back(step.exitPosition); @@ -224,15 +229,15 @@ BOOST_DATA_TEST_CASE(walkTerrainCurveSetsFromFace, using FindEntiesData = std::tuple<GlobalPosition2D, GlobalPosition2D, int>; -BOOST_DATA_TEST_CASE(findEntries, +BOOST_DATA_TEST_CASE(FindEntryHalfEdge, boost::unit_test::data::make<FindEntiesData>({ {{307739360, 494851616}, {314056992, 500079744}, 160667}, {{308597952, 498417056}, {315154144, 504671456}, 233623}, {{302690592, 502270912}, {311585184, 497868064}, 207311}, }), - from, to, heh) + fromPoint, toPoint, heh) { - BOOST_CHECK_EQUAL(fixedTerrtain.findEntry(from, to).idx(), heh); + BOOST_CHECK_EQUAL(FIXED_TERRTAIN.findEntry(fromPoint, toPoint).idx(), heh); } using DeformTerrainData = std::tuple<std::vector<GlobalPosition3D>, @@ -240,17 +245,15 @@ using DeformTerrainData = std::tuple<std::vector<GlobalPosition3D>, BOOST_TEST_DECORATOR(*boost::unit_test::timeout(2)); -BOOST_DATA_TEST_CASE(deform, loadFixtureJson<DeformTerrainData>("geoData/deform/1.json"), points, cams) +BOOST_DATA_TEST_CASE(Deform, loadFixtureJson<DeformTerrainData>("geoData/deform/1.json"), points, cams) { Surface surface; surface.colorBias = RGB {0, 0, 1}; - ApplicationBase ab; - TestMainWindow tmw; TestRenderOutput tro {{640, 480}}; struct TestTerrain : public SceneProvider { - explicit TestTerrain(GeoData gd) : terrain(std::move(gd)) { } + explicit TestTerrain(GeoData geoData) : terrain(std::move(geoData)) { } const Terrain terrain; @@ -261,10 +264,10 @@ BOOST_DATA_TEST_CASE(deform, loadFixtureJson<DeformTerrainData>("geoData/deform/ } void - environment(const SceneShader &, const SceneRenderer & sr) const override + environment(const SceneShader &, const SceneRenderer & renderer) const override { - sr.setAmbientLight({0.1, 0.1, 0.1}); - sr.setDirectionalLight({1, 1, 1}, {{quarter_pi, -3 * half_pi}}, *this); + renderer.setAmbientLight({0.1, 0.1, 0.1}); + renderer.setDirectionalLight({1, 1, 1}, {{quarter_pi, -3 * half_pi}}, *this); } void @@ -279,15 +282,15 @@ BOOST_DATA_TEST_CASE(deform, loadFixtureJson<DeformTerrainData>("geoData/deform/ } }; - TestTerrain t {[&points, &surface]() { - auto gd = GeoData::createFlat({0, 0}, {1000000, 1000000}, 100); - BOOST_CHECK_NO_THROW(gd.setHeights(points, {.surface = &surface})); - return gd; + TestTerrain terrain {[&points, &surface]() { + auto geoData = GeoData::createFlat({0, 0}, {1000000, 1000000}, 100); + BOOST_CHECK_NO_THROW(geoData.setHeights(points, {.surface = &surface})); + return geoData; }()}; - SceneRenderer ss {tro.size, tro.output}; - std::for_each(cams.begin(), cams.end(), [&ss, &t, &tro](const auto & cam) { - ss.camera.setView(cam.first.first, glm::normalize(cam.first.second)); - BOOST_CHECK_NO_THROW(ss.render(t)); + SceneRenderer renderer {tro.size, tro.output}; + std::for_each(cams.begin(), cams.end(), [&renderer, &terrain, &tro](const auto & cam) { + renderer.camera.setView(cam.first.first, glm::normalize(cam.first.second)); + BOOST_CHECK_NO_THROW(renderer.render(terrain)); Texture::save(tro.outImage, cam.second.c_str()); }); } @@ -299,9 +302,9 @@ BOOST_DATA_TEST_CASE( { BOOST_REQUIRE(!points.empty()); Surface surface; - auto gd = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100)); + auto geoData = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100)); for (const auto & strip : points) { BOOST_REQUIRE_GE(strip.size(), 3); - BOOST_CHECK_NO_THROW(gd->setHeights(strip, {.surface = &surface, .nearNodeTolerance = 50})); + BOOST_CHECK_NO_THROW(geoData->setHeights(strip, {.surface = &surface, .nearNodeTolerance = 50})); } } diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp index 332d440..7f82e13 100644 --- a/test/test-glContainer.cpp +++ b/test/test-glContainer.cpp @@ -18,7 +18,7 @@ BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); BOOST_FIXTURE_TEST_SUITE(i, glContainer<int>) -BOOST_AUTO_TEST_CASE(createDestroy, *boost::unit_test::timeout(1)) +BOOST_AUTO_TEST_CASE(CreateDestroy, *boost::unit_test::timeout(1)) { // Unmapped BOOST_CHECK(!data_); @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE(createDestroy, *boost::unit_test::timeout(1)) BOOST_CHECK(!access_); } -BOOST_AUTO_TEST_CASE(mapModes) +BOOST_AUTO_TEST_CASE(MapModes) { BOOST_CHECK_EQUAL(std::accumulate(begin(), end(), 0), 0); BOOST_CHECK(!data_); @@ -78,14 +78,13 @@ BOOST_AUTO_TEST_CASE(mapModes) BOOST_CHECK_EQUAL(access_, GL_READ_WRITE); BOOST_CHECK_NO_THROW(unmap()); - const auto & c = *this; - BOOST_CHECK_EQUAL(std::accumulate(c.begin(), c.end(), 0), 10); + BOOST_CHECK_EQUAL(std::ranges::fold_left(std::as_const(*this), 0, std::plus {}), 10); BOOST_CHECK(data_); BOOST_CHECK_EQUAL(access_, GL_READ_ONLY); BOOST_CHECK_NO_THROW(unmap()); } -BOOST_AUTO_TEST_CASE(push_back_test, *boost::unit_test::timeout(1)) +BOOST_AUTO_TEST_CASE(PushBackTest, *boost::unit_test::timeout(1)) { BOOST_CHECK_EQUAL(capacity_, 1); BOOST_CHECK_EQUAL(size_, 0); @@ -103,7 +102,7 @@ BOOST_AUTO_TEST_CASE(push_back_test, *boost::unit_test::timeout(1)) } } -BOOST_AUTO_TEST_CASE(emplace_back_test, *boost::unit_test::timeout(1)) +BOOST_AUTO_TEST_CASE(EmplaceBackTest, *boost::unit_test::timeout(1)) { BOOST_CHECK_EQUAL(capacity_, 1); BOOST_CHECK_EQUAL(size_, 0); @@ -146,7 +145,7 @@ BOOST_AUTO_TEST_CASE(emplace_back_test, *boost::unit_test::timeout(1)) BOOST_CHECK_EQUAL(7, rend() - rbegin()); } -BOOST_AUTO_TEST_CASE(resize_test) +BOOST_AUTO_TEST_CASE(ResizeTest) { BOOST_CHECK_NO_THROW(push_back(1)); BOOST_CHECK_NO_THROW(emplace_back(2)); @@ -177,7 +176,7 @@ BOOST_AUTO_TEST_CASE(resize_test) BOOST_CHECK_EQUAL(rbegin(), rend()); } -BOOST_AUTO_TEST_CASE(shrink_to_fit_test) +BOOST_AUTO_TEST_CASE(ShrinkToFitTest) { BOOST_CHECK_NO_THROW(reserve(4)); BOOST_CHECK_NO_THROW(emplace_back(1)); @@ -205,7 +204,7 @@ BOOST_AUTO_TEST_CASE(shrink_to_fit_test) BOOST_CHECK_EQUAL(size(), 0); } -BOOST_AUTO_TEST_CASE(getters) +BOOST_AUTO_TEST_CASE(Getters) { BOOST_CHECK(empty()); BOOST_CHECK_NO_THROW(emplace_back(1)); @@ -238,24 +237,24 @@ BOOST_AUTO_TEST_CASE(getters) BOOST_CHECK_THROW(std::ignore = constCont.at(2), std::out_of_range); } -BOOST_AUTO_TEST_CASE(random_access) +BOOST_AUTO_TEST_CASE(RandomAccess) { BOOST_CHECK_NO_THROW(emplace_back(1)); BOOST_CHECK_NO_THROW(emplace_back(2)); BOOST_CHECK_NO_THROW(emplace_back(3)); - auto i = begin(); - BOOST_CHECK_EQUAL(1, *i); - BOOST_CHECK_EQUAL(2, *++i); - BOOST_CHECK_EQUAL(2, *i++); - BOOST_CHECK_EQUAL(3, *i); - BOOST_CHECK_EQUAL(3, *i--); - BOOST_CHECK_EQUAL(2, *i); - BOOST_CHECK_EQUAL(1, *--i); - BOOST_CHECK_EQUAL(1, *i); + auto iterator = begin(); + BOOST_CHECK_EQUAL(1, *iterator); + BOOST_CHECK_EQUAL(2, *++iterator); + BOOST_CHECK_EQUAL(2, *iterator++); + BOOST_CHECK_EQUAL(3, *iterator); + BOOST_CHECK_EQUAL(3, *iterator--); + BOOST_CHECK_EQUAL(2, *iterator); + BOOST_CHECK_EQUAL(1, *--iterator); + BOOST_CHECK_EQUAL(1, *iterator); } -BOOST_AUTO_TEST_CASE(random_write) +BOOST_AUTO_TEST_CASE(RandomWrite) { BOOST_CHECK_NO_THROW(resize(3)); BOOST_CHECK_EQUAL(size(), 3); @@ -278,7 +277,7 @@ BOOST_AUTO_TEST_CASE(random_write) BOOST_CHECK_THROW(at(4, 0), std::out_of_range); } -BOOST_AUTO_TEST_CASE(insert_remove_test) +BOOST_AUTO_TEST_CASE(InsertRemoveTest) { BOOST_CHECK_NO_THROW(emplace_back(1)); BOOST_CHECK_NO_THROW(emplace_back(2)); @@ -323,19 +322,19 @@ BOOST_AUTO_TEST_CASE(insert_remove_test) } } -BOOST_AUTO_TEST_CASE(stl) +BOOST_AUTO_TEST_CASE(StlCompatilibty) { BOOST_CHECK_NO_THROW(resize(10)); - BOOST_CHECK_NO_THROW(std::generate(begin(), end(), [x = 0]() mutable { - return x++; + BOOST_CHECK_NO_THROW(std::generate(begin(), end(), [value = 0]() mutable { + return value++; })); BOOST_CHECK_NO_THROW(std::sort(rbegin(), rend())); { std::array expected1 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); } - const auto newend = std::remove_if(begin(), end(), [](const auto & x) { - return x % 2 == 0; + const auto newend = std::remove_if(begin(), end(), [](const auto & value) { + return value % 2 == 0; }); { std::array expected1 {9, 7, 5, 3, 1}; @@ -343,7 +342,7 @@ BOOST_AUTO_TEST_CASE(stl) } } -BOOST_AUTO_TEST_CASE(iter_compare) +BOOST_AUTO_TEST_CASE(IterCompare) { BOOST_CHECK_EQUAL(begin(), end()); BOOST_CHECK_EQUAL(rbegin(), rend()); @@ -356,7 +355,7 @@ BOOST_AUTO_TEST_CASE(iter_compare) BOOST_AUTO_TEST_SUITE_END(); -BOOST_AUTO_TEST_CASE(create_copy_source, *boost::unit_test::timeout(1)) +BOOST_AUTO_TEST_CASE(CreateCopySource, *boost::unit_test::timeout(1)) { const std::vector src {4, 6, 2, 4, 6, 0}; glContainer dst {src}; @@ -365,16 +364,36 @@ BOOST_AUTO_TEST_CASE(create_copy_source, *boost::unit_test::timeout(1)) BOOST_CHECK_EQUAL_COLLECTIONS(src.begin(), src.end(), dst.begin(), dst.end()); } -struct C { - int x; - float y; -}; +namespace { + struct C { + int x; + float y; + }; -static_assert(std::is_trivially_destructible_v<C>); + static_assert(std::is_trivially_destructible_v<C>); + + struct CC { + CC() = default; + + CC(int intValue, float floatValue) noexcept : x {intValue}, y {floatValue} { } + + ~CC() + { + ++x; + } + + DEFAULT_MOVE_COPY(CC); + + int x; + float y; + }; + + static_assert(!std::is_trivially_destructible_v<CC>); +} BOOST_FIXTURE_TEST_SUITE(c, glContainer<C>) -BOOST_AUTO_TEST_CASE(basic) +BOOST_AUTO_TEST_CASE(Basic) { BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); BOOST_CHECK_EQUAL(1, begin()->x); @@ -391,27 +410,9 @@ BOOST_AUTO_TEST_CASE(basic) BOOST_AUTO_TEST_SUITE_END(); -struct CC { - CC() = default; - - CC(int a, float b) noexcept : x {a}, y {b} { } - - ~CC() - { - ++x; - } - - DEFAULT_MOVE_COPY(CC); - - int x; - float y; -}; - -static_assert(!std::is_trivially_destructible_v<CC>); - BOOST_FIXTURE_TEST_SUITE(cc, glContainer<CC>) -BOOST_AUTO_TEST_CASE(basic) +BOOST_AUTO_TEST_CASE(Basic) { BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); BOOST_CHECK_EQUAL(1, begin()->x); @@ -435,7 +436,7 @@ BOOST_AUTO_TEST_CASE(basic) BOOST_CHECK_EQUAL(capacity(), 1); } -BOOST_AUTO_TEST_CASE(insert_remove_test) +BOOST_AUTO_TEST_CASE(InsertRemoveTest) { BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); BOOST_CHECK_NO_THROW(emplace_back(3, 4.F)); diff --git a/test/test-glContextBhvr.cpp b/test/test-glContextBhvr.cpp index ec5cc21..e4b20e1 100644 --- a/test/test-glContextBhvr.cpp +++ b/test/test-glContextBhvr.cpp @@ -11,35 +11,39 @@ BOOST_GLOBAL_FIXTURE(ApplicationBase); #define TEST_WINDOW_PARAMS __FILE__, 0, 0, 640, 480, static_cast<Uint32>(SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN) -static void -CreateProgramTest() -{ - const ProgramRef p; - BOOST_REQUIRE(p); +namespace { + void + createProgramTest() + { + const ProgramRef prog; + BOOST_REQUIRE(prog); + } } -BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour1) +BOOST_AUTO_TEST_CASE(WindowContextThingsBehaviour1) { BOOST_REQUIRE(!glCreateProgram); // Init not called yet { const SDL_WindowPtr window {TEST_WINDOW_PARAMS}; BOOST_REQUIRE(window); BOOST_REQUIRE(!glCreateProgram); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) BOOST_REQUIRE_EQUAL(gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)), 0); // No context yet { const SDL_GLContextPtr context {window}; BOOST_REQUIRE(context); BOOST_REQUIRE(!glCreateProgram); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) BOOST_REQUIRE_GT(gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)), 0); BOOST_REQUIRE(glCreateProgram); - CreateProgramTest(); + createProgramTest(); } // Context destroyed BOOST_REQUIRE(glCreateProgram); // Functions still set - BOOST_REQUIRE_THROW({ const ProgramRef p; }, std::exception); // Get fails with no context + BOOST_REQUIRE_THROW({ const ProgramRef prog; }, std::exception); // Get fails with no context { const SDL_GLContextPtr context {window}; BOOST_REQUIRE(context); - CreateProgramTest(); + createProgramTest(); } } { @@ -47,11 +51,11 @@ BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour1) BOOST_REQUIRE(window); const SDL_GLContextPtr context {window}; BOOST_REQUIRE(context); - CreateProgramTest(); + createProgramTest(); } } -BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour2) +BOOST_AUTO_TEST_CASE(WindowContextThingsBehaviour2) { const SDL_WindowPtr window1 {TEST_WINDOW_PARAMS}; BOOST_REQUIRE(window1); @@ -60,12 +64,12 @@ BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour2) BOOST_REQUIRE(window2); const SDL_GLContextPtr context {window2}; BOOST_REQUIRE(context); - CreateProgramTest(); + createProgramTest(); } - BOOST_REQUIRE_THROW({ const ProgramRef p; }, std::exception); // Get fails with no context + BOOST_REQUIRE_THROW({ const ProgramRef prog; }, std::exception); // Get fails with no context } -BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour3) +BOOST_AUTO_TEST_CASE(WindowContextThingsBehaviour3) { std::optional<SDL_WindowPtr> window1 {std::in_place, TEST_WINDOW_PARAMS}; const std::optional<SDL_WindowPtr> window2 {std::in_place, TEST_WINDOW_PARAMS}; @@ -73,11 +77,11 @@ BOOST_AUTO_TEST_CASE(windowContextThingsBehaviour3) BOOST_REQUIRE(window1.value()); const SDL_GLContextPtr context {window1.value()}; BOOST_REQUIRE(context); - CreateProgramTest(); + createProgramTest(); window1.reset(); - BOOST_REQUIRE_THROW({ const ProgramRef p; }, std::exception); // Get fails with context's window gone + BOOST_REQUIRE_THROW({ const ProgramRef prog; }, std::exception); // Get fails with context's window gone window1.emplace(TEST_WINDOW_PARAMS); BOOST_REQUIRE(window1); BOOST_REQUIRE(window1.value()); - BOOST_REQUIRE_THROW({ const ProgramRef p; }, std::exception); // Get still fails with context's window gone + BOOST_REQUIRE_THROW({ const ProgramRef prog; }, std::exception); // Get still fails with context's window gone } diff --git a/test/test-instancing.cpp b/test/test-instancing.cpp index 1dd4cea..21d78e1 100644 --- a/test/test-instancing.cpp +++ b/test/test-instancing.cpp @@ -11,36 +11,38 @@ BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -struct TestInstanceVertices : public InstanceVertices<int> { - void - checkReverseIndex(std::source_location ctx = std::source_location::current()) - { - BOOST_TEST_CONTEXT(ctx.function_name() << ":" << ctx.line()) { - std::vector<size_t> genIndexIndex(size(), npos); - for (size_t i {}; i < index.size(); i++) { - if (index[i] != npos) { - BOOST_REQUIRE_EQUAL(genIndexIndex.at(index[i]), npos); - genIndexIndex.at(index[i]) = i; +namespace { + struct TestInstanceVertices : public InstanceVertices<int> { + void + checkReverseIndex(std::source_location ctx = std::source_location::current()) + { + BOOST_TEST_CONTEXT(ctx.function_name() << ":" << ctx.line()) { + std::vector<size_t> genIndexIndex(size(), npos); + for (size_t i {}; i < index.size(); i++) { + if (index[i] != npos) { + BOOST_REQUIRE_EQUAL(genIndexIndex.at(index[i]), npos); + genIndexIndex.at(index[i]) = i; + } } - } - BOOST_TEST_CONTEXT(reverseIndex << genIndexIndex) { - BOOST_CHECK_EQUAL_COLCOL(reverseIndex, genIndexIndex); + BOOST_TEST_CONTEXT(reverseIndex << genIndexIndex) { + BOOST_CHECK_EQUAL_COLCOL(reverseIndex, genIndexIndex); + } } } - } -}; + }; +} BOOST_FIXTURE_TEST_SUITE(i, TestInstanceVertices) -BOOST_AUTO_TEST_CASE(createDestroy) +BOOST_AUTO_TEST_CASE(CreateDestroy) { BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); BOOST_CHECK(reverseIndex.empty()); } -BOOST_AUTO_TEST_CASE(acquireRelease) +BOOST_AUTO_TEST_CASE(AcquireRelease) { { auto proxy = acquire(); @@ -58,7 +60,7 @@ BOOST_AUTO_TEST_CASE(acquireRelease) BOOST_CHECK(reverseIndex.empty()); } -BOOST_AUTO_TEST_CASE(acquireReleaseMove) +BOOST_AUTO_TEST_CASE(AcquireReleaseMove) { { auto proxy1 = acquire(); @@ -75,7 +77,7 @@ BOOST_AUTO_TEST_CASE(acquireReleaseMove) BOOST_CHECK(reverseIndex.empty()); } -BOOST_AUTO_TEST_CASE(autoMapUnmap) +BOOST_AUTO_TEST_CASE(AutoMapUnmap) { { auto proxy = acquire(); @@ -88,7 +90,7 @@ BOOST_AUTO_TEST_CASE(autoMapUnmap) BOOST_CHECK_EQUAL(0, size()); } -BOOST_AUTO_TEST_CASE(initialize) +BOOST_AUTO_TEST_CASE(Initialize) { auto proxy = acquire(5); const auto & constProxy = proxy; @@ -97,20 +99,20 @@ BOOST_AUTO_TEST_CASE(initialize) BOOST_CHECK_EQUAL(constProxy.get(), constProxy.get()); } -BOOST_AUTO_TEST_CASE(resize) +BOOST_AUTO_TEST_CASE(Resize) { constexpr auto COUNT = 500; std::vector<decltype(acquire())> proxies; std::vector<int> expected; - for (auto n = 0; n < COUNT; n++) { - proxies.push_back(acquire(n)); - expected.emplace_back(n); + for (auto value = 0; value < COUNT; value++) { + proxies.push_back(acquire(value)); + expected.emplace_back(value); } BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), data(), data() + COUNT); BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), proxies.begin(), proxies.end()); } -BOOST_AUTO_TEST_CASE(shuffle) +BOOST_AUTO_TEST_CASE(Shuffle) { std::vector<decltype(acquire())> proxies; BOOST_CHECK_EQUAL(0, proxies.emplace_back(acquire(0))); @@ -176,30 +178,33 @@ BOOST_AUTO_TEST_CASE(shuffle) checkReverseIndex(); } -BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) +BOOST_DATA_TEST_CASE(ShuffleRandom, boost::unit_test::data::xrange(0, 10), iteration) { - std::ignore = x; + std::ignore = iteration; std::mt19937 gen(std::random_device {}()); std::map<int, InstanceVertices<int>::InstanceProxy> proxies; - const std::string_view actions = "aaaaaaaarararrraarrrararararaarrrarararararararararraarrrraaaarararaararar"; - int n {}; - for (const auto action : actions) { + static constexpr std::string_view ACTIONS + = "aaaaaaaarararrraarrrararararaarrrarararararararararraarrrraaaarararaararar"; + int count {}; + for (const auto action : ACTIONS) { switch (action) { + default: + std::unreachable(); case 'a': - BOOST_REQUIRE_EQUAL(n, proxies.emplace(n, acquire(n)).first->second); - n++; + BOOST_REQUIRE_EQUAL(count, proxies.emplace(count, acquire(count)).first->second); + count++; break; case 'r': BOOST_REQUIRE(!proxies.empty()); - auto e = std::next(proxies.begin(), + auto toErase = std::next(proxies.begin(), std::uniform_int_distribution<> {0, static_cast<int>(proxies.size() - 1)}(gen)); - proxies.erase(e); + proxies.erase(toErase); break; } BOOST_REQUIRE_EQUAL(size(), proxies.size()); - for (const auto & [n, p] : proxies) { - BOOST_REQUIRE_EQUAL(n, p); + for (const auto & [value, proxy] : proxies) { + BOOST_REQUIRE_EQUAL(value, proxy); } std::set<size_t> iused; for (size_t i {}; i < index.size(); i++) { @@ -219,25 +224,25 @@ BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) } } -BOOST_AUTO_TEST_CASE(partition_by, *boost::unit_test::timeout(1)) +BOOST_AUTO_TEST_CASE(PartitionBy, *boost::unit_test::timeout(1)) { std::mt19937 gen(std::random_device {}()); std::uniform_int_distribution<int> dist(0, 100000); - static constexpr auto N = 1000; - reserve(N); + static constexpr auto COUNT = 1000; + reserve(COUNT); std::vector<decltype(acquire(0))> instances; - instances.reserve(N); + instances.reserve(COUNT); // At least one of each instances.push_back(acquire(1)); instances.push_back(acquire(3)); - while (instances.size() < N) { + while (instances.size() < COUNT) { instances.push_back(acquire(dist(gen))); } const std::vector<int> values(instances.begin(), instances.end()); - BOOST_REQUIRE_EQUAL(size(), N); + BOOST_REQUIRE_EQUAL(size(), COUNT); - const auto pred = [](auto x) { - return (x % 3) == 0; + const auto pred = [](auto value) { + return (value % 3) == 0; }; auto matchedEnd = partition(pred); // The underlying data is partitioned... diff --git a/test/test-lib.cpp b/test/test-lib.cpp index ec91f6e..c79ef6e 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -11,44 +11,46 @@ #include <glad/gl.h> #include <set> -std::set<GLuint> active; +namespace { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - tracker + std::set<GLuint> active; -void -generator(GLsizei n, GLuint * out) -{ - static GLuint next {1}; + void + generator(GLsizei count, GLuint * out) + { + static GLuint next {1}; - while (n--) { - active.insert(next); - *out++ = next++; + std::generate_n(out, count, []() { + return *active.emplace(next++).first; + }); } -} -void -deleter(GLsizei n, GLuint * in) -{ - BOOST_CHECK(std::all_of(in, in + n, [](GLuint id) { - return active.erase(id) > 0; - })); -} + void + deleter(GLsizei n, GLuint * input) + { + BOOST_CHECK(std::ranges::all_of(std::span(input, static_cast<size_t>(n)), [](GLuint name) { + return active.erase(name) > 0; + })); + } -using TestArray = glArrays<5, &generator, &deleter>; + using TestArray = glArrays<5, &generator, &deleter>; +} -BOOST_AUTO_TEST_CASE(generate_and_delete) +BOOST_AUTO_TEST_CASE(GenerateAndDelete) { { - const TestArray a; + const TestArray arr; } BOOST_CHECK(active.empty()); } -BOOST_AUTO_TEST_CASE(generate_move_and_delete) +BOOST_AUTO_TEST_CASE(GenerateMoveAndDelete) { { - TestArray a; - BOOST_CHECK_EQUAL(TestArray::size, active.size()); - const TestArray b {std::move(a)}; - BOOST_CHECK_EQUAL(TestArray::size, active.size()); + TestArray arr1; + BOOST_CHECK_EQUAL(TestArray::size(), active.size()); + const TestArray arr2 {std::move(arr1)}; + BOOST_CHECK_EQUAL(TestArray::size(), active.size()); } BOOST_CHECK(active.empty()); } @@ -56,22 +58,22 @@ BOOST_AUTO_TEST_CASE(generate_move_and_delete) constexpr std::array TRIANGLE_STRIP_IN {0, 1, 2, 3, 4, 5}; static_assert(std::distance(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN)) == 4); -BOOST_AUTO_TEST_CASE(triangle_strip_iter) +BOOST_AUTO_TEST_CASE(TriangleStripIter) { constexpr std::array TRIANGLE_STRIP_EXPECTED {0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5}; std::vector<int> out; - std::for_each(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN), [&out](const auto & t) { - const auto [a, b, c] = t; - out.push_back(a); - out.push_back(b); - out.push_back(c); + std::for_each(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN), [&out](const auto & triangle) { + const auto [corner1, corner2, corner3] = triangle; + out.push_back(corner1); + out.push_back(corner2); + out.push_back(corner3); }); BOOST_REQUIRE_EQUAL(out.size(), (TRIANGLE_STRIP_IN.size() - 2) * 3); BOOST_CHECK_EQUAL_COLCOL(out, TRIANGLE_STRIP_EXPECTED); } -BOOST_AUTO_TEST_CASE(triangle_strip_range_adapter) +BOOST_AUTO_TEST_CASE(TriangleStripRangeAdapter) { using TriTuple = std::tuple<int, int, int>; std::vector<TriTuple> outRange; @@ -82,7 +84,7 @@ BOOST_AUTO_TEST_CASE(triangle_strip_range_adapter) using MergeCloseData = std::tuple<std::vector<int>, int, std::vector<int>>; -BOOST_DATA_TEST_CASE(mergeCloseInts, +BOOST_DATA_TEST_CASE(MergeCloseInts, boost::unit_test::data::make<MergeCloseData>({ {{0}, 0, {0}}, {{0, 1}, 0, {0, 1}}, @@ -112,7 +114,7 @@ BOOST_DATA_TEST_CASE(mergeCloseInts, BOOST_CHECK_EQUAL_COLCOL(mutableCollection, expected); } -BOOST_AUTO_TEST_CASE(aabb_from_points) +BOOST_AUTO_TEST_CASE(AABBFromPoints) { const auto aabb = AxisAlignedBoundingBox<GlobalDistance>::fromPoints(std::vector<GlobalPosition3D> { {1, 2, 3}, diff --git a/test/test-maths.cpp b/test/test-maths.cpp index 6f1f030..c181686 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -6,7 +6,6 @@ #include <glm/gtx/transform.hpp> #include <stream_support.h> #include <string_view> -#include <type_traits> #include <game/network/link.h> #include <gfx/camera.h> @@ -15,8 +14,8 @@ #include <triangle.h> #include <tuple> -using vecter_and_angle = std::tuple<glm::vec3, float>; -using angle_pair = std::tuple<float, float>; +using VecterAndAngle = std::tuple<glm::vec3, Angle>; +using AnglePair = std::tuple<Angle, Angle>; // // STANDARD DEFINITIONS // @@ -48,17 +47,17 @@ static_assert(west.x < 0); // Positive rotation on the XY plane (y member, yaw, around the down axis, as would be expected for vehicle or building // on flat land) shall be clockwise, in radians. Cycles shall be considered equal; 0 == 2pi, pi == -pi, 1/4pi == -3/4pi. -BOOST_DATA_TEST_CASE(test_vector_yaw, - boost::unit_test::data::make<vecter_and_angle>( +BOOST_DATA_TEST_CASE(TestVectorYaw, + boost::unit_test::data::make<VecterAndAngle>( {{up, 0}, {north, 0}, {south, pi}, {west, -half_pi}, {east, half_pi}, {north + east, quarter_pi}, {south + east, quarter_pi * 3}, {north + west, -quarter_pi}, {south + west, -quarter_pi * 3}}), - v, a) + vec, exp) { - BOOST_CHECK_CLOSE(vector_yaw(v), a, 1.F); + BOOST_CHECK_CLOSE(vector_yaw(vec), exp, 1.F); } -BOOST_DATA_TEST_CASE(test_angle_normalize, - boost::unit_test::data::make<angle_pair>({ +BOOST_DATA_TEST_CASE(TestAngleNormalize, + boost::unit_test::data::make<AnglePair>({ {0, 0}, {two_pi, 0}, {-two_pi, 0}, @@ -67,17 +66,17 @@ BOOST_DATA_TEST_CASE(test_angle_normalize, {half_pi * 3, -half_pi}, {-half_pi * 3, half_pi}, }), - in, exp) + angle, exp) { - BOOST_CHECK_CLOSE(normalize(in), exp, 1); + BOOST_CHECK_CLOSE(normalize(angle), exp, 1); } // Positive rotation on the YZ plane (x member, pitch, around the east axis relative to its yaw, as would be expected // for a vehicle travelling forward uphill), in radians. Cycles can be considered non-sense as even in the worst/best // cases pitch beyond +/- 1/2pi would be crashing. -BOOST_DATA_TEST_CASE(test_vector_pitch, - boost::unit_test::data::make<vecter_and_angle>({ +BOOST_DATA_TEST_CASE(TestVectorPitch, + boost::unit_test::data::make<VecterAndAngle>({ {north, 0}, {east, 0}, {south, 0}, @@ -93,9 +92,9 @@ BOOST_DATA_TEST_CASE(test_vector_pitch, {north + west - up, -quarter_pi}, {north + west + up, quarter_pi}, }), - v, a) + vec, exp) { - BOOST_CHECK_CLOSE(vector_pitch(v), a, 1.F); + BOOST_CHECK_CLOSE(vector_pitch(vec), exp, 1.F); } // Positive rotation on the ZX plane (z member, roll, around Y axis relative to its yaw and pitch, as would be expected @@ -104,26 +103,26 @@ BOOST_DATA_TEST_CASE(test_vector_pitch, // The ILT functions rotate_yaw, rotate_pitch and rotate_roll provide a simple equivelent to glm::rotate around the // stated axis. -const auto angs = boost::unit_test::data::make({pi, half_pi, two_pi, quarter_pi, -pi, -half_pi, -quarter_pi, 0.F}) +const auto ANGLES = boost::unit_test::data::make({pi, half_pi, two_pi, quarter_pi, -pi, -half_pi, -quarter_pi, 0.F}) * boost::unit_test::data::make(0); -const auto random_angs = boost::unit_test::data::random(-two_pi, two_pi) ^ boost::unit_test::data::xrange(1000); -const auto rots = boost::unit_test::data::make<std::tuple<glm::vec3, glm::mat4 (*)(float), std::string_view>>({ +const auto RANDOM_ANGLES = boost::unit_test::data::random(-two_pi, two_pi) ^ boost::unit_test::data::xrange(1000); +const auto ROTATIONS = boost::unit_test::data::make<std::tuple<glm::vec3, glm::mat4 (*)(float), std::string_view>>({ {down, rotate_yaw<4>, "yaw"}, {east, rotate_pitch<4>, "pitch"}, {north, rotate_roll<4>, "roll"}, }); -BOOST_DATA_TEST_CASE(test_rotations, (angs + random_angs) * rots, angle, ai, axis, ilt_func, name) +BOOST_DATA_TEST_CASE(TestRotations, (ANGLES + RANDOM_ANGLES) * ROTATIONS, angle, index, axis, iltFunc, name) { - (void)ai; + (void)index; BOOST_TEST_CONTEXT(name) { - const auto g {glm::rotate(angle, axis)}, ilt {ilt_func(angle)}; - for (glm::length_t c = 0; c < 4; c++) { - BOOST_TEST_CONTEXT(c) { - for (glm::length_t r = 0; r < 4; r++) { - BOOST_TEST_CONTEXT(r) { - BOOST_CHECK_CLOSE(g[c][r], ilt[c][r], 0.0001); + const auto glmVal {glm::rotate(angle, axis)}, iltVal {iltFunc(angle)}; + for (glm::length_t col = 0; col < 4; col++) { + BOOST_TEST_CONTEXT(col) { + for (glm::length_t row = 0; row < 4; row++) { + BOOST_TEST_CONTEXT(row) { + BOOST_CHECK_CLOSE(glmVal[col][row], iltVal[col][row], 0.0001); } } } @@ -133,10 +132,10 @@ BOOST_DATA_TEST_CASE(test_rotations, (angs + random_angs) * rots, angle, ai, axi // An arc shall be defined as a centre point, start point and end point. The arc shall progress positively from start to // end in a clockwise manner. Arc start shall be the yaw from centre to start, arc end shall be greater than arc start. -using pos3_to_arc = std::tuple<glm::vec3, glm::vec3, glm::vec3, Arc>; +using Pos3ToArc = std::tuple<glm::vec3, glm::vec3, glm::vec3, Arc>; -BOOST_DATA_TEST_CASE(test_create_arc, - boost::unit_test::data::make<pos3_to_arc>({ +BOOST_DATA_TEST_CASE(TestCreateArc, + boost::unit_test::data::make<Pos3ToArc>({ {{0, 0, 0}, north, east, {0, half_pi}}, {{0, 0, 0}, west, east, {-half_pi, half_pi}}, {{0, 0, 0}, south, east, {pi, half_pi * 5}}, @@ -144,194 +143,199 @@ BOOST_DATA_TEST_CASE(test_create_arc, {{0, 0, 0}, south, north, {pi, two_pi}}, {{0, 0, 0}, east, south, {half_pi, pi}}, }), - c, s, e, a) + centre, start, end, expAngles) { - const Arc arc {c, s, e}; + const Arc arc {centre, start, end}; BOOST_REQUIRE_LT(arc.first, arc.second); - BOOST_CHECK_CLOSE(arc.first, a.first, 1.F); - BOOST_CHECK_CLOSE(arc.second, a.second, 1.F); + BOOST_CHECK_CLOSE(arc.first, expAngles.first, 1.F); + BOOST_CHECK_CLOSE(arc.second, expAngles.second, 1.F); } -using fac = std::tuple<glm::vec2, float, glm::vec2, float, glm::vec2, bool>; +using FindArcCentreData = std::tuple<glm::vec2, float, glm::vec2, float, glm::vec2, bool>; -BOOST_DATA_TEST_CASE(test_find_arc_centre, - boost::unit_test::data::make<fac>({ +BOOST_DATA_TEST_CASE(TestFindArcCentre, + boost::unit_test::data::make<FindArcCentreData>({ {{2, 2}, pi, {3, 3}, half_pi, {3, 2}, true}, {{2, 2}, pi, {1, 3}, -half_pi, {1, 2}, false}, {{-1100, -1000}, pi, {-900, -800}, half_pi, {-900, -1000}, true}, {{1100, 1000}, 0, {1050, 900}, pi + 0.92F, {973, 1000}, true}, {{1050, 900}, 0.92F, {1000, 800}, pi, {1127, 800}, false}, }), - s, es, e, ee, exp, lr) + startPoint, startEntryAngle, endPoint, endEntryAngle, expCentre, leftRight) { - const auto c = find_arc_centre(s, es, e, ee); - BOOST_CHECK_CLOSE(exp.x, c.first.x, 1); - BOOST_CHECK_CLOSE(exp.y, c.first.y, 1); - BOOST_CHECK_EQUAL(lr, c.second); + const auto centre = find_arc_centre(startPoint, startEntryAngle, endPoint, endEntryAngle); + BOOST_CHECK_CLOSE(expCentre.x, centre.first.x, 1); + BOOST_CHECK_CLOSE(expCentre.y, centre.first.y, 1); + BOOST_CHECK_EQUAL(leftRight, centre.second); } -BOOST_AUTO_TEST_CASE(test_find_arcs_radius) +BOOST_AUTO_TEST_CASE(TestFindArcsRadius) { BOOST_CHECK_CLOSE( find_arcs_radius(RelativePosition2D {10.32, 26.71}, {0.4, .92}, {2.92, 22.41}, {-0.89, -0.45}), 2.29, 1); } -struct TestLinkStraight : public LinkStraight { - explicit TestLinkStraight(glm::vec3 v) : - Link {{std::make_shared<Node>(GlobalPosition3D {}), vector_yaw(v)}, {std::make_shared<Node>(v), vector_yaw(-v)}, - glm::length(v)} +namespace { + struct TestLinkStraight : public LinkStraight { + explicit TestLinkStraight(glm::vec3 entryVector) : + Link {{.node = std::make_shared<Node>(GlobalPosition3D {}), .dir = vector_yaw(entryVector)}, + {.node = std::make_shared<Node>(entryVector), .dir = vector_yaw(-entryVector)}, + glm::length(entryVector)} + { + } + }; + + using StraightsData = std::tuple<glm::vec3, float /*angFor*/, float /* angBack*/>; + + struct TestLinkCurve : public LinkCurve { + explicit TestLinkCurve(glm::vec3 startPos, glm::vec3 endPos, glm::vec3 ctr) : + Link {{.node = std::make_shared<Node>(startPos), .dir = normalize(vector_yaw(ctr - startPos) - half_pi)}, + {.node = std::make_shared<Node>(endPos), .dir = normalize(vector_yaw(ctr - endPos) - half_pi)}, + glm::length(endPos - startPos)}, + LinkCurve(ctr, glm::length(startPos - ctr), {ctr, startPos, endPos}) + { + } + }; + + using CurvesData + = std::tuple<GlobalPosition3D /*e1*/, GlobalPosition3D /*ctr*/, Angle /*angFor*/, Angle /* angBack*/>; + + template<typename T = float> + auto + nTestPointsBetween(std::size_t n = 2, T min = -100.F, T max = 100.F) { + return boost::unit_test::data::xrange(n) ^ boost::unit_test::data::random(min, max); } -}; - -using StraightsData = std::tuple<glm::vec3, float /*angFor*/, float /* angBack*/>; +} -BOOST_DATA_TEST_CASE(straight1, +BOOST_DATA_TEST_CASE(Straight1, boost::unit_test::data::make<StraightsData>({ {north, 0, pi}, {south, pi, 0}, {east, half_pi, -half_pi}, {west, -half_pi, half_pi}, }), - v, angFor, angBack) + direction, angFor, angBack) { - const TestLinkStraight l(v); + const TestLinkStraight link(direction); { - const auto p = l.positionAt(0, 0); - BOOST_CHECK_EQUAL(p.pos, GlobalPosition3D {}); - BOOST_CHECK_EQUAL(p.rot, glm::vec3(0, angFor, 0)); + const auto position = link.positionAt(0, 0); + BOOST_CHECK_EQUAL(position.pos, GlobalPosition3D {}); + BOOST_CHECK_EQUAL(position.rot, glm::vec3(0, angFor, 0)); } { - const auto p = l.positionAt(0, 1); - BOOST_CHECK_EQUAL(p.pos, GlobalPosition3D {v}); - BOOST_CHECK_EQUAL(p.rot, glm::vec3(0, angBack, 0)); + const auto position = link.positionAt(0, 1); + BOOST_CHECK_EQUAL(position.pos, GlobalPosition3D {direction}); + BOOST_CHECK_EQUAL(position.rot, glm::vec3(0, angBack, 0)); } } -struct TestLinkCurve : public LinkCurve { - explicit TestLinkCurve(glm::vec3 e0, glm::vec3 e1, glm::vec3 ctr) : - Link {{std::make_shared<Node>(e0), normalize(vector_yaw(ctr - e0) - half_pi)}, - {std::make_shared<Node>(e1), normalize(vector_yaw(ctr - e1) - half_pi)}, glm::length(e1 - e0)}, - LinkCurve(ctr, glm::length(e0 - ctr), {ctr, e0, e1}) - { - } -}; - -using CurvesData = std::tuple<GlobalPosition3D /*e1*/, GlobalPosition3D /*ctr*/, Angle /*angFor*/, Angle /* angBack*/>; - -BOOST_DATA_TEST_CASE(curve1, +BOOST_DATA_TEST_CASE(Curve1, boost::unit_test::data::make<CurvesData>({ {north + east, east, 0, -half_pi}, {east * 2.F, east, 0, 0}, {south + east, east, 0, half_pi}, {south + west, west, pi, half_pi}, }), - e1, ctr, angFor, angBack) + endPos, ctr, angFor, angBack) { { // One-way... - const TestLinkCurve l({}, e1, ctr); - BOOST_CHECK_EQUAL(l.radius, 1.F); + const TestLinkCurve link({}, endPos, ctr); + BOOST_CHECK_EQUAL(link.radius, 1.F); { - const auto p = l.positionAt(0, 0); - BOOST_CHECK_CLOSE_VECI(p.pos, GlobalPosition3D {}); - BOOST_CHECK_CLOSE_VEC(p.rot, glm::vec3(0, angFor, 0)); + const auto position = link.positionAt(0, 0); + BOOST_CHECK_CLOSE_VECI(position.pos, GlobalPosition3D {}); + BOOST_CHECK_CLOSE_VEC(position.rot, glm::vec3(0, angFor, 0)); } { - const auto p = l.positionAt(0, 1); - BOOST_CHECK_CLOSE_VECI(p.pos, e1); - BOOST_CHECK_CLOSE_VEC(p.rot, glm::vec3(0, angBack, 0)); + const auto position = link.positionAt(0, 1); + BOOST_CHECK_CLOSE_VECI(position.pos, endPos); + BOOST_CHECK_CLOSE_VEC(position.rot, glm::vec3(0, angBack, 0)); } } { // The other way... - const TestLinkCurve l(e1, {}, ctr); - BOOST_CHECK_EQUAL(l.radius, 1.F); + const TestLinkCurve link(endPos, {}, ctr); + BOOST_CHECK_EQUAL(link.radius, 1.F); { - const auto p = l.positionAt(0, 0); - const auto angForReversed = normalize(vector_yaw(difference({}, e1)) * 2 - angFor); - BOOST_CHECK_CLOSE_VECI(p.pos, e1); - BOOST_CHECK_CLOSE_VEC(p.rot, glm::vec3(0, angForReversed, 0)); + const auto position = link.positionAt(0, 0); + const auto angForReversed = normalize(vector_yaw(difference({}, endPos)) * 2 - angFor); + BOOST_CHECK_CLOSE_VECI(position.pos, endPos); + BOOST_CHECK_CLOSE_VEC(position.rot, glm::vec3(0, angForReversed, 0)); } { - const auto p = l.positionAt(0, 1); - const auto angBackReversed = normalize(vector_yaw(difference(e1, {})) * 2 - angBack); - BOOST_CHECK_CLOSE_VECI(p.pos, GlobalPosition3D {}); - BOOST_CHECK_CLOSE_VEC(p.rot, glm::vec3(0, angBackReversed, 0)); + const auto position = link.positionAt(0, 1); + const auto angBackReversed = normalize(vector_yaw(difference(endPos, {})) * 2 - angBack); + BOOST_CHECK_CLOSE_VECI(position.pos, GlobalPosition3D {}); + BOOST_CHECK_CLOSE_VEC(position.rot, glm::vec3(0, angBackReversed, 0)); } } } -BOOST_AUTO_TEST_CASE(camera_clicks) +BOOST_AUTO_TEST_CASE(CameraClicks) { Camera camera {{}, ::half_pi, 1.25F, 1000, 10000000}; - constexpr float centre {0.5F}, right {0.9F}, left {0.1F}, top {1.F}, bottom {0.F}; + constexpr float CENTRE {0.5F}, RIGHT {0.9F}, LEFT {0.1F}, TOP {1.F}, BOTTOM {0.F}; camera.setForward(::north); - BOOST_CHECK_EQUAL(camera.unProject({centre, centre}).start, GlobalPosition3D {}); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}).direction, ::north); - BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}).direction, glm::normalize(::north + ::west)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}).direction, glm::normalize(::north + ::east)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}).direction, glm::normalize(::north + ::up)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, bottom}).direction, glm::normalize(::north + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({left, top}).direction, glm::normalize(::north + ::west + ::up)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({right, top}).direction, glm::normalize(::north + ::east + ::up)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({left, bottom}).direction, glm::normalize(::north + ::west + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({right, bottom}).direction, glm::normalize(::north + ::east + ::down)); + BOOST_CHECK_EQUAL(camera.unProject({CENTRE, CENTRE}).start, GlobalPosition3D {}); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, CENTRE}).direction, ::north); + BOOST_CHECK_CLOSE_VEC(camera.unProject({LEFT, CENTRE}).direction, glm::normalize(::north + ::west)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({RIGHT, CENTRE}).direction, glm::normalize(::north + ::east)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, TOP}).direction, glm::normalize(::north + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, BOTTOM}).direction, glm::normalize(::north + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({LEFT, TOP}).direction, glm::normalize(::north + ::west + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({RIGHT, TOP}).direction, glm::normalize(::north + ::east + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({LEFT, BOTTOM}).direction, glm::normalize(::north + ::west + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({RIGHT, BOTTOM}).direction, glm::normalize(::north + ::east + ::down)); camera.setForward(::east); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}).direction, ::east); - BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}).direction, glm::normalize(::north + ::east)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}).direction, glm::normalize(::south + ::east)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, CENTRE}).direction, ::east); + BOOST_CHECK_CLOSE_VEC(camera.unProject({LEFT, CENTRE}).direction, glm::normalize(::north + ::east)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({RIGHT, CENTRE}).direction, glm::normalize(::south + ::east)); camera.setForward(glm::normalize(::north + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}).direction, glm::normalize(::north + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}).direction, glm::normalize(::north)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, CENTRE}).direction, glm::normalize(::north + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, TOP}).direction, glm::normalize(::north)); camera.setForward(glm::normalize(::north + ::west + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}).direction, glm::normalize(::north + ::west + ::down)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}).direction, glm::normalize(::north + ::west + ::up * 0.2F)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, CENTRE}).direction, glm::normalize(::north + ::west + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, TOP}).direction, glm::normalize(::north + ::west + ::up * 0.2F)); camera.setForward(glm::normalize(::north + ::west)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}).direction, glm::normalize(::north + ::west)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}).direction, glm::normalize(::north + ::west + ::up * 1.2F)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}).direction, glm::normalize(::north)); - BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}).direction, glm::normalize(::west)); -} - -template<typename T = float> -auto -n_test_points_between(std::size_t n = 2, T min = -100.F, T max = 100.F) -{ - return boost::unit_test::data::xrange(n) ^ boost::unit_test::data::random(min, max); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, CENTRE}).direction, glm::normalize(::north + ::west)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({CENTRE, TOP}).direction, glm::normalize(::north + ::west + ::up * 1.2F)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({RIGHT, CENTRE}).direction, glm::normalize(::north)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({LEFT, CENTRE}).direction, glm::normalize(::west)); } -BOOST_DATA_TEST_CASE(rayLineDistance, - n_test_points_between() * // n1x - n_test_points_between() * // n1y - n_test_points_between() * // n1z - n_test_points_between() * // n2x - n_test_points_between() * // n2y - n_test_points_between() * // n2z - n_test_points_between() * // cx - n_test_points_between() * // cy - n_test_points_between(), // cz - i1, n1x, i2, n1y, i3, n1z, i4, n2x, i5, n2y, i6, n2z, i7, cx, i8, cy, i9, cz) +BOOST_DATA_TEST_CASE(RayLineDistance, + nTestPointsBetween() * // n1x + nTestPointsBetween() * // n1y + nTestPointsBetween() * // n1z + nTestPointsBetween() * // n2x + nTestPointsBetween() * // n2y + nTestPointsBetween() * // n2z + nTestPointsBetween() * // cx + nTestPointsBetween() * // cy + nTestPointsBetween(), // cz + idx1, n1x, idx2, n1y, idx3, n1z, idx4, n2x, idx5, n2y, idx6, n2z, idx7, originx, idx8, originy, idx9, originz) { - (void)i1; - (void)i2; - (void)i3; - (void)i4; - (void)i5; - (void)i6; - (void)i7; - (void)i8; - (void)i9; - const glm::vec3 n1 {n1x, n1y, n1z}, n2 {n2x, n2y, n2z}, c {cx, cy, cz}; - - const auto nstep = n2 - n1; + (void)idx1; + (void)idx2; + (void)idx3; + (void)idx4; + (void)idx5; + (void)idx6; + (void)idx7; + (void)idx8; + (void)idx9; + const glm::vec3 node1 {n1x, n1y, n1z}, node2 {n2x, n2y, n2z}, origin {originx, originy, originz}; + + const auto nstep = node2 - node1; for (float along = 0.2F; along <= 0.8F; along += 0.1F) { - const auto target = n1 + (along * nstep); - const auto direction = glm::normalize(target - c); - BOOST_CHECK_LE(Ray<RelativePosition3D>(c, direction).distanceToLine(n1, n2), 0.01F); + const auto target = node1 + (along * nstep); + const auto direction = glm::normalize(target - origin); + BOOST_CHECK_LE(Ray<RelativePosition3D>(origin, direction).distanceToLine(node1, node2), 0.01F); } } @@ -343,44 +347,44 @@ static_assert(linesIntersectAt(GlobalPosition2D {311000100, 491100100}, {3110500 == GlobalPosition2D {311000100, 491100100}); static_assert(!linesIntersectAt(glm::dvec2 {0, 1}, {0, 4}, {1, 8}, {1, 4}).has_value()); -BOOST_AUTO_TEST_CASE(triangle2d_helpers) +BOOST_AUTO_TEST_CASE(Triangle2dHelpers) { - constexpr static Triangle<2, float> t {{0, 0}, {5, 0}, {5, 5}}; + constexpr static Triangle<2, float> TRIANGLE {{0, 0}, {5, 0}, {5, 5}}; - BOOST_CHECK_CLOSE(t.angle(0), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({0, 0}), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(1), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 0}), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(2), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 5}), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angle(0), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({0, 0}), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angle(1), half_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({5, 0}), half_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angle(2), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({5, 5}), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({0, 1}), 0.F, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({0, 1}), 0.F, 0.01F); - BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.area(), 12.5F, 0.01F); } -BOOST_AUTO_TEST_CASE(triangle3d_helpers) +BOOST_AUTO_TEST_CASE(Triangle3dHelpers) { - constexpr static Triangle<3, float> t {{0, 0, 0}, {5, 0, 0}, {5, 5, 0}}; + constexpr static Triangle<3, float> TRIANGLE {{0, 0, 0}, {5, 0, 0}, {5, 5, 0}}; - BOOST_CHECK_EQUAL(t.nnormal(), up); - BOOST_CHECK_CLOSE(t.angle(0), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({0, 0, 0}), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(1), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 0, 0}), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(2), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 5, 0}), quarter_pi, 0.01F); + BOOST_CHECK_EQUAL(TRIANGLE.nnormal(), up); + BOOST_CHECK_CLOSE(TRIANGLE.angle(0), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({0, 0, 0}), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angle(1), half_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({5, 0, 0}), half_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angle(2), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({5, 5, 0}), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({0, 1, 0}), 0.F, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.angleAt({0, 1, 0}), 0.F, 0.01F); - BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); + BOOST_CHECK_CLOSE(TRIANGLE.area(), 12.5F, 0.01F); } using ArcLineIntersectExp = std::pair<GlobalPosition2D, Angle>; using ArcLineIntersectData = std::tuple<GlobalPosition2D, GlobalPosition2D, GlobalPosition2D, GlobalPosition2D, GlobalPosition2D, std::optional<ArcLineIntersectExp>>; -BOOST_DATA_TEST_CASE(arcline_intersection, +BOOST_DATA_TEST_CASE(ArclineIntersection, boost::unit_test::data::make<ArcLineIntersectData>({ {{0, 0}, {0, 100}, {100, 0}, {200, 0}, {0, 200}, std::nullopt}, {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {10, 10}, std::nullopt}, diff --git a/test/test-network.cpp b/test/test-network.cpp index 51fea8b..1c91981 100644 --- a/test/test-network.cpp +++ b/test/test-network.cpp @@ -21,214 +21,210 @@ BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -struct TestLinkS; - -struct TestLink : public virtual Link { - using StraightLink = TestLinkS; - using CurveLink = TestLinkS; -}; - -struct TestLinkS : public TestLink, public LinkStraight { - TestLinkS(NetworkLinkHolder<TestLinkS> & network, const Node::Ptr & a, const Node::Ptr & b) : - TestLinkS {network, a, b, (a->pos - b->pos)} - { - } +namespace { + struct TestLinkS; + + struct TestLink : public virtual Link { + using StraightLink = TestLinkS; + using CurveLink = TestLinkS; + }; + + struct TestLinkS : public TestLink, public LinkStraight { + TestLinkS(NetworkLinkHolder<TestLinkS> & network, const Node::Ptr & nodeA, const Node::Ptr & nodeB) : + TestLinkS {network, nodeA, nodeB, (nodeA->pos - nodeB->pos)} + { + } - TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr a, Node::Ptr b, RelativePosition2D l) : - Link {{std::move(a), 0}, {std::move(b), pi}, glm::length(l)} - { - } + TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr nodeA, Node::Ptr nodeB, RelativePosition2D difference) : + Link {{.node = std::move(nodeA), .dir = 0}, {.node = std::move(nodeB), .dir = pi}, glm::length(difference)} + { + } - struct Vertex { }; + struct Vertex { }; - TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr a, Node::Ptr b, float l) : - Link {{std::move(a), 0}, {std::move(b), pi}, l} - { - } -}; + TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr nodeA, Node::Ptr nodeB, float length) : + Link {{.node = std::move(nodeA), .dir = 0}, {.node = std::move(nodeB), .dir = pi}, length} + { + } + }; -constexpr GlobalPosition3D p000 {0, 0, 500}, p100 {10500, 0, 1000}, p200 {20100, 0, 2000}, p300 {30700, 0, 3000}; -constexpr GlobalPosition3D p110 {10300, 10400, 4000}; + constexpr GlobalPosition3D P000 {0, 0, 500}, P100 {10500, 0, 1000}, P200 {20100, 0, 2000}, P300 {30700, 0, 3000}; + constexpr GlobalPosition3D P110 {10300, 10400, 4000}; +} template<> NetworkLinkHolder<TestLinkS>::NetworkLinkHolder() = default; -struct TestNetwork : public NetworkOf<TestLink, TestLinkS> { - TestNetwork() : NetworkOf<TestLink, TestLinkS> {RESDIR "rails.jpg"} - { - // 0 1 2 - // p000 <-> p100 <-> p200 <-> p300 - // \ | / - // \ 5 / - // 3 | 4 - // \-> p110 <-/ - addLink<TestLinkS>(p000, p100, 1.F); - addLink<TestLinkS>(p100, p200, 1.F); - addLink<TestLinkS>(p200, p300, 1.F); - addLink<TestLinkS>(p000, p110, 2.F); - addLink<TestLinkS>(p200, p110, 2.F); - addLink<TestLinkS>(p100, p110, 1.F); - } +namespace { + struct TestNetwork : public NetworkOf<TestLink, TestLinkS> { + TestNetwork() : NetworkOf<TestLink, TestLinkS> {RESDIR "rails.jpg"} + { + // 0 1 2 + // p000 <-> p100 <-> p200 <-> p300 + // \ | / + // \ 5 / + // 3 | 4 + // \-> p110 <-/ + addLink<TestLinkS>(P000, P100, 1.F); + addLink<TestLinkS>(P100, P200, 1.F); + addLink<TestLinkS>(P200, P300, 1.F); + addLink<TestLinkS>(P000, P110, 2.F); + addLink<TestLinkS>(P200, P110, 2.F); + addLink<TestLinkS>(P100, P110, 1.F); + } - void - render(const SceneShader &, const Frustum &) const override - { - } + void + render(const SceneShader &, const Frustum &) const override + { + } - const Surface * - getBaseSurface() const override - { - return nullptr; - } + const Surface * + getBaseSurface() const override + { + return nullptr; + } - RelativeDistance - getBaseWidth() const override - { - return 5'700; - } -}; - -const auto VALID_NODES = boost::unit_test::data::make<GlobalPosition3D>({ - p000, - p100, - p200, - p300, -}); -const auto INVALID_NODES = boost::unit_test::data::make<GlobalPosition3D>({ - {1000, 0, 0}, - {0, 1000, 0}, - {0, 0, 1000}, -}); + RelativeDistance + getBaseWidth() const override + { + return 5'700; + } + }; + + constexpr auto VALID_NODES = std::array<GlobalPosition3D, 4>({ + P000, + P100, + P200, + P300, + }); + constexpr auto INVALID_NODES = std::array<GlobalPosition3D, 3>({ + {1000, 0, 0}, + {0, 1000, 0}, + {0, 0, 1000}, + }); +} BOOST_FIXTURE_TEST_SUITE(tn, TestNetwork) -BOOST_DATA_TEST_CASE(findNodeAt_valid, VALID_NODES, p) +BOOST_DATA_TEST_CASE(FindNodeAtValid, VALID_NODES, point) { - auto n = findNodeAt(p); - BOOST_REQUIRE(n); - BOOST_CHECK_EQUAL(n->pos, p); + auto node = findNodeAt(point); + BOOST_REQUIRE(node); + BOOST_CHECK_EQUAL(node->pos, point); } -BOOST_DATA_TEST_CASE(findNodeAt_invalid, INVALID_NODES, p) +BOOST_DATA_TEST_CASE(FindNodeAtInvalid, INVALID_NODES, point) { - BOOST_REQUIRE(!findNodeAt(p)); + BOOST_REQUIRE(!findNodeAt(point)); } -BOOST_DATA_TEST_CASE(nodeAt, VALID_NODES + INVALID_NODES, p) +BOOST_DATA_TEST_CASE(NodeAt, VALID_NODES + INVALID_NODES, point) { - auto n = nodeAt(p); - BOOST_REQUIRE(n); - BOOST_CHECK_EQUAL(n->pos, p); + auto node = nodeAt(point); + BOOST_REQUIRE(node); + BOOST_CHECK_EQUAL(node->pos, point); } -BOOST_DATA_TEST_CASE(newNodeAt_existing, VALID_NODES, p) +BOOST_DATA_TEST_CASE(NewNodeAtExisting, VALID_NODES, point) { - auto n = newNodeAt(p); - BOOST_CHECK_EQUAL(n.second, Network::NodeIs::InNetwork); - BOOST_REQUIRE(n.first); - BOOST_CHECK_EQUAL(n.first->pos, p); + auto node = newNodeAt(point); + BOOST_CHECK_EQUAL(node.second, Network::NodeIs::InNetwork); + BOOST_REQUIRE(node.first); + BOOST_CHECK_EQUAL(node.first->pos, point); } -BOOST_DATA_TEST_CASE(newNodeAt_new, INVALID_NODES, p) +BOOST_DATA_TEST_CASE(NewNodeAtNew, INVALID_NODES, point) { - auto n = newNodeAt(p); - BOOST_CHECK_EQUAL(n.second, Network::NodeIs::NotInNetwork); - BOOST_REQUIRE(n.first); - BOOST_CHECK_EQUAL(n.first->pos, p); + auto node = newNodeAt(point); + BOOST_CHECK_EQUAL(node.second, Network::NodeIs::NotInNetwork); + BOOST_REQUIRE(node.first); + BOOST_CHECK_EQUAL(node.first->pos, point); } -BOOST_AUTO_TEST_CASE(network_joins) +BOOST_AUTO_TEST_CASE(NetworkJoins) { // Ends - BOOST_CHECK(links.objects[2]->ends[1].nexts.empty()); + BOOST_CHECK(links[2]->ends[1].nexts.empty()); // Join 0 <-> 1 - BOOST_REQUIRE_EQUAL(links.objects[0]->ends[1].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].second, 0); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].first.lock().get(), links.objects[5].get()); - BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].second, 0); - BOOST_REQUIRE_EQUAL(links.objects[1]->ends[0].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].first.lock().get(), links.objects[0].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].second, 1); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].first.lock().get(), links.objects[5].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].second, 0); + BOOST_REQUIRE_EQUAL(links[0]->ends[1].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[0].second, 0); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[1].first.lock().get(), links[5].get()); + BOOST_CHECK_EQUAL(links[0]->ends[1].nexts[1].second, 0); + BOOST_REQUIRE_EQUAL(links[1]->ends[0].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[0].first.lock().get(), links[0].get()); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[0].second, 1); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[1].first.lock().get(), links[5].get()); + BOOST_CHECK_EQUAL(links[1]->ends[0].nexts[1].second, 0); // Join 1 <-> 2 - BOOST_REQUIRE_EQUAL(links.objects[1]->ends[1].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].first.lock().get(), links.objects[2].get()); - BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].second, 0); - BOOST_REQUIRE_EQUAL(links.objects[2]->ends[0].nexts.size(), 2); - BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].second, 1); + BOOST_REQUIRE_EQUAL(links[1]->ends[1].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[1]->ends[1].nexts[0].first.lock().get(), links[2].get()); + BOOST_CHECK_EQUAL(links[1]->ends[1].nexts[0].second, 0); + BOOST_REQUIRE_EQUAL(links[2]->ends[0].nexts.size(), 2); + BOOST_CHECK_EQUAL(links[2]->ends[0].nexts[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(links[2]->ends[0].nexts[0].second, 1); } -BOOST_DATA_TEST_CASE(routeTo_nodeNotInNetwork, INVALID_NODES, dest) +BOOST_DATA_TEST_CASE(RouteToNodeNotInNetwork, INVALID_NODES, dest) { - const auto & start = links.objects.front()->ends[1]; + const auto & start = links[0]->ends[1]; BOOST_CHECK_THROW((void)routeFromTo(start, dest), std::out_of_range); } -BOOST_AUTO_TEST_CASE(routeTo_noSteps) +BOOST_AUTO_TEST_CASE(RouteToNoSteps) { - const auto & start = links.objects.front()->ends[1]; - auto r = this->routeFromTo(start, p100); - BOOST_CHECK(r.empty()); + const auto & start = links[0]->ends[1]; + auto route = this->routeFromTo(start, P100); + BOOST_CHECK(route.empty()); } -BOOST_AUTO_TEST_CASE(routeTo_upStream_to2) +BOOST_AUTO_TEST_CASE(RouteToUpStreamTo2) { - const auto & start = links.objects.front()->ends[1]; - auto r = this->routeFromTo(start, p200); - BOOST_REQUIRE_EQUAL(r.size(), 1); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); + const auto & start = links[0]->ends[1]; + auto route = this->routeFromTo(start, P200); + BOOST_REQUIRE_EQUAL(route.size(), 1); + BOOST_CHECK_EQUAL(route[0].first.lock().get(), links[1].get()); } -BOOST_AUTO_TEST_CASE(routeTo_upStream_to3) +BOOST_AUTO_TEST_CASE(RouteToUpStreamTo3) { - const auto & start = links.objects[0]->ends[1]; - auto r = this->routeFromTo(start, p300); - BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); + const auto & start = links[0]->ends[1]; + auto route = this->routeFromTo(start, P300); + BOOST_REQUIRE_EQUAL(route.size(), 2); + BOOST_CHECK_EQUAL(route[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(route[1].first.lock().get(), links[2].get()); } -BOOST_AUTO_TEST_CASE(routeTo_downStream_to0) +BOOST_AUTO_TEST_CASE(RouteToDownStreamTo0) { - const auto & start = links.objects[2]->ends[0]; - auto r = this->routeFromTo(start, p000); - BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[0].get()); + const auto & start = links[2]->ends[0]; + auto route = this->routeFromTo(start, P000); + BOOST_REQUIRE_EQUAL(route.size(), 2); + BOOST_CHECK_EQUAL(route[0].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(route[1].first.lock().get(), links[0].get()); } -BOOST_AUTO_TEST_CASE(routeTo_upStream_3to300) +BOOST_AUTO_TEST_CASE(RouteToUpStream3to300) { - const auto & start = links.objects[3]->ends[1]; - auto r = this->routeFromTo(start, p300); - BOOST_REQUIRE_EQUAL(r.size(), 2); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[4].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); + const auto & start = links[3]->ends[1]; + auto route = this->routeFromTo(start, P300); + BOOST_REQUIRE_EQUAL(route.size(), 2); + BOOST_CHECK_EQUAL(route[0].first.lock().get(), links[4].get()); + BOOST_CHECK_EQUAL(route[1].first.lock().get(), links[2].get()); } -BOOST_AUTO_TEST_CASE(routeTo_downStream_3to300) +BOOST_AUTO_TEST_CASE(RouteToDownStream3to300) { - const auto & start = links.objects[3]->ends[0]; - auto r = this->routeFromTo(start, p300); - BOOST_REQUIRE_EQUAL(r.size(), 3); - BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[0].get()); - BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[1].get()); - BOOST_CHECK_EQUAL(r[2].first.lock().get(), links.objects[2].get()); + const auto & start = links[3]->ends[0]; + auto route = this->routeFromTo(start, P300); + BOOST_REQUIRE_EQUAL(route.size(), 3); + BOOST_CHECK_EQUAL(route[0].first.lock().get(), links[0].get()); + BOOST_CHECK_EQUAL(route[1].first.lock().get(), links[1].get()); + BOOST_CHECK_EQUAL(route[2].first.lock().get(), links[2].get()); } BOOST_AUTO_TEST_SUITE_END() -namespace std { - std::ostream & - operator<<(std::ostream & s, const Link::End & e) - { - return s << std::format("End[dir: {}, loc: ({}, {}, {})]", e.dir, e.node->pos.x, e.node->pos.y, e.node->pos.z); - } -} - -BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) +BOOST_FIXTURE_TEST_CASE(TestRailNetwork, RailLinks) { // 0 1 2 // --p000 <-> p100 <-> p200 <-> p300 \ x @@ -239,60 +235,60 @@ BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) // \ | \ / // \ / ------/ // -------- - auto l0 = addLinksBetween(p000, p100); - BOOST_CHECK(dynamic_cast<RailLinkStraight *>(l0.get())); - BOOST_CHECK_EQUAL(l0->length, ::distance(p000, p100)); - BOOST_CHECK_CLOSE(l0->ends[0].dir, half_pi, 0.1F); - BOOST_CHECK_CLOSE(l0->ends[1].dir, -half_pi, 0.1F); - BOOST_CHECK(l0->ends[0].nexts.empty()); - BOOST_CHECK(l0->ends[1].nexts.empty()); - - auto l1 = addLinksBetween(p200, p100); - BOOST_CHECK(dynamic_cast<RailLinkStraight *>(l1.get())); - BOOST_CHECK_EQUAL(l1->length, ::distance(p200, p100)); - BOOST_CHECK_CLOSE(l1->ends[0].dir, half_pi, 0.1F); - BOOST_CHECK_CLOSE(l1->ends[1].dir, -half_pi, 0.1F); - BOOST_CHECK(l0->ends[0].nexts.empty()); - BOOST_CHECK_EQUAL(l0->ends[1].nexts.at(0).first.lock(), l1); - BOOST_CHECK_EQUAL(l0->ends[1].nexts.at(0).second, 0); - BOOST_CHECK_EQUAL(l1->ends[0].nexts.at(0).first.lock(), l0); - BOOST_CHECK_EQUAL(l1->ends[0].nexts.at(0).second, 1); - BOOST_CHECK(l1->ends[1].nexts.empty()); - - auto l2 = addLinksBetween(p200, p300); - BOOST_CHECK(dynamic_cast<RailLinkStraight *>(l2.get())); - BOOST_CHECK_EQUAL(l2->length, ::distance(p200, p300)); - BOOST_CHECK_CLOSE(l2->ends[0].dir, half_pi, 0.1F); - BOOST_CHECK_CLOSE(l2->ends[1].dir, -half_pi, 0.1F); - BOOST_CHECK(l0->ends[0].nexts.empty()); - BOOST_CHECK_EQUAL(l1->ends[1].nexts.at(0).first.lock(), l2); - BOOST_CHECK_EQUAL(l1->ends[1].nexts.at(0).second, 0); - BOOST_CHECK_EQUAL(l2->ends[0].nexts.at(0).first.lock(), l1); - BOOST_CHECK_EQUAL(l2->ends[0].nexts.at(0).second, 1); - BOOST_CHECK(l2->ends[1].nexts.empty()); - - BOOST_CHECK_IF(l3, addLinksBetween(p000, p110)) { - BOOST_CHECK_IF(l3c, dynamic_cast<RailLinkCurve *>(l3.get())) { - BOOST_CHECK_CLOSE(l3c->radius, 10'300.F, 0.1F); - BOOST_CHECK_CLOSE(l3c->arc.length(), pi + half_pi, 0.5F); - BOOST_CHECK_CLOSE(l3->length, 48'563.F, 0.1F); - BOOST_CHECK_CLOSE(l3->ends[0].dir, -half_pi, 0.5F); - BOOST_CHECK_CLOSE(l3->ends[1].dir, -0.0097F, 0.5F); - BOOST_CHECK_EQUAL(l0->ends[0].nexts.at(0).first.lock(), l3); - BOOST_CHECK_EQUAL(l0->ends[0].nexts.at(0).second, 0); - BOOST_CHECK_EQUAL(l3->ends[0].nexts.at(0).first.lock(), l0); - BOOST_CHECK_EQUAL(l3->ends[0].nexts.at(0).second, 0); - BOOST_CHECK(l3->ends[1].nexts.empty()); + auto link0 = addLinksBetween(P000, P100); + BOOST_CHECK(dynamic_cast<RailLinkStraight *>(link0.get())); + BOOST_CHECK_EQUAL(link0->length, ::distance(P000, P100)); + BOOST_CHECK_CLOSE(link0->ends[0].dir, half_pi, 0.1F); + BOOST_CHECK_CLOSE(link0->ends[1].dir, -half_pi, 0.1F); + BOOST_CHECK(link0->ends[0].nexts.empty()); + BOOST_CHECK(link0->ends[1].nexts.empty()); + + auto link1 = addLinksBetween(P200, P100); + BOOST_CHECK(dynamic_cast<RailLinkStraight *>(link1.get())); + BOOST_CHECK_EQUAL(link1->length, ::distance(P200, P100)); + BOOST_CHECK_CLOSE(link1->ends[0].dir, half_pi, 0.1F); + BOOST_CHECK_CLOSE(link1->ends[1].dir, -half_pi, 0.1F); + BOOST_CHECK(link0->ends[0].nexts.empty()); + BOOST_CHECK_EQUAL(link0->ends[1].nexts.at(0).first.lock(), link1); + BOOST_CHECK_EQUAL(link0->ends[1].nexts.at(0).second, 0); + BOOST_CHECK_EQUAL(link1->ends[0].nexts.at(0).first.lock(), link0); + BOOST_CHECK_EQUAL(link1->ends[0].nexts.at(0).second, 1); + BOOST_CHECK(link1->ends[1].nexts.empty()); + + auto link2 = addLinksBetween(P200, P300); + BOOST_CHECK(dynamic_cast<RailLinkStraight *>(link2.get())); + BOOST_CHECK_EQUAL(link2->length, ::distance(P200, P300)); + BOOST_CHECK_CLOSE(link2->ends[0].dir, half_pi, 0.1F); + BOOST_CHECK_CLOSE(link2->ends[1].dir, -half_pi, 0.1F); + BOOST_CHECK(link0->ends[0].nexts.empty()); + BOOST_CHECK_EQUAL(link1->ends[1].nexts.at(0).first.lock(), link2); + BOOST_CHECK_EQUAL(link1->ends[1].nexts.at(0).second, 0); + BOOST_CHECK_EQUAL(link2->ends[0].nexts.at(0).first.lock(), link1); + BOOST_CHECK_EQUAL(link2->ends[0].nexts.at(0).second, 1); + BOOST_CHECK(link2->ends[1].nexts.empty()); + + BOOST_CHECK_IF(link3, addLinksBetween(P000, P110)) { + BOOST_CHECK_IF(link3c, dynamic_cast<RailLinkCurve *>(link3.get())) { + BOOST_CHECK_CLOSE(link3c->radius, 10'300.F, 0.1F); + BOOST_CHECK_CLOSE(link3c->arc.length(), pi + half_pi, 0.5F); + BOOST_CHECK_CLOSE(link3->length, 48'563.F, 0.1F); + BOOST_CHECK_CLOSE(link3->ends[0].dir, -half_pi, 0.5F); + BOOST_CHECK_CLOSE(link3->ends[1].dir, -0.0097F, 0.5F); + BOOST_CHECK_EQUAL(link0->ends[0].nexts.at(0).first.lock(), link3); + BOOST_CHECK_EQUAL(link0->ends[0].nexts.at(0).second, 0); + BOOST_CHECK_EQUAL(link3->ends[0].nexts.at(0).first.lock(), link0); + BOOST_CHECK_EQUAL(link3->ends[0].nexts.at(0).second, 0); + BOOST_CHECK(link3->ends[1].nexts.empty()); } } - BOOST_CHECK_IF(l4, addLinksBetween(p110, p300)) { - BOOST_CHECK_IF(l4c, dynamic_cast<RailLinkCurve *>(l4.get())) { - BOOST_CHECK_CLOSE(l4c->radius, 6950.F, 0.1F); - BOOST_CHECK_CLOSE(l4c->arc.length(), 4.456F, 0.1F); - BOOST_CHECK_CLOSE(l4->length, 30'981.F, 0.1F); - BOOST_CHECK_BETWEEN(l4->ends[0].dir, .25F, .26F); - BOOST_CHECK_CLOSE(l4->ends[1].dir, half_pi, 0.1F); + BOOST_CHECK_IF(link4, addLinksBetween(P110, P300)) { + BOOST_CHECK_IF(link4c, dynamic_cast<RailLinkCurve *>(link4.get())) { + BOOST_CHECK_CLOSE(link4c->radius, 6950.F, 0.1F); + BOOST_CHECK_CLOSE(link4c->arc.length(), 4.456F, 0.1F); + BOOST_CHECK_CLOSE(link4->length, 30'981.F, 0.1F); + BOOST_CHECK_BETWEEN(link4->ends[0].dir, .25F, .26F); + BOOST_CHECK_CLOSE(link4->ends[1].dir, half_pi, 0.1F); } } } diff --git a/test/test-pack.cpp b/test/test-pack.cpp index 1f9f061..1e5848c 100644 --- a/test/test-pack.cpp +++ b/test/test-pack.cpp @@ -10,7 +10,7 @@ using IntegerVectorPack = pack<int, std::vector>; BOOST_FIXTURE_TEST_SUITE(pint, IntegerVectorPack) -BOOST_AUTO_TEST_CASE(basics) +BOOST_AUTO_TEST_CASE(Basics) { BOOST_CHECK_EQUAL(size(), 0); BOOST_CHECK_NO_THROW(emplace(1)); diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index ce53f72..1385424 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -15,191 +15,193 @@ #include <tuple> #include <vector> -struct JPP { - template<typename T> - T - load_json(const std::filesystem::path & path) - { - BOOST_TEST_CONTEXT(path) { - std::ifstream ss {path}; - auto to = Persistence::JsonParsePersistence {}.loadState<T>(ss); - BOOST_REQUIRE(to); - return to; +namespace { + struct JPP { + template<typename T> + T + loadJson(const std::filesystem::path & path) + { + BOOST_TEST_CONTEXT(path) { + std::ifstream inputStream {path}; + auto object = Persistence::JsonParsePersistence {}.loadState<T>(inputStream); + BOOST_REQUIRE(object); + return object; + } + + // Presumably BOOST_TEST_CONTEXT is implemented as an if (...) { } + std::unreachable(); } + }; - // Presumably BOOST_TEST_CONTEXT is implemented as an if (...) { } - throw std::logic_error("We shouldn't ever get here, but apparently we can!"); + std::vector<std::filesystem::path> + fixturesIn(const std::filesystem::path & root) + { + return {std::filesystem::directory_iterator {root}, {}}; } -}; - -BOOST_FIXTURE_TEST_CASE(load_object, JPP) -{ - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/load_object.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_CHECK_CLOSE(to->flt, 3.14, 0.01); - BOOST_CHECK_EQUAL(to->str, "Lovely string"); - BOOST_CHECK_EQUAL(to->bl, true); - BOOST_CHECK_CLOSE(to->pos[0], 3.14, 0.01); - BOOST_CHECK_CLOSE(to->pos[1], 6.28, 0.01); - BOOST_CHECK_CLOSE(to->pos[2], 1.57, 0.01); - BOOST_CHECK_EQUAL(to->gpos[0], 2147483647); - BOOST_CHECK_EQUAL(to->gpos[1], 2147483646); - BOOST_CHECK_EQUAL(to->gpos[2], -2147483648); - BOOST_REQUIRE_EQUAL(to->flts.size(), 6); - BOOST_CHECK_CLOSE(to->flts[0], 3.14, 0.01); - BOOST_CHECK_CLOSE(to->flts[1], 6.28, 0.01); - BOOST_CHECK_CLOSE(to->flts[2], 1.57, 0.01); - BOOST_CHECK_CLOSE(to->flts[3], 0, 0.01); - BOOST_CHECK_CLOSE(to->flts[4], -1, 0.01); - BOOST_CHECK_CLOSE(to->flts[5], -3.14, 0.01); - BOOST_REQUIRE_EQUAL(to->poss.size(), 2); - BOOST_CHECK_CLOSE(to->poss[0][0], 3.14, 0.01); - BOOST_CHECK_CLOSE(to->poss[0][1], 6.28, 0.01); - BOOST_CHECK_CLOSE(to->poss[0][2], 1.57, 0.01); - BOOST_CHECK_CLOSE(to->poss[1][0], 0, 0.01); - BOOST_CHECK_CLOSE(to->poss[1][1], -1, 0.01); - BOOST_CHECK_CLOSE(to->poss[1][2], -3.14, 0.01); - BOOST_REQUIRE_EQUAL(to->nest.size(), 3); - BOOST_REQUIRE_EQUAL(to->nest.at(0).size(), 2); - BOOST_REQUIRE_EQUAL(to->nest.at(0).at(0).size(), 2); - BOOST_REQUIRE_EQUAL(to->nest.at(0).at(1).size(), 3); - BOOST_REQUIRE_EQUAL(to->nest.at(1).size(), 1); - BOOST_REQUIRE_EQUAL(to->nest.at(1).at(0).size(), 1); - BOOST_REQUIRE_EQUAL(to->nest.at(2).size(), 0); - BOOST_REQUIRE(to->ptr); - BOOST_CHECK_CLOSE(to->ptr->flt, 3.14, 0.01); - BOOST_CHECK_EQUAL(to->ptr->str, "Lovely string"); } -BOOST_FIXTURE_TEST_CASE(load_nested_object, JPP) +BOOST_FIXTURE_TEST_CASE(LoadObject, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/nested.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_CHECK_EQUAL(to->flt, 1.F); - BOOST_CHECK_EQUAL(to->str, "one"); - BOOST_REQUIRE(to->ptr); - BOOST_CHECK_EQUAL(to->ptr->flt, 2.F); - BOOST_CHECK_EQUAL(to->ptr->str, "two"); - BOOST_REQUIRE(to->ptr->ptr); - BOOST_CHECK_EQUAL(to->ptr->ptr->flt, 3.F); - BOOST_CHECK_EQUAL(to->ptr->ptr->str, "three"); - BOOST_REQUIRE(to->ptr->ptr->ptr); - BOOST_CHECK_EQUAL(to->ptr->ptr->ptr->flt, 4.F); - BOOST_CHECK_EQUAL(to->ptr->ptr->ptr->str, "four"); - BOOST_REQUIRE(!to->ptr->ptr->ptr->ptr); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/load_object.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_CHECK_CLOSE(object->flt, 3.14, 0.01); + BOOST_CHECK_EQUAL(object->str, "Lovely string"); + BOOST_CHECK_EQUAL(object->bl, true); + BOOST_CHECK_CLOSE(object->pos[0], 3.14, 0.01); + BOOST_CHECK_CLOSE(object->pos[1], 6.28, 0.01); + BOOST_CHECK_CLOSE(object->pos[2], 1.57, 0.01); + BOOST_CHECK_EQUAL(object->gpos[0], 2147483647); + BOOST_CHECK_EQUAL(object->gpos[1], 2147483646); + BOOST_CHECK_EQUAL(object->gpos[2], -2147483648); + BOOST_REQUIRE_EQUAL(object->flts.size(), 6); + BOOST_CHECK_CLOSE(object->flts[0], 3.14, 0.01); + BOOST_CHECK_CLOSE(object->flts[1], 6.28, 0.01); + BOOST_CHECK_CLOSE(object->flts[2], 1.57, 0.01); + BOOST_CHECK_CLOSE(object->flts[3], 0, 0.01); + BOOST_CHECK_CLOSE(object->flts[4], -1, 0.01); + BOOST_CHECK_CLOSE(object->flts[5], -3.14, 0.01); + BOOST_REQUIRE_EQUAL(object->poss.size(), 2); + BOOST_CHECK_CLOSE(object->poss[0][0], 3.14, 0.01); + BOOST_CHECK_CLOSE(object->poss[0][1], 6.28, 0.01); + BOOST_CHECK_CLOSE(object->poss[0][2], 1.57, 0.01); + BOOST_CHECK_CLOSE(object->poss[1][0], 0, 0.01); + BOOST_CHECK_CLOSE(object->poss[1][1], -1, 0.01); + BOOST_CHECK_CLOSE(object->poss[1][2], -3.14, 0.01); + BOOST_REQUIRE_EQUAL(object->nest.size(), 3); + BOOST_REQUIRE_EQUAL(object->nest.at(0).size(), 2); + BOOST_REQUIRE_EQUAL(object->nest.at(0).at(0).size(), 2); + BOOST_REQUIRE_EQUAL(object->nest.at(0).at(1).size(), 3); + BOOST_REQUIRE_EQUAL(object->nest.at(1).size(), 1); + BOOST_REQUIRE_EQUAL(object->nest.at(1).at(0).size(), 1); + BOOST_REQUIRE_EQUAL(object->nest.at(2).size(), 0); + BOOST_REQUIRE(object->ptr); + BOOST_CHECK_CLOSE(object->ptr->flt, 3.14, 0.01); + BOOST_CHECK_EQUAL(object->ptr->str, "Lovely string"); } -BOOST_FIXTURE_TEST_CASE(load_implicit_object, JPP) +BOOST_FIXTURE_TEST_CASE(LoadNestedObject, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/implicit.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_CHECK(to->ptr); - BOOST_CHECK_EQUAL(to->flt, 1.F); - BOOST_CHECK_EQUAL(to->ptr->str, "trigger"); - BOOST_CHECK_EQUAL(to->str, "after"); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/nested.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_CHECK_EQUAL(object->flt, 1.F); + BOOST_CHECK_EQUAL(object->str, "one"); + BOOST_REQUIRE(object->ptr); + BOOST_CHECK_EQUAL(object->ptr->flt, 2.F); + BOOST_CHECK_EQUAL(object->ptr->str, "two"); + BOOST_REQUIRE(object->ptr->ptr); + BOOST_CHECK_EQUAL(object->ptr->ptr->flt, 3.F); + BOOST_CHECK_EQUAL(object->ptr->ptr->str, "three"); + BOOST_REQUIRE(object->ptr->ptr->ptr); + BOOST_CHECK_EQUAL(object->ptr->ptr->ptr->flt, 4.F); + BOOST_CHECK_EQUAL(object->ptr->ptr->ptr->str, "four"); + BOOST_REQUIRE(!object->ptr->ptr->ptr->ptr); } -BOOST_FIXTURE_TEST_CASE(load_empty_object, JPP) +BOOST_FIXTURE_TEST_CASE(LoadImplicitObject, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/empty.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_CHECK_EQUAL(to->flt, 1.F); - BOOST_CHECK(to->ptr); - BOOST_CHECK_EQUAL(to->str, "after"); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/implicit.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_CHECK(object->ptr); + BOOST_CHECK_EQUAL(object->flt, 1.F); + BOOST_CHECK_EQUAL(object->ptr->str, "trigger"); + BOOST_CHECK_EQUAL(object->str, "after"); } -static std::vector<std::filesystem::path> -fixtures_in(const std::filesystem::path & root) +BOOST_FIXTURE_TEST_CASE(LoadEmptyObject, JPP) { - return {std::filesystem::directory_iterator {root}, {}}; + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/empty.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_CHECK_EQUAL(object->flt, 1.F); + BOOST_CHECK(object->ptr); + BOOST_CHECK_EQUAL(object->str, "after"); } -BOOST_DATA_TEST_CASE_F(JPP, various_parse_failures, fixtures_in(FIXTURESDIR "json/bad"), path) +BOOST_DATA_TEST_CASE_F(JPP, various_parse_failures, fixturesIn(FIXTURESDIR "json/bad"), path) { - BOOST_CHECK_THROW(load_json<std::unique_ptr<TestObject>>(path), std::runtime_error); + BOOST_CHECK_THROW(loadJson<std::unique_ptr<TestObject>>(path), std::runtime_error); } -BOOST_FIXTURE_TEST_CASE(load_obj_no_such_type, JPP) +BOOST_FIXTURE_TEST_CASE(LoadObjNoSuchType, JPP) { - BOOST_CHECK_THROW(load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/bad_type.json"), std::out_of_range); + BOOST_CHECK_THROW(loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/bad_type.json"), std::out_of_range); } -BOOST_FIXTURE_TEST_CASE(load_abs_object, JPP) +BOOST_FIXTURE_TEST_CASE(LoadAbsObject, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/abs.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_REQUIRE(to->aptr); - BOOST_CHECK_NO_THROW(to->aptr->dummy()); - BOOST_CHECK_EQUAL(to->aptr->base, "set base"); - auto s = dynamic_cast<SubObject *>(to->aptr.get()); - BOOST_REQUIRE(s); - BOOST_CHECK_EQUAL(s->sub, "set sub"); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/abs.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_REQUIRE(object->aptr); + BOOST_CHECK_NO_THROW(object->aptr->dummy()); + BOOST_CHECK_EQUAL(object->aptr->base, "set base"); + auto subObject = dynamic_cast<SubObject *>(object->aptr.get()); + BOOST_REQUIRE(subObject); + BOOST_CHECK_EQUAL(subObject->sub, "set sub"); } -BOOST_FIXTURE_TEST_CASE(load_vector_ptr, JPP) +BOOST_FIXTURE_TEST_CASE(LoadVectorPtr, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/vector_ptr.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_CHECK(to->str.empty()); - BOOST_CHECK_EQUAL(to->vptr.size(), 4); - BOOST_CHECK_EQUAL(to->vptr.at(0)->str, "type"); - BOOST_CHECK_CLOSE(to->vptr.at(1)->flt, 3.14, .01); - BOOST_CHECK(!to->vptr.at(2)); - BOOST_CHECK(to->vptr.at(3)->str.empty()); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/vector_ptr.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_CHECK(object->str.empty()); + BOOST_CHECK_EQUAL(object->vptr.size(), 4); + BOOST_CHECK_EQUAL(object->vptr.at(0)->str, "type"); + BOOST_CHECK_CLOSE(object->vptr.at(1)->flt, 3.14, .01); + BOOST_CHECK(!object->vptr.at(2)); + BOOST_CHECK(object->vptr.at(3)->str.empty()); } -BOOST_FIXTURE_TEST_CASE(test_conversion, JPP) +BOOST_FIXTURE_TEST_CASE(TestConversion, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/conv.json"); - BOOST_CHECK_EQUAL(to->postLoadCalled, 1); - BOOST_REQUIRE(to); - BOOST_CHECK_EQUAL(to->bl, true); - BOOST_CHECK_EQUAL(to->flt, 3.14F); + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/conv.json"); + BOOST_CHECK_EQUAL(object->postLoadCalled, 1); + BOOST_REQUIRE(object); + BOOST_CHECK_EQUAL(object->bl, true); + BOOST_CHECK_EQUAL(object->flt, 3.14F); } -BOOST_FIXTURE_TEST_CASE(load_shared_object_diff, JPP) +BOOST_FIXTURE_TEST_CASE(LoadSharedObjectDiff, JPP) { - auto to = load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_diff.json"); - BOOST_CHECK(to->sptr); - BOOST_CHECK(to->ssptr); - BOOST_CHECK_NE(to->sptr, to->ssptr); - BOOST_CHECK_EQUAL(to->sptr.use_count(), 1); - BOOST_CHECK_EQUAL(to->ssptr.use_count(), 1); + auto object = loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_diff.json"); + BOOST_CHECK(object->sptr); + BOOST_CHECK(object->ssptr); + BOOST_CHECK_NE(object->sptr, object->ssptr); + BOOST_CHECK_EQUAL(object->sptr.use_count(), 1); + BOOST_CHECK_EQUAL(object->ssptr.use_count(), 1); } -BOOST_FIXTURE_TEST_CASE(load_shared_object_same, JPP) +BOOST_FIXTURE_TEST_CASE(LoadSharedObjectSame, JPP) { - auto to = load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_same.json"); - BOOST_CHECK(to->sptr); - BOOST_CHECK(to->ssptr); - BOOST_CHECK_EQUAL(to->sptr, to->ssptr); - BOOST_CHECK_EQUAL(to->sptr.use_count(), 2); - BOOST_CHECK_EQUAL(to->ssptr.use_count(), 2); + auto object = loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_same.json"); + BOOST_CHECK(object->sptr); + BOOST_CHECK(object->ssptr); + BOOST_CHECK_EQUAL(object->sptr, object->ssptr); + BOOST_CHECK_EQUAL(object->sptr.use_count(), 2); + BOOST_CHECK_EQUAL(object->ssptr.use_count(), 2); } -BOOST_FIXTURE_TEST_CASE(load_shared_object_diff_default, JPP) +BOOST_FIXTURE_TEST_CASE(LoadSharedObjectDiffDefault, JPP) { - auto to = load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_diff_default.json"); - BOOST_CHECK(to->sptr); - BOOST_CHECK(to->ssptr); - BOOST_CHECK_NE(to->sptr, to->ssptr); + auto object = loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_diff_default.json"); + BOOST_CHECK(object->sptr); + BOOST_CHECK(object->ssptr); + BOOST_CHECK_NE(object->sptr, object->ssptr); } -BOOST_FIXTURE_TEST_CASE(load_shared_object_wrong_type, JPP) +BOOST_FIXTURE_TEST_CASE(LoadSharedObjectWrongType, JPP) { - BOOST_CHECK_THROW(load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_wrong_type.json"), + BOOST_CHECK_THROW(loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_wrong_type.json"), std::runtime_error); } -BOOST_FIXTURE_TEST_CASE(load_shared_object_null, JPP) +BOOST_FIXTURE_TEST_CASE(LoadSharedObjectNull, JPP) { - auto to = load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_null.json"); - BOOST_CHECK(to->sptr); - BOOST_CHECK(!to->ssptr); + auto object = loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_null.json"); + BOOST_CHECK(object->sptr); + BOOST_CHECK(!object->ssptr); } -using svs = std::tuple<const char * const, std::string_view>; -auto const TEST_STRINGS = boost::unit_test::data::make<svs>({ +using InputStringAndExpected = std::tuple<const char * const, std::string_view>; +auto const TEST_STRINGS = boost::unit_test::data::make<InputStringAndExpected>({ {R"J("")J", ""}, {R"J("non empty")J", "non empty"}, {R"J("new\nline")J", "new\nline"}, @@ -209,7 +211,7 @@ auto const TEST_STRINGS = boost::unit_test::data::make<svs>({ {R"J("form\ffeed?")J", "form\ffeed?"}, {R"J("a \u0007 bell")J", "a \a bell"}, }); -auto const TEST_STRINGS_DECODE_ONLY = boost::unit_test::data::make<svs>({ +auto const TEST_STRINGS_DECODE_ONLY = boost::unit_test::data::make<InputStringAndExpected>({ {R"J("forward\/slash")J", "forward/slash"}, {R"J("\u00a5 yen")J", "¥ yen"}, {R"J("gbp \u00a3")J", "gbp £"}, @@ -218,93 +220,94 @@ auto const TEST_STRINGS_DECODE_ONLY = boost::unit_test::data::make<svs>({ {R"J("\u0833 SAMARITAN PUNCTUATION BAU")J", "࠳ SAMARITAN PUNCTUATION BAU"}, }); -BOOST_DATA_TEST_CASE(load_strings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, in, exp) +BOOST_DATA_TEST_CASE(LoadStrings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, input, exp) { - std::stringstream str {in}; + std::stringstream str {input}; BOOST_CHECK_EQUAL(Persistence::JsonParsePersistence {}.loadState<std::string>(str), exp); } -using cpstr = std::tuple<unsigned long, std::string_view>; +using CodePointAndString = std::tuple<unsigned long, std::string_view>; -BOOST_DATA_TEST_CASE(utf8_decode, - boost::unit_test::data::make<cpstr>({ +BOOST_DATA_TEST_CASE(Utf8Decode, + boost::unit_test::data::make<CodePointAndString>({ {9, "\t"}, {0x00010000, "𐀀"}, }), - cp, str) + codePoint, str) { std::string out; - BOOST_CHECK_NO_THROW(json::jsonParser::appendEscape(cp, out)); + BOOST_CHECK_NO_THROW(json::jsonParser::appendEscape(codePoint, out)); BOOST_CHECK_EQUAL(out, str); } -BOOST_DATA_TEST_CASE(utf8_decode_bad, boost::unit_test::data::make<unsigned long>({0xd800, 0xdfff, 0x110000}), cp) +BOOST_DATA_TEST_CASE(Utf8DecodeBad, boost::unit_test::data::make<unsigned long>({0xd800, 0xdfff, 0x110000}), codePoint) { std::string out; - BOOST_CHECK_THROW(json::jsonParser::appendEscape(cp, out), std::runtime_error); + BOOST_CHECK_THROW(json::jsonParser::appendEscape(codePoint, out), std::runtime_error); } -BOOST_AUTO_TEST_CASE(write_test_null) +BOOST_AUTO_TEST_CASE(WriteTestNull) { - std::unique_ptr<TestObject> to {}; - std::stringstream ss; - Persistence::JsonWritePersistence {ss}.saveState(to); - BOOST_CHECK_EQUAL(ss.str(), "null"); + std::unique_ptr<TestObject> object {}; + std::stringstream outStream; + Persistence::JsonWritePersistence {outStream}.saveState(object); + BOOST_CHECK_EQUAL(outStream.view(), "null"); } -BOOST_AUTO_TEST_CASE(write_test_dfl) +BOOST_AUTO_TEST_CASE(WriteTestDfl) { - auto to = std::make_unique<TestObject>(); - std::stringstream ss; - Persistence::JsonWritePersistence {ss}.saveState(to); - BOOST_CHECK_EQUAL(ss.str(), + auto object = std::make_unique<TestObject>(); + std::stringstream outStream; + Persistence::JsonWritePersistence {outStream}.saveState(object); + BOOST_CHECK_EQUAL(outStream.view(), R"({"p.typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"gpos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]})"); } -BOOST_FIXTURE_TEST_CASE(write_test_loaded, JPP) +BOOST_FIXTURE_TEST_CASE(WriteTestLoaded, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/load_object.json"); - std::stringstream ss; - Persistence::JsonWritePersistence {ss}.saveState(to); - BOOST_CHECK_EQUAL(ss.str(), + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/load_object.json"); + std::stringstream outStream; + Persistence::JsonWritePersistence {outStream}.saveState(object); + BOOST_CHECK_EQUAL(outStream.view(), R"({"p.typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":true,"pos":[3.14,6.28,1.57],"gpos":[2147483647,2147483646,-2147483648],"flts":[3.14,6.28,1.57,0,-1,-3.14],"poss":[[3.14,6.28,1.57],[0,-1,-3.14]],"nest":[[["a","b"],["c","d","e"]],[["f"]],[]],"ptr":{"p.typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":false,"pos":[0,0,0],"gpos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]},"vptr":[]})"); } -BOOST_FIXTURE_TEST_CASE(write_test_loaded_abs, JPP) +BOOST_FIXTURE_TEST_CASE(WriteTestLoadedAbs, JPP) { - auto to = load_json<std::unique_ptr<TestObject>>(FIXTURESDIR "json/abs.json"); - std::stringstream ss; - Persistence::JsonWritePersistence {ss}.saveState(to); - BOOST_CHECK_EQUAL(ss.str(), + auto object = loadJson<std::unique_ptr<TestObject>>(FIXTURESDIR "json/abs.json"); + std::stringstream outStream; + Persistence::JsonWritePersistence {outStream}.saveState(object); + BOOST_CHECK_EQUAL(outStream.view(), R"({"p.typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"gpos":[0,0,0],"flts":[],"poss":[],"nest":[],"aptr":{"p.typeid":"SubObject","base":"set base","sub":"set sub"},"vptr":[]})"); } -BOOST_FIXTURE_TEST_CASE(write_test_loaded_shared, JPP) +BOOST_FIXTURE_TEST_CASE(WriteTestLoadedShared, JPP) { - auto to = load_json<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_same.json"); - std::stringstream ss; + auto object = loadJson<std::unique_ptr<SharedTestObject>>(FIXTURESDIR "json/shared_ptr_same.json"); + std::stringstream outStream; Persistence::seenSharedObjects.clear(); - Persistence::JsonWritePersistence {ss}.saveState(to); + Persistence::JsonWritePersistence {outStream}.saveState(object); BOOST_CHECK_EQUAL(Persistence::seenSharedObjects.size(), 1); - BOOST_CHECK_EQUAL(ss.str(), + BOOST_CHECK_EQUAL(outStream.view(), R"({"p.typeid":"SharedTestObject","sptr":{"p.typeid":"SubObject","p.id":"someid","base":"","sub":""},"ssptr":"someid"})"); } -BOOST_DATA_TEST_CASE(write_special_strings, TEST_STRINGS, exp, in) +BOOST_DATA_TEST_CASE(WriteSpecialStrings, TEST_STRINGS, exp, input) { - std::stringstream ss; - std::string copy(in); - Persistence::JsonWritePersistence {ss}.saveState(copy); - BOOST_CHECK_EQUAL(ss.str(), exp); + std::stringstream outStream; + std::string copy(input); + Persistence::JsonWritePersistence {outStream}.saveState(copy); + BOOST_CHECK_EQUAL(outStream.view(), exp); } -BOOST_AUTO_TEST_CASE(get_default_id) +BOOST_AUTO_TEST_CASE(GetDefaultId) { - SubObject2 so; - const auto id {so.getId()}; + SubObject2 subObject; + const auto subObjectId {subObject.getId()}; - BOOST_TEST_CONTEXT(id) { - auto ptr = std::stoul(id, nullptr, 16); - BOOST_CHECK_EQUAL(ptr, reinterpret_cast<decltype(ptr)>(&so)); + BOOST_TEST_CONTEXT(subObjectId) { + auto ptr = std::stoul(subObjectId, nullptr, 16); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + BOOST_CHECK_EQUAL(ptr, reinterpret_cast<decltype(ptr)>(&subObject)); } } diff --git a/test/test-render.cpp b/test/test-render.cpp index 8390d25..90fa894 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -26,118 +26,123 @@ #include <ui/applicationBase.h> #include <ui/window.h> -class TestScene : public SceneProvider { - RailVehicleClassPtr brush47rvc; - std::shared_ptr<RailVehicle> train1, train2; - RailLinks rail; - std::shared_ptr<Environment> env = std::make_shared<Environment>(); - - std::shared_ptr<Terrain> terrain = std::make_shared<Terrain>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 1)); - Water water {terrain}; - -public: - TestScene() - { - terrain->point(GeoData::VertexHandle {517}).z = 100'000; - terrain->generateMeshes(); - gameState->assets = AssetFactory::loadAll(RESDIR); - brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>(gameState->assets.at("brush-47")); - std::random_device randomdev {}; - std::uniform_real_distribution<Angle> rotationDistribution {0, two_pi}; - std::uniform_int_distribution<GlobalDistance> positionOffsetDistribution {-1500, +1500}; - std::uniform_int_distribution<int> treeDistribution {1, 3}; - std::uniform_int_distribution<int> treeVariantDistribution {1, 4}; - train1 = std::make_shared<RailVehicle>(brush47rvc); - train1->location.setPosition({52000, 50000, 2000}); - train1->bogies.front().setPosition(train1->bogies.front().position() + train1->location.position()); - train1->bogies.back().setPosition(train1->bogies.back().position() + train1->location.position()); - train2 = std::make_shared<RailVehicle>(brush47rvc); - train2->location.setPosition({52000, 30000, 2000}); - train2->bogies.front().setPosition(train2->bogies.front().position() + train2->location.position()); - train2->bogies.back().setPosition(train2->bogies.back().position() + train2->location.position()); - for (auto x = 40000; x < 100000; x += 5000) { - for (auto y = 65000; y < 125000; y += 5000) { - gameState->world.create<Plant>( - std::dynamic_pointer_cast<Foliage>(gameState->assets.at(std::format( - "Tree-{:#02}-{}", treeDistribution(randomdev), treeVariantDistribution(randomdev)))), - Location {{x + positionOffsetDistribution(randomdev), y + positionOffsetDistribution(randomdev), - 1}, - {0, rotationDistribution(randomdev), 0}}); +namespace { + class TestScene : public SceneProvider { + RailVehicleClassPtr brush47rvc; + std::shared_ptr<RailVehicle> train1, train2; + RailLinks rail; + std::shared_ptr<Environment> env = std::make_shared<Environment>(); + + std::shared_ptr<Terrain> terrain + = std::make_shared<Terrain>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 1)); + Water water {terrain}; + + public: + TestScene() + { + terrain->point(GeoData::VertexHandle {517}).z = 100'000; + terrain->generateMeshes(); + gameState->assets = AssetFactory::loadAll(RESDIR); + brush47rvc = gameState->assets.at("brush-47").dynamicCast<RailVehicleClass>(); + std::random_device randomdev {}; + std::uniform_real_distribution<Angle> rotationDistribution {0, two_pi}; + std::uniform_int_distribution<GlobalDistance> positionOffsetDistribution {-1500, +1500}; + std::uniform_int_distribution<int> treeDistribution {1, 3}; + std::uniform_int_distribution<int> treeVariantDistribution {1, 4}; + train1 = std::make_shared<RailVehicle>(brush47rvc); + train1->location.setPosition({52000, 50000, 2000}); + train1->bogies.front().setPosition(train1->bogies.front().position() + train1->location.position()); + train1->bogies.back().setPosition(train1->bogies.back().position() + train1->location.position()); + train2 = std::make_shared<RailVehicle>(brush47rvc); + train2->location.setPosition({52000, 30000, 2000}); + train2->bogies.front().setPosition(train2->bogies.front().position() + train2->location.position()); + train2->bogies.back().setPosition(train2->bogies.back().position() + train2->location.position()); + for (auto posX = 40000; posX < 100000; posX += 5000) { + for (auto posY = 65000; posY < 125000; posY += 5000) { + gameState->world.create<Plant>( + gameState->assets + .at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev), + treeVariantDistribution(randomdev))) + .dynamicCast<Foliage>(), + Location {.pos = {posX + positionOffsetDistribution(randomdev), + posY + positionOffsetDistribution(randomdev), 1}, + .rot = {0, rotationDistribution(randomdev), 0}}); + } } + rail.addLinksBetween({42000, 50000, 1000}, {65000, 50000, 1000}); + rail.addLinksBetween({65000, 50000, 1000}, {75000, 45000, 2000}); } - rail.addLinksBetween({42000, 50000, 1000}, {65000, 50000, 1000}); - rail.addLinksBetween({65000, 50000, 1000}, {75000, 45000, 2000}); - } - - void - content(const SceneShader & shader, const Frustum & frustum) const override - { - terrain->render(shader, frustum); - water.render(shader, frustum); - rail.render(shader, frustum); - std::ranges::for_each(gameState->assets, [&shader, &frustum](const auto & asset) { - if (const auto renderable = std::dynamic_pointer_cast<const Renderable>(asset.second)) { - renderable->render(shader, frustum); - } - }); - } - - void - lights(const SceneShader &) const override - { - } - - void - environment(const SceneShader &, const SceneRenderer & r) const override - { - env->render(r, *this); - } - - void - shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const override - { - terrain->shadows(shadowMapper, frustum); - std::ranges::for_each(gameState->assets, [&shadowMapper, &frustum](const auto & asset) { - if (const auto renderable = std::dynamic_pointer_cast<const Renderable>(asset.second)) { - renderable->shadows(shadowMapper, frustum); - } - }); - } -}; + + void + content(const SceneShader & shader, const Frustum & frustum) const override + { + terrain->render(shader, frustum); + water.render(shader, frustum); + rail.render(shader, frustum); + std::ranges::for_each(gameState->assets, [&shader, &frustum](const auto & asset) { + if (const auto renderable = asset.second.template getAs<const Renderable>()) { + renderable->render(shader, frustum); + } + }); + } + + void + lights(const SceneShader &) const override + { + } + + void + environment(const SceneShader &, const SceneRenderer & renderer) const override + { + env->render(renderer, *this); + } + + void + shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const override + { + terrain->shadows(shadowMapper, frustum); + std::ranges::for_each(gameState->assets, [&shadowMapper, &frustum](const auto & asset) { + if (const auto renderable = asset.second.template getAs<const Renderable>()) { + renderable->shadows(shadowMapper, frustum); + } + }); + } + }; +} BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -BOOST_DATA_TEST_CASE(cam, +BOOST_DATA_TEST_CASE(Cam, boost::unit_test::data::xrange(500, 30000, 1300) * boost::unit_test::data::xrange(500, 10000, 300) * boost::unit_test::data::xrange(50000, 500000, 70000), dist, near, far) { - static constexpr GlobalPosition4D pos {-10, -10, 60000, 0}; - const Camera cam {pos, half_pi, 1.F, near, far}; + static constexpr GlobalPosition4D POS {-10, -10, 60000, 0}; + const Camera cam {POS, half_pi, 1.F, near, far}; - const auto e = cam.extentsAtDist(dist); + const auto extents = cam.extentsAtDist(dist); - BOOST_CHECK_CLOSE_VECI(e[0], pos + GlobalPosition4D(-dist, dist, -dist, dist)); - BOOST_CHECK_CLOSE_VECI(e[1], pos + GlobalPosition4D(-dist, dist, dist, dist)); - BOOST_CHECK_CLOSE_VECI(e[2], pos + GlobalPosition4D(dist, dist, -dist, dist)); - BOOST_CHECK_CLOSE_VECI(e[3], pos + GlobalPosition4D(dist, dist, dist, dist)); + BOOST_CHECK_CLOSE_VECI(extents[0], POS + GlobalPosition4D(-dist, dist, -dist, dist)); + BOOST_CHECK_CLOSE_VECI(extents[1], POS + GlobalPosition4D(-dist, dist, dist, dist)); + BOOST_CHECK_CLOSE_VECI(extents[2], POS + GlobalPosition4D(dist, dist, -dist, dist)); + BOOST_CHECK_CLOSE_VECI(extents[3], POS + GlobalPosition4D(dist, dist, dist, dist)); } -BOOST_AUTO_TEST_CASE(camSeaFloor) +BOOST_AUTO_TEST_CASE(CamSeaFloor) { const Camera cam {{100, 200, 300}, half_pi, 1.F, 100, 2000}; - const auto e = cam.extentsAtDist(2000); + const auto extents = cam.extentsAtDist(2000); - BOOST_CHECK_CLOSE_VECI(e[0], GlobalPosition4D(-1700, 2000, -1500, 1800)); - BOOST_CHECK_CLOSE_VECI(e[1], GlobalPosition4D(-1900, 2200, 2300, 2000)); - BOOST_CHECK_CLOSE_VECI(e[2], GlobalPosition4D(1900, 2000, -1500, 1800)); - BOOST_CHECK_CLOSE_VECI(e[3], GlobalPosition4D(2100, 2200, 2300, 2000)); + BOOST_CHECK_CLOSE_VECI(extents[0], GlobalPosition4D(-1700, 2000, -1500, 1800)); + BOOST_CHECK_CLOSE_VECI(extents[1], GlobalPosition4D(-1900, 2200, 2300, 2000)); + BOOST_CHECK_CLOSE_VECI(extents[2], GlobalPosition4D(1900, 2000, -1500, 1800)); + BOOST_CHECK_CLOSE_VECI(extents[3], GlobalPosition4D(2100, 2200, 2300, 2000)); } BOOST_FIXTURE_TEST_SUITE(w, TestRenderOutput); -BOOST_AUTO_TEST_CASE(basic) +BOOST_AUTO_TEST_CASE(Basic) { class TestSceneRenderer : public SceneRenderer { using SceneRenderer::SceneRenderer; @@ -154,18 +159,18 @@ BOOST_AUTO_TEST_CASE(basic) } }; - TestSceneRenderer ss {size, output}; - ss.camera.setView({-10000, -10000, 60000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); + TestSceneRenderer renderer {size, output}; + renderer.camera.setView({-10000, -10000, 60000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); const TestScene scene; - ss.render(scene); - ss.saveBuffers("/tmp/basic"); + renderer.render(scene); + renderer.saveBuffers("/tmp/basic"); Texture::save(outImage, "/tmp/basic/final.tga"); } -BOOST_AUTO_TEST_CASE(terrain) +BOOST_AUTO_TEST_CASE(TerrainSD19) { - SceneRenderer ss {size, output}; - ss.camera.setView({310000000, 490000000, 600000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); + SceneRenderer renderer {size, output}; + renderer.camera.setView({310000000, 490000000, 600000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); class TestTerrain : public SceneProvider { std::shared_ptr<Terrain> terrain @@ -180,10 +185,10 @@ BOOST_AUTO_TEST_CASE(terrain) } void - environment(const SceneShader &, const SceneRenderer & sr) const override + environment(const SceneShader &, const SceneRenderer & renderer) const override { - sr.setAmbientLight({0.1, 0.1, 0.1}); - sr.setDirectionalLight({1, 1, 1}, {{0, quarter_pi}}, *this); + renderer.setAmbientLight({0.1, 0.1, 0.1}); + renderer.setDirectionalLight({1, 1, 1}, {{0, quarter_pi}}, *this); } void @@ -198,14 +203,14 @@ BOOST_AUTO_TEST_CASE(terrain) } }; - ss.render(TestTerrain {}); + renderer.render(TestTerrain {}); Texture::save(outImage, "/tmp/terrain.tga"); } -BOOST_AUTO_TEST_CASE(railnet) +BOOST_AUTO_TEST_CASE(RailNetwork) { - SceneRenderer ss {size, output}; - ss.camera.setView({0, 0, 10000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); + SceneRenderer renderer {size, output}; + renderer.camera.setView({0, 0, 10000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); class TestRail : public SceneProvider { RailLinks net; @@ -227,10 +232,10 @@ BOOST_AUTO_TEST_CASE(railnet) } void - environment(const SceneShader &, const SceneRenderer & sr) const override + environment(const SceneShader &, const SceneRenderer & renderer) const override { - sr.setAmbientLight({0.1, 0.1, 0.1}); - sr.setDirectionalLight({1, 1, 1}, {{0, quarter_pi}}, *this); + renderer.setAmbientLight({0.1, 0.1, 0.1}); + renderer.setDirectionalLight({1, 1, 1}, {{0, quarter_pi}}, *this); } void @@ -244,7 +249,7 @@ BOOST_AUTO_TEST_CASE(railnet) } }; - ss.render(TestRail {}); + renderer.render(TestRail {}); Texture::save(outImage, "/tmp/railnet.tga"); } diff --git a/test/test-static-enumDetails.cpp b/test/test-static-enumDetails.cpp index 5f81753..932a2af 100644 --- a/test/test-static-enumDetails.cpp +++ b/test/test-static-enumDetails.cpp @@ -3,76 +3,76 @@ #include <enumDetails.h> // Test type name -static_assert(EnumTypeDetails<GlobalUnscoped>::typeName == "GlobalUnscoped"); -static_assert(EnumTypeDetails<GlobalScoped>::typeName == "GlobalScoped"); -static_assert(EnumTypeDetails<ns::Unscoped>::typeName == "ns::Unscoped"); -static_assert(EnumTypeDetails<ns::Scoped>::typeName == "ns::Scoped"); +static_assert(EnumTypeDetails<GlobalUnscoped>::TYPE_NAME == "GlobalUnscoped"); +static_assert(EnumTypeDetails<GlobalScoped>::TYPE_NAME == "GlobalScoped"); +static_assert(EnumTypeDetails<ns::Unscoped>::TYPE_NAME == "ns::Unscoped"); +static_assert(EnumTypeDetails<ns::Scoped>::TYPE_NAME == "ns::Scoped"); -static_assert(EnumValueDetails<GlobalUnscoped::aa>::valueName == "aa"); -static_assert(EnumValueDetails<GlobalScoped::aa>::valueName == "aa"); -static_assert(EnumValueDetails<ns::Unscoped::aa>::valueName == "aa"); -static_assert(EnumValueDetails<ns::Scoped::aa>::valueName == "aa"); +static_assert(EnumValueDetails<GlobalUnscoped::Aa>::VALUE_NAME == "Aa"); +static_assert(EnumValueDetails<GlobalScoped::Aa>::VALUE_NAME == "Aa"); +static_assert(EnumValueDetails<ns::Unscoped::Aa>::VALUE_NAME == "Aa"); +static_assert(EnumValueDetails<ns::Scoped::Aa>::VALUE_NAME == "Aa"); namespace test1 { - static_assert(EnumValueDetails<DefaultDense::a>::valid); - static_assert(EnumValueDetails<DefaultDense::de>::valid); - static_assert(EnumValueDetails<static_cast<DefaultDense>(0)>::valid); - static_assert(EnumValueDetails<static_cast<DefaultDense>(3)>::valid); - static_assert(!EnumValueDetails<static_cast<DefaultDense>(-1)>::valid); - static_assert(!EnumValueDetails<static_cast<DefaultDense>(4)>::valid); - static_assert(EnumValueDetails<DefaultDense::a>::valueName == "a"); - static_assert(EnumValueDetails<DefaultDense::de>::valueName == "de"); - using ED_DD = EnumDetails<DefaultDense>; - static_assert(EnumValueCollection<DefaultDense>::Vs::size() == 256); - static_assert(ED_DD::valid_flags.size() == 256); - static_assert(ED_DD::values.size() == 4); - static_assert(std::is_sorted(ED_DD::values.begin(), ED_DD::values.end())); - static_assert(ED_DD::values.at(0) == DefaultDense::a); - static_assert(ED_DD::values.at(3) == DefaultDense::de); - static_assert(ED_DD::names.at(0) == "a"); - static_assert(ED_DD::names.at(3) == "de"); + static_assert(EnumValueDetails<DefaultDense::A>::VALID); + static_assert(EnumValueDetails<DefaultDense::De>::VALID); + static_assert(EnumValueDetails<static_cast<DefaultDense>(0)>::VALID); + static_assert(EnumValueDetails<static_cast<DefaultDense>(3)>::VALID); + static_assert(!EnumValueDetails<static_cast<DefaultDense>(-1)>::VALID); + static_assert(!EnumValueDetails<static_cast<DefaultDense>(4)>::VALID); + static_assert(EnumValueDetails<DefaultDense::A>::VALUE_NAME == "A"); + static_assert(EnumValueDetails<DefaultDense::De>::VALUE_NAME == "De"); + using EdDd = EnumDetails<DefaultDense>; + static_assert(EnumValueCollection<DefaultDense>::Vs::size() == 127); + static_assert(EdDd::VALID_FLAGS.size() == 127); + static_assert(EdDd::VALUES.size() == 4); + static_assert(std::ranges::is_sorted(EdDd::VALUES)); + static_assert(EdDd::VALUES.at(0) == DefaultDense::A); + static_assert(EdDd::VALUES.at(3) == DefaultDense::De); + static_assert(EdDd::NAMES.at(0) == "A"); + static_assert(EdDd::NAMES.at(3) == "De"); - static_assert(ED_DD::is_valid(DefaultDense::a)); - static_assert(ED_DD::is_valid(DefaultDense::de)); - static_assert(!ED_DD::is_valid(DefaultDense(-1))); - static_assert(!ED_DD::parse("").has_value()); - static_assert(!ED_DD::parse("nonsense").has_value()); - static_assert(ED_DD::parse("bee").value() == DefaultDense::bee); - static_assert(ED_DD::parse("ci").value() == DefaultDense::ci); - static_assert(ED_DD::to_string(DefaultDense::de).value() == "de"); - static_assert(!ED_DD::to_string(static_cast<DefaultDense>(10)).has_value()); + static_assert(EdDd::isValid(DefaultDense::A)); + static_assert(EdDd::isValid(DefaultDense::De)); + static_assert(!EdDd::isValid(DefaultDense(-1))); + static_assert(!EdDd::parse("").has_value()); + static_assert(!EdDd::parse("nonsense").has_value()); + static_assert(EdDd::parse("Bee").value() == DefaultDense::Bee); + static_assert(EdDd::parse("Ci").value() == DefaultDense::Ci); + static_assert(EdDd::toString(DefaultDense::De).value() == "De"); + static_assert(!EdDd::toString(static_cast<DefaultDense>(10)).has_value()); } namespace test2 { - static_assert(EnumValueDetails<NumberedSparse::bee>::valid); - static_assert(EnumValueDetails<static_cast<NumberedSparse>(0)>::valid); - static_assert(EnumValueDetails<static_cast<NumberedSparse>(3)>::valid); - static_assert(EnumValueDetails<static_cast<NumberedSparse>(-20)>::valid); - static_assert(EnumValueDetails<static_cast<NumberedSparse>(100)>::valid); - static_assert(!EnumValueDetails<static_cast<NumberedSparse>(2)>::valid); - static_assert(EnumValueDetails<NumberedSparse::a>::valueName == "a"); - static_assert(EnumValueDetails<NumberedSparse::de>::valueName == "de"); - using ED_NS = EnumDetails<NumberedSparse>; + static_assert(EnumValueDetails<NumberedSparse::Bee>::VALID); + static_assert(EnumValueDetails<static_cast<NumberedSparse>(0)>::VALID); + static_assert(EnumValueDetails<static_cast<NumberedSparse>(3)>::VALID); + static_assert(EnumValueDetails<static_cast<NumberedSparse>(-20)>::VALID); + static_assert(EnumValueDetails<static_cast<NumberedSparse>(100)>::VALID); + static_assert(!EnumValueDetails<static_cast<NumberedSparse>(2)>::VALID); + static_assert(EnumValueDetails<NumberedSparse::A>::VALUE_NAME == "A"); + static_assert(EnumValueDetails<NumberedSparse::De>::VALUE_NAME == "De"); + using EdNs = EnumDetails<NumberedSparse>; static_assert(EnumValueCollection<NumberedSparse>::Vs::size() == 7); - static_assert(ED_NS::values.size() == 4); - static_assert(ED_NS::valid_flags.size() == 7); - static_assert(std::is_sorted(ED_NS::values.begin(), ED_NS::values.end())); - static_assert(ED_NS::values.at(0) == NumberedSparse::ci); - static_assert(ED_NS::values.at(1) == NumberedSparse::a); - static_assert(ED_NS::values.at(2) == NumberedSparse::bee); - static_assert(ED_NS::values.at(3) == NumberedSparse::de); - static_assert(ED_NS::names.at(0) == "ci"); - static_assert(ED_NS::names.at(1) == "a"); - static_assert(ED_NS::names.at(2) == "bee"); - static_assert(ED_NS::names.at(3) == "de"); + static_assert(EdNs::VALUES.size() == 4); + static_assert(EdNs::VALID_FLAGS.size() == 7); + static_assert(std::ranges::is_sorted(EdNs::VALUES)); + static_assert(EdNs::VALUES.at(0) == NumberedSparse::Ci); + static_assert(EdNs::VALUES.at(1) == NumberedSparse::A); + static_assert(EdNs::VALUES.at(2) == NumberedSparse::Bee); + static_assert(EdNs::VALUES.at(3) == NumberedSparse::De); + static_assert(EdNs::NAMES.at(0) == "Ci"); + static_assert(EdNs::NAMES.at(1) == "A"); + static_assert(EdNs::NAMES.at(2) == "Bee"); + static_assert(EdNs::NAMES.at(3) == "De"); - static_assert(ED_NS::is_valid(NumberedSparse::a)); - static_assert(ED_NS::is_valid(NumberedSparse::de)); - static_assert(!ED_NS::is_valid(NumberedSparse(-1))); - static_assert(!ED_NS::parse("").has_value()); - static_assert(!ED_NS::parse("nonsense").has_value()); - static_assert(ED_NS::parse("bee").value() == NumberedSparse::bee); - static_assert(ED_NS::parse("ci").value() == NumberedSparse::ci); - static_assert(ED_NS::to_string(NumberedSparse::ci).value() == "ci"); - static_assert(!ED_NS::to_string(static_cast<NumberedSparse>(10)).has_value()); + static_assert(EdNs::isValid(NumberedSparse::A)); + static_assert(EdNs::isValid(NumberedSparse::De)); + static_assert(!EdNs::isValid(NumberedSparse(-1))); + static_assert(!EdNs::parse("").has_value()); + static_assert(!EdNs::parse("nonsense").has_value()); + static_assert(EdNs::parse("Bee").value() == NumberedSparse::Bee); + static_assert(EdNs::parse("Ci").value() == NumberedSparse::Ci); + static_assert(EdNs::toString(NumberedSparse::Ci).value() == "Ci"); + static_assert(!EdNs::toString(static_cast<NumberedSparse>(10)).has_value()); } diff --git a/test/test-static-stream_support.cpp b/test/test-static-stream_support.cpp index 6bf9ea4..ec99f52 100644 --- a/test/test-static-stream_support.cpp +++ b/test/test-static-stream_support.cpp @@ -14,7 +14,7 @@ static_assert(NonStringIterableCollection<std::array<char, 1>>); static_assert(!NonStringIterableCollection<std::string>); static_assert(!NonStringIterableCollection<std::string_view>); -static_assert(requires(std::vector<int> i, std::ostream & o) { o << i; }); -static_assert(requires(std::array<int, 10> i, std::ostream & o) { o << i; }); -static_assert(requires(std::set<int> i, std::ostream & o) { o << i; }); -static_assert(requires(std::map<int, int> i, std::ostream & o) { o << i; }); +static_assert(requires(std::vector<int> input, std::ostream & strm) { strm << input; }); +static_assert(requires(std::array<int, 10> input, std::ostream & strm) { strm << input; }); +static_assert(requires(std::set<int> input, std::ostream & strm) { strm << input; }); +static_assert(requires(std::map<int, int> input, std::ostream & strm) { strm << input; }); diff --git a/test/test-text.cpp b/test/test-text.cpp index b0a9503..a68718e 100644 --- a/test/test-text.cpp +++ b/test/test-text.cpp @@ -6,8 +6,6 @@ #include <stream_support.h> #include "testMainWindow.h" -#include "testRenderOutput.h" -#include "ui/text.h" #include <array> #include <gfx/models/texture.h> #include <glm/glm.hpp> @@ -17,16 +15,16 @@ BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -BOOST_AUTO_TEST_CASE(utf8_string_view_iter) +BOOST_AUTO_TEST_CASE(Utf8StringViewIter) { - static constexpr utf8_string_view text {"Some UTF-8 €£²¹ text."}; - static constexpr std::array codepoints { + static constexpr utf8_string_view TEXT {"Some UTF-8 €£²¹ text."}; + static constexpr std::array CODEPOINTS { 83, 111, 109, 101, 32, 85, 84, 70, 45, 56, 32, 8364, 163, 178, 185, 32, 116, 101, 120, 116, 46}; - BOOST_CHECK_EQUAL(std::count_if(text.begin(), text.end(), isspace), 3); - BOOST_CHECK_EQUAL(text.length(), 21); + BOOST_CHECK_EQUAL(std::count_if(TEXT.begin(), TEXT.end(), isspace), 3); + BOOST_CHECK_EQUAL(TEXT.length(), 21); std::vector<uint32_t> codepointsOut; - std::copy(text.begin(), text.end(), std::back_inserter(codepointsOut)); - BOOST_CHECK_EQUAL_COLLECTIONS(codepoints.begin(), codepoints.end(), codepointsOut.begin(), codepointsOut.end()); + std::copy(TEXT.begin(), TEXT.end(), std::back_inserter(codepointsOut)); + BOOST_CHECK_EQUAL_COLLECTIONS(CODEPOINTS.begin(), CODEPOINTS.end(), codepointsOut.begin(), codepointsOut.end()); } struct FontTest : public Font { @@ -37,10 +35,10 @@ BOOST_TEST_DONT_PRINT_LOG_VALUE(Font::CharData); using TextureSizeTestData = std::tuple<unsigned, unsigned, unsigned>; -BOOST_DATA_TEST_CASE(fontTextureSize, boost::unit_test::data::make<unsigned>({2, 3, 10, 50, 250}), fontHeight) +BOOST_DATA_TEST_CASE(FontTextureSize, boost::unit_test::data::make<unsigned>({2, 3, 10, 50, 250}), fontHeight) { - auto isPowerOfTwo = [](auto x) { - return (x & (x - 1)) == 0; + auto isPowerOfTwo = [](std::integral auto number) { + return (number & (number - 1)) == 0; }; const auto res = Font::getTextureSize(fontHeight); // Power of 2 dimensions... @@ -58,7 +56,7 @@ BOOST_DATA_TEST_CASE(fontTextureSize, boost::unit_test::data::make<unsigned>({2, BOOST_FIXTURE_TEST_SUITE(ft, FontTest); -BOOST_AUTO_TEST_CASE(initialize_chardata) +BOOST_AUTO_TEST_CASE(InitializeCharData) { BOOST_CHECK_GE(charsData.size(), 72); BOOST_CHECK_EQUAL(fontTextures.size(), 2); @@ -66,7 +64,7 @@ BOOST_AUTO_TEST_CASE(initialize_chardata) using CharDataTest = std::tuple<decltype(get_codepoint(nullptr)), Font::CharData>; -BOOST_DATA_TEST_CASE(initialize_chardata_A, +BOOST_DATA_TEST_CASE(InitializeCharDataA, boost::unit_test::data::make<CharDataTest>({ {'A', {0, {34, 35}, {712, 0}, {-1, 35}, 32}}, {'I', {0, {6, 35}, {947, 0}, {4, 35}, 13}}, @@ -75,74 +73,61 @@ BOOST_DATA_TEST_CASE(initialize_chardata_A, }), character, expected) { - const auto & cd = charsData.at(character); - BOOST_CHECK_EQUAL(cd.textureIdx, expected.textureIdx); - BOOST_CHECK_EQUAL(cd.size, expected.size); - BOOST_CHECK_EQUAL(cd.position, expected.position); - BOOST_CHECK_EQUAL(cd.bearing, expected.bearing); - BOOST_CHECK_EQUAL(cd.advance, expected.advance); + const auto & charData = charsData.at(character); + BOOST_CHECK_EQUAL(charData.textureIdx, expected.textureIdx); + BOOST_CHECK_EQUAL(charData.size, expected.size); + BOOST_CHECK_EQUAL(charData.position, expected.position); + BOOST_CHECK_EQUAL(charData.bearing, expected.bearing); + BOOST_CHECK_EQUAL(charData.advance, expected.advance); } static_assert(glm::vec2 {862, 0} / glm::vec2 {2048, 64} == glm::vec2 {0.4208984375, 0}); static_assert(glm::vec2 {866, 35} / glm::vec2 {2048, 64} == glm::vec2 {0.4228515625, 0.546875}); -BOOST_AUTO_TEST_CASE(render_font) +BOOST_AUTO_TEST_CASE(RenderFont) { - constexpr std::string_view text {"I Like Trains"}; - const auto spaces = static_cast<std::size_t>(std::count_if(text.begin(), text.end(), isspace)); - const auto tqs = render(text); - BOOST_REQUIRE_EQUAL(tqs.size(), 1); - const auto & t1 = tqs.begin(); - BOOST_CHECK_EQUAL(t1->first, fontTextures.front().texture); - const auto & v = t1->second; - BOOST_CHECK_EQUAL(v.size(), text.size() - spaces); + constexpr std::string_view TEXT {"I Like Trains"}; + const auto spaces = static_cast<std::size_t>(std::ranges::count_if(TEXT, isspace)); + const auto textureQuads = render(TEXT); + BOOST_REQUIRE_EQUAL(textureQuads.size(), 1); + const auto & textureQuad = textureQuads.begin(); + BOOST_CHECK_EQUAL(textureQuad->first, fontTextures.front().texture); + const auto & vertices = textureQuad->second; + BOOST_CHECK_EQUAL(vertices.size(), TEXT.size() - spaces); BOOST_TEST_CONTEXT(size) { // I - BOOST_CHECK_CLOSE_VEC(v[0][0], glm::vec4(4, 0, 0.42, 0.54)); - BOOST_CHECK_CLOSE_VEC(v[0][1], glm::vec4(10, 0, 0.42, 0.54)); - BOOST_CHECK_CLOSE_VEC(v[0][2], glm::vec4(10, 35, 0.42, 0)); - BOOST_CHECK_CLOSE_VEC(v[0][3], glm::vec4(4, 35, 0.42, 0)); + BOOST_CHECK_CLOSE_VEC(vertices[0][0], glm::vec4(4, 0, 0.42, 0.54)); + BOOST_CHECK_CLOSE_VEC(vertices[0][1], glm::vec4(10, 0, 0.42, 0.54)); + BOOST_CHECK_CLOSE_VEC(vertices[0][2], glm::vec4(10, 35, 0.42, 0)); + BOOST_CHECK_CLOSE_VEC(vertices[0][3], glm::vec4(4, 35, 0.42, 0)); // (space, no glyph) // L - BOOST_CHECK_CLOSE_VEC(v[1][0], glm::vec4(32, 0, 0.42, 0.54)); - BOOST_CHECK_CLOSE_VEC(v[1][1], glm::vec4(54, 0, 0.42, 0.54)); - BOOST_CHECK_CLOSE_VEC(v[1][2], glm::vec4(54, 35, 0.42, 0)); - BOOST_CHECK_CLOSE_VEC(v[1][3], glm::vec4(32, 35, 0.42, 0)); + BOOST_CHECK_CLOSE_VEC(vertices[1][0], glm::vec4(32, 0, 0.42, 0.54)); + BOOST_CHECK_CLOSE_VEC(vertices[1][1], glm::vec4(54, 0, 0.42, 0.54)); + BOOST_CHECK_CLOSE_VEC(vertices[1][2], glm::vec4(54, 35, 0.42, 0)); + BOOST_CHECK_CLOSE_VEC(vertices[1][3], glm::vec4(32, 35, 0.42, 0)); } } -BOOST_AUTO_TEST_CASE(render_text) -{ - TestRenderOutput output; - glBindFramebuffer(GL_FRAMEBUFFER, output.output); - glViewport(0, 0, 640, 480); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Text t {"I Like Trains", *this, {{10, 10}, {200, 40}}, {1, 1, 1}}; - UIShader s {640, 480}; - t.render(s, {}); - Texture::save(output.outImage, "/tmp/text.tga"); -} - BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_CASE(stream_vec) +BOOST_AUTO_TEST_CASE(StreamVec) { BOOST_CHECK_EQUAL(streamed_string(glm::vec3 {1.2, 2.3, 3.4}), "(1.2, 2.3, 3.4)"); } -BOOST_AUTO_TEST_CASE(stream_array) +BOOST_AUTO_TEST_CASE(StreamArray) { BOOST_CHECK_EQUAL(streamed_string(std::array {1.2, 2.3, 3.4}), "(1.2, 2.3, 3.4)"); } -BOOST_AUTO_TEST_CASE(stream_vector) +BOOST_AUTO_TEST_CASE(StreamVector) { BOOST_CHECK_EQUAL(streamed_string(std::vector {1.2, 2.3, 3.4}), "(1.2, 2.3, 3.4)"); } -BOOST_AUTO_TEST_CASE(stream_mat) +BOOST_AUTO_TEST_CASE(StreamMat) { BOOST_CHECK_EQUAL(streamed_string(glm::mat2 {1.2, 2.3, 3.4, 4.5}), "((1.2, 2.3), (3.4, 4.5))"); } diff --git a/test/test-ui.cpp b/test/test-ui.cpp new file mode 100644 index 0000000..2810cda --- /dev/null +++ b/test/test-ui.cpp @@ -0,0 +1,20 @@ +#define BOOST_TEST_MODULE UI +#include <boost/test/unit_test.hpp> +#include <stream_support.h> + +#include "testMainWindow.h" +#include <gfx/models/texture.h> +#include <resource.h> +#include <ui/svgIcon.h> + +constexpr GLsizei RENDER_SIZE = 64; + +BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); + +BOOST_AUTO_TEST_CASE(LoadFromFile) +{ + SvgIcon svg(ImageDimensions {RENDER_SIZE}, Resource::mapPath("ui/icon/rails.svg")); + const auto size = Texture::getSize(svg.texture); + BOOST_CHECK_EQUAL(size, TextureDimensions(RENDER_SIZE, RENDER_SIZE, 1)); + Texture::save(svg.texture, "/tmp/rails.tga"); +} diff --git a/test/test-worker.cpp b/test/test-worker.cpp index cadc366..c259079 100644 --- a/test/test-worker.cpp +++ b/test/test-worker.cpp @@ -1,104 +1,101 @@ #define BOOST_TEST_MODULE test_worker -#include "testHelpers.h" #include <boost/test/unit_test.hpp> #include <set> #include <stream_support.h> #include <worker.h> -uint32_t -workCounter() -{ - static std::atomic_uint32_t n; - usleep(1000); - return n++; -} +namespace { + uint32_t + workCounter() + { + static std::atomic_uint32_t counter; + usleep(1000); + return counter++; + } -void -workVoid() -{ - usleep(1000); -} + void + workVoid() + { + usleep(1000); + } -void -workFail() -{ - usleep(1000); - throw std::runtime_error {"test"}; + void + workFail() + { + usleep(1000); + throw std::runtime_error {"test"}; + } } -BOOST_AUTO_TEST_CASE(basic_slow_counter) +BOOST_AUTO_TEST_CASE(BasicSlowCounter) { - std::vector<Worker::WorkPtrT<uint32_t>> ps; + std::vector<Worker::WorkPtrT<uint32_t>> jobs; for (int i {}; i < 30; ++i) { - ps.push_back(Worker::addWork(workCounter)); + jobs.emplace_back(Worker::addWork(workCounter)); } std::set<uint32_t> out; - std::transform(ps.begin(), ps.end(), std::inserter(out, out.end()), [](auto && p) { - return p->get(); - }); - BOOST_REQUIRE_EQUAL(out.size(), ps.size()); + std::ranges::transform(jobs, std::inserter(out, out.end()), &Worker::WorkItemT<uint32_t>::get); + BOOST_REQUIRE_EQUAL(out.size(), jobs.size()); BOOST_CHECK_EQUAL(*out.begin(), 0); - BOOST_CHECK_EQUAL(*out.rbegin(), ps.size() - 1); + BOOST_CHECK_EQUAL(*out.rbegin(), jobs.size() - 1); } -BOOST_AUTO_TEST_CASE(basic_error_handler) +BOOST_AUTO_TEST_CASE(BasicErrorHandler) { auto workitem = Worker::addWork(workFail); BOOST_CHECK_THROW(workitem->get(), std::runtime_error); } -BOOST_AUTO_TEST_CASE(basic_void_work) +BOOST_AUTO_TEST_CASE(BasicVoidWork) { auto workitem = Worker::addWork(workVoid); BOOST_CHECK_NO_THROW(workitem->get()); } -BOOST_AUTO_TEST_CASE(lambda_void) +BOOST_AUTO_TEST_CASE(LambdaVoid) { - BOOST_CHECK_NO_THROW(Worker::addWork([]() {})->get()); - BOOST_CHECK_NO_THROW(Worker::addWork([](int) {}, 0)->get()); - BOOST_CHECK_NO_THROW(Worker::addWork([](int, int) {}, 0, 0)->get()); + BOOST_CHECK_NO_THROW(Worker::addWork([]() { })->get()); + BOOST_CHECK_NO_THROW(Worker::addWork([](int) { }, 0)->get()); + BOOST_CHECK_NO_THROW(Worker::addWork([](int, int) { }, 0, 0)->get()); } -BOOST_AUTO_TEST_CASE(lambda_value) +BOOST_AUTO_TEST_CASE(LambdaValue) { BOOST_CHECK_EQUAL(1, Worker::addWork([]() { return 1; })->get()); BOOST_CHECK_EQUAL(2, Worker::addWork( - [](int i) { - return i; + [](int value) { + return value; }, 2) ->get()); BOOST_CHECK_EQUAL(3, Worker::addWork( - [](int i, int j) { - return i + j; + [](int valueA, int valueB) { + return valueA + valueB; }, 1, 2) ->get()); } -BOOST_AUTO_TEST_CASE(recursive, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(Recursive, *boost::unit_test::timeout(5)) { auto recurse = []() { - std::vector<Worker::WorkPtrT<uint32_t>> ps; + std::vector<Worker::WorkPtrT<uint32_t>> jobs; for (int i {}; i < 30; ++i) { - ps.push_back(Worker::addWork(workCounter)); + jobs.emplace_back(Worker::addWork(workCounter)); } - return std::accumulate(ps.begin(), ps.end(), 0U, [](auto && out, auto && p) { - return out += p->get(); + return std::ranges::fold_left(jobs, 0U, [](auto && out, auto && job) { + return out += job->get(); }); }; - std::vector<Worker::WorkPtrT<uint32_t>> ps; + std::vector<Worker::WorkPtrT<uint32_t>> jobs; for (int i {}; i < 30; ++i) { - ps.push_back(Worker::addWork(recurse)); + jobs.emplace_back(Worker::addWork(recurse)); } std::set<uint32_t> out; - std::transform(ps.begin(), ps.end(), std::inserter(out, out.end()), [](auto && p) { - return p->get(); - }); + std::ranges::transform(jobs, std::inserter(out, out.end()), &Worker::WorkItemT<uint32_t>::get); } diff --git a/test/testHelpers.cpp b/test/testHelpers.cpp new file mode 100644 index 0000000..7d78cd2 --- /dev/null +++ b/test/testHelpers.cpp @@ -0,0 +1,6 @@ +#include "testHelpers.h" + +const std::filesystem::path ANALYSIS_DIRECTORY = []() { + auto xdgRuntimeDir = getenv("XDG_RUNTIME_DIR"); + return std::filesystem::path {xdgRuntimeDir ? xdgRuntimeDir : "/tmp"} / "ilt-output"; +}(); diff --git a/test/testHelpers.h b/test/testHelpers.h index a261b3d..e1fed97 100644 --- a/test/testHelpers.h +++ b/test/testHelpers.h @@ -11,34 +11,36 @@ template<typename T> decltype(auto) loadFixtureJson(const std::filesystem::path & path) { - std::ifstream in {FIXTURESDIR / path}; - return Persistence::JsonParsePersistence {}.loadState<std::vector<T>>(in); + std::ifstream inputStream {FIXTURESDIR / path}; + return Persistence::JsonParsePersistence {}.loadState<std::vector<T>>(inputStream); } +extern const std::filesystem::path ANALYSIS_DIRECTORY; + #define BOOST_CHECK_CLOSE_VEC(a_, b_) \ { \ - const auto a {a_}, b {b_}; \ - BOOST_TEST_CONTEXT("BOOST_CHECK_CLOSE_VEC(" << std::setprecision(8) << a << ", " << b << ")") { \ - BOOST_CHECK_LT(glm::length(a - b), 0.1F); \ + const auto left_ {a_}, right_ {b_}; \ + BOOST_TEST_CONTEXT("BOOST_CHECK_CLOSE_VEC(" << std::setprecision(8) << left_ << ", " << right_ << ")") { \ + BOOST_CHECK_LT(glm::length(left_ - right_), 0.1F); \ } \ } #define BOOST_CHECK_CLOSE_VECI(a_, b_) \ { \ - const auto a {a_}, b {b_}; \ - BOOST_TEST_CONTEXT("BOOST_CHECK_CLOSE_VEC(" << std::setprecision(8) << a << ", " << b << ")") { \ - BOOST_CHECK_LE(std::abs(a.x - b.x), 1); \ - BOOST_CHECK_LE(std::abs(a.y - b.y), 1); \ - BOOST_CHECK_LE(std::abs(a.z - b.z), 1); \ + const auto left_ {a_}, right_ {b_}; \ + BOOST_TEST_CONTEXT("BOOST_CHECK_CLOSE_VEC(" << std::setprecision(8) << left_ << ", " << right_ << ")") { \ + BOOST_CHECK_LE(std::abs(left_.x - right_.x), 1); \ + BOOST_CHECK_LE(std::abs(left_.y - right_.y), 1); \ + BOOST_CHECK_LE(std::abs(left_.z - right_.z), 1); \ } \ } #define BOOST_CHECK_BETWEEN(a_, b_, c_) \ { \ - const auto a {a_}, b {b_}, c {c_}; \ - BOOST_TEST_CONTEXT("BOOST_CHECK_BETWEEN(" << a << ", " << b << ", " << c << ")") { \ - BOOST_CHECK_LE(b, a); \ - BOOST_CHECK_GE(c, a); \ + const auto value_ {a_}, min_ {b_}, max_ {c_}; \ + BOOST_TEST_CONTEXT("BOOST_CHECK_BETWEEN(" << min_ << " <= " << value_ << " <= " << max_ << ")") { \ + BOOST_CHECK_LE(min_, value_); \ + BOOST_CHECK_GE(max_, value_); \ } \ } #define BOOST_REQUIRE_THEN(VAR, EXPR) \ diff --git a/test/testMainWindow.cpp b/test/testMainWindow.cpp index 4a76044..d048682 100644 --- a/test/testMainWindow.cpp +++ b/test/testMainWindow.cpp @@ -2,7 +2,7 @@ #include <boost/test/test_tools.hpp> #include <format> -TestMainWindow::TestMainWindow() : MainWindow {1, 1, __FILE__, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN} +TestMainWindow::TestMainWindow() : MainWindow {{1, 1}, __FILE__, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN} { glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback( diff --git a/test/testRenderOutput.cpp b/test/testRenderOutput.cpp index 68b46f6..2c74ceb 100644 --- a/test/testRenderOutput.cpp +++ b/test/testRenderOutput.cpp @@ -2,7 +2,7 @@ #include <gl_traits.h> #include <stdexcept> -TestRenderOutput::TestRenderOutput(TextureAbsCoord s) : size {s} +TestRenderOutput::TestRenderOutput(TextureAbsCoord outputSize) : size {outputSize} { glBindFramebuffer(GL_FRAMEBUFFER, output); const auto configuregdata diff --git a/test/testStructures.h b/test/testStructures.h index 4eb4764..61bef68 100644 --- a/test/testStructures.h +++ b/test/testStructures.h @@ -30,7 +30,7 @@ struct TestObject : public Persistence::Persistable { TestObject() = default; float flt {}; - std::string str {}; + std::string str; bool bl {}; RelativePosition3D pos {}; GlobalPosition3D gpos {}; diff --git a/thirdparty/Jamfile.jam b/thirdparty/Jamfile.jam index b6ed163..26497c9 100644 --- a/thirdparty/Jamfile.jam +++ b/thirdparty/Jamfile.jam @@ -28,3 +28,15 @@ lib imguisdl2 : : : <include>imgui ; + +lib lunasvg : + [ glob lunasvg/source/*.cpp lunasvg/plutovg/source/*.c ] + : + <link>static + <include>lunasvg/include + <include>lunasvg/plutovg/include + <warnings>off + <cflags>-fPIC + : : + <include>lunasvg/include + ; diff --git a/thirdparty/imgui b/thirdparty/imgui -Subproject 2db79d0868f7b02d26f7557a72504a0b6f84493 +Subproject b4c96355c9b51b54c4deb52e7d7cdfc7bf79bc2 diff --git a/thirdparty/lunasvg b/thirdparty/lunasvg new file mode 160000 +Subproject f8aabfb444bb37f69df7290790f57e4a27730a9 diff --git a/thirdparty/openmesh/helpers.h b/thirdparty/openmesh/helpers.h index a0105c8..0e29261 100644 --- a/thirdparty/openmesh/helpers.h +++ b/thirdparty/openmesh/helpers.h @@ -7,6 +7,7 @@ namespace OpenMesh { template<typename Iter, typename... IterParams> using IteratorFunction = Iter (OpenMesh::PolyConnectivity::*)(IterParams...) const; +#if OM_GET_VER < 8 template<typename Iter, typename CenterEntityHandle, IteratorFunction<Iter, CenterEntityHandle> BeginFunc, IteratorFunction<Iter, CenterEntityHandle> EndFunc, typename Adaptor> auto @@ -26,6 +27,27 @@ namespace OpenMesh { { return std::views::iota(range.begin(), range.end()) | std::forward<Adaptor>(adaptor); } +#else + template<typename Iter, typename CenterEntityHandle, typename ToEntityHandle, + IteratorFunction<Iter, CenterEntityHandle> BeginFunc, IteratorFunction<Iter, CenterEntityHandle> EndFunc, + typename Adaptor> + auto + operator|(const CirculatorRange<CirculatorRangeTraitT<OpenMesh::PolyConnectivity, Iter, CenterEntityHandle, + ToEntityHandle, BeginFunc, EndFunc>> & range, + Adaptor && adaptor) + { + return std::views::iota(range.begin(), range.end()) | std::forward<Adaptor>(adaptor); + } + + template<typename Iter, IteratorFunction<Iter> BeginFunc, IteratorFunction<Iter> EndFunc, typename Adaptor> + auto + operator|(const EntityRange<RangeTraitT<const OpenMesh::PolyConnectivity, Iter, BeginFunc, EndFunc>> & range, + Adaptor && adaptor) + { + return std::views::iota(range.begin(), range.end()) | std::forward<Adaptor>(adaptor); + } + +#endif namespace Helpers { template<typename Type, template<typename> typename PropertyT> struct Property : public PropertyT<Type> { diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index ab5a998..aff7cd7 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -16,17 +16,17 @@ BuilderFreeExtend::move( { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(*p1, p->pos); + candidateLinks = network->candidateJoins(*p1, p->pos); } else if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateExtend(*p1, p->first); + candidateLinks = network->candidateExtend(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } diff --git a/ui/builders/join.cpp b/ui/builders/join.cpp index 6941e23..f6cbce5 100644 --- a/ui/builders/join.cpp +++ b/ui/builders/join.cpp @@ -15,10 +15,10 @@ BuilderJoin::move(Network * network, const GeoData *, const SDL_MouseMotionEvent { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(p1->pos, p->pos); + candidateLinks = network->candidateJoins(p1->pos, p->pos); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } @@ -33,7 +33,7 @@ BuilderJoin::click( if (p1) { create(network, geoData, p1, p); p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); } else { p1 = p; diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index 338aa8a..e7d83b5 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -17,10 +17,10 @@ BuilderStraight::move( { if (p1) { if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateStraight(*p1, p->first); + candidateLinks = network->candidateStraight(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } @@ -34,7 +34,7 @@ BuilderStraight::click( if (const auto p = geoData->intersectRay(ray)) { if (p1) { create(network, geoData, *p1, p->first); - candidateLinks.removeAll(); + candidateLinks.clear(); p1.reset(); } else { @@ -44,7 +44,7 @@ BuilderStraight::click( return; case SDL_BUTTON_MIDDLE: p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); return; } } diff --git a/ui/editNetwork.cpp b/ui/editNetwork.cpp index 2887491..c900191 100644 --- a/ui/editNetwork.cpp +++ b/ui/editNetwork.cpp @@ -2,25 +2,15 @@ #include "builders/freeExtend.h" #include "builders/join.h" #include "builders/straight.h" -#include "text.h" +#include "imgui_wrap.h" #include <game/gamestate.h> #include <game/terrain.h> #include <gfx/gl/sceneShader.h> #include <gfx/models/texture.h> -const std::filesystem::path fontpath {"/usr/share/fonts/hack/Hack-Regular.ttf"}; constexpr const glm::u8vec4 TRANSPARENT_BLUE {30, 50, 255, 200}; -EditNetwork::EditNetwork(Network * n) : - network {n}, - builderToolbar { - {"ui/icon/network.png", mode.toggle<BuilderStraight>()}, - {"ui/icon/network.png", mode.toggle<BuilderJoin>()}, - {"ui/icon/network.png", mode.toggle<BuilderFreeExtend>()}, - }, - blue {1, 1, &TRANSPARENT_BLUE}, font {fontpath, 15} -{ -} +EditNetwork::EditNetwork(Network * n) : network {n}, blue {1, 1, &TRANSPARENT_BLUE} { } bool EditNetwork::click(const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) @@ -42,9 +32,9 @@ EditNetwork::move(const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> & } bool -EditNetwork::handleInput(const SDL_Event & e, const UIComponent::Position & parentPos) +EditNetwork::handleInput(const SDL_Event &) { - return builderToolbar.handleInput(e, parentPos); + return false; } void @@ -75,10 +65,20 @@ EditNetwork::Builder::setHeightsFor(Network * network, const Link::CCollection & } void -EditNetwork::render(const UIShader & shader, const UIComponent::Position & parentPos) const +EditNetwork::render(bool & open) { - if (builder) { - Text {builder->hint(), font, {{50, 10}, {0, 15}}, {1, 1, 0}}.render(shader, parentPos); - } - builderToolbar.render(shader, parentPos); + ImGui::SetNextWindowSize({-1, -1}); + ImGui::Begin("Edit Network", &open); + + auto builderChoice = [this]<typename Impl>(const char * name) { + if (ImGui::RadioButton(name, dynamic_cast<Impl *>(builder.get()))) { + builder = std::make_unique<Impl>(); + } + }; + builderChoice.operator()<BuilderStraight>("Straight"); + builderChoice.operator()<BuilderJoin>("Join"); + builderChoice.operator()<BuilderFreeExtend>("Free Extend"); + ImGui::TextUnformatted(builder ? builder->hint().c_str() : "Select a build mode"); + + ImGui::End(); } diff --git a/ui/editNetwork.h b/ui/editNetwork.h index ae887bd..4155534 100644 --- a/ui/editNetwork.h +++ b/ui/editNetwork.h @@ -2,8 +2,6 @@ #include "game/geoData.h" #include "gameMainSelector.h" -#include "modeHelper.h" -#include "toolbar.h" #include "worldOverlay.h" #include <game/gamestate.h> #include <game/network/network.h> @@ -17,9 +15,9 @@ public: bool click(const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> &) override; bool move(const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> &) override; - bool handleInput(const SDL_Event & e, const UIComponent::Position &) override; + bool handleInput(const SDL_Event & e) override; void render(const SceneShader &, const Frustum &) const override; - void render(const UIShader & shader, const UIComponent::Position & pos) const override; + void render(bool & open) override; using NetworkClickPos = std::variant<GlobalPosition3D, Node::Ptr>; @@ -36,16 +34,13 @@ public: using Ptr = std::unique_ptr<Builder>; protected: - Collection<const Link> candidateLinks; + SharedCollection<const Link> candidateLinks; }; private: Network * network; Builder::Ptr builder; - Mode<Builder::Ptr, ModeSecondClick::NoAction> mode {builder}; - Toolbar builderToolbar; Texture blue; - const Font font; }; template<typename T> class EditNetworkOf : public EditNetwork { diff --git a/ui/gameMainSelector.cpp b/ui/gameMainSelector.cpp index 23ae8c0..0c40abc 100644 --- a/ui/gameMainSelector.cpp +++ b/ui/gameMainSelector.cpp @@ -1,6 +1,4 @@ #include "gameMainSelector.h" -#include "collection.h" -#include "text.h" #include "ui/uiComponent.h" #include <SDL2/SDL.h> #include <game/gamestate.h> @@ -8,27 +6,21 @@ #include <game/terrain.h> #include <game/worldobject.h> // IWYU pragma: keep #include <gfx/camera.h> -#include <optional> #include <stream_support.h> -#include <typeinfo> -const std::filesystem::path fontpath {"/usr/share/fonts/hack/Hack-Regular.ttf"}; - -GameMainSelector::GameMainSelector(const Camera * c, ScreenAbsCoord size) : - UIComponent {{{}, size}}, camera {c}, font {fontpath, 15} -{ -} +GameMainSelector::GameMainSelector(const Camera * c) : camera {c} { } constexpr ScreenAbsCoord TargetPos {5, 45}; void -GameMainSelector::render(const UIShader & shader, const Position & parentPos) const +GameMainSelector::render() { if (target) { - target->render(shader, parentPos + position + TargetPos); - } - if (!clicked.empty()) { - Text {clicked, font, {{50, 10}, {0, 15}}, {1, 1, 0}}.render(shader, parentPos); + bool open = true; + target->render(open); + if (!open) { + target.reset(); + } } } @@ -41,10 +33,12 @@ GameMainSelector::render(const SceneShader & shader, const Frustum & frustum) co } bool -GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) +GameMainSelector::handleInput(const SDL_Event & e) { - const auto getRay = [this](const auto & e) { - const auto mouse = ScreenRelCoord {e.x, e.y} / position.size; + const auto getRay = [this, &window = e.window](const auto & e) { + glm::ivec2 size {}; + SDL_GetWindowSizeInPixels(SDL_GetWindowFromID(window.windowID), &size.x, &size.y); + const auto mouse = ScreenRelCoord {e.x, e.y} / ScreenRelCoord {size}; return camera->unProject(mouse); }; if (target) { @@ -60,7 +54,7 @@ GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) } break; } - return target->handleInput(e, parentPos + position + TargetPos); + return target->handleInput(e); } else { switch (e.type) { @@ -73,22 +67,8 @@ GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) } void -GameMainSelector::defaultClick(const Ray<GlobalPosition3D> & ray) +GameMainSelector::defaultClick(const Ray<GlobalPosition3D> &) { - BaryPosition baryPos {}; - RelativeDistance distance {}; - - if (const auto selected = gameState->world.applyOne<Selectable>(&Selectable::intersectRay, ray, baryPos, distance); - selected != gameState->world.end()) { - const auto & ref = *selected.base()->get(); - clicked = typeid(ref).name(); - } - else if (const auto pos = gameState->terrain->intersectRay(ray)) { - clicked = streamed_string(*pos); - } - else { - clicked.clear(); - } } bool @@ -104,13 +84,13 @@ GameMainSelector::Component::move(const SDL_MouseMotionEvent &, const Ray<Global } bool -GameMainSelector::Component::handleInput(const SDL_Event &, const Position &) +GameMainSelector::Component::handleInput(const SDL_Event &) { return false; } void -GameMainSelector::Component::render(const UIShader &, const UIComponent::Position &) const +GameMainSelector::Component::render(bool &) { } diff --git a/ui/gameMainSelector.h b/ui/gameMainSelector.h index e715823..8c2be4b 100644 --- a/ui/gameMainSelector.h +++ b/ui/gameMainSelector.h @@ -2,16 +2,13 @@ #include "SDL_events.h" #include "config/types.h" -#include "font.h" #include "uiComponent.h" #include "worldOverlay.h" #include <glm/glm.hpp> #include <memory> -#include <string> class SceneShader; template<typename> class Ray; -class UIShader; class Camera; class GameMainSelector : public UIComponent, public WorldOverlay { @@ -22,17 +19,17 @@ public: virtual bool click(const SDL_MouseButtonEvent &, const Ray<GlobalPosition3D> &); virtual bool move(const SDL_MouseMotionEvent &, const Ray<GlobalPosition3D> &); - virtual bool handleInput(const SDL_Event &, const Position & pos); - virtual void render(const UIShader & shader, const Position & pos) const; + virtual bool handleInput(const SDL_Event &); + virtual void render(bool & open); virtual void render(const SceneShader &, const Frustum &) const; }; - GameMainSelector(const Camera * c, ScreenAbsCoord size); + GameMainSelector(const Camera * c); - void render(const UIShader & shader, const Position & pos) const override; + void render() override; void render(const SceneShader & shader, const Frustum &) const override; - bool handleInput(const SDL_Event & e, const Position &) override; + bool handleInput(const SDL_Event & e) override; void defaultClick(const Ray<GlobalPosition3D> & ray); @@ -40,6 +37,4 @@ public: private: const Camera * camera; - const Font font; - std::string clicked; }; diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index d88bab5..dbbf8a7 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -1,10 +1,10 @@ #include "gameMainWindow.h" #include "editNetwork.h" #include "gameMainSelector.h" +#include "imgui_extras.h" #include "manualCameraController.h" -#include "modeHelper.h" -#include "toolbar.h" -#include "window.h" +#include "queryTool.h" +#include "svgIcon.h" #include <SDL2/SDL.h> #include <collection.h> #include <game/environment.h> @@ -17,26 +17,46 @@ #include <glm/glm.hpp> #include <memory> -class GameMainToolbar : Mode<decltype(GameMainSelector::target)>, public Toolbar { +class GameMainToolbar : public UIComponent { public: - explicit GameMainToolbar(GameMainSelector * gms_) : - Mode<decltype(GameMainSelector::target)> {gms_->target}, - Toolbar { - {"ui/icon/network.png", toggle<EditNetworkOf<RailLinks>>()}, + static constexpr auto TOOLBAR_HEIGHT = 54.F; + template<typename T> static constexpr T TOOLBAR_ICON_SIZE {32, 32}; + + explicit GameMainToolbar(GameMainSelector * gms) : gms {gms} { } + + void + render() override + { + if (IltGui::BeginToolbar("bottomBar", ImGuiDir_Down, TOOLBAR_HEIGHT)) { + if (ImGui::ImageButton("Build rails", *buildRailsIcon, TOOLBAR_ICON_SIZE<ImVec2>)) { + gms->target = std::make_unique<EditNetworkOf<RailLinks>>(); + } + if (ImGui::ImageButton("Query", *queryToolIcon, TOOLBAR_ICON_SIZE<ImVec2>)) { + gms->target = std::make_unique<QueryTool>(); + } + IltGui::EndToolbar(); } + } + + bool + handleInput(const SDL_Event &) override { + return false; } + +private: + SvgIcon buildRailsIcon {TOOLBAR_ICON_SIZE<ImageDimensions>, "ui/icon/rails.svg"}; + SvgIcon queryToolIcon {TOOLBAR_ICON_SIZE<ImageDimensions>, "ui/icon/magnifier.svg"}; + GameMainSelector * gms; }; -GameMainWindow::GameMainWindow(size_t w, size_t h) : WindowContent {w, h}, SceneRenderer {{w, h}, 0} +GameMainWindow::GameMainWindow(ScreenAbsCoord size) : SceneRenderer {size, 0} { uiComponents.create<ManualCameraController>(glm::vec2 {310'727'624, 494'018'810}); - auto gms = uiComponents.create<GameMainSelector>(&camera, ScreenAbsCoord {w, h}); - uiComponents.create<GameMainToolbar>(gms.get()); + auto gms = uiComponents.create<GameMainSelector>(&camera); + uiComponents.create<GameMainToolbar>(gms); } -GameMainWindow::~GameMainWindow() { } - void GameMainWindow::tick(TickDuration) { @@ -63,24 +83,24 @@ GameMainWindow::handleInput(const SDL_Event & event) } void -GameMainWindow::render() const +GameMainWindow::render() { SceneRenderer::render(*this); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); - uiComponents.apply(&UIComponent::render, uiShader, UIComponent::Position {}); + uiComponents.apply(&UIComponent::render); } void GameMainWindow::content(const SceneShader & shader, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { - r->render(shader, frustum); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs<const Renderable>()) { + renderable->render(shader, frustum); } } - gameState->world.apply<Renderable>(&Renderable::render, shader, frustum); + gameState->world.apply<const Renderable>(&Renderable::render, shader, frustum); uiComponents.apply<WorldOverlay>(&WorldOverlay::render, shader, frustum); } @@ -93,16 +113,16 @@ GameMainWindow::environment(const SceneShader &, const SceneRenderer & r) const void GameMainWindow::lights(const SceneShader & shader) const { - gameState->world.apply<Renderable>(&Renderable::lights, shader); + gameState->world.apply<const Renderable>(&Renderable::lights, shader); } void GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { - r->shadows(shadowMapper, frustum); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs<const Renderable>()) { + renderable->shadows(shadowMapper, frustum); } } - gameState->world.apply<Renderable>(&Renderable::shadows, shadowMapper, frustum); + gameState->world.apply<const Renderable>(&Renderable::shadows, shadowMapper, frustum); } diff --git a/ui/gameMainWindow.h b/ui/gameMainWindow.h index b0f1592..71b6314 100644 --- a/ui/gameMainWindow.h +++ b/ui/gameMainWindow.h @@ -3,18 +3,13 @@ #include "chronology.h" #include "gfx/gl/sceneRenderer.h" #include "windowContent.h" -#include <cstddef> class GameMainWindow : public WindowContent, SceneRenderer, public SceneProvider { public: - GameMainWindow(size_t w, size_t h); - ~GameMainWindow() override; - - NO_MOVE(GameMainWindow); - NO_COPY(GameMainWindow); + GameMainWindow(ScreenAbsCoord size); void tick(TickDuration) override; - void render() const override; + void render() override; private: bool handleInput(const SDL_Event &) override; diff --git a/ui/icon.cpp b/ui/icon.cpp index c3b5078..0bdc91a 100644 --- a/ui/icon.cpp +++ b/ui/icon.cpp @@ -22,9 +22,10 @@ Icon::Icon(const Image & tex) : size {tex.width, tex.height} GL_RGBA, GL_UNSIGNED_BYTE, tex.data.data()); } -void -Icon::Bind() const +ImTextureID +Icon::operator*() const { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_texture); + static_assert(sizeof(m_texture) <= sizeof(ImTextureID)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) This is how ImGui works + return reinterpret_cast<ImTextureID>(*m_texture); } @@ -1,5 +1,6 @@ #pragma once +#include "imgui_wrap.h" #include <filesystem> #include <glArrays.h> #include <glm/glm.hpp> @@ -11,8 +12,8 @@ public: explicit Icon(const std::filesystem::path & fileName); explicit Icon(const Image & image); - void Bind() const; const glm::vec2 size; + ImTextureID operator*() const; private: glTexture m_texture; diff --git a/ui/iconButton.cpp b/ui/iconButton.cpp deleted file mode 100644 index fe8c817..0000000 --- a/ui/iconButton.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "iconButton.h" -#include "glArrays.h" -#include "ui/icon.h" -#include "ui/uiComponent.h" -#include <SDL2/SDL.h> -#include <array> -#include <filesystem> -#include <functional> -#include <glad/gl.h> -#include <glm/gtc/type_ptr.hpp> -#include <utility> - -IconButton::IconButton(const std::string & icon_, glm::vec2 position_, UIEvent click_) : - UIComponent {{position_, ICON_SIZE}}, icon {icon_}, click {std::move(click_)} -{ - glBindVertexArray(m_vertexArrayObject); - - glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffer); - glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec4) * 4), nullptr, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), nullptr); - - glBindVertexArray(0); -} - -void -IconButton::render(const UIShader &, const Position & parentPos) const -{ - icon.Bind(); - glBindVertexArray(m_vertexArrayObject); - glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffer); - const auto abs = parentPos.origin + position.origin; - const auto limit = abs + ICON_SIZE; - std::array<glm::vec4, 4> vertices {{ - {abs.x, abs.y, 0, 0}, - {limit.x, abs.y, 1, 0}, - {limit.x, limit.y, 1, 1}, - {abs.x, limit.y, 0, 1}, - }}; - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), glm::value_ptr(vertices.front())); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -bool -IconButton::handleInput(const SDL_Event & e, const Position & parentPos) -{ - const auto absPos = position + parentPos; - if (absPos & e.button) { - if (e.button.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT) { - click(e); - } - } - return false; -} diff --git a/ui/iconButton.h b/ui/iconButton.h deleted file mode 100644 index 0afe92d..0000000 --- a/ui/iconButton.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "icon.h" -#include "uiComponent.h" -#include <glArrays.h> -#include <glm/glm.hpp> -#include <string> - -class UIShader; -union SDL_Event; - -static const constexpr glm::vec2 ICON_SIZE {32.F, 32.F}; - -class IconButton : public UIComponent { -public: - IconButton(const std::string & icon, glm::vec2 position, UIEvent click); - - void render(const UIShader &, const Position & parentPos) const override; - - bool handleInput(const SDL_Event & e, const Position & parentPos) override; - - Icon icon; - UIEvent click; - glVertexArray m_vertexArrayObject; - glBuffer m_vertexArrayBuffer; -}; diff --git a/ui/imgui_extras.cpp b/ui/imgui_extras.cpp new file mode 100644 index 0000000..1643f4f --- /dev/null +++ b/ui/imgui_extras.cpp @@ -0,0 +1,33 @@ +#define IMGUI_INTERNAL +#include "imgui_extras.h" + +namespace IltGui { + bool + BeginToolbar(const char * name, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags) + { + return BeginToolbar(name, ImGui::GetMainViewport(), dir, axisSize, windowFlags); + } + + bool + BeginToolbar( + const char * name, ImGuiViewport * viewport, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags) + { + bool isOpen = ImGui::BeginViewportSideBar(name, viewport, dir, axisSize, + windowFlags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); + if (isOpen) { + if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) { + ImGui::GetCurrentWindow()->DC.LayoutType = ImGuiLayoutType_Horizontal; + } + } + else { + ImGui::End(); + } + return isOpen; + } + + void + EndToolbar() + { + ImGui::End(); + } +} diff --git a/ui/imgui_extras.h b/ui/imgui_extras.h new file mode 100644 index 0000000..0babaa3 --- /dev/null +++ b/ui/imgui_extras.h @@ -0,0 +1,10 @@ +#include "imgui_wrap.h" + +namespace IltGui { + // NOLINTBEGIN(readability-identifier-naming) + bool BeginToolbar(const char * name, ImGuiViewport * viewport, ImGuiDir dir, float axisSize, + ImGuiWindowFlags windowFlags = 0); + bool BeginToolbar(const char * name, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags = 0); + void EndToolbar(); + // NOLINTEND(readability-identifier-naming) +} diff --git a/ui/imgui_wrap.h b/ui/imgui_wrap.h index 1d619a4..520d8b8 100644 --- a/ui/imgui_wrap.h +++ b/ui/imgui_wrap.h @@ -3,4 +3,11 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" #include "imgui.h" // IWYU pragma: export +#ifdef IMGUI_INTERNAL +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# include "imgui_internal.h" // IWYU pragma: export +# pragma GCC diagnostic pop +#endif #pragma GCC diagnostic pop diff --git a/ui/mainApplication.h b/ui/mainApplication.h index a6cb126..1489587 100644 --- a/ui/mainApplication.h +++ b/ui/mainApplication.h @@ -6,7 +6,7 @@ class MainApplication : public ApplicationBase { public: - using Windows = Collection<Window>; + using Windows = SharedCollection<Window>; void mainLoop(); protected: diff --git a/ui/mainWindow.cpp b/ui/mainWindow.cpp index 73c0b5b..57dabc0 100644 --- a/ui/mainWindow.cpp +++ b/ui/mainWindow.cpp @@ -8,12 +8,7 @@ #include "backends/imgui_impl_sdl2.h" #pragma GCC diagnostic pop -MainWindow::MainWindow(size_t w, size_t h) : - MainWindow {w, h, "I Like Trains", SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL} -{ -} - -MainWindow::MainWindow(size_t w, size_t h, const std::string & title, Uint32 flags) : Window {w, h, title, flags} +MainWindow::MainWindow(ScreenAbsCoord size, const char * title, Uint32 flags) : Window {size, title, flags} { if (const auto version = gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)); version < 30003) { throw std::runtime_error {std::format("Insufficient OpenGL version: {}", version)}; diff --git a/ui/mainWindow.h b/ui/mainWindow.h index fe26c5c..d2de9b3 100644 --- a/ui/mainWindow.h +++ b/ui/mainWindow.h @@ -5,12 +5,9 @@ class MainWindow : public Window { public: - MainWindow(size_t w, size_t h); + MainWindow(ScreenAbsCoord size, const char * title, Uint32 flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); ~MainWindow() override; NO_MOVE(MainWindow); NO_COPY(MainWindow); - -protected: - MainWindow(size_t width, size_t height, const std::string & title, Uint32 flags); }; diff --git a/ui/manualCameraController.cpp b/ui/manualCameraController.cpp index fbd0ca3..553afc1 100644 --- a/ui/manualCameraController.cpp +++ b/ui/manualCameraController.cpp @@ -5,7 +5,7 @@ #include <maths.h> bool -ManualCameraController::handleInput(const SDL_Event & e, const Position &) +ManualCameraController::handleInput(const SDL_Event & e) { switch (e.type) { case SDL_KEYDOWN: @@ -72,7 +72,7 @@ ManualCameraController::handleInput(const SDL_Event & e, const Position &) } void -ManualCameraController::render(const UIShader &, const Position &) const +ManualCameraController::render() { } diff --git a/ui/manualCameraController.h b/ui/manualCameraController.h index 2f955e7..6501762 100644 --- a/ui/manualCameraController.h +++ b/ui/manualCameraController.h @@ -6,15 +6,14 @@ #include <glm/glm.hpp> #include <maths.h> -class UIShader; class Camera; class ManualCameraController : public CameraController, public UIComponent { public: - explicit ManualCameraController(GlobalPosition2D f) : UIComponent {{}}, focus {f} { } + explicit ManualCameraController(GlobalPosition2D f) : focus {f} { } - bool handleInput(const SDL_Event & e, const Position &) override; - void render(const UIShader &, const Position & parentPos) const override; + bool handleInput(const SDL_Event & e) override; + void render() override; void updateCamera(Camera * camera) const override; diff --git a/ui/modeHelper.h b/ui/modeHelper.h deleted file mode 100644 index d20f2db..0000000 --- a/ui/modeHelper.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <memory> -union SDL_Event; - -enum ModeSecondClick { Unset, Reset, NoAction }; - -template<typename Target, ModeSecondClick msc = ModeSecondClick::Unset> class Mode { -public: - explicit Mode(Target & t) : target {t} { } - - Target & target; - - template<typename Mode, typename... Params> - auto - toggle(Params &&... params) - { - return [params..., this](const SDL_Event &) { - toggleSetMode<Mode>(std::forward<Params>(params)...); - }; - } - -private: - template<typename Mode, typename... Params> - void - toggleSetMode(Params &&... params) - { - if (dynamic_cast<Mode *>(target.get())) { - if constexpr (msc == ModeSecondClick::Unset) { - target.reset(); - } - if constexpr (msc == ModeSecondClick::Reset) { - target = std::make_unique<Mode>(std::forward<Params>(params)...); - } - } - else { - target = std::make_unique<Mode>(std::forward<Params>(params)...); - } - } -}; diff --git a/ui/queryTool.cpp b/ui/queryTool.cpp new file mode 100644 index 0000000..549bd9e --- /dev/null +++ b/ui/queryTool.cpp @@ -0,0 +1,42 @@ +#include "queryTool.h" +#include "imgui_wrap.h" +#include <game/gamestate.h> +#include <game/selectable.h> +#include <game/terrain.h> +#include <game/worldobject.h> +#include <ray.h> +#include <stream_support.h> + +QueryTool::QueryTool() : clicked {"Click something for details"} { } + +bool +QueryTool::click(const SDL_MouseButtonEvent & event, const Ray<GlobalPosition3D> & ray) +{ + if (event.button != SDL_BUTTON_LEFT) { + return false; + } + BaryPosition baryPos {}; + RelativeDistance distance {}; + + if (const auto selected = gameState->world.applyOne<Selectable>(&Selectable::intersectRay, ray, baryPos, distance); + selected != gameState->world.end()) { + const auto & ref = *selected.base()->get(); + clicked = typeid(ref).name(); + } + else if (const auto pos = gameState->terrain->intersectRay(ray)) { + clicked = streamed_string(*pos); + } + else { + clicked.clear(); + } + return true; +} + +void +QueryTool::render(bool & open) +{ + ImGui::SetNextWindowSize({-1, -1}); + ImGui::Begin("Query Tool", &open); + ImGui::TextUnformatted(clicked.c_str()); + ImGui::End(); +} diff --git a/ui/queryTool.h b/ui/queryTool.h new file mode 100644 index 0000000..74c5380 --- /dev/null +++ b/ui/queryTool.h @@ -0,0 +1,17 @@ +#pragma once + +#include "gameMainSelector.h" + +class QueryTool : public GameMainSelector::Component { +public: + QueryTool(); + +protected: + using GameMainSelector::Component::render; + + bool click(const SDL_MouseButtonEvent &, const Ray<GlobalPosition3D> &) override; + void render(bool & open) override; + +private: + std::string clicked; +}; diff --git a/ui/svgIcon.cpp b/ui/svgIcon.cpp new file mode 100644 index 0000000..499d9cc --- /dev/null +++ b/ui/svgIcon.cpp @@ -0,0 +1,34 @@ +#include "svgIcon.h" +#include "gl_traits.h" +#include <resource.h> + +SvgIcon::SvgIcon(ImageDimensions dim, const std::filesystem::path & path) +{ + const auto svgDoc = lunasvg::Document::loadFromFile(Resource::mapPath(path).native()); + if (!svgDoc) { + throw std::runtime_error("Failed to load SVG from " + path.string()); + } + + auto bitmap = svgDoc->renderToBitmap(dim.x, dim.y); + if (bitmap.isNull()) { + throw std::runtime_error("Failed to render SVG " + path.string()); + } + bitmap.convertToRGBA(); + + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim.x, dim.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap.data()); +} + +ImTextureID +SvgIcon::operator*() const +{ + static_assert(sizeof(glTexture) <= sizeof(ImTextureID)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) This is how ImGui works + return reinterpret_cast<ImTextureID>(*texture); +} diff --git a/ui/svgIcon.h b/ui/svgIcon.h new file mode 100644 index 0000000..106f97c --- /dev/null +++ b/ui/svgIcon.h @@ -0,0 +1,18 @@ +#pragma once + +#include "glArrays.h" +#include "imgui_wrap.h" +#include <config/types.h> +#include <filesystem> +#include <lunasvg.h> + +class SvgIcon { +public: + SvgIcon(ImageDimensions, const std::filesystem::path &); + + ImTextureID operator*() const; + +private: + friend class LoadFromFile; // Test case verifying size/content + glTexture texture; +}; diff --git a/ui/text.cpp b/ui/text.cpp deleted file mode 100644 index bdaaba5..0000000 --- a/ui/text.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "text.h" -#include "font.h" -#include "gfx/gl/uiShader.h" -#include "gfx/gl/vertexArrayObject.h" -#include "uiComponent.h" -#include <array> -#include <collections.h> -#include <glArrays.h> -#include <glm/gtc/type_ptr.hpp> -#include <maths.h> -#include <numeric> -#include <utility> - -Text::Text(std::string_view s, const Font & font, Position pos, glm::vec3 c) : - UIComponent {pos}, colour {c}, font {font} -{ - VertexArrayObject {vao}.addAttribs<Font::Quad::value_type>(quads.bufferName(), 0); - operator=(s); -} - -Text & -Text::operator=(const std::string_view s) -{ - auto tquads = font.render(s); - models.resize(tquads.size()); - const auto glyphCount = std::accumulate(tquads.begin(), tquads.end(), size_t {}, [](auto && init, const auto & q) { - return init += q.second.size(); - }); - quads.resize(glyphCount); - GLushort current = 0; - auto model = models.begin(); - auto quad = quads.begin(); - for (const auto & [texture, fquads] : tquads) { - model->textureId = texture; - model->range.resize(fquads.size() * 6); - for (auto out = model->range.begin(); const auto & q [[maybe_unused]] : fquads) { - static constexpr std::array<GLushort, 6> quadIndices {0, 1, 2, 2, 3, 0}; - std::transform(quadIndices.begin(), quadIndices.end(), out, [current](auto x) { - return current + x; - }); - current += 4; - out += 6; - } - model++; - quad = std::transform(fquads.begin(), fquads.end(), quad, [this](const Font::Quad & q) { - return q * [this](const glm::vec4 & corner) { - return corner + glm::vec4 {this->position.origin, 0, 0}; - }; - }); - } - quads.unmap(); - return *this; -} - -void -Text::render(const UIShader & shader, const Position &) const -{ - shader.text.use(colour); - glActiveTexture(GL_TEXTURE0); - glBindVertexArray(vao); - for (const auto & m : models) { - glBindTexture(GL_TEXTURE_2D, m.textureId); - glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m.range.size()), GL_UNSIGNED_SHORT, m.range.data()); - } - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); -} - -bool -Text::handleInput(const SDL_Event &, const Position &) -{ - return false; -} diff --git a/ui/text.h b/ui/text.h deleted file mode 100644 index a367456..0000000 --- a/ui/text.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "font.h" -#include "glContainer.h" -#include "uiComponent.h" -#include <glArrays.h> -#include <glad/gl.h> -#include <glm/glm.hpp> -#include <string_view> - -class UIShader; -union SDL_Event; - -class Text : public UIComponent { -public: - Text(std::string_view s, const Font &, Position, glm::vec3 colour); - - void render(const UIShader &, const Position & parentPos) const override; - bool handleInput(const SDL_Event &, const Position & parentPos) override; - - Text & operator=(const std::string_view s); - -private: - struct TextData { - GLuint textureId; - std::vector<unsigned short> range; - }; - - std::vector<TextData> models; - glContainer<Font::Quad> quads; - glVertexArray vao; - glm::vec3 colour; - const Font & font; -}; diff --git a/ui/toolbar.cpp b/ui/toolbar.cpp deleted file mode 100644 index 31d87dc..0000000 --- a/ui/toolbar.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "toolbar.h" -#include "gfx/gl/uiShader.h" -#include "ui/iconButton.h" -#include "ui/uiComponent.h" -#include "uiComponentPlacer.h" -#include <SDL2/SDL.h> -#include <glm/glm.hpp> - -Toolbar::Toolbar(const std::initializer_list<InitInfo> & initInfo) : UIComponent {{{}, {}}} -{ - UIComponentPlacer placer {{10, 10}, 5, 1}; - for (const auto & ii : initInfo) { - icons.create(ii.first, placer.next(ICON_SIZE), ii.second); - } - this->position.size = placer.getLimit(); -} - -void -Toolbar::render(const UIShader & uiShader, const Position & parentPos) const -{ - uiShader.icon.use(); - const auto absPos = this->position + parentPos; - icons.apply(&UIComponent::render, uiShader, absPos); -} - -bool -Toolbar::handleInput(const SDL_Event & e, const Position & parentPos) -{ - const auto absPos = this->position + parentPos; - if (absPos & e.button) { - icons.applyOne(&UIComponent::handleInput, e, absPos); - return true; - } - return false; -} diff --git a/ui/toolbar.h b/ui/toolbar.h deleted file mode 100644 index ea560f5..0000000 --- a/ui/toolbar.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "collection.h" -#include "iconButton.h" -#include "uiComponent.h" -#include <initializer_list> -#include <string> -#include <utility> - -class UIShader; -union SDL_Event; - -class Toolbar : public UIComponent { -public: - using InitInfo = std::pair<std::string, UIEvent>; - explicit Toolbar(const std::initializer_list<InitInfo> & initInfo); - - void render(const UIShader & uiShader, const Position & parentPos) const override; - - bool handleInput(const SDL_Event & e, const Position & parentPos) override; - - Collection<IconButton, false> icons; -}; diff --git a/ui/uiComponent.cpp b/ui/uiComponent.cpp deleted file mode 100644 index aa4838d..0000000 --- a/ui/uiComponent.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "uiComponent.h" -#include <SDL2/SDL.h> - -UIComponent::UIComponent(Position position) : position {position} { } - -UIComponent::Position -UIComponent::Position::operator+(const Position & parentPos) const -{ - return *this + parentPos.origin; -} - -UIComponent::Position -UIComponent::Position::operator+(const glm::vec2 & parentPos) const -{ - return {origin + parentPos, size}; -} - -bool -UIComponent::Position::operator&(const glm::vec2 & pos) const -{ - return (pos.x >= origin.x && pos.y >= origin.y && pos.x < origin.x + size.x && pos.y < origin.y + size.y); -} - -bool -UIComponent::Position::operator&(const SDL_MouseButtonEvent & pos) const -{ - switch (pos.type) { - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEBUTTONDOWN: - return *this & glm::vec2 {pos.x, pos.y}; - } - return false; -} diff --git a/ui/uiComponent.h b/ui/uiComponent.h index 71d2659..b2c1a8f 100644 --- a/ui/uiComponent.h +++ b/ui/uiComponent.h @@ -1,32 +1,18 @@ #pragma once -#include <functional> #include <glm/glm.hpp> #include <special_members.h> -class UIShader; union SDL_Event; -struct SDL_MouseButtonEvent; -using UIEvent = std::function<void(const SDL_Event &)>; class UIComponent { public: - struct Position { - glm::vec2 origin, size; - Position operator+(const Position &) const; - Position operator+(const glm::vec2 &) const; - bool operator&(const SDL_MouseButtonEvent &) const; - bool operator&(const glm::vec2 &) const; - }; - - explicit UIComponent(Position); + UIComponent() = default; virtual ~UIComponent() = default; NO_MOVE(UIComponent); NO_COPY(UIComponent); - virtual void render(const UIShader &, const Position & parentPos) const = 0; - virtual bool handleInput(const SDL_Event &, const Position & parentPos) = 0; - - Position position; + virtual void render() = 0; + virtual bool handleInput(const SDL_Event &) = 0; }; diff --git a/ui/uiComponentPlacer.cpp b/ui/uiComponentPlacer.cpp deleted file mode 100644 index 5e645d8..0000000 --- a/ui/uiComponentPlacer.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "uiComponentPlacer.h" -#include <algorithm> - -UIComponentPlacer::UIComponentPlacer(glm::vec2 padding, float spacing, glm::length_t axis) : - padding {padding}, spacing {spacing}, axis {axis}, current {padding[axis]} -{ -} - -glm::vec2 -UIComponentPlacer::next(glm::vec2 size) -{ - glm::vec2 n {}; - n[axis] = current; - n[1 - axis] = padding[1 - axis]; - current += spacing + size[axis]; - max = std::max(max, size[1 - axis]); - return n; -} - -glm::vec2 -UIComponentPlacer::getLimit() const -{ - glm::vec2 n {}; - n[axis] = current + padding[axis]; - n[1 - axis] = max + padding[1 - axis]; - return n; -} diff --git a/ui/uiComponentPlacer.h b/ui/uiComponentPlacer.h deleted file mode 100644 index 1e64f78..0000000 --- a/ui/uiComponentPlacer.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include <glm/glm.hpp> - -class UIComponentPlacer { -public: - UIComponentPlacer(glm::vec2 padding, float spacing, glm::length_t axis = 0); - - glm::vec2 next(glm::vec2 size); - glm::vec2 getLimit() const; - -private: - const glm::vec2 padding; - const float spacing; - const glm::length_t axis; - - float current {}; - float max {}; -}; diff --git a/ui/window.cpp b/ui/window.cpp index 732e9ef..06857b2 100644 --- a/ui/window.cpp +++ b/ui/window.cpp @@ -7,10 +7,9 @@ #include "backends/imgui_impl_sdl2.h" #pragma GCC diagnostic pop -Window::Window(size_t width, size_t height, const std::string & title, Uint32 flags) : - size {static_cast<int>(width), static_cast<int>(height)}, - m_window {title.c_str(), static_cast<int>(SDL_WINDOWPOS_CENTERED), static_cast<int>(SDL_WINDOWPOS_CENTERED), size.x, - size.y, flags}, +Window::Window(ScreenAbsCoord size, const char * title, Uint32 flags) : + m_window {title, static_cast<int>(SDL_WINDOWPOS_CENTERED), static_cast<int>(SDL_WINDOWPOS_CENTERED), size.x, size.y, + flags}, glContext {m_window} { } diff --git a/ui/window.h b/ui/window.h index 62c34de..99a977f 100644 --- a/ui/window.h +++ b/ui/window.h @@ -15,7 +15,7 @@ using SDL_GLContextPtr = wrapped_ptrt<GL_Context, SDL_GL_CreateContext, SDL_GL_D class Window { public: - Window(size_t width, size_t height, const std::string & title, Uint32 flags); + Window(ScreenAbsCoord size, const char * title, Uint32 flags); virtual ~Window() = default; NO_COPY(Window); @@ -27,7 +27,7 @@ public: { glm::ivec2 size {}; SDL_GetWindowSizeInPixels(m_window, &size.x, &size.y); - content = std::make_unique<C>(size.x, size.y, std::forward<P>(p)...); + content = std::make_unique<C>(ScreenAbsCoord {size.x, size.y}, std::forward<P>(p)...); } void tick(TickDuration elapsed); @@ -39,7 +39,6 @@ public: protected: void clear(float r, float g, float b, float a) const; - const ScreenAbsCoord size; SDL_WindowPtr m_window; SDL_GLContextPtr glContext; WindowContent::Ptr content; diff --git a/ui/windowContent.cpp b/ui/windowContent.cpp index 91732a7..0f6dc04 100644 --- a/ui/windowContent.cpp +++ b/ui/windowContent.cpp @@ -1,8 +1,6 @@ #include "windowContent.h" #include "SDL_events.h" -WindowContent::WindowContent(size_t width, size_t height) : uiShader {width, height} { } - void WindowContent::tick(TickDuration) { @@ -27,6 +25,6 @@ WindowContent::handleInput(const SDL_Event & e) eAdjusted.motion.y = size.y - e.motion.y; break; } - uiComponents.rapplyOne(&UIComponent::handleInput, eAdjusted, UIComponent::Position {{}, size}); + uiComponents.rapplyOne(&UIComponent::handleInput, eAdjusted); return true; } diff --git a/ui/windowContent.h b/ui/windowContent.h index 474445a..34cbea3 100644 --- a/ui/windowContent.h +++ b/ui/windowContent.h @@ -2,25 +2,21 @@ #include "chronology.h" #include "collection.h" -#include "gfx/gl/uiShader.h" #include "special_members.h" #include "stdTypeDefs.h" #include "uiComponent.h" // IWYU pragma: keep -#include <functional> class WindowContent : public StdTypeDefs<WindowContent> { public: - using Factory = std::function<Ptr(size_t width, size_t height)>; - WindowContent(size_t width, size_t height); + WindowContent() = default; virtual ~WindowContent() = default; NO_MOVE(WindowContent); NO_COPY(WindowContent); virtual void tick(TickDuration); - virtual void render() const = 0; + virtual void render() = 0; virtual bool handleInput(const SDL_Event & e); protected: - ::Collection<UIComponent> uiComponents; - UIShader uiShader; + UniqueCollection<UIComponent> uiComponents; }; |