17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 06:19:19 +02:00
Files
maszyna/maj0sted/src/domain/niweleta.cpp
maj00r a083269867 Vendor the maj0sted track layout library and link it into the editor
Drop-in copy of C:\Users\maj00r\Projects\maj0sted-master minus its web/
directory - the WASM frontend is not being ported, the editor drives the
same C++ API natively. What is left is plain C++20 with no dependencies:
domain model (niweleta, plan and profile geometry, fitting, parallelism),
project serialization, rail sampling and the editor document.

src/web/ribbon.cpp was missing from the library source list; added, since
without it the ribbon symbols never make it into the archive.
2026-08-27 19:36:55 +02:00

91 lines
3.2 KiB
C++

#include "maj0sted/domain/niweleta.hpp"
#include <cmath>
#include <cstddef>
#include <stdexcept>
#include <utility>
#include <variant>
namespace maj0sted::domain {
Niweleta::Niweleta(NiweletaId id, std::string name)
: id_{id}, name_{std::move(name)} {}
StraightId Niweleta::next_straight_id() noexcept { return StraightId{++straight_seq_}; }
StraightId Niweleta::add_straight(CartesianPosition start, CartesianPosition end,
JointContinuity joint) {
const StraightId id = next_straight_id();
plan_.append(Straight{id, start, end}, joint);
return id;
}
void Niweleta::add_plan_element(PlanElement element, JointContinuity joint) {
if (auto* straight = std::get_if<Straight>(&element); straight != nullptr) {
if (straight->id().is_null()) {
// A straight added without an identity gets one from the niweleta.
element = Straight{next_straight_id(), straight->start(), straight->end()};
} else if (straight->id().value() > straight_seq_) {
// Keep the counter ahead of any explicitly supplied id (e.g. on load).
straight_seq_ = straight->id().value();
}
}
plan_.append(std::move(element), joint);
}
void Niweleta::apply_fit(const FitResult& result) {
const StraightId entry_id = result.entry.id();
const StraightId exit_id = result.exit.id();
if (entry_id.is_null() || exit_id.is_null()) {
throw std::invalid_argument{"apply_fit: trimmed straights must be identified"};
}
const auto& elements = plan_.elements();
const auto index_of = [&](StraightId id) -> std::size_t {
for (std::size_t k = 0; k < elements.size(); ++k) {
const auto* straight = std::get_if<Straight>(&elements[k]);
if (straight != nullptr && !straight->id().is_null() &&
straight->id() == id) {
return k;
}
}
throw std::invalid_argument{"apply_fit: straight not found in plan"};
};
const std::size_t i = index_of(entry_id);
const std::size_t j = index_of(exit_id);
if (j != i + 1) {
throw std::invalid_argument{
"apply_fit: the two straights must be adjacent (entry then exit)"};
}
// Rebuild the plan, re-validating every joint via append().
HorizontalAlignment rebuilt;
const auto add = [&](PlanElement element, JointContinuity joint) {
rebuilt.append(std::move(element), joint);
};
for (std::size_t k = 0; k < i; ++k) {
add(elements[k], k == 0 ? JointContinuity::Tangent : plan_.joint(k - 1));
}
add(result.entry, i == 0 ? JointContinuity::Tangent : plan_.joint(i - 1));
for (const auto& curve_element : result.curve) {
add(curve_element, JointContinuity::Tangent);
}
add(result.exit, JointContinuity::Tangent);
for (std::size_t k = j + 1; k < elements.size(); ++k) {
add(elements[k], plan_.joint(k - 1));
}
plan_ = std::move(rebuilt);
}
void Niweleta::add_profile_element(ProfileElement element) {
profile_.append(std::move(element));
}
bool Niweleta::lengths_consistent(double tolerance_m) const {
return std::abs(plan_length().metres() - profile_length().metres()) <= tolerance_m;
}
} // namespace maj0sted::domain