From fe9d78b1729d7f0bfd13e607adea0b8c83c9630e Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 11 Oct 2026 14:08:47 +0200 Subject: [PATCH] fix(editor): a switch takes the cant set by the gizmo on a track joined to it Canting a track at its joint with a switch cants the whole switch, its diverging path goes onto the new cone, and the tracks joined at the other ends of the switch follow it and take the cant at their joints. --- application/editormode.h | 9 +++++ application/editormodetrack.cpp | 66 +++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index f6453368..22e65119 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -666,6 +666,15 @@ class editor_mode : public application_mode, private editor_track::observer double pivot_chainage{0.0}; std::vector chainages; std::vector lengths; + struct switch_joint + { + TTrack *track{nullptr}; + editor_track::point_ref own; + editor_track::point_ref theirs; + double roll{0.0}; + std::vector joints; + }; + std::vector switches; }; track_gizmo_drag m_track_gizmo_drag; gizmo_operation track_gizmo_operation(TTrack const &Track, bool const Pointmode) const; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 2a978ddd..850c2912 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -419,6 +419,21 @@ double roll_into(TTrack const &Track, editor_track::point_ref const &Point) return Point.kind == editor_track::point_kind::start ? path.rolls[0] : -path.rolls[1]; } +void match_rolls(TTrack const &Track, std::vector const &Joints) +{ + for (auto const &joint : Joints) + { + if (joint.own.path >= static_cast(Track.m_paths.size()) || joint.theirs.path >= static_cast(joint.other->m_paths.size())) + continue; + auto const roll{static_cast(roll_into(Track, joint.own))}; + auto &theirs{joint.other->m_paths[joint.theirs.path]}; + if (joint.theirs.kind == editor_track::point_kind::start) + theirs.rolls[0] = -roll; + else + theirs.rolls[1] = roll; + } +} + editor_track::point_ref switch_main(TTrack const &Track, editor_track::point_ref Point) { if (Track.eType == tt_Switch && Point.valid() && Point.path > 0) @@ -788,6 +803,31 @@ void editor_mode::track_gizmo_begin(TTrack &Track, bool const Pointmode, gizmo_o m_track_drag.emplace_back(joint.other); } } + if (Operation == gizmo_operation::rotate && m_track_drag_connected) + { + for (int i = 0; i < static_cast(Track.m_paths.size()); ++i) + for (auto const kind : {editor_track::point_kind::start, editor_track::point_kind::end}) + { + editor_track::point_ref const own{i, kind}; + for (auto const &connection : editor_track::connected_points(Track, editor_track::point_position(Track, own))) + { + auto *other{connection.first}; + std::string reason; + if (other->eType != tt_Switch || other->m_paths.size() != 2 || false == editor_track::can_edit_geometry(*other, reason)) + continue; + if (std::any_of(drag.switches.begin(), drag.switches.end(), [&](auto const &Link) { return Link.track == other; })) + continue; + auto joints{editor_track::joints(*other)}; + joints.erase(std::remove_if(joints.begin(), joints.end(), [&](editor_track::joint const &Joint) { return Joint.other == &Track; }), joints.end()); + if (std::find(m_track_drag.begin(), m_track_drag.end(), other) == m_track_drag.end()) + m_track_drag.emplace_back(other); + for (auto const &joint : joints) + if (std::find(m_track_drag.begin(), m_track_drag.end(), joint.other) == m_track_drag.end()) + m_track_drag.emplace_back(joint.other); + drag.switches.push_back({other, own, connection.second, roll_into(Track, own), std::move(joints)}); + } + } + } std::vector> states; for (auto *dragged : m_track_drag) states.emplace_back(dragged, editor_track::capture(*dragged)); @@ -925,16 +965,26 @@ void editor_mode::track_gizmo_turn(TTrack &Track, int const Axis, double const A editor_track::follow(Track, m_track_joints); editor_track::follow_grade(Track, m_track_joints); } - for (auto const &joint : m_track_joints) + match_rolls(Track, m_track_joints); + for (auto const &link : drag.switches) { - if (joint.own.path >= static_cast(Track.m_paths.size()) || joint.theirs.path >= static_cast(joint.other->m_paths.size())) + auto const roll{roll_into(Track, link.own)}; + if (std::abs(roll - link.roll) < 1e-6) continue; - auto const roll{static_cast(roll_into(Track, joint.own))}; - auto &theirs{joint.other->m_paths[joint.theirs.path]}; - if (joint.theirs.kind == editor_track::point_kind::start) - theirs.rolls[0] = -roll; - else - theirs.rolls[1] = roll; + auto &main{link.track->m_paths[0]}; + bool const start{link.theirs.kind == editor_track::point_kind::start}; + auto value{start ? -roll : roll}; + if (link.theirs.path != 0) + { + auto const t{start ? 0.0 : 1.0}; + if (glm::dot(plan_of(bezier{link.track->m_paths[link.theirs.path]}.first(t)), plan_of(bezier{main}.first(t))) < 0.0) + value = -value; + } + main.rolls = {static_cast(value), static_cast(value)}; + editor_track::conform_switch(*link.track); + editor_track::follow(*link.track, link.joints); + editor_track::follow_grade(*link.track, link.joints); + match_rolls(*link.track, link.joints); } for (auto const &partner : drag.partners) {