diff --git a/application/editormode.cpp b/application/editormode.cpp index 334485dd..b1cebb77 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1407,6 +1407,8 @@ bool editor_mode::update() render_viewport_overlays(); render_track_inspector(); + if (ui()->mode() == nodebank_panel::TRACK) + draw_track_lines(); if (selected_track()) draw_track_overlay(); if (route_active()) @@ -4780,7 +4782,7 @@ void editor_mode::render_gizmo_options() { ImGui::SameLine(); ImGui::SetNextItemWidth(120.0f); - ImGui::SliderFloat(STR_C("Extent (m), wheel / MMB pan"), &Global.EditorOrthoExtent, 5.0f, 5000.0f, "%.0f", ImGuiSliderFlags_Logarithmic); + ImGui::SliderFloat(STR_C("Extent (m), wheel / MMB pan"), &Global.EditorOrthoExtent, 5.0f, 50000.0f, "%.0f", ImGuiSliderFlags_Logarithmic); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("Wheel zooms. Hold the wheel and drag to pan.")); } @@ -4825,6 +4827,18 @@ void editor_mode::render_gizmo_options() void editor_mode::render_gizmo() { + if (straights_active()) + { + render_straight_gizmo(); + return; + } + + if (route_active()) + { + render_route_gizmo(); + return; + } + // the transform gizmo is suppressed while editing terrain, so the brush/chunk tool owns the mouse if (!m_gizmo_enabled || terrain_brush() || terrain_clicking()) { @@ -4838,18 +4852,6 @@ void editor_mode::render_gizmo() return; } - if (straights_active()) - { - render_straight_gizmo(); - return; - } - - if (route_active()) - { - render_route_gizmo(); - return; - } - if (!m_node && m_instance == 0) { m_gizmo_using = false; @@ -5123,7 +5125,7 @@ void editor_mode::on_scroll(double const Xoffset, double const Yoffset) if (false == Global.EditorOrtho || ImGui::GetIO().WantCaptureMouse) return; auto const before{Global.EditorOrthoExtent}; - auto const after{std::clamp(before * static_cast(std::pow(0.85, Yoffset)), 5.0f, 5000.0f)}; + auto const after{std::clamp(before * static_cast(std::pow(0.85, Yoffset)), 5.0f, 50000.0f)}; screen_projection const projection; glm::dvec3 origin; glm::dvec3 direction; @@ -5288,7 +5290,7 @@ void editor_mode::exit() m_track_snap = {}; m_track_point = {}; m_route = {}; - m_route_gizmo_using = false; + m_route_dragging = false; if (Global.EditorOrtho) toggle_ortho(); diff --git a/application/editormode.h b/application/editormode.h index 88137c31..77df0691 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -876,7 +876,6 @@ class editor_mode : public application_mode, private editor_track::observer std::vector> drag_states; glm::dvec3 preview_start{0.0}; glm::dvec3 preview_end{0.0}; - glm::mat4 gizmo{1.0f}; glm::dvec3 edit_start{0.0}; glm::dvec3 edit_end{0.0}; double edit_length{0.0}; @@ -886,17 +885,16 @@ class editor_mode : public application_mode, private editor_track::observer bool auto_transitions{true}; double transition{0.0}; int tool{0}; + int tool_before{0}; bool tool_mouse{false}; std::vector detour; editor_track::straight detour_line; glm::dvec3 detour_mouse{0.0}; bool tool_placed{false}; - bool tool_dragging{false}; double tool_at{0.0}; glm::dvec3 tool_point{0.0}; glm::dvec3 tool_handle{0.0}; editor_track::straight tool_line; - glm::mat4 tool_gizmo{1.0f}; std::string status; }; void render_straights_ui(); @@ -908,7 +906,6 @@ class editor_mode : public application_mode, private editor_track::observer void straights_apply(std::vector const &Lines, std::vector> const &Ends); bool in_straight_set(editor_track::straight const &Line) const; bool place_straight_tool(); - void render_straight_tool_gizmo(); void apply_straight_tool(); double straight_tool_radius(editor_track::straight const &Line) const; void straight_reshape(editor_track::straight const &Line, double const From, double const To, std::function const &Tail, double const Radius); @@ -1264,6 +1261,7 @@ class editor_mode : public application_mode, private editor_track::observer track_hover track_under_cursor(TTrack const *Only = nullptr, float const Pixels = 0.0f) const; track_hover split_spot() const; void update_track_hover(); + void draw_track_lines() const; void draw_track_hover() const; struct track_intent { @@ -2114,8 +2112,7 @@ class editor_mode : public application_mode, private editor_track::observer void render_infra_bound(); void draw_infra_overlay() const; - bool m_route_gizmo_using{false}; - glm::mat4 m_route_gizmo{1.0f}; + bool m_route_dragging{false}; float m_track_snap_radius{1.0f}; bool m_track_align_tangent{true}; bool m_track_drag_connected{true}; diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index 5cb4eb50..3f9dd817 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -149,6 +149,18 @@ editor_mode::selftest_result editor_mode::selftest_scene(std::string const &comm save(); return selftest_result::handled; } + if (command == "ortho") + { + float extent{0.0f}; + words >> extent; + choose_work_area(work_area::tracks); + if (false == Global.EditorOrtho) + toggle_ortho(); + if (extent > 0.0f) + Global.EditorOrthoExtent = std::clamp(extent, 5.0f, 50000.0f); + WriteLog(format("SELFTEST ortho extent %.0f", Global.EditorOrthoExtent)); + return selftest_result::handled; + } if (command == "mapstate") { auto const marked{scene::Layers.empty() ? std::vector{} : scene::Layers.marked(scene::layer_handle{1}, "//$o")}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 1fc12de7..6e641f70 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -665,7 +665,7 @@ std::string editor_mode::track_mode_name() const if (false == m_straights.detour.empty()) return STR_C("Plan, straight: shift around, Shift+click the points"); if (m_straights.tool == 1) - return STR_C("Plan, straight: break, LMB on the straight"); + return STR_C("Plan, straight: break, press on the straight and drag"); if (m_straights.tool == 2) return STR_C("Plan, straight: S-curve, LMB on the straight"); return STR_C("Plan, straight: drag its ends or the middle"); @@ -2292,42 +2292,24 @@ void editor_mode::render_route_gizmo() auto &route{m_route}; auto const count{static_cast(route.design.vertices.size())}; bool const grip{route.grip >= 0 && route.grip < count && route.grip < static_cast(route.result.vertex_chainages.size())}; - if ((false == grip && (route.vertex < 0 || route.vertex >= count)) || false == m_gizmo_enabled) + if (false == grip && (route.vertex < 0 || route.vertex >= count)) { - m_route_gizmo_using = false; + m_route_dragging = false; return; } - gizmo_frame const frame; - auto const position{grip ? alignment::evaluate(route.result, route.result.vertex_chainages[route.grip]).position : route_vertex_position(route.vertex)}; glm::dvec3 moved{position}; - bool held{false}; - if (m_handle_drag.active) + if (false == handle_drag_step(moved)) { - held = handle_drag_step(moved); - if (false == held && false == m_route_gizmo_using) - return; - } - else - { - if (false == m_route_gizmo_using) - m_route_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(position - frame.camera)); - frame.manipulate(ImGuizmo::TRANSLATE, m_route_gizmo, m_gizmo_snap); - held = ImGuizmo::IsUsing(); - moved = frame.camera + glm::dvec3(m_route_gizmo[3]); - } - - if (false == held) - { - if (m_route_gizmo_using) + if (m_route_dragging) { - m_route_gizmo_using = false; + m_route_dragging = false; route_apply(); } return; } - m_route_gizmo_using = true; + m_route_dragging = true; if (grip) { auto &vertex{route.design.vertices[route.grip]}; @@ -3079,38 +3061,18 @@ void editor_mode::render_straight_gizmo() typed_turn(); return; } - if (state.tool_placed && m_gizmo_enabled) - { - render_straight_tool_gizmo(); - return; - } auto const &line{state.dragging ? state.drag_line : current_straight()}; - if (state.handle < 0 || line.tracks.empty() || false == m_gizmo_enabled) + if (state.handle < 0 || line.tracks.empty()) { state.dragging = false; return; } - gizmo_frame const frame; - glm::dvec3 const anchors[] = {line.start, line.end, (line.start + line.end) * 0.5}; - auto const anchor{anchors[state.handle]}; - glm::dvec3 handled{anchor}; - bool held{false}; - if (m_handle_drag.active) - { - held = handle_drag_step(handled); - if (false == held && false == state.dragging) - return; - } - else - { - if (false == state.dragging) - state.gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(anchor - frame.camera)); - frame.manipulate(ImGuizmo::TRANSLATE, state.gizmo, m_gizmo_snap); - held = ImGuizmo::IsUsing(); - handled = frame.camera + glm::dvec3(state.gizmo[3]); - } + glm::dvec3 handled{anchors[state.handle]}; + bool const held{handle_drag_step(handled)}; + if (false == held && false == state.dragging) + return; if (held) { @@ -3523,32 +3485,10 @@ bool editor_mode::place_straight_tool() auto const reach{std::min(50.0, std::max(1.0, line.length - along))}; state.tool_handle = state.tool_point + axis * reach; state.tool_placed = true; - state.tool_dragging = false; state.handle = -1; return true; } -void editor_mode::render_straight_tool_gizmo() -{ - auto &state{m_straights}; - gizmo_frame const frame; - if (false == state.tool_dragging) - state.tool_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(state.tool_handle - frame.camera)); - frame.manipulate(ImGuizmo::TRANSLATE, state.tool_gizmo, 0.0f); - if (ImGuizmo::IsUsing()) - { - state.tool_dragging = true; - auto const moved{frame.camera + glm::dvec3(state.tool_gizmo[3])}; - state.tool_handle = {moved.x, state.tool_handle.y, moved.z}; - typed_turn(); - } - else if (state.tool_dragging) - { - state.tool_dragging = false; - apply_straight_tool(); - } -} - void editor_mode::apply_straight_tool() { auto &state{m_straights}; @@ -3596,11 +3536,11 @@ void editor_mode::apply_straight_tool() void editor_mode::start_straight_gesture(int const Tool) { auto &state{m_straights}; - auto const previous{state.tool}; + state.tool_before = state.tool; state.tool = Tool; if (false == place_straight_tool()) { - state.tool = previous; + state.tool = state.tool_before; return; } state.tool_mouse = true; @@ -3611,7 +3551,7 @@ void editor_mode::finish_straight_gesture() auto &state{m_straights}; state.tool_mouse = false; apply_straight_tool(); - state.tool = 0; + state.tool = state.tool_before; state.tool_placed = false; } @@ -3879,7 +3819,7 @@ std::string editor_mode::track_readout() const return text; } auto const &state{m_straights}; - if (state.tool_placed && state.tool_dragging) + if (state.tool_placed && state.tool_mouse) { auto const &line{state.tool_line}; auto const offset{plan_of(state.tool_handle - state.tool_point)}; @@ -4304,7 +4244,7 @@ bool editor_mode::handle_drag_step(glm::dvec3 &Position) drag.moved = true; screen_projection const projection; glm::dvec3 level; - if (projection.on_level(io.MousePos, drag.height, level) && glm::distance(level, glm::dvec3{Global.pCamera.Pos}) <= static_cast(kMaxPlacementDistance)) + if (projection.on_level(io.MousePos, drag.height, level) && (Global.editor_tracks || glm::distance(level, glm::dvec3{Global.pCamera.Pos}) <= static_cast(kMaxPlacementDistance))) drag.position = level + drag.offset; Position = drag.position; return true; @@ -4377,7 +4317,7 @@ void editor_mode::point_drag_update() } screen_projection const projection; glm::dvec3 position; - if (false == projection.on_level(io.MousePos, drag.height, position) || glm::distance(position, glm::dvec3{Global.pCamera.Pos}) > static_cast(kMaxPlacementDistance)) + if (false == projection.on_level(io.MousePos, drag.height, position) || (false == Global.editor_tracks && glm::distance(position, glm::dvec3{Global.pCamera.Pos}) > static_cast(kMaxPlacementDistance))) return; m_track_snap = {}; if (editor_track::is_end(m_track_point.kind) && m_track_drag_points.empty()) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 16ea91cd..2a54b75b 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -38,8 +38,11 @@ namespace { using geometry::bezier; +using geometry::plan_of; float const kHoverPixels{12.0f}; +float const kFarHoverPixels{16.0f}; +double const kLinesFrom{0.5}; double const kHoverLabelDelay{0.35}; // s the cursor rests on a path before its data shows up double const kFocusDistance{25.0}; auto const kContextClick{std::chrono::milliseconds(350)}; @@ -81,6 +84,19 @@ char const *type_of(TTrack const &Track) } } +double ortho_metres_per_pixel() +{ + auto const height{ImGui::GetIO().DisplaySize.y}; + return Global.EditorOrtho && height > 0.0f ? 2.0 * static_cast(Global.EditorOrthoExtent) / static_cast(height) : 0.0; +} + +bool plan_near(bezier const &Curve, glm::dvec2 const &Point, double const Reach) +{ + auto const centre{(plan_of(Curve.p0) + plan_of(Curve.p1) + plan_of(Curve.p2) + plan_of(Curve.p3)) * 0.25}; + auto const radius{std::max({glm::distance(centre, plan_of(Curve.p0)), glm::distance(centre, plan_of(Curve.p1)), glm::distance(centre, plan_of(Curve.p2)), glm::distance(centre, plan_of(Curve.p3))})}; + return glm::distance(centre, Point) <= radius + Reach; +} + double segment_distance2(glm::vec2 const &Point, glm::vec2 const &A, glm::vec2 const &B, double &T) { auto const ab{B - A}; @@ -98,7 +114,14 @@ editor_mode::track_hover editor_mode::track_under_cursor(TTrack const *Only, flo auto const &io{ImGui::GetIO()}; screen_projection const projection; glm::vec2 const mouse{io.MousePos.x, io.MousePos.y}; - double best{Pixels > 0.0f ? Pixels * Pixels : Only != nullptr ? std::numeric_limits::max() : kHoverPixels * kHoverPixels}; + auto const metres{ortho_metres_per_pixel()}; + auto const hover{metres >= kLinesFrom ? kFarHoverPixels : kHoverPixels}; + double best{Pixels > 0.0f ? Pixels * Pixels : Only != nullptr ? std::numeric_limits::max() : hover * hover}; + glm::dvec3 origin; + glm::dvec3 direction; + projection.ray(io.MousePos, origin, direction); + bool const cull{metres > 0.0 && Only == nullptr}; + auto const reach{cull ? std::sqrt(best) * metres : 0.0}; for (auto *track : simulation::Paths.sequence()) { if (track == nullptr || false == rail(*track) || track->m_editorremoved || (Only != nullptr && track != Only)) @@ -106,6 +129,8 @@ editor_mode::track_hover editor_mode::track_under_cursor(TTrack const *Only, flo for (int i = 0; i < static_cast(track->m_paths.size()); ++i) { bezier const curve{track->m_paths[i]}; + if (cull && false == plan_near(curve, plan_of(origin), reach)) + continue; auto const count{std::clamp(static_cast(curve.plan_length() / 3.0), 6, 64)}; auto previous{curve.point(0.0)}; ImVec2 from; @@ -140,7 +165,7 @@ void editor_mode::update_track_hover() { if (typed_meaning() == nullptr) m_typed.clear(); - bool const blocked{ImGui::GetIO().WantCaptureMouse || mouseHold || m_track_set_using || m_track_box.active || m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS || m_track_gizmo_using || m_route_gizmo_using || m_extend.active || m_switch.placing || + bool const blocked{ImGui::GetIO().WantCaptureMouse || mouseHold || m_track_set_using || m_track_box.active || m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS || m_track_gizmo_using || m_route_dragging || m_extend.active || m_switch.placing || m_straights.dragging || m_lay.active}; if (blocked) { @@ -152,6 +177,37 @@ void editor_mode::update_track_hover() m_hover = found; } +void editor_mode::draw_track_lines() const +{ + auto const metres{ortho_metres_per_pixel()}; + if (metres < kLinesFrom) + return; + auto const &io{ImGui::GetIO()}; + screen_projection const projection; + glm::dvec2 const centre{Global.pCamera.Pos.x, Global.pCamera.Pos.z}; + auto const reach{0.5 * metres * std::hypot(static_cast(io.DisplaySize.x), static_cast(io.DisplaySize.y))}; + auto *drawlist{ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())}; + for (auto *track : simulation::Paths.sequence()) + { + if (track == nullptr || false == rail(*track)) + continue; + for (auto const &path : track->m_paths) + { + bezier const curve{path}; + if (false == plan_near(curve, centre, reach)) + continue; + auto const count{std::clamp(static_cast(curve.plan_length(4) / metres / 8.0), 1, 32)}; + auto previous{curve.point(0.0)}; + for (int k = 1; k <= count; ++k) + { + auto const next{curve.point(static_cast(k) / count)}; + projection.line(drawlist, previous, next, IM_COL32(235, 235, 235, 150), 1.5f); + previous = next; + } + } + } +} + void editor_mode::draw_track_hover() const { using kind = track_intent::kind; @@ -180,7 +236,7 @@ void editor_mode::draw_track_hover() const bool editor_mode::track_gesture_on() const { - return mouseHold || m_track_set_using || m_track_box.active || m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS || m_track_gizmo_using || m_route_gizmo_using || m_extend.active || m_switch.placing || m_straights.dragging || m_straights.tool_mouse || + return mouseHold || m_track_set_using || m_track_box.active || m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS || m_track_gizmo_using || m_route_dragging || m_extend.active || m_switch.placing || m_straights.dragging || m_straights.tool_mouse || m_point_drag.active || m_handle_drag.active || m_sweep.dragging || m_turntable.placing || m_signal.placing || m_signal.moving || m_switch.slide.active || ImGuizmo::IsUsing(); } @@ -349,7 +405,7 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods) if (m_straights.tool != 0 && false == line.tracks.empty()) { intent.position = line.start + glm::dvec3{line.direction.x, line.grade, line.direction.y} * std::clamp(line.along(ground), 0.0, line.length); - return make(kind::straight_tool, m_straights.tool == 1 ? "Click: break the straight here" : "Click: start of the shift"); + return make(kind::straight_tool, m_straights.tool == 1 ? "Press: break the straight here, drag to turn the rest" : "Click: start of the shift"); } } if ((m_track_tab == track_tab::turnout || m_track_tab == track_tab::path) && m_hover.track != nullptr && switch_slides(*m_hover.track)) @@ -445,7 +501,12 @@ void editor_mode::track_press(track_intent const &Intent) } case kind::straight_break: start_straight_gesture(1); return; case kind::detour: add_detour_point(); return; - case kind::straight_tool: place_straight_tool(); return; + case kind::straight_tool: + if (m_straights.tool == 1) + start_straight_gesture(1); + else + place_straight_tool(); + return; case kind::straight_handle: m_straights.handle = Intent.index; handle_drag_start(Intent.position); @@ -758,7 +819,7 @@ char const *editor_mode::typed_meaning() const auto const &straights{m_straights}; if (straights.dragging) return straights.handle == 2 ? "type the shift, + to the right" : "type the length"; - if (straights.tool == 1 && (straights.tool_mouse || straights.tool_dragging)) + if (straights.tool == 1 && straights.tool_mouse) return "type the turn in degrees, + to the right"; if (m_lay.active && false == m_lay.points.empty()) return "type the length of the next leg, Enter adds the point"; diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index e828a01f..bfff5613 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -399,7 +399,7 @@ void editor_mode::render_toolbar_tools() void editor_mode::render_viewport_gizmo() { // the gizmo moves the models and the tracks; on the terrain the mouse is the brush's - if (false == m_gizmo_enabled || current_work_area() == work_area::terrain) + if (false == m_gizmo_enabled || current_work_area() == work_area::terrain || (ui()->mode() == nodebank_panel::TRACK && track_plan_tab())) return; auto const tooltip{STR_C("What the handles of the gizmo on the selection do when dragged; Ctrl held snaps the values")}; std::array const buttons{{ diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 0d445a2a..36ddad7c 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -44,8 +44,8 @@ msgstr "Plan, prosta: objazd, Shift+klik w punkty" msgid "New path from the free end" msgstr "Nowy odcinek z wolnego końca" -msgid "Plan, straight: break, LMB on the straight" -msgstr "Plan, prosta: załamanie, LPM na prostej" +msgid "Plan, straight: break, press on the straight and drag" +msgstr "Plan, prosta: załamanie, wciśnij LPM na prostej i ciągnij" msgid "Plan, straight: S-curve, LMB on the straight" msgstr "Plan, prosta: przesunięcie (esowanie), LPM na prostej" @@ -2868,9 +2868,6 @@ msgstr "Wciśnij: załam prostą tutaj, przeciągnij, by obrócić resztę" msgid "Click: next point of the shift around an obstacle" msgstr "Klik: następny punkt objazdu przeszkody" -msgid "Click: break the straight here" -msgstr "Klik: załam prostą tutaj" - msgid "Click: start of the shift" msgstr "Klik: początek przesunięcia"