17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 01:02:26 +01:00

Editor checkpoint: turnout domain service, web->editor rename, Id-1 templates

Checkpoint of the editor branch so work isn't carried loose across a branch
switch. Builds.

- maj0sted turnout geometry moved into the domain: domain::lay_turnout lays the
  diverging path (pre-blade lead, blade at beta, arc of alfa-beta, frog rail) and
  solve_junction is a thin adapter onto it.
- the maj0sted web/ tree renamed to editor/ (namespace and paths) - there is no
  WASM here, it's plain native C++ the editor calls.
- click places the switch at its start (początek rozjazdu); the branch's leaving
  straight is locked tangent to the frog; switches can be slid along the through
  track and flipped L/P.
- Polish turnout templates carry catalogue length (Id-1-consistent lengths), plus
  blade-angle and pre-blade fields (left 0 until the catalogue figures are in).
- TURNOUT_SPEC.md: the Bezier-based runtime/model contract.
- also folds in other pending branch changes (scenery, tests) to leave a clean tree.
This commit is contained in:
maj00r
2026-08-01 19:55:33 +02:00
committed by maj00r
parent d069743265
commit 707b4de2f4
41 changed files with 2224 additions and 471 deletions

View File

@@ -136,11 +136,19 @@ bool http_get(std::string const &, std::vector<std::uint8_t> &, std::string &)
} // namespace
orthophoto_source::orthophoto_source()
orthophoto_source::orthophoto_source(maj0sted::editor::WmsConfig Config, int const Gridzoom, std::string Cachesubdir)
: m_service([&] {
if (Gridzoom >= maj0sted::editor::TileGrid::kCellZoomMin)
{
Config.tile_pixels = 512; // coarse cells do not need orto's 1024 px
}
return std::move(Config);
}()),
m_gridzoom(Gridzoom), m_cachesubdir(std::move(Cachesubdir))
{
// the library asks for a URL and expects the answer whenever it arrives; the transfer is queued
// here and handed back on the thread that drains it, so the service stays single threaded
m_service.set_fetcher([this](std::string const &Url, maj0sted::web::TileService::DoneFn Done) {
m_service.set_fetcher([this](std::string const &Url, maj0sted::editor::TileService::DoneFn Done) {
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queued.push_back({Url, std::move(Done), false, {}, {}});
@@ -210,7 +218,7 @@ void orthophoto_source::worker()
}
}
std::vector<orthophoto_source::ready_tile> orthophoto_source::collect(maj0sted::web::TileBBox const &View, int const Maxtiles)
std::vector<orthophoto_source::ready_tile> orthophoto_source::collect(maj0sted::editor::TileBBox const &View, int const Maxtiles)
{
// hand finished transfers to the library here, on the caller's thread
std::deque<transfer> arrived;
@@ -227,8 +235,10 @@ std::vector<orthophoto_source::ready_tile> orthophoto_source::collect(maj0sted::
--m_inflight;
}
std::vector<ready_tile> tiles;
for (auto const &key : maj0sted::web::TileGrid::tiles_for_view(View, maj0sted::web::TileGrid::kLevel, Maxtiles))
// fetch only what fits the budget for the current view. the window is recentred when the
// view is larger than the cap, and zoom-to-cursor moves the camera, so the set of keys asked
// for changes from frame to frame - that must not decide what is drawn
for (auto const &key : maj0sted::editor::TileGrid::tiles_for_view(View, m_gridzoom, Maxtiles))
{
// a tile kept from an earlier session seeds the cache, so no request goes out for it at all
if (false == m_service.has(key))
@@ -237,33 +247,51 @@ std::vector<orthophoto_source::ready_tile> orthophoto_source::collect(maj0sted::
}
auto const status = m_service.request(key);
if (status == maj0sted::web::TileStatus::Failed)
if (status == maj0sted::editor::TileStatus::Failed)
{
++m_failed;
// counted once per tile, not once per frame it stays unavailable
m_failedkeys.insert(key);
continue;
}
if (status != maj0sted::web::TileStatus::Ready)
if (status != maj0sted::editor::TileStatus::Ready)
{
continue;
}
auto found = m_textures.find(key);
if (found == m_textures.end())
if (m_textures.find(key) == m_textures.end())
{
// the first time a tile is drawn is also the moment its bytes are known to be good
// the first time a tile is ready is also the moment its bytes are known to be good
save_to_disk(key);
found = m_textures.emplace(key, upload(key)).first;
m_textures.emplace(key, upload(key));
}
if (found->second != 0)
}
// draw every texture we already hold that still covers the view. returning only the fetch
// window made tiles blink out during zoom as soon as they fell outside the cap, then blink
// back in once the window moved over them again
auto const overlaps = [](maj0sted::editor::TileBBox const &Left, maj0sted::editor::TileBBox const &Right) {
return Left.min_x < Right.max_x && Left.max_x > Right.min_x && Left.min_y < Right.max_y && Left.max_y > Right.min_y;
};
std::vector<ready_tile> tiles;
tiles.reserve(m_textures.size());
for (auto const &[key, texture] : m_textures)
{
if (texture == 0)
{
tiles.push_back({maj0sted::web::TileGrid::bbox(key), found->second});
continue;
}
auto const box = maj0sted::editor::TileGrid::bbox(key);
if (overlaps(box, View))
{
tiles.push_back({box, texture});
}
}
return tiles;
}
unsigned int orthophoto_source::upload(maj0sted::web::TileKey const &Key)
unsigned int orthophoto_source::upload(maj0sted::editor::TileKey const &Key)
{
auto const *image = m_service.peek(Key);
if (image == nullptr || image->bytes.empty())
@@ -296,12 +324,12 @@ unsigned int orthophoto_source::upload(maj0sted::web::TileKey const &Key)
return texture;
}
std::string orthophoto_source::tile_path(maj0sted::web::TileKey const &Key)
std::string orthophoto_source::tile_path(maj0sted::editor::TileKey const &Key) const
{
return "editor/cache/orto/" + std::to_string(Key.zoom) + "_" + std::to_string(Key.x) + "_" + std::to_string(Key.y) + ".img";
return "editor/cache/" + m_cachesubdir + "/" + std::to_string(Key.zoom) + "_" + std::to_string(Key.x) + "_" + std::to_string(Key.y) + ".img";
}
bool orthophoto_source::load_from_disk(maj0sted::web::TileKey const &Key)
bool orthophoto_source::load_from_disk(maj0sted::editor::TileKey const &Key)
{
std::ifstream file(tile_path(Key), std::ios::binary);
if (false == file.is_open())
@@ -320,7 +348,7 @@ bool orthophoto_source::load_from_disk(maj0sted::web::TileKey const &Key)
return true;
}
void orthophoto_source::save_to_disk(maj0sted::web::TileKey const &Key)
void orthophoto_source::save_to_disk(maj0sted::editor::TileKey const &Key)
{
auto const *image = m_service.peek(Key);
if (image == nullptr || image->bytes.empty())
@@ -350,7 +378,7 @@ std::size_t orthophoto_source::pending() const
std::size_t orthophoto_source::failed() const
{
return m_failed;
return m_failedkeys.size();
}
namespace
@@ -383,7 +411,7 @@ unsigned int upload_image(std::vector<std::uint8_t> const &Bytes)
return texture;
}
bool same_box(maj0sted::web::TileBBox const &Left, maj0sted::web::TileBBox const &Right)
bool same_box(maj0sted::editor::TileBBox const &Left, maj0sted::editor::TileBBox const &Right)
{
auto const close = [](double const A, double const B) { return std::abs(A - B) < 1.0; };
return close(Left.min_x, Right.min_x) && close(Left.min_y, Right.min_y) && close(Left.max_x, Right.max_x) && close(Left.max_y, Right.max_y);
@@ -391,7 +419,7 @@ bool same_box(maj0sted::web::TileBBox const &Left, maj0sted::web::TileBBox const
} // namespace
wms_image::wms_image(maj0sted::web::WmsConfig Config) : m_config(std::move(Config))
wms_image::wms_image(maj0sted::editor::WmsConfig Config) : m_config(std::move(Config))
{
m_worker = std::thread([this]() { worker(); });
}
@@ -446,7 +474,7 @@ void wms_image::worker()
}
}
unsigned int wms_image::texture_for(maj0sted::web::TileBBox const &Box, int const Pixels)
unsigned int wms_image::texture_for(maj0sted::editor::TileBBox const &Box, int const Pixels)
{
// a finished download becomes the texture on this thread, where the graphics API is safe to touch
{
@@ -482,7 +510,7 @@ unsigned int wms_image::texture_for(maj0sted::web::TileBBox const &Box, int cons
config.tile_pixels = Pixels;
{
std::lock_guard<std::mutex> lock(m_mutex);
m_url = maj0sted::web::wms_getmap_url(config, Box);
m_url = maj0sted::editor::wms_getmap_url(config, Box);
m_haswork = true;
}
m_loading = true;