diff --git a/application/editormode.cpp b/application/editormode.cpp index e18a2520..6832aa49 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -6865,7 +6865,21 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const if (m_extend.active) finish_extend(); if (m_switch.placing) - finish_switch_placement(); + { + // a press let go with no drag places no switch, a click on the ground with no track there selects nothing + auto const &io{ImGui::GetIO()}; + if (glm::length(glm::vec2{io.MousePos.x, io.MousePos.y} - m_switch.pressed_at) <= 10.0f) + { + m_switch.placing = false; + if (m_switch.free) + { + m_switch.free = false; + track_click_select(nullptr); + } + } + else + finish_switch_placement(); + } if (m_turntable.placing) turntable_finish_placement(); if (m_signal.placing || m_signal.moving) diff --git a/application/editormode.h b/application/editormode.h index b821f601..ba029e4c 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1104,6 +1104,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri static std::vector double_paths(double_switch const &Set); // the switch of the template, or the two switches of a double one with the plain track between them, committed and recorded std::vector create_switch_set(editor_track::switch_template const &Shape, std::vector const &Paths, TTrack const &Style, std::string const &Name); + // with no Style, as the lay tool lays its tracks + std::vector create_switch_set(editor_track::switch_template const &Shape, std::vector const &Paths, TTrack const *Style, std::string const &Name); void record_slip(std::vector const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base, double const Middle = 0.0); void render_switch_record(TTrack const &Track) const; void route_store(); @@ -1267,6 +1269,11 @@ class editor_mode : public application_mode, private editor_track::observer, pri { std::vector keep_names; // the names the switches laid next take, before the ones of the tool bool curved{false}; + // a switch placed where there's no track: started on the ground, or at the free end it goes on from in its direction; the direction + // of its main track taken from the drag once it's long enough, then the drag sideways gives the side of the diverging one + bool free{false}; + editor_track::snap_target free_end; + std::optional free_heading; std::vector frame; std::vector frame_tracks; std::vector templates; @@ -1274,6 +1281,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri int armed{-1}; int last{0}; // template armed the last time bool placing{false}; + // where it was pressed on the screen: let go no farther from there, the press places nothing, a click doesn't lay a switch by chance + glm::vec2 pressed_at{0.f}; double along{0.0}; double pressed{0.0}; // along a curve: where the press was, the start of the switch drawn from it to the ends of the cant ramps glm::dvec3 point{0.0}; @@ -1565,6 +1574,14 @@ class editor_mode : public application_mode, private editor_track::observer, pri void render_rail_profile_hint(TTrack &Track); bool start_switch_placement(); void finish_switch_placement(); + // a switch with no track there: where a press on the ground starts it, at the free end of a track within reach or right there + editor_track::snap_target free_switch_end(glm::dvec3 const &Ground) const; + bool free_switch_offered() const; + void start_free_switch(glm::dvec3 const &Ground); + // the start of the switch, the way its main track goes and the side of its diverging one, as the drag gives them so far + void free_switch_pose(glm::dvec3 &Start, glm::dvec2 &Heading, int &Side, double &Grade) const; + std::vector free_switch_paths() const; + void insert_free_switch(); std::vector switch_preview() const; void insert_switch(editor_track::straight const &Line, double const Along, int const Direction, int const Side); void switch_placed(std::vector const &Created); @@ -1815,7 +1832,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri void draw_track_hover() const; struct track_intent { - enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style, fouling, measure, route_trace, kilometrage, srk_boundary }; + enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, switch_free, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style, fouling, measure, route_trace, kilometrage, srk_boundary }; kind what{kind::none}; TTrack *track{nullptr}; int path{0}; diff --git a/application/editormodelay.cpp b/application/editormodelay.cpp index ad7ad4f7..934bbce6 100644 --- a/application/editormodelay.cpp +++ b/application/editormodelay.cpp @@ -102,7 +102,19 @@ void editor_mode::update_build_tools() joining(m_extend.snap); } if (m_switch.placing) + { m_switch.mouse = cursor_level(m_switch.point.y); + // a switch with no track takes the direction of its main track once the drag is long enough, dragged sideways after that it + // takes the side; back by its start the direction is free again + if (auto &tool{m_switch}; tool.free && tool.free_end.track == nullptr) + { + auto const offset{plan_of(tool.mouse - tool.point)}; + if (glm::length(offset) < kFreeSwitchRelease) + tool.free_heading.reset(); + else if (false == tool.free_heading.has_value() && glm::length(offset) >= kFreeSwitchLock) + tool.free_heading = glm::normalize(offset); + } + } auto &lay{m_lay}; lay.preview.clear(); lay.preview_error.clear(); diff --git a/application/editormodeselftestswitch.cpp b/application/editormodeselftestswitch.cpp index 31f4ce77..ab881fbc 100644 --- a/application/editormodeselftestswitch.cpp +++ b/application/editormodeselftestswitch.cpp @@ -481,6 +481,49 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com model->location().x, model->location().y, model->location().z, path.points[segment_data::point::start].y)); return selftest_result::handled; } + if (command == "switchfree") + { + // a switch with no track: pressed at the point, the direction of its main track toward the second one (none at a free end), let + // go at the third one, whose side it takes + std::string label; + glm::dvec3 press{0.0}; + glm::dvec3 heading{0.0}; + glm::dvec3 release{0.0}; + words >> label >> press.x >> press.z >> heading.x >> heading.z >> release.x >> release.z; + std::replace(label.begin(), label.end(), '_', ' '); + auto &tool{m_switch}; + show_track_tab(track_tab::turnout); + tool.templates = editor_track::standard_switch_templates(); + tool.collected = true; + tool.connection = -1; + tool.armed = -1; + for (int i = 0; i < static_cast(tool.templates.size()); ++i) + if (tool.templates[i].label.rfind(label, 0) == 0) + { + tool.armed = i; + break; + } + start_free_switch(press); + if (false == tool.placing) + { + WriteLog("SELFTEST switchfree: not started, no template " + label); + return selftest_result::handled; + } + if (tool.free_end.track == nullptr) + tool.free_heading = glm::normalize(geometry::plan_of(heading - tool.point)); + tool.mouse = glm::dvec3{release.x, tool.point.y, release.z}; + tool.placing = false; + finish_switch_placement(); + WriteLog("SELFTEST switchfree" + (tool.free_end.track != nullptr ? " from " + tool.free_end.track->name() : std::string{}) + ": " + tool.status); + if (auto const *track{selected_track()}; track != nullptr && track->eType == tt_Switch) + for (std::size_t i = 0; i < track->m_paths.size(); ++i) + { + auto const &a{track->m_paths[i].points[segment_data::point::start]}; + auto const &b{track->m_paths[i].points[segment_data::point::end]}; + WriteLog(format("SELFTEST switchfree path %s/%zu: %.3f %.3f %.3f -> %.3f %.3f %.3f", track->name().c_str(), i, a.x, a.y, a.z, b.x, b.y, b.z)); + } + return selftest_result::handled; + } if (command == "switchplace" || command == "switchcurve" || command == "slipplace") { std::string label; diff --git a/application/editormodeswitchplace.cpp b/application/editormodeswitchplace.cpp index 865ed645..6e662d71 100644 --- a/application/editormodeswitchplace.cpp +++ b/application/editormodeswitchplace.cpp @@ -49,6 +49,8 @@ std::vector editor_mode::switch_preview() const auto const &tool{m_switch}; if (false == tool.placing || tool.armed < 0 || tool.armed >= static_cast(tool.templates.size())) return {}; + if (tool.free) + return free_switch_paths(); if (tool.curved) { int forward; @@ -56,6 +58,20 @@ std::vector editor_mode::switch_preview() const curved_switch_pose(forward, side); return curved_switch_paths(tool.templates[tool.armed], tool.frame, curved_switch_start(forward), forward, side); } + if (tool.templates[tool.armed].double_slip) + { + // a slip at the crossing where it was pressed: its two straights, laid there once dragged + slip_plan plan; + if (false == plan_double_slip(tool.line, tool.point, tool.templates[tool.armed], plan) || plan.replaces != nullptr) + return {}; + auto const straight = [](glm::dvec3 const &From, glm::dvec3 const &To) { + segment_data path; + path.points[segment_data::point::start] = From; + path.points[segment_data::point::end] = To; + return path; + }; + return {straight(plan.points[0], plan.points[1]), straight(plan.points[2], plan.points[3])}; + } if (switch_link plan; plan_switch_link(plan)) { plan.paths.insert(plan.paths.end(), plan.link.pieces.begin(), plan.link.pieces.end()); @@ -80,6 +96,7 @@ bool editor_mode::start_switch_placement() return false; glm::dvec3 const ground{cursor_level(selected->m_paths.front().points[segment_data::point::start].y)}; tool.curved = false; + tool.free = false; tool.status.clear(); if (current_straight().tracks.empty()) { @@ -141,7 +158,12 @@ bool editor_mode::start_switch_placement() return false; if (tool.templates[tool.armed].double_slip) { - insert_double_slip(line, cursor, tool.templates[tool.armed]); + // at the crossing where it was pressed, laid only once dragged, as a switch is + tool.along = along; + tool.point = cursor; + tool.mouse = cursor; + tool.line = line; + tool.placing = true; return true; } tool.along = along; @@ -155,6 +177,12 @@ bool editor_mode::start_switch_placement() void editor_mode::finish_switch_placement() { auto &tool{m_switch}; + if (tool.free) + { + tool.placing = false; + insert_free_switch(); + return; + } if (tool.connection >= 0) { if (false == tool.curved) @@ -170,6 +198,11 @@ void editor_mode::finish_switch_placement() tool.placing = false; if (tool.armed < 0 || tool.armed >= static_cast(tool.templates.size())) return; + if (tool.templates[tool.armed].double_slip && false == tool.curved) + { + insert_double_slip(tool.line, tool.point, tool.templates[tool.armed]); + return; + } if (linked) { insert_linked_switch(plan); @@ -207,6 +240,98 @@ void editor_mode::insert_switch(editor_track::straight const &Line, double const } +bool editor_mode::free_switch_offered() const +{ + auto const &tool{m_switch}; + return m_track_tab == track_tab::turnout && tool.connection < 0 && tool.armed >= 0 && tool.armed < static_cast(tool.templates.size()) && false == tool.templates[tool.armed].double_slip; +} + +editor_track::snap_target editor_mode::free_switch_end(glm::dvec3 const &Ground) const +{ + return editor_track::find_free_end(nullptr, kRailCategory, Ground, kLaySnapRadius, {}); +} + +void editor_mode::start_free_switch(glm::dvec3 const &Ground) +{ + auto &tool{m_switch}; + if (false == free_switch_offered()) + return; + tool.status.clear(); + tool.curved = false; + tool.line = {}; + tool.free = true; + tool.free_end = free_switch_end(Ground); + tool.free_heading.reset(); + if (tool.free_end.track != nullptr) + { + // going on from the free end in its direction + auto const outward{plan_of(-tool.free_end.direction)}; + if (glm::length(outward) > 1e-9) + tool.free_heading = glm::normalize(outward); + tool.point = tool.free_end.position; + } + else + tool.point = lay_bed(Ground); + tool.mouse = tool.point; + tool.placing = true; +} + +void editor_mode::free_switch_pose(glm::dvec3 &Start, glm::dvec2 &Heading, int &Side, double &Grade) const +{ + auto const &tool{m_switch}; + Start = tool.point; + glm::dvec2 const offset{plan_of(tool.mouse - tool.point)}; + // till the drag is long enough to take the direction from, it follows the cursor, the diverging track to the left + Heading = tool.free_heading.value_or(glm::length(offset) > 1e-6 ? glm::normalize(offset) : glm::dvec2{0.0, 1.0}); + Side = cross(Heading, offset) >= 0.0 ? 1 : -1; + Grade = 0.0; + if (tool.free_end.track != nullptr) + { + // the grade of the track it goes on from + auto const &into{tool.free_end.direction}; + auto const run{glm::length(plan_of(into))}; + Grade = run > 1e-9 ? -into.y / run : 0.0; + } +} + +std::vector editor_mode::free_switch_paths() const +{ + auto const &tool{m_switch}; + if (tool.armed < 0 || tool.armed >= static_cast(tool.templates.size())) + return {}; + glm::dvec3 start; + glm::dvec2 heading; + int side; + double grade; + free_switch_pose(start, heading, side, grade); + return editor_track::place_switch(tool.templates[tool.armed], start, heading, side, grade); +} + +void editor_mode::insert_free_switch() +{ + auto &tool{m_switch}; + tool.free = false; + // a click with no drag on the ground gives no direction; from a free end it's the end's + if (tool.free_end.track == nullptr && false == tool.free_heading.has_value()) + { + tool.status = refused(STR_C("Drag along the main track of the switch, then sideways to the side of the diverging one")); + return; + } + auto const paths{free_switch_paths()}; + if (paths.size() < 2) + return; + auto const made{create_switch_set(tool.templates[tool.armed], paths, tool.free_end.track, switch_name_for_new())}; + if (made.empty()) + return; + push_track_snapshot({}, made, {}); + straight_refresh(); + switch_placed(made); + auto const text{tool.free_end.track != nullptr ? format(STR_C("Switch %s laid on from %s, its other ends free: lay on from them"), made.front()->name().c_str(), tool.free_end.track->name().c_str()) + : format(STR_C("Switch %s laid with no track, its ends free: lay on from them"), made.front()->name().c_str())}; + tool.status = text + (tool.status.empty() ? std::string{} : "\n" + tool.status); + ui()->set_status(tool.status, false); +} + void editor_mode::switch_placed(std::vector const &Created) { auto const found{std::find_if(Created.begin(), Created.end(), [](TTrack const *Track) { return Track != nullptr && Track->eType == tt_Switch; })}; @@ -424,15 +549,24 @@ bool editor_mode::place_switch_on_straight(editor_track::straight const &Line, e std::vector editor_mode::create_switch_set(editor_track::switch_template const &Shape, std::vector const &Paths, TTrack const &Style, std::string const &Name) { + return create_switch_set(Shape, Paths, &Style, Name); +} + +std::vector editor_mode::create_switch_set(editor_track::switch_template const &Shape, std::vector const &Paths, TTrack const *Style, std::string const &Name) +{ + // with no track to copy, styled as the lay tool lays its tracks + auto const switch_of = [&](editor_track::switch_template const &Template, std::vector const &Each, std::string const &Named) { + return Style != nullptr ? editor_track::create_switch(Template, Each, *Style, Named) : editor_track::create_switch(Template, Each, m_lay.style, Named); + }; if (Shape.twofold() && Paths.size() >= 5) { auto firstshape{Shape}; firstshape.length = glm::distance(Paths[0].points[segment_data::point::start], Paths[0].points[segment_data::point::end]); auto secondshape{Shape}; secondshape.length = Shape.length - Shape.stagger; - auto *one{editor_track::create_switch(firstshape, Paths, Style, Name)}; - auto *two{one != nullptr ? editor_track::create_switch(secondshape, {Paths[2], Paths[3]}, Style, Name + "b") : nullptr}; - auto *link{two != nullptr ? editor_track::create_path(Style, Paths[4]) : nullptr}; + auto *one{switch_of(firstshape, Paths, Name)}; + auto *two{one != nullptr ? switch_of(secondshape, {Paths[2], Paths[3]}, Name + "b") : nullptr}; + auto *link{two == nullptr ? nullptr : Style != nullptr ? editor_track::create_path(*Style, Paths[4]) : editor_track::create_path(m_lay.style, Paths[4])}; if (link == nullptr) { m_switch.status = refused(STR_C("The switch couldn't be created from this template")); @@ -443,7 +577,7 @@ std::vector editor_mode::create_switch_set(editor_track::switch_templa m_switch.status = format(STR_C("Double switch %s with its second switch %s inserted"), one->name().c_str(), two->name().c_str()); return {one, two, link}; } - auto *track{editor_track::create_switch(Shape, Paths, Style, Name)}; + auto *track{switch_of(Shape, Paths, Name)}; if (track == nullptr) { m_switch.status = refused(STR_C("The switch couldn't be created from this template")); diff --git a/application/editormodeswitchtool.cpp b/application/editormodeswitchtool.cpp index 41f163a5..014e56a4 100644 --- a/application/editormodeswitchtool.cpp +++ b/application/editormodeswitchtool.cpp @@ -139,7 +139,7 @@ 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("Click at a crossing of two straights, on the side of the tracks to join") : STR_C("Click at a crossing of two straights"); + 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"); 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); @@ -275,13 +275,13 @@ void editor_mode::render_switch_ui() switch (kind) { case switch_kind::slip: - case switch_kind::crossing: how = {{"LMB on a crossing", "of two straights: 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"}}; if (tool.connection == static_cast(connection::kind::double_crossover)) how.push_back({"Alt+wheel", "where they meet"}); break; - default: how = {{"Drag on a track", "along: the direction, sideways: the side"}}; break; + default: how = {{"Drag on a track", "along: the direction, sideways: the side"}, {"Drag on the ground", "a switch with no track, on from a free end near it"}}; break; } gesture_rows(how); diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 3f02fe31..a674a49f 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -1727,6 +1727,9 @@ std::vector editor_mode::track_key_hints(bool const All) hints.push_back({"Ctrl+wheel", "radius"}); hints.insert(hints.end(), {{"Release", "build"}, {"Esc", "cancel"}}); } + else if (m_switch.placing && m_switch.free) + hints = m_switch.free_end.track != nullptr ? std::vector{{"Drag sideways", "side of the diverging track"}, {"Esc", "cancel"}} + : std::vector{{"Drag along", "the direction of the main track, held from 4 m on"}, {"Drag sideways", "side of the diverging track"}, {"Back to the start", "the direction free again"}, {"Esc", "cancel"}}; else if (m_switch.placing) hints = m_switch.connection >= 0 && false == m_switch.curved ? std::vector{{"Drag onto the other track", "the connection there"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}, {"Alt+wheel", "where a double crossover meets"}, {"Esc", "cancel"}} : @@ -1739,8 +1742,8 @@ std::vector editor_mode::track_key_hints(bool const All) { case switch_kind::connection: hints = {{"Press on a straight", "and drag onto the other track"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}}; break; case switch_kind::slip: - case switch_kind::crossing: hints = {{"LMB", "at a crossing of two straights"}}; break; - default: hints = {{"LMB on a track", "the straight or the curve for the switch"}}; break; + case switch_kind::crossing: hints = {{"Press+drag at a crossing", "of two straights"}}; break; + default: hints = {{"LMB on a track", "the straight or the curve for the switch"}, {"Press+drag on the ground", "a switch with no track, on from a free end near it"}}; break; } if (selected != nullptr) { @@ -2472,7 +2475,7 @@ void editor_mode::draw_build_overlay() const { marker = plan.replaces->location(); colour = overlay_color::highlight; - text = STR_C("Click: this double slip is replaced by the one chosen"); + text = STR_C("Press and drag: this double slip is replaced by the one chosen"); } else if (valid) { @@ -2485,7 +2488,7 @@ void editor_mode::draw_build_overlay() const } marker = {plan.crossing.x, line.start.y + line.grade * plan.alongfirst, plan.crossing.y}; colour = overlay_color::highlight; - text = plan.note.empty() ? std::string{STR_C("Click: it goes here")} : plan.note; + text = plan.note.empty() ? std::string{STR_C("Press and drag: it goes here")} : plan.note; } ImVec2 screen; if (projection.project(marker, screen)) diff --git a/application/editormodetrackshared.h b/application/editormodetrackshared.h index 00e5399b..9a239391 100644 --- a/application/editormodetrackshared.h +++ b/application/editormodetrackshared.h @@ -207,6 +207,8 @@ float const kHandleRadius{10.0f}; double const kLaySnapRadius{5.0}; float const kSnapPixels{16.0f}; double const kSwitchReach{6.0}; // a press farther from the selected track selects another one instead of placing the switch +double const kFreeSwitchLock{4.0}; // m of the drag a switch with no track takes the direction of its main track from +double const kFreeSwitchRelease{2.0}; // m from its start the drag of a switch with no track lets the direction go again double const kSlipReach{12.0}; // m; a slip goes to the crossing nearest the cursor, if it's this close // m, of the straights past the ends of a curve a crossover laid along it reaches onto: from a transition it goes on onto the // straight after it diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 737d86e3..1beebf86 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -829,6 +829,14 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods) return make(kind::switch_at, "Press and drag: a switch here, along sets its direction, sideways its side"); } } + // a press on the ground lays a switch with no track there, going on from a free end within reach + if (m_hover.track == nullptr && free_switch_offered()) + { + auto const end{free_switch_end(ground)}; + intent.position = end.track != nullptr ? end.position : ground; + return make(kind::switch_free, end.track != nullptr ? "Press and drag: a switch going on from this free end, sideways its side" + : "Press and drag: a switch with no track, along the direction of its main track, then sideways its side"); + } if (m_track_tab == track_tab::lineside && m_fouling.tab().open) if (auto const hit{m_fouling.hit()}; hit.has_value()) { @@ -1022,7 +1030,20 @@ void editor_mode::track_press(track_intent const &Intent) m_route.grip = Intent.index; handle_drag_start(Intent.position); return; - case kind::switch_at: start_switch_placement(); return; + case kind::switch_at: + { + auto const &io{ImGui::GetIO()}; + m_switch.pressed_at = {io.MousePos.x, io.MousePos.y}; + start_switch_placement(); + return; + } + case kind::switch_free: + { + auto const &io{ImGui::GetIO()}; + m_switch.pressed_at = {io.MousePos.x, io.MousePos.y}; + start_free_switch(Intent.position); + return; + } case kind::kilometrage: if (Intent.track != nullptr) m_km.pick(*Intent.track, Intent.position); @@ -1099,6 +1120,7 @@ void editor_mode::draw_track_intent() const case kind::route_vertex: case kind::route_grip: drawlist->AddCircle(at, 13.0f, overlay_color::marked, 24, 2.5f); break; case kind::switch_at: + case kind::switch_free: case kind::straight_tool: drawlist->AddCircleFilled(at, 4.0f, overlay_color::grip); drawlist->AddCircle(at, 11.0f, overlay_color::grip, 20, 2.0f); diff --git a/editor/editorTrack.cpp b/editor/editorTrack.cpp index 11150cb6..5f4ed495 100644 --- a/editor/editorTrack.cpp +++ b/editor/editorTrack.cpp @@ -3159,6 +3159,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 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::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) diff --git a/editor/editorTrack.hpp b/editor/editorTrack.hpp index c2e6e530..6799d35b 100644 --- a/editor/editorTrack.hpp +++ b/editor/editorTrack.hpp @@ -512,6 +512,8 @@ class editor_track static glm::dvec3 point_at(TTrack const &Track, double const T); static std::vector place_switch(switch_template const &Template, glm::dvec3 const &Origin, glm::dvec2 const &Direction, int const Side, double const Grade); static TTrack *create_switch(switch_template const &Template, std::vector const &Paths, TTrack const &Style, std::string const &Name = {}); + // one laid where there's no track to copy, styled as the lay tool lays its tracks + static TTrack *create_switch(switch_template const &Template, std::vector const &Paths, path_style const &Style, std::string const &Name = {}); static TTrack *create_turntable(segment_data const &Path, TTrack const *Style, std::string const &Name); static void move_straights(std::vector const &Lines, std::vector> const &Ends); // Intervals: the path of the chain each piece takes the place of, else they're handed out in turn diff --git a/lang/editor_pl.po b/lang/editor_pl.po index bab22d1b..3f30b0b3 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -12315,3 +12315,54 @@ msgid_plural "The drives of %zu switches are moved to the tips of their blades" msgstr[0] "Napęd %zu rozjazdu przestawiony na ostrza iglic" msgstr[1] "Napędy %zu rozjazdów przestawione na ostrza iglic" msgstr[2] "Napędy %zu rozjazdów przestawione na ostrza iglic" + +msgid "Press and drag: a switch going on from this free end, sideways its side" +msgstr "Wciśnij i przeciągnij: rozjazd od tego wolnego końca, w bok jego strona" + +msgid "Press and drag: a switch with no track, along the direction of its main track, then sideways its side" +msgstr "Wciśnij i przeciągnij: rozjazd bez toru, wzdłuż kierunek toru zasadniczego, potem w bok jego strona" + +msgid "Drag along the main track of the switch, then sideways to the side of the diverging one" +msgstr "Przeciągnij wzdłuż toru zasadniczego rozjazdu, potem w bok na stronę toru zwrotnego" + +msgid "Switch %s laid on from %s, its other ends free: lay on from them" +msgstr "Rozjazd %s ułożony od %s, pozostałe końce wolne: kładź od nich dalej" + +msgid "Switch %s laid with no track, its ends free: lay on from them" +msgstr "Rozjazd %s ułożony bez toru, końce wolne: kładź od nich dalej" + +msgid "Press and drag: this double slip is replaced by the one chosen" +msgstr "Wciśnij i przeciągnij: ten rozjazd krzyżowy zostanie zastąpiony wybranym" + +msgid "Press and drag: it goes here" +msgstr "Wciśnij i przeciągnij: stanie tutaj" + +msgid "Press+drag at a crossing" +msgstr "Wciśnij i przeciągnij na skrzyżowaniu" + +msgid "of two straights" +msgstr "dwóch prostych" + +msgid "Press+drag on the ground" +msgstr "Wciśnij i przeciągnij na terenie" + +msgid "a switch with no track, on from a free end near it" +msgstr "rozjazd bez toru, od pobliskiego wolnego końca" + +msgid "the direction of the main track, held from 4 m on" +msgstr "kierunek toru zasadniczego, od 4 m zatrzaśnięty" + +msgid "Back to the start" +msgstr "Z powrotem do początku" + +msgid "the direction free again" +msgstr "kierunek znów wolny" + +msgid "Drag at a crossing" +msgstr "Przeciągnij na skrzyżowaniu" + +msgid "Drag at a crossing of two straights" +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"