17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 13:19:20 +02:00
Files
maszyna/editor/plan_sketch.cppm
maj00r 321c9dd7ab Editor: fold the plan library into the tree as modules, drop its own name
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.
2026-09-13 13:31:01 +02:00

138 lines
6.2 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 <string>
#include <vector>
export module eu07.editor.plan_sketch;
export import eu07.editor.plan_model;
export import eu07.editor.plan_solution;
export {
namespace editor::plan {
/// Laying track by clicking along the ground.
///
/// The corner is worked out here, at the click, and written into the document as
/// ordinary elements with ordinary lengths — a straight shortened by the
/// tangent, an arc of the radius asked for, a straight running out to the point
/// clicked. Nothing is left pointing at the drawing, so the numbers in the
/// document are the numbers on the ground.
/// What rounding one corner costs.
struct Corner {
bool ok{false};
/// How far back from the corner the arc starts, and how far past it the arc
/// ends: R·tan(|Δ|/2). Both straights lose this much.
double tangent{0.0};
double arc_length{0.0}; ///< R·|Δ|
int hand{0}; ///< +1 left, -1 right
double deflection{0.0}; ///< Δ, radians, signed, + = left
};
/// Rounds the corner between a heading in and a heading out with an arc of
/// @p radius. Both headings are unit vectors in the plan's frame.
///
/// Not ok for a corner that does not bend, one that doubles back on itself, or a
/// non-positive radius: none of those is an arc, and guessing one is how a fit
/// used to quietly lay geometry nobody asked for.
[[nodiscard]] Corner fit_corner(double in_hx, double in_hy, double out_hx, double out_hy,
double radius);
/// The three elements a straight-arc-straight corner is made of, ready to append
/// after @p incoming has been shortened by `corner.tangent`.
[[nodiscard]] Element make_arc(Document& document, double radius, int hand, double length);
[[nodiscard]] Element make_line(Document& document, double length);
[[nodiscard]] Element make_clothoid(Document& document, double end_radius, int hand,
double length);
/// Carries a change of radius or hand across the joint it belongs to.
///
/// A transition curve's radius is the radius it ends on, so it belongs to
/// whatever comes after it: the arc it eases into, or nothing at all when a
/// straight follows and it runs out flat at R=0. That is one number, not two, so
/// editing either side of the joint carries the other with it — and the joint
/// only ever looks forward. The transition behind an arc is the one that eases
/// into it; the one in front of it eases into whatever is beyond, which is none
/// of that arc's business.
void sync_joint(Track& track, std::size_t at);
// ---------------------------------------------------------------------------
// Straights as lines, and the arcs between them
// ---------------------------------------------------------------------------
/// The line a straight lies on: a point on it and the direction of travel.
///
/// This is what a straight really is once it is drawn — where it lies and which
/// way it points. Its length is not part of it: that falls out of where the arcs
/// at either end come off it.
struct Support {
double px{0.0}, py{0.0};
double dx{1.0}, dy{0.0};
};
/// What a track hangs on: the line under every one of its straights, and where
/// the first and last of them begin and end.
///
/// The corners between two straights are not in here, because they do not have
/// to be: given the lines, each of them is worked out. A corner hanging off
/// either end of the chain — a track that starts or finishes mid-curve — is not
/// in here either, for the opposite reason: it has nothing on its far side to be
/// fitted against, so it is carried along exactly as it was authored.
struct Skeleton {
std::vector<Support> lines;
double start_x{0.0}, start_y{0.0}; ///< where the first straight begins
double end_x{0.0}, end_y{0.0}; ///< where the last straight ends
/// Where the chain has to begin, and whether that is a place anybody may
/// move. A branch on a turnout's port may not: its first curve is laid from
/// the frog and the line under the straight after it follows wherever that
/// curve comes out, rather than the curve being slid along a line drawn
/// before the edit.
bool pinned{false};
geometry::Pose begins{};
};
/// Pulls @p track's skeleton out of @p solved.
///
/// A chain must read as straights with corners between them — a corner being any
/// run of arcs and transition curves that turns one way, one arc or a basket of
/// them. Two straights may never touch: two straights end to end are one
/// straight. A track with no straight at all has no skeleton, and says so.
[[nodiscard]] bool skeleton_of(const Track& track, const SolvedTrack& solved,
Skeleton& out, std::string& why);
/// Rewrites @p track to run along @p skeleton, keeping every arc's radius and
/// hand, and every transition curve's length.
///
/// Each corner with a straight on both sides is fitted: the transitions turn what
/// they turn, the arcs share out what is left of the corner in the proportions
/// they were drawn with — so a basket curve stays that basket, tighter or wider —
/// and the whole corner is then slid along the line it leaves until it lands on
/// the line it joins. The turn is taken the way the arcs go, so a corner may run
/// past a half circle, up to a full one, and nothing shortens it to the way round
/// that happens to be shorter.
///
/// A corner hanging off either end of the chain keeps exactly what it was given:
/// there is no second line to fit it against, so a track ending mid-curve simply
/// ends where that curve takes it.
///
/// The skeleton's ends stay put and the anchor follows them — unless the track is
/// pinned to a turnout's port, which is not an anchor anyone may move: a change
/// that would drag the branch off its frog is refused instead.
///
/// False, with a reason, when the skeleton cannot carry the chain. Nothing is
/// written to @p track unless all of it works out.
[[nodiscard]] bool lay_along_skeleton(Track& track, const Skeleton& skeleton,
std::string& why);
} // namespace editor::plan
} // export