mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 10:59:20 +02:00
The track layout library came in as a subproject of its own -- its own CMakeLists, its own install and export rules, its own include tree -- which only made the build harder to follow now that everything else is a module. It is part of the editor, so it lives where the editor lives. - editor/plan_*.cppm / plan_*.cpp: the fifteen interface units and twelve implementation units, flat in editor/ beside editorTerrain and orthophoto, with the plan_ prefix keeping them clear of what was already there (editor/turnouts is the preset catalogue, so the geometry is plan_turnout_geometry). - modules are named eu07.editor.plan_*, like every other module in the tree. - namespaces move under the editor's own: editor::plan, editor::plan::geometry, ::io, ::render. The library's old name is gone from the sources and the docs. - every moved file carries the MPL header the rest of the tree carries; it had none. version.hpp went with the subproject: macros only, and unused. - the umbrella header is dropped -- nothing imported it. - the unit tests survive the fold: editor/tests, behind EU07_EDITOR_PLAN_TESTS (off by default), compiles the interfaces a second time into a small library of its own, because a test can only import modules from a target it links and linking the game would drag the whole simulator in. The code under test needs nothing but a compiler. 7/7 pass. - a switch may now be written with two dashes as well: --editor beside -editor. Builds with clang 22 (-DWITH_BETTER_RENDERER=OFF); -editor and --editor both come up in the editor on empty ground.
133 lines
4.6 KiB
C++
133 lines
4.6 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
module;
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
module eu07.editor.plan_render;
|
|
import eu07.editor.plan_rail_renderer;
|
|
|
|
namespace editor::plan {
|
|
namespace {
|
|
|
|
render::ElementKind kind_of(Kind kind) {
|
|
switch (kind) {
|
|
case Kind::Line: return render::ElementKind::Straight;
|
|
case Kind::Arc: return render::ElementKind::Arc;
|
|
case Kind::Clothoid: return render::ElementKind::Transition;
|
|
}
|
|
return render::ElementKind::Straight;
|
|
}
|
|
|
|
Kind kind_from(render::ElementKind kind) {
|
|
switch (kind) {
|
|
case render::ElementKind::Straight: return Kind::Line;
|
|
case render::ElementKind::Arc: return Kind::Arc;
|
|
case render::ElementKind::Transition: return Kind::Clothoid;
|
|
}
|
|
return Kind::Line;
|
|
}
|
|
|
|
double radius_of(double curvature) {
|
|
return std::abs(curvature) > 1e-9 ? 1.0 / std::abs(curvature) : 0.0;
|
|
}
|
|
|
|
render::CentrelineElement centreline_of(Kind kind, double k0, double k1, double length,
|
|
const std::vector<PlanPoint>& points,
|
|
std::uint32_t element_id, std::uint32_t owner_id,
|
|
bool from_turnout) {
|
|
render::CentrelineElement element;
|
|
element.kind = kind_of(kind);
|
|
element.element_id = element_id;
|
|
element.owner_id = owner_id;
|
|
element.from_turnout = from_turnout;
|
|
element.length = length;
|
|
element.radius_start = radius_of(k0);
|
|
element.radius_end = radius_of(k1);
|
|
element.points.reserve(points.size());
|
|
for (const auto& point : points) {
|
|
element.points.push_back(render::Point{point.x, point.y});
|
|
}
|
|
return element;
|
|
}
|
|
|
|
Kind kind_of_segment(double k0, double k1) {
|
|
if (std::abs(k0) < 1e-12 && std::abs(k1) < 1e-12) {
|
|
return Kind::Line;
|
|
}
|
|
return std::abs(k1 - k0) < 1e-12 ? Kind::Arc : Kind::Clothoid;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::vector<PlanPolyline> render_rails(const Solution& solution, double gauge_metres) {
|
|
render::RailRenderer rail_renderer{gauge_metres};
|
|
render::TrackRenderer& renderer = rail_renderer;
|
|
|
|
// Owner ids are recorded alongside so a rail can be traced back without
|
|
// widening the render layer's vocabulary.
|
|
for (const auto& track : solution.tracks) {
|
|
for (const auto& element : track.elements) {
|
|
if (element.points.size() < 2) {
|
|
continue;
|
|
}
|
|
renderer.add(centreline_of(element.kind, element.k0, element.k1, element.length,
|
|
element.points,
|
|
static_cast<std::uint32_t>(element.id),
|
|
static_cast<std::uint32_t>(track.id), false));
|
|
}
|
|
}
|
|
for (const auto& turnout : solution.turnouts) {
|
|
if (!turnout.valid) {
|
|
continue;
|
|
}
|
|
std::uint32_t index = 0;
|
|
for (const auto& segment : turnout.path) {
|
|
if (segment.points.size() < 2) {
|
|
++index;
|
|
continue;
|
|
}
|
|
renderer.add(centreline_of(kind_of_segment(segment.k0, segment.k1), segment.k0,
|
|
segment.k1, segment.length, segment.points, index++,
|
|
static_cast<std::uint32_t>(turnout.id), true));
|
|
}
|
|
}
|
|
|
|
std::vector<PlanPolyline> out;
|
|
out.reserve(rail_renderer.rails().size());
|
|
for (const auto& rail : rail_renderer.rails()) {
|
|
PlanPolyline polyline;
|
|
polyline.kind = kind_from(rail.kind);
|
|
polyline.length = rail.length;
|
|
polyline.radius_start = rail.radius_start;
|
|
polyline.radius_end = rail.radius_end;
|
|
if (rail.from_turnout) {
|
|
polyline.turnout = static_cast<TurnoutId>(rail.owner_id);
|
|
polyline.piece = rail.element_id;
|
|
const SolvedTurnout* owner = find_turnout(solution, polyline.turnout);
|
|
if (owner != nullptr && polyline.piece < owner->path.size()) {
|
|
polyline.part = static_cast<int>(owner->path[polyline.piece].part);
|
|
}
|
|
} else {
|
|
polyline.element = static_cast<ElementId>(rail.element_id);
|
|
polyline.track = static_cast<TrackId>(rail.owner_id);
|
|
}
|
|
polyline.points.reserve(rail.points.size());
|
|
for (const auto& point : rail.points) {
|
|
polyline.points.push_back(PlanPoint{point.x, point.y});
|
|
}
|
|
out.push_back(std::move(polyline));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
} // namespace editor::plan
|