diff --git a/application/editormode.h b/application/editormode.h index ba029e4c..459eea99 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1674,6 +1674,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri std::array points{}; }; bool plan_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape, slip_plan &Plan) const; + // a slip is made for its skos: where the tracks cross at another one, the refusal, which tells the template made for theirs; empty + // where it fits, and for a crossing + std::string slip_skew(editor_track::switch_template const &Shape, double const Angle) const; // the cursor where it points at the straight, at the height of the straight there glm::dvec3 cursor_on_straight(editor_track::straight const &Line) const; bool insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape); diff --git a/application/editormodeswitchplace.cpp b/application/editormodeswitchplace.cpp index 6e662d71..3024f427 100644 --- a/application/editormodeswitchplace.cpp +++ b/application/editormodeswitchplace.cpp @@ -44,6 +44,9 @@ http: using namespace track_mode; +// the name of the template in the language of the editor, editormodeswitchtool.cpp +std::string switch_name_translated(std::string Title); + std::vector editor_mode::switch_preview() const { auto const &tool{m_switch}; @@ -1065,6 +1068,11 @@ bool editor_mode::plan_double_slip(editor_track::straight const &Line, glm::dvec Plan.problem = STR_C("The straights cross at an angle unsuitable for a double slip"); return false; } + if (auto const skew{slip_skew(Shape, angle)}; false == skew.empty()) + { + Plan.problem = skew; + return false; + } auto const alongfirst{glm::dot(crossing - plan_of(Line.start), Line.direction)}; auto const alongsecond{glm::dot(crossing - plan_of(second.start), second.direction)}; auto const available{std::min({alongfirst, Line.length - alongfirst, alongsecond, second.length - alongsecond}) - 0.5}; @@ -1110,6 +1118,33 @@ bool editor_mode::plan_double_slip(editor_track::straight const &Line, glm::dvec return true; } +std::string editor_mode::slip_skew(editor_track::switch_template const &Shape, double const Angle) const +{ + // rad: the tracks crossing at the skos of the slip to within this, half of what keeps 1:9.51 and 1:10 of the catalogue apart + auto const tolerance{glm::radians(0.14)}; + auto const fits = [&](editor_track::switch_template const &Template) { return Template.ratio > 0.0 && std::abs(Angle - std::atan(1.0 / Template.ratio)) <= tolerance; }; + if (Shape.type == "Skrz" || false == Shape.double_slip || Shape.ratio <= 0.0 || fits(Shape)) + return {}; + // a slip of the same kind made for the skos of the tracks, of the same rail and radius before the others + editor_track::switch_template const *fitting{nullptr}; + auto best{-1}; + for (auto const &entry : m_switch.templates) + { + if (false == entry.double_slip || entry.type != Shape.type || false == fits(entry)) + continue; + auto const rank{(entry.rail == Shape.rail ? 2 : 0) + (std::abs(entry.radius - Shape.radius) < 0.5 ? 1 : 0)}; + if (rank > best) + { + best = rank; + fitting = &entry; + } + } + auto const ratio{1.0 / std::tan(Angle)}; + if (fitting == nullptr) + return format(STR_C("The straights cross at 1:%.2f, %s is made for 1:%.2f and no slip template is made for that"), ratio, switch_name_translated(Shape.title()).c_str(), Shape.ratio); + return format(STR_C("The straights cross at 1:%.2f, %s is made for 1:%.2f: %s fits that crossing"), ratio, switch_name_translated(Shape.title()).c_str(), Shape.ratio, switch_name_translated(fitting->title()).c_str()); +} + bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape) { auto &state{m_straights}; @@ -1241,6 +1276,11 @@ bool editor_mode::replace_double_slip(TTrack &Part, editor_track::switch_templat auto const t{cross(plan_of(c.point) - plan_of(a.point), direction) / determinant}; glm::dvec2 const crossing{plan_of(a.point) + first * t}; auto const angle{std::atan2(std::abs(cross(first, direction)), glm::dot(first, direction))}; + if (auto const skew{slip_skew(Shape, angle)}; false == skew.empty()) + { + state.status = refused(skew); + return false; + } auto const oldhalf{glm::distance(plan_of(a.point), crossing)}; auto const blade{switch_design::secant_blade(Shape.rail, Shape.radius, Shape.ratio)}; auto curve{editor_track::slip_of(Shape.radius, Shape.middle, angle, 0.5 * std::abs(switches[0]->fTrackWidth) + switches[0]->rail_dimensions().head + 0.002, blade.first, blade.second)}; diff --git a/application/editormodeswitchtool.cpp b/application/editormodeswitchtool.cpp index 014e56a4..ecdf8822 100644 --- a/application/editormodeswitchtool.cpp +++ b/application/editormodeswitchtool.cpp @@ -139,7 +139,10 @@ std::string template_tooltip(editor_track::switch_template const &Template) text += "\n"; text += Template.type == "Rkp" ? STR_C("Single slip (Rkp)") : Template.type == "Rkpd" ? STR_C("Double slip (Rkpd)") : STR_C("Crossing (Skrz)"); text += "\n" + format(STR_C("a %.3f m from the middle of the crossing to the joint"), Template.a) + "\n"; - text += Template.type == "Rkp" ? STR_C("Drag at a crossing of two straights, on the side of the tracks to join") : STR_C("Drag at a crossing of two straights"); + if (Template.type == "Skrz") + text += STR_C("Drag at a crossing of two straights"); + else + text += Template.type == "Rkp" ? STR_C("Drag at a crossing of two straights at its skos, on the side of the tracks to join") : STR_C("Drag at a crossing of two straights at its skos"); return text; } text += "\n" + format(STR_C("a %.3f m to the centre of the switch, b %.3f m on to its end"), Template.a, Template.b); @@ -228,7 +231,7 @@ void editor_mode::render_switch_ui() char const *tooltip; }; choice const choices[] = {{switch_kind::single, "Switch", "A switch in a straight or a curve"}, - {switch_kind::slip, "Slip switch", "Rkp or Rkpd at a crossing of two straights"}, + {switch_kind::slip, "Slip switch", "Rkp or Rkpd at a crossing of two straights at its skos"}, {switch_kind::crossing, "Crossing", "A crossing of two straights"}, {switch_kind::connection, "Connection", "Crossover, double, scissors, ladder"}}; { @@ -274,7 +277,7 @@ void editor_mode::render_switch_ui() std::vector> how; switch (kind) { - case switch_kind::slip: + case switch_kind::slip: how = {{"Drag at a crossing", "of two straights at the skos of the slip: it goes there"}}; break; case switch_kind::crossing: how = {{"Drag at a crossing", "of two straights: it goes there"}}; break; case switch_kind::connection: how = {{"Drag", "from a straight onto the other track"}, {"Shift+wheel", "the kind"}, {"Ctrl+wheel", "R of the aligning curve"}}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index a674a49f..5ea94092 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -1754,6 +1754,8 @@ std::vector editor_mode::track_key_hints(bool const All) } else if (m_track_tab == track_tab::turnout && m_switch.armed < 0) hints = {{"List in the panel", "the type of the switch"}, {"LMB on a track", "another track"}}; + else if (m_track_tab == track_tab::turnout && current_switch_kind() == switch_kind::slip) + hints = {{"Press+drag at a crossing", "of two straights at the skos of the slip"}}; else if (m_track_tab == track_tab::turnout) hints = {{"Press+drag on the track", "along: direction, sideways: side"}, {"List in the panel", "the type of the switch"}, {"LMB on another track", "choose it"}}; else if (m_track_tab == track_tab::profile && m_profile.route.spans.empty()) diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 3f30b0b3..e3957fd7 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -12366,3 +12366,24 @@ msgstr "Przeciągnij na skrzyżowaniu dwóch prostych" msgid "Drag at a crossing of two straights, on the side of the tracks to join" msgstr "Przeciągnij na skrzyżowaniu dwóch prostych, po stronie torów do połączenia" + +msgid "The straights cross at 1:%.2f, %s is made for 1:%.2f: %s fits that crossing" +msgstr "Proste krzyżują się pod 1:%.2f, %s jest na 1:%.2f: do tego skrzyżowania pasuje %s" + +msgid "The straights cross at 1:%.2f, %s is made for 1:%.2f and no slip template is made for that" +msgstr "Proste krzyżują się pod 1:%.2f, %s jest na 1:%.2f i żaden szablon rozjazdu krzyżowego nie jest na taki skos" + +msgid "Drag at a crossing of two straights at its skos, on the side of the tracks to join" +msgstr "Przeciągnij na skrzyżowaniu dwóch prostych pod jego skosem, po stronie torów do połączenia" + +msgid "Drag at a crossing of two straights at its skos" +msgstr "Przeciągnij na skrzyżowaniu dwóch prostych pod jego skosem" + +msgid "Rkp or Rkpd at a crossing of two straights at its skos" +msgstr "Rozjazd krzyżowy pojedynczy (Rkp) albo podwójny (Rkpd) na skrzyżowaniu dwóch prostych pod jego skosem" + +msgid "of two straights at the skos of the slip: it goes there" +msgstr "dwóch prostych pod skosem rozjazdu: tam stanie" + +msgid "of two straights at the skos of the slip" +msgstr "dwóch prostych pod skosem rozjazdu"