17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-02 05:49:13 +01:00

Merge remote-tracking branch 'origin/feat/switch-blades' into feat/renderers-cleanup

This commit is contained in:
2026-10-29 23:55:04 +01:00
145 changed files with 36946 additions and 3791 deletions

View File

@@ -89,15 +89,11 @@ double path_radius(segment_data const &Path, double const T)
{
if (Path.points[segment_data::point::control1] == glm::dvec3{} && Path.points[segment_data::point::control2] == glm::dvec3{})
return 0.0;
bezier const curve{Path};
auto const first{curve.first(T)};
auto const second{curve.second(T)};
auto const speed{std::hypot(first.x, first.z)};
auto const curvature{std::abs(first.x * second.z - first.z * second.x) / (speed * speed * speed)};
auto const curvature{std::abs(bezier{Path}.plan_curvature(T))};
return curvature > 1e-7 ? 1.0 / curvature : 0.0;
}
bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool const Leaving, glm::dvec3 &Direction, double &Radius)
bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool const Leaving, glm::dvec3 &Direction, double &Radius, int &Turn)
{
if (Neighbour == nullptr)
return false;
@@ -108,15 +104,118 @@ bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool
auto const &point{path.points[atend ? segment_data::point::end : segment_data::point::start]};
if (glm::distance(point, Joint) > kSamePoint)
continue;
auto const tangent{glm::normalize(path_tangent(path, atend))};
Direction = (Leaving != atend) ? tangent : -tangent;
Radius = path_radius(path, atend ? 1.0 : 0.0);
// measured on the piece at the end where the path has them: the path only approximates them, its curvature off at its ends,
// R 300.9 at the end of the diverging track of a switch of R 300
auto const &end{path.pieces.empty() ? path : atend ? path.pieces.back() : path.pieces.front()};
auto const tangent{glm::normalize(path_tangent(end, atend))};
auto const along{Leaving != atend};
Direction = along ? tangent : -tangent;
Radius = path_radius(end, atend ? 1.0 : 0.0);
// the side it turns to as the chain runs, the path gone against its points turning the other way
Turn = 0;
if (Radius > 0.0)
Turn = (bezier{end}.plan_curvature(atend ? 1.0 : 0.0) > 0.0) == along ? 1 : -1;
return true;
}
}
return false;
}
// the element of a stored design a regular track lies along from end to end: its curvature as designed rather than as the path
// approximates it, an arc of its radius however short it is, or a transition. None where the track lies along no design, or across
// the joint of two of its elements
std::optional<alignment::element> designed_element(TTrack const &Track)
{
if (Track.eType != tt_Normal || Track.m_paths.empty())
return std::nullopt;
auto const found{alignment_store::on_track(Track)};
if (false == found.has_value())
return std::nullopt;
bezier const curve{Track.m_paths.front()};
for (auto const t : {0.1, 0.9})
if (auto const at{alignment_store::on_record(found->record, curve.point(t), 0.05)}; false == at.has_value() || at->element != found->element)
return std::nullopt;
auto const &elements{alignment_store::records()[static_cast<std::size_t>(found->record)].result.elements};
if (found->element < 0 || found->element >= static_cast<int>(elements.size()))
return std::nullopt;
return elements[static_cast<std::size_t>(found->element)];
}
// a stretch of a curve along the run of its paths: its length, its curvature at its start and at its end, unsigned
struct curve_piece
{
double length{0.0};
double from{0.0};
double to{0.0};
};
// the curvature of an element of a design at its start and at its end, unsigned
std::pair<double, double> element_curvatures(alignment::element const &Element)
{
auto const [start, end]{alignment::end_curvatures(Element)};
return {std::abs(start), std::abs(end)};
}
// the pieces of a curve along the run of its paths: of the elements of the stored design a path lies along, of the path itself where
// it lies along none, its curvature as at its middle where it barely changes along it. False where no path lies along a design
bool designed_pieces(std::vector<std::pair<TTrack *, bool>> const &Run, std::vector<curve_piece> &Pieces)
{
Pieces.clear();
auto designed{false};
for (auto const &[track, forward] : Run)
{
auto const &path{track->m_paths.front()};
auto const length{track->eType == tt_Switch ? bezier{path}.plan_length() : track->Length()};
auto const &begin{path.points[forward ? segment_data::point::start : segment_data::point::end]};
auto const &finish{path.points[forward ? segment_data::point::end : segment_data::point::start]};
std::optional<alignment_store::hit> first;
std::optional<alignment_store::hit> last;
if (auto const found{track->eType == tt_Normal ? alignment_store::on_track(*track) : std::nullopt})
{
first = alignment_store::on_record(found->record, begin, 0.05);
last = alignment_store::on_record(found->record, finish, 0.05);
}
if (false == first.has_value() || false == last.has_value() || first->record != last->record || std::abs(last->chainage - first->chainage) < 1e-3)
{
auto const curvature_at = [&](double const T) {
auto const radius{path_radius(path, forward ? T : 1.0 - T)};
return radius > 0.0 ? 1.0 / radius : 0.0;
};
auto const start{curvature_at(0.0)};
auto const end{curvature_at(1.0)};
if (std::abs(start - end) <= 0.1 * std::max(start, end))
Pieces.push_back({length, curvature_at(0.5), curvature_at(0.5)});
else
Pieces.push_back({length, start, end});
continue;
}
designed = true;
auto const &elements{alignment_store::records()[static_cast<std::size_t>(first->record)].result.elements};
auto const low{std::min(first->chainage, last->chainage)};
auto const high{std::max(first->chainage, last->chainage)};
std::vector<curve_piece> own;
for (auto const &element : elements)
{
auto const from{std::max(low, element.chainage)};
auto const to{std::min(high, element.chainage + element.length)};
if (to - from < 1e-6)
continue;
auto const [start, end]{element_curvatures(element)};
auto const at = [&](double const Chainage) { return element.length > 1e-9 ? start + (end - start) * (Chainage - element.chainage) / element.length : start; };
own.push_back({to - from, at(from), at(to)});
}
// the design running against the run
if (last->chainage < first->chainage)
{
std::reverse(own.begin(), own.end());
for (auto &piece : own)
std::swap(piece.from, piece.to);
}
Pieces.insert(Pieces.end(), own.begin(), own.end());
}
return designed;
}
}
editor_track::observer *editor_track::s_observer{nullptr};
@@ -216,6 +315,7 @@ void editor_track::isolated(TTrack &Track, std::vector<std::string> const &Names
isolated->AssignEvents();
Track.Isolated.push_back(isolated);
}
Track.mark_dirty();
}
void editor_track::overhead(TTrack &Track, float const Overhead)
@@ -369,7 +469,7 @@ editor_track::name_uses editor_track::uses_of_name(TTrack const &Track)
return result;
}
void editor_track::rename(TTrack &Track, std::string const &Name)
void editor_track::rename(TTrack &Track, std::string const &Name, std::vector<scene::instance_handle> const &Kept)
{
auto const old{Track.name()};
if (old == Name)
@@ -390,7 +490,7 @@ void editor_track::rename(TTrack &Track, std::string const &Name)
std::string file;
std::vector<std::string> values;
auto const index{track_parameter(handle, file, values)};
if (index < 0 || lower_name(values[index]) != old)
if (index < 0 || lower_name(values[index]) != old || std::find(Kept.begin(), Kept.end(), handle) != Kept.end())
continue;
values[index] = Name;
scene::Layers.modify(handle, editor_includes::compose_directive(file, values));
@@ -469,8 +569,11 @@ void editor_track::commit(std::vector<TTrack *> const &Tracks)
for (auto *track : Tracks)
track->mark_dirty();
for (auto *track : affected)
for (auto *vehicle : track->Dynamics)
{
auto const vehicles{track->Dynamics};
for (auto *vehicle : vehicles)
vehicle->Move(0.000001);
}
if (s_observer != nullptr)
s_observer->tracks_committed(Tracks);
}
@@ -611,21 +714,15 @@ std::vector<std::pair<TTrack *, editor_track::point_ref>> editor_track::connecte
return result;
}
namespace
glm::dvec2 editor_track::end_direction(segment_data const &Path, point_kind const Kind)
{
// direction of the path at the end, into the path
glm::dvec2 end_direction(segment_data const &Path, editor_track::point_kind const Kind)
{
bool const start{Kind == editor_track::point_kind::start};
bool const start{Kind == point_kind::start};
auto const &point{Path.points[start ? segment_data::point::start : segment_data::point::end]};
auto const &control{Path.points[start ? segment_data::point::control1 : segment_data::point::control2]};
auto const direction{plan_of(control != glm::dvec3{} ? control : Path.points[start ? segment_data::point::end : segment_data::point::start] - point)};
return glm::length(direction) > 1e-9 ? glm::normalize(direction) : glm::dvec2{0.0, 1.0};
}
} // namespace
std::vector<editor_track::joint> editor_track::joints(TTrack const &Track)
{
std::vector<joint> result;
@@ -1126,6 +1223,8 @@ void editor_track::join(TTrack &Track)
join(*other);
}
}
// and a switch right after another one
Track.join_switches();
break;
}
default:
@@ -1328,14 +1427,14 @@ bool editor_track::complete_chain(chain &Chain, std::string &Error)
auto const firstforward{Chain.forward.front()};
Chain.start = first.points[firstforward ? segment_data::point::start : segment_data::point::end];
Chain.start_direction = firstforward ? path_tangent(first, false) : -path_tangent(first, true);
adjoining_direction(firstforward ? from->trPrev : from->trNext, Chain.start, false, Chain.start_direction, Chain.start_radius);
adjoining_direction(firstforward ? from->trPrev : from->trNext, Chain.start, false, Chain.start_direction, Chain.start_radius, Chain.start_turn);
Chain.start_direction = glm::normalize(Chain.start_direction);
auto const &last{to->m_paths.front()};
auto const lastforward{Chain.forward.back()};
Chain.end = last.points[lastforward ? segment_data::point::end : segment_data::point::start];
Chain.end_direction = lastforward ? path_tangent(last, true) : -path_tangent(last, false);
adjoining_direction(lastforward ? to->trNext : to->trPrev, Chain.end, true, Chain.end_direction, Chain.end_radius);
adjoining_direction(lastforward ? to->trNext : to->trPrev, Chain.end, true, Chain.end_direction, Chain.end_radius, Chain.end_turn);
Chain.end_direction = glm::normalize(Chain.end_direction);
Error.clear();
@@ -1606,7 +1705,14 @@ std::vector<editor_track::straight> editor_track::find_straights(double const Mi
bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve)
{
Curve = {};
auto const curved = [&](TTrack const *Other) { return Other != nullptr && Other->eType == tt_Normal && is_supported(*Other) && false == Other->m_editorremoved && false == is_straight(*Other, Tolerance); };
// a track too short to tell its bend from a straight is a part of the curve where it lies along an arc or a transition of a design
auto const designed_bend = [](TTrack const &Other) {
auto const element{designed_element(Other)};
return element.has_value() && element->kind != alignment::element_kind::straight;
};
auto const curved = [&](TTrack const *Other) {
return Other != nullptr && Other->eType == tt_Normal && is_supported(*Other) && false == Other->m_editorremoved && (false == is_straight(*Other, Tolerance) || designed_bend(*Other));
};
if (false == curved(&Track))
return false;
@@ -1795,6 +1901,48 @@ bool editor_track::analyse_curve(std::vector<std::pair<TTrack *, bool>> const &R
Curve.arcs = arcs;
}
}
// along the stored designs the curve is read from their elements, as it was laid, the paths along none by their own curvature: the
// curve of two lines laid one after another made of the arcs and the transitions of both, however short they are
if (std::vector<curve_piece> pieces; Curve.reversals == 0 && designed_pieces(run, pieces))
{
std::vector<curve::arc_part> designedarcs;
double in{0.0};
double between{0.0};
auto inside{false}; // a straight between two arcs: no curve of one vertex
auto straight{false};
for (auto const &piece : pieces)
{
auto const even{std::abs(piece.from - piece.to) <= 0.02 * std::max(piece.from, piece.to)};
if (even && piece.from < 1e-6)
{
straight = false == designedarcs.empty();
continue;
}
if (false == even)
{
(designedarcs.empty() ? in : between) += piece.length;
continue;
}
inside = inside || straight;
auto const radius{2.0 / (piece.from + piece.to)};
if (false == designedarcs.empty() && between < 1e-6 && std::abs(designedarcs.back().radius - radius) <= 0.02 * radius)
designedarcs.back().turn += piece.length / radius;
else
designedarcs.push_back({radius, piece.length / radius, designedarcs.empty() ? 0.0 : between});
between = 0.0;
}
if (false == designedarcs.empty() && false == inside)
{
Curve.laid = true;
Curve.transition_in = in;
Curve.transition_out = between;
Curve.radius = designedarcs.front().radius;
Curve.compound = designedarcs.size() > 1;
Curve.arcs.clear();
if (Curve.compound)
Curve.arcs = designedarcs;
}
}
if (Curve.reversals == 0)
{
if (auto const designed{alignment_store::vertex_of(Run)})
@@ -2181,11 +2329,25 @@ bool editor_track::find_route(TTrack *From, TTrack *To, route &Route, std::strin
return true;
}
editor_track::route editor_track::run_route(TTrack &Track, double const Maximum)
editor_track::route editor_track::run_route(TTrack &Track, double const Maximum, int const Path)
{
route result;
if (false == is_supported(Track))
return result;
// through a switch the run goes along its first path, the main one; but where that one bends and the other runs straight,
// as in the quarters of a double slip, straight on
auto const bends = [](TTrack const &Of, int const Path) {
bezier const curve{Of.m_paths[Path]};
auto const from{plan_of(curve.first(0.0))};
auto const to{plan_of(curve.first(1.0))};
return glm::length(from) > 1e-9 && glm::length(to) > 1e-9 && std::abs(std::atan2(from.x * to.y - from.y * to.x, glm::dot(from, to))) > 0.002;
};
auto const main_of = [&](TTrack const &Of) {
for (int i = 1; i < static_cast<int>(Of.m_paths.size()); ++i)
if (bends(Of, 0) && false == bends(Of, i))
return i;
return 0;
};
auto const walk = [&](route_node Node) {
std::vector<route_node> nodes;
double length{0.0};
@@ -2196,7 +2358,7 @@ editor_track::route editor_track::run_route(TTrack &Track, double const Maximum)
break;
auto chosen{candidates.front()};
for (auto const &candidate : candidates)
if (std::get<1>(candidate) == 0)
if (std::get<1>(candidate) == main_of(*std::get<0>(candidate)))
chosen = candidate;
auto *track{std::get<0>(chosen)};
if (track == &Track || std::any_of(nodes.begin(), nodes.end(), [&](route_node const &Other) { return std::get<0>(Other) == track; }))
@@ -2207,12 +2369,13 @@ editor_track::route editor_track::run_route(TTrack &Track, double const Maximum)
}
return nodes;
};
auto const ahead{walk({&Track, 0, true})};
auto const behind{walk({&Track, 0, false})};
auto const own{Path >= 0 && Path < static_cast<int>(Track.m_paths.size()) ? Path : main_of(Track)};
auto const ahead{walk({&Track, own, true})};
auto const behind{walk({&Track, own, false})};
for (auto node = behind.rbegin(); node != behind.rend(); ++node)
if (std::none_of(ahead.begin(), ahead.end(), [&](route_node const &Other) { return std::get<0>(Other) == std::get<0>(*node); }))
result.spans.push_back({std::get<0>(*node), std::get<1>(*node), false == std::get<2>(*node)});
result.spans.push_back({&Track, 0, true});
result.spans.push_back({&Track, own, true});
for (auto const &node : ahead)
result.spans.push_back({std::get<0>(node), std::get<1>(node), std::get<2>(node)});
return result;
@@ -2226,6 +2389,24 @@ editor_track::route editor_track::route_of(chain const &Chain)
return result;
}
double editor_track::route_speed(route const &Route)
{
std::vector<std::pair<double, double>> speeds;
auto total{0.0};
for (auto const &span : Route.spans)
if (auto const speed{velocity(*span.track)}; speed > 0.0)
{
speeds.emplace_back(speed, std::abs(span.to - span.from));
total += speeds.back().second;
}
std::sort(speeds.begin(), speeds.end());
auto passed{0.0};
for (auto const &[speed, length] : speeds)
if ((passed += length) >= total * 0.5)
return speed;
return 100.0;
}
std::vector<editor_track::route_sample> editor_track::sample_route(route &Route, double const Step)
{
std::vector<route_sample> samples;
@@ -2442,6 +2623,20 @@ bool editor_track::adjoining_grade(route const &Route, bool const Atend, double
return true;
}
std::vector<editor_track::continuation> editor_track::continuations(route_span const &Span)
{
std::vector<continuation> result;
for (auto const &[track, path, forward] : route_next({Span.track, Span.path, Span.forward}))
{
auto const tangent{path_tangent(track->m_paths[path], false == forward)};
auto const plan{std::hypot(tangent.x, tangent.z)};
if (plan < 1e-9)
continue;
result.push_back({{track, path, forward}, (forward ? 1.0 : -1.0) * tangent.y / plan});
}
return result;
}
std::vector<editor_track::height_gap> editor_track::apply_profile(route &Route, std::function<double(double)> const &Elevation, std::function<double(double)> const &Grade, std::vector<double> const &Breaks, std::vector<std::pair<TTrack *, state>> &States, std::vector<TTrack *> &Created)
{
double constexpr margin{0.25};
@@ -2553,7 +2748,9 @@ std::vector<editor_track::height_gap> editor_track::apply_profile(route &Route,
auto const endchainage{span.forward ? span.to : span.from};
auto const sign{span.forward ? 1.0 : -1.0};
auto &path{span.track->m_paths[span.path]};
set_heights(path, Elevation(startchainage), Elevation(endchainage), Grade(startchainage) * sign, Grade(endchainage) * sign);
// at a break of the grade without a curve each path takes the grade on its own side of it
auto const inward = [&](double const Chainage, double const Other) { return Grade(Chainage + std::min(1e-3, std::abs(Other - Chainage) * 0.5) * (Other > Chainage ? 1.0 : -1.0)); };
set_heights(path, Elevation(startchainage), Elevation(endchainage), inward(startchainage, endchainage) * sign, inward(endchainage, startchainage) * sign);
// the pieces of a path made of several curves follow the grade line along them, else the path would part from them and lose them
std::vector<double> lengths;
double total{0.0};
@@ -2570,7 +2767,7 @@ std::vector<editor_track::height_gap> editor_track::apply_profile(route &Route,
{
auto const first{at(along)};
auto const last{at(along + lengths[k])};
set_heights(path.pieces[k], Elevation(first), Elevation(last), Grade(first) * sign, Grade(last) * sign);
set_heights(path.pieces[k], Elevation(first), Elevation(last), inward(first, last) * sign, inward(last, first) * sign);
along += lengths[k];
}
}
@@ -2785,6 +2982,8 @@ std::optional<editor_track::switch_template> editor_track::template_of(switch_de
entry.movable = Design.movable;
entry.heel = Design.heel;
entry.bladethrow = Design.bladethrow;
if (false == Design.parts.empty() && Design.parts.back().radius1 <= 0.0 && Design.parts.back().radius2 <= 0.0)
entry.frog_straight = Design.parts.back().length;
entry.local = {shape.paths[0], shape.paths[1]};
entry.local[1].radius = static_cast<float>(shape.radius);
if (shape.paths.size() >= 5)
@@ -2954,7 +3153,7 @@ std::vector<editor_track::switch_template> editor_track::standard_switch_templat
slip.rail = std::string{entry.name}.substr(0, std::string{entry.name}.find('-'));
slip.label = std::string{entry.type} + ' ' + entry.name + format(" a %.3f", entry.half) + (slip.type == "Rkp" ? " single slip" : " double slip") +
(entry.middle < 0.0 ? ", the blades outside of the diamond" : "") +
(slip.type == "Rkp" ? " (click at a crossing of two straights, on the side of the joined tracks)" : " (click at a crossing of two straights)");
(slip.type == "Rkp" ? " (click at a crossing of two straights, on the side of the joined tracks, or drag it out on a straight)" : " (click at a crossing of two straights, or drag it out on a straight)");
result.push_back(slip);
}
struct crossing
@@ -2992,7 +3191,7 @@ std::vector<editor_track::switch_template> editor_track::standard_switch_templat
plain.a = entry.half;
plain.ratio = ratio_of(entry.name);
plain.rail = std::string{entry.name}.substr(0, std::string{entry.name}.find('-'));
plain.label = std::string{"Skrz "} + entry.name + format(" a %.3f", entry.half) + " crossing (click at a crossing of two straights)";
plain.label = std::string{"Skrz "} + entry.name + format(" a %.3f", entry.half) + " crossing (click at a crossing of two straights, or drag it out on a straight)";
result.push_back(plain);
}
return result;
@@ -3098,6 +3297,33 @@ TTrack *editor_track::create_switch(switch_template const &Template, std::vector
return track;
}
TTrack *editor_track::create_switch(switch_template const &Template, std::vector<segment_data> const &Paths, path_style const &Style, std::string const &Name)
{
if (Paths.size() < 2)
return nullptr;
if (Template.source != nullptr)
return create_switch(Template, Paths, *Template.source, Name);
std::ostringstream text;
text.precision(std::numeric_limits<double>::digits10);
// the rails of the style on both paths, its ballast under the switch
text << "switch " << Template.length << " 1.435 0.15 10 20 0 flat vis " << Style.rail << " 6 " << Style.rail << " 0.2 0.5 1.1 ";
write_points(text, Paths[0]);
write_points(text, Paths[1]);
text << "trackbed " << Style.ballast << ' ';
if (Style.velocity > 0.0)
text << "velocity " << Style.velocity << ' ';
if (false == Style.rail_profile.empty())
text << "railprofile " << Style.rail_profile << ' ';
text << "endtrack\n";
auto *track{load_text(text.str(), nullptr, Name)};
track->SwitchExtension->m_frogs = true;
track->SwitchExtension->m_movablenose = Template.movable;
track->SwitchExtension->m_bladeheel = Template.heel;
track->SwitchExtension->m_bladethrow = Template.bladethrow;
track->m_paths.assign(Paths.begin(), Paths.begin() + 2);
return track;
}
bool editor_track::touches(TTrack const &Track, glm::dvec3 const &Point)
{
for (auto const &path : Track.m_paths)
@@ -3298,18 +3524,22 @@ void editor_track::move_straight(straight const &Line, glm::dvec3 const &Start,
}
}
std::vector<TTrack *> editor_track::relay(chain const &Chain, std::vector<segment_data> const &Pieces, TTrack **Last)
std::vector<TTrack *> editor_track::relay(chain const &Chain, std::vector<segment_data> const &Pieces, TTrack **Last, std::vector<std::size_t> const *Intervals)
{
std::vector<TTrack *> created;
auto const count{Chain.tracks.size()};
if (count == 0 || Pieces.size() < count)
return created;
// each path of the chain keeps the stretch of the ground it had, where the pieces were cut at its joints: its events and its
// track circuit stay where they were, not handed on along the line
auto const along{Intervals != nullptr && Intervals->size() == Pieces.size() && Intervals->front() == 0 && Intervals->back() + 1 == count &&
std::adjacent_find(Intervals->begin(), Intervals->end(), [](std::size_t const A, std::size_t const B) { return B < A || B > A + 1; }) == Intervals->end()};
std::vector<TTrack *> tracks;
std::size_t previous{count};
for (std::size_t i = 0; i < Pieces.size(); ++i)
{
auto const index{i * count / Pieces.size()};
auto const index{along ? (*Intervals)[i] : i * count / Pieces.size()};
TTrack *track;
bool forward;
if (index != previous)
@@ -3334,6 +3564,18 @@ std::vector<TTrack *> editor_track::relay(chain const &Chain, std::vector<segmen
return created;
}
std::vector<glm::dvec2> editor_track::chain_joints(chain const &Chain)
{
std::vector<glm::dvec2> joints;
for (std::size_t i = 0; i + 1 < Chain.tracks.size() && i < Chain.forward.size(); ++i)
{
auto const &path{Chain.tracks[i]->m_paths.front()};
auto const &end{path.points[Chain.forward[i] ? segment_data::point::end : segment_data::point::start]};
joints.emplace_back(end.x, end.z);
}
return joints;
}
TTrack *editor_track::clone(TTrack const &Template, std::string const &Name)
{
std::ostringstream text;
@@ -3514,6 +3756,8 @@ void editor_track::retire(TTrack &Track)
disconnect(Track);
simulation::Region->erase_and_unregister(&Track);
Track.m_editorremoved = true;
if (s_observer != nullptr)
s_observer->track_retired(Track);
// the name is free for the path laid in its place, as the switches of a crossover laid again keep theirs
simulation::Paths.rename(&Track, std::string{});
for (auto *neighbour : adjoining)
@@ -3529,8 +3773,6 @@ void editor_track::retire(TTrack &Track)
for (auto *crossing : crossings)
if (crossing != &Track && false == crossing->m_editorremoved)
rebuild_geometry(*crossing);
if (s_observer != nullptr)
s_observer->track_retired(Track);
}
void editor_track::revive(TTrack &Track)