From b27d1772a73c683427c8aba401ea724c1986504b Mon Sep 17 00:00:00 2001 From: maj00r Date: Mon, 12 Oct 2026 00:25:01 +0200 Subject: [PATCH] feat(editor): a switch being placed slides along its straight first, so its branch lies on the line of the track it leads into (a straight between, or an arc of the switch radius right after it with the arc ending at the track's end); only when the slide leaves the straight it stays put and reverse curves fill in; the straight between reverse curves takes the speed of the switch's branch (R<300 40, <500 50, <760 60, <1200 80, <2500 100 km/h) instead of the track's --- application/editormode.h | 1 + application/editormodetrack.cpp | 41 ++++++++++++++++++++++++------- editor/editorTrack.hpp | 2 ++ editor/editorTrackShape.cpp | 43 +++++++++++++++++++++++++++++++++ lang/editor_pl.po | 3 +++ 5 files changed, 81 insertions(+), 9 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index 5df3f4c2..b0550310 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1132,6 +1132,7 @@ class editor_mode : public application_mode, private editor_track::observer double along{0.0}; int forward{1}; int side{1}; + double moved{0.0}; std::vector paths; std::string reason; }; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index f84d4643..07c0f439 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -5884,15 +5884,33 @@ bool editor_mode::plan_switch_link(switch_link &Plan) const } if (shape.length > line.length - 0.02) return false; + auto const kink{glm::radians(m_joints.angle * 0.5)}; + auto const plan = [&](double const Along) { + Plan.along = Along; + Plan.paths = place(Along); + if (Plan.paths.size() < 2) + return false; + auto const radius{editor_track::branch_radius(Plan.paths[1])}; + Plan.link = editor_track::link_branch(branch_start(Plan.paths[1]), radius, branch_target(Plan.target), kink, + editor_track::reverse_insert(std::min(editor_track::diverging_speed(radius), design_speed(nullptr, Plan.target.track)))); + Plan.reason = link_problem(Plan.target, Plan.link); + return Plan.reason.empty(); + }; auto const from{switch_from(line, shape.length, tool.along, Plan.forward, true)}; - Plan.along = Plan.forward > 0 ? from : from + shape.length; - Plan.paths = place(Plan.along); - if (Plan.paths.size() < 2) + auto const snapped{Plan.forward > 0 ? from : from + shape.length}; + auto const paths{place(snapped)}; + if (paths.size() < 2) return false; - Plan.link = editor_track::link_branch(branch_start(Plan.paths[1]), editor_track::branch_radius(Plan.paths[1]), branch_target(Plan.target), glm::radians(m_joints.angle * 0.5), - editor_track::reverse_insert(design_speed(line.tracks.front(), Plan.target.track))); - Plan.reason = link_problem(Plan.target, Plan.link); - return Plan.reason.empty(); + if (auto const shift{editor_track::branch_shift(branch_start(paths[1]), line.direction * static_cast(Plan.forward), editor_track::branch_radius(paths[1]), branch_target(Plan.target), kink)}) + { + auto const along{snapped + *shift * Plan.forward}; + auto const begin{Plan.forward > 0 ? along : along - shape.length}; + Plan.moved = std::abs(*shift); + if (begin >= -1e-6 && begin + shape.length <= line.length + 1e-6 && plan(along) && false == Plan.link.reverse) + return true; + } + Plan.moved = 0.0; + return plan(snapped); } void editor_mode::insert_linked_switch(switch_link const &Plan) @@ -5908,7 +5926,10 @@ void editor_mode::insert_linked_switch(switch_link const &Plan) straight_refresh(); m_switch.status.clear(); switch_placed(placed); - m_switch.status = link_summary(Plan.target, Plan.link, editor_track::branch_radius(Plan.paths[1])) + (m_switch.status.empty() ? std::string{} : "\n" + m_switch.status); + auto summary{link_summary(Plan.target, Plan.link, editor_track::branch_radius(Plan.paths[1]))}; + if (Plan.moved >= 0.005) + summary += format(STR_C(", the switch moved %.2f m"), Plan.moved); + m_switch.status = summary + (m_switch.status.empty() ? std::string{} : "\n" + m_switch.status); ui()->set_status(m_switch.status, false); } @@ -5922,7 +5943,9 @@ bool editor_mode::plan_branch_link(editor_track::branch_link &Link, std::string if (std::find(tool.line.begin(), tool.line.end(), target.track) != tool.line.end()) return false; auto const &diverging{tool.track->m_paths[1]}; - Link = editor_track::link_branch(branch_start(diverging), editor_track::branch_radius(diverging), branch_target(target), glm::radians(m_joints.angle * 0.5), editor_track::reverse_insert(design_speed(tool.track, target.track))); + auto const radius{editor_track::branch_radius(diverging)}; + Link = editor_track::link_branch(branch_start(diverging), radius, branch_target(target), glm::radians(m_joints.angle * 0.5), + editor_track::reverse_insert(std::min(editor_track::diverging_speed(radius), design_speed(tool.track, target.track)))); Reason = link_problem(target, Link); return Reason.empty(); } diff --git a/editor/editorTrack.hpp b/editor/editorTrack.hpp index ad1ab085..d63a157d 100644 --- a/editor/editorTrack.hpp +++ b/editor/editorTrack.hpp @@ -225,6 +225,8 @@ class editor_track }; static double branch_radius(segment_data const &Diverging); static double reverse_insert(double const Speed); + static double diverging_speed(double const Radius); + static std::optional branch_shift(branch_end const &From, glm::dvec2 const &Slide, double const Radius, branch_end const &To, double const Kink); static branch_link link_branch(branch_end const &From, double const Radius, branch_end const &To, double const Kink, double const Insert); struct snap_target diff --git a/editor/editorTrackShape.cpp b/editor/editorTrackShape.cpp index 9cc4cc3d..9f282a65 100644 --- a/editor/editorTrackShape.cpp +++ b/editor/editorTrackShape.cpp @@ -386,6 +386,19 @@ double editor_track::reverse_insert(double const Speed) return Speed <= 100.0 ? 0.15 * Speed : 0.25 * Speed; } +double editor_track::diverging_speed(double const Radius) +{ + if (Radius < 300.0) + return 40.0; + if (Radius < 500.0) + return 50.0; + if (Radius < 760.0) + return 60.0; + if (Radius < 1200.0) + return 80.0; + return Radius < 2500.0 ? 100.0 : 130.0; +} + namespace { @@ -455,6 +468,35 @@ std::optional fit_reverse(glm::dvec2 const &Offset, glm::dvec2 cons } // namespace +std::optional editor_track::branch_shift(branch_end const &From, glm::dvec2 const &Slide, double const Radius, branch_end const &To, double const Kink) +{ + if (glm::length(From.direction) < 1e-9 || glm::length(To.direction) < 1e-9 || glm::length(Slide) < 1e-9) + return std::nullopt; + auto const slide{glm::normalize(Slide)}; + auto const tangent{glm::normalize(From.direction)}; + auto const into{glm::normalize(To.direction)}; + auto const turn{signed_angle(tangent, into)}; + auto const offset{plan_of(To.position) - plan_of(From.position)}; + auto const across{cross(into, slide)}; + if (std::abs(turn) > glm::radians(170.0)) + return std::nullopt; + if (std::abs(turn) <= Kink) + { + if (std::abs(across) < 1e-6) + return std::nullopt; + return cross(into, offset) / across; + } + if (Radius < 1.0) + return std::nullopt; + auto const gap{offset - unit_chord(tangent, turn) * Radius}; + if (std::abs(across) >= std::sin(glm::radians(1.0))) + return cross(into, gap) / across; + auto const determinant{cross(tangent, slide)}; + if (std::abs(determinant) < 1e-6 || cross(gap, slide) / determinant < -0.01) + return std::nullopt; + return cross(tangent, gap) / determinant; +} + editor_track::branch_link editor_track::link_branch(branch_end const &From, double const Radius, branch_end const &To, double const Kink, double const Insert) { branch_link result; @@ -508,6 +550,7 @@ editor_track::branch_link editor_track::link_branch(branch_end const &From, doub } else if (false == reverse()) { + result.radius = Radius; result.shortfall = std::max(0.01, -result.before); return result; } diff --git a/lang/editor_pl.po b/lang/editor_pl.po index dd3af8ca..738c3a82 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5481,3 +5481,6 @@ msgstr ", między nimi prosta %.1f m" msgid ", sharper than the switch (R %.0f m)" msgstr ", ostrzej niż rozjazd (R %.0f m)" + +msgid ", the switch moved %.2f m" +msgstr ", rozjazd przesunięty o %.2f m"