mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 12:09:19 +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.
192 lines
9.5 KiB
C++
192 lines
9.5 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>
|
||
|
||
export module eu07.editor.plan_turnout_geometry;
|
||
export import eu07.editor.plan_segment;
|
||
|
||
export {
|
||
|
||
namespace editor::plan {
|
||
|
||
/// The crossing mark of a turnout — the skos 1:n. It is the tangent of the angle
|
||
/// between the through and diverging tracks, so it fixes the total angle the
|
||
/// diverging curve must turn through: a turnout is 1:9, 1:12, 1:18,5 and so on.
|
||
/// Nothing is fitted to it any more: it is what the laid path is checked against.
|
||
class CrossingMark {
|
||
public:
|
||
explicit CrossingMark(double denominator) noexcept : denominator_{denominator} {}
|
||
|
||
[[nodiscard]] double denominator() const noexcept { return denominator_; }
|
||
/// The angle between the tracks, radians. Zero for a non-positive mark.
|
||
[[nodiscard]] double angle() const noexcept;
|
||
|
||
private:
|
||
double denominator_;
|
||
};
|
||
|
||
/// Which way the branch leaves the through track.
|
||
enum class DivergeSide { Left, Right };
|
||
|
||
/// Which part of the turnout a piece of the diverging path is. The role names the
|
||
/// piece in switch terms; it does not constrain its shape — a blade may be straight
|
||
/// (iglica prosta sieczna) or an arc (iglica łukowa styczna).
|
||
enum class TurnoutPart {
|
||
PreBlade, ///< odcinek przediglicowy: PR to the blade tip, along the through track
|
||
Blade, ///< iglica, from its (theoretical) tip to its heel
|
||
Curve, ///< the diverging curve between the blade heel and the frog rail
|
||
FrogRail, ///< the straight frog rail out to KR
|
||
};
|
||
|
||
/// One authored piece of the diverging path, in order from PR. Every length is
|
||
/// explicit — the catalogue says it, and nothing here is solved for. A piece is a
|
||
/// straight when both radii are zero, an arc when they are equal, a clothoid
|
||
/// otherwise; radii are unsigned, the hand comes from DivergeSide.
|
||
struct TurnoutPiece {
|
||
TurnoutPart part{TurnoutPart::Curve};
|
||
double length{0.0};
|
||
double radius_start{0.0}; ///< 0 = straight
|
||
double radius_end{0.0}; ///< != radius_start = clothoid
|
||
/// Kąt nagięcia iglicy (beta): the heading break applied before this piece,
|
||
/// radians, unsigned. Zero everywhere for a blade tangent to the stock rail.
|
||
double turn_in{0.0};
|
||
};
|
||
|
||
/// Iglica — not the shape of the axis (that is a TurnoutPiece), but the two
|
||
/// dimensions of the blade rail itself that place its real tip. A blade cannot be
|
||
/// ground to nothing, so its tip is cut back to a thickness and the point where
|
||
/// the axis leaves the stock rail is only a theoretical one.
|
||
struct Blade {
|
||
double tip_thickness{0.0}; ///< u: the blade's width where its planed nose ends
|
||
double nose{0.0}; ///< d: the planed nose itself, from the tip to that point
|
||
double railtop_width{0.0}; ///< w: railhead width, metres (49E1 ≈ 0,070)
|
||
};
|
||
|
||
/// Half the standard gauge. The gap the blade opens is taken between two rails,
|
||
/// and the blade's own curve is `R + s/2` where R is the diverging track's radius
|
||
/// (Koc, 5.2), so this is what turns a gap into an angle. Stations stay on the
|
||
/// axis: the rail radius converts, it does not measure.
|
||
inline constexpr double kHalfGauge = 0.7175;
|
||
|
||
/// A turnout (rozjazd), described the way a catalogue drawing is: the crossing
|
||
/// mark and length it is meant to work out to, and the list of pieces the
|
||
/// diverging path is actually laid from.
|
||
struct Turnout {
|
||
CrossingMark mark;
|
||
DivergeSide side{DivergeSide::Left};
|
||
std::vector<TurnoutPiece> path; ///< PR -> KR, in order; nothing implicit
|
||
/// Catalogue length: a + b, the two tangent legs of the drawing — PR to the
|
||
/// theoretical crossing point along the through track, and that point to KR
|
||
/// along the diverging one. This is the L the type tables give (Rz 190-1:9:
|
||
/// a 13,834 + b 13,304 = 27,138), not the projection on the through track.
|
||
double length{0.0};
|
||
Blade blade{};
|
||
};
|
||
|
||
/// One laid segment of the diverging path: a piece with its curvature signed for
|
||
/// the hand it diverges to, ready for layout_segment.
|
||
struct TurnoutSegment {
|
||
double turn_in{0.0}; ///< signed heading break applied before laying this segment
|
||
double k0{0.0};
|
||
double k1{0.0};
|
||
double length{0.0};
|
||
TurnoutPart part{TurnoutPart::Curve};
|
||
};
|
||
|
||
/// What a turnout works out to when laid from its start pose.
|
||
struct TurnoutGeometry {
|
||
bool valid{false};
|
||
std::vector<TurnoutSegment> path; ///< diverging segments from the switch start to the frog
|
||
geometry::Pose frog{}; ///< the end (koniec rozjazdu): position + heading
|
||
double tangent_front{0.0}; ///< theoretical tangent length, switch start to the crossing point
|
||
double tangent_back{0.0}; ///< theoretical tangent length, crossing point to the frog
|
||
double diverging_length{0.0}; ///< total length of the diverging path
|
||
/// Where the turnout ends as measured on the through track: the frog projected
|
||
/// back onto it. This is the point that closes the switch on the straight — or
|
||
/// on the arc, when the turnout is bent.
|
||
geometry::Pose through_end{};
|
||
double through_length{0.0};
|
||
|
||
// --- łukowanie ----------------------------------------------------------
|
||
/// Signed curvature of the through track the turnout was laid against, and
|
||
/// what the diverging path came out at. Both zero for a rozjazd zwyczajny;
|
||
/// `diverging_curvature` is zero too when a two-sided bend straightens the
|
||
/// branch out. Positive turns left.
|
||
double bend{0.0};
|
||
double diverging_curvature{0.0};
|
||
/// Beta: how far the through track turns over the turnout. The bend is this
|
||
/// angle as much as it is a radius, and it is what the crossing angle is
|
||
/// measured from once the tor zasadniczy no longer runs straight.
|
||
double bend_angle{0.0};
|
||
|
||
// --- the blade, as dimensioned rather than as laid ----------------------
|
||
// The curve is theoretically tangent to the stock rail at the switch start, but
|
||
// the blade only begins where there is metal enough to begin it. What runs from
|
||
// PR to that tip is the odcinek przediglicowy: the curve is already there, the
|
||
// vehicle is still on the through track.
|
||
bool has_blade{false};
|
||
geometry::Pose blade_tip{}; ///< ostrze iglicy, where the blade piece starts
|
||
geometry::Pose blade_nose_end{}; ///< A: where the planed nose ends and the blade is u thick
|
||
geometry::Pose blade_planed{}; ///< where the blade reaches full railhead width
|
||
double blade_station{0.0}; ///< a: PR -> the tip, the odcinek przediglicowy
|
||
double nose_end{0.0}; ///< station of A, theoretically a + d
|
||
double planing_length{0.0}; ///< struganie: the tip to full railhead width
|
||
double planing_end{0.0}; ///< station where the planing ends
|
||
double blade_angle{0.0}; ///< delta: kąt przylegania, the tangent angle at A
|
||
/// (a + d) minus where the rail really stands `u` off the stock rail. Zero means
|
||
/// the type's own a, d and u agree with the curve it is laid on.
|
||
double blade_residual{0.0};
|
||
|
||
// --- what the catalogue numbers say about all that ----------------------
|
||
/// Total deflection of the path minus the crossing angle. Zero = the path
|
||
/// closes on the mark; non-zero is reported, never fitted away.
|
||
double angle_residual{0.0};
|
||
/// The laid tangent legs (a + b) minus the catalogue length.
|
||
double length_residual{0.0};
|
||
};
|
||
|
||
/// How far along an axis of radius @p radius, leaving the stock rail at angle
|
||
/// @p angle, the blade stands @p offset away from it: `R·(acos(cos β − offset/R1) − β)`
|
||
/// for a curved blade, `offset / sin β` for a straight one. The gap opens on the
|
||
/// blade's own curve `R1 = R + s/2`, but the answer is a station on the axis, which
|
||
/// is what every length here is measured in.
|
||
///
|
||
/// This is the relation the catalogue's own numbers obey. A drawing dimensions the
|
||
/// blade on the rail rather than on the axis, so what Koc's Tablica 5.1 gives as
|
||
/// `a + d` is this run scaled by `R1/R` — at 1200-1:18,5 the two agree to a tenth
|
||
/// of a millimetre. Zero when the curve neither turns nor breaks away, or when the
|
||
/// offset is out of its reach.
|
||
[[nodiscard]] double blade_run_to_offset(double radius, double angle, double offset);
|
||
|
||
/// Lays @p turnout from @p start. @p bend is the signed curvature (1/m, positive
|
||
/// turns left) of the through track under it: zero for a rozjazd zwyczajny, and
|
||
/// anything else for a rozjazd łukowy.
|
||
///
|
||
/// Bending is not adding curvature. Koc, 5.3.6: it rotates the triangle OBC about
|
||
/// the centre of the turnout, so the crossing angle alfa and the tangent
|
||
/// `t = R·tg(alfa/2)` come through untouched and every radius follows from them —
|
||
/// the through track turns by `beta = 2·arctg(t·bend)`, the diverging path by
|
||
/// `beta + alfa`, and each gets the radius `t/tg(its own angle/2)`. That is where
|
||
/// Koc's (5.7) and (5.8) come from, and it is why a Rz 49E1-190-1:9 bent
|
||
/// symmetrically is the catalogue's Rłs 49E1-380,292-1:9.
|
||
///
|
||
/// The switch and the frog are not re-cut, only bent: a straight krzyżownica stays
|
||
/// straight and the blade keeps its own dimensions, measured — as on the ground —
|
||
/// against the opornica that curves with it.
|
||
///
|
||
/// Invalid when the path has no arc to bend or its arcs disagree on a radius:
|
||
/// there is then no single triangle to rotate, and guessing one would be a lie.
|
||
[[nodiscard]] TurnoutGeometry lay_turnout(const geometry::Pose& start, const Turnout& turnout,
|
||
double bend = 0.0);
|
||
|
||
} // namespace editor::plan
|
||
|
||
} // export
|