diff --git a/application/editormode.cpp b/application/editormode.cpp index a6d3772c..a1cac045 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -5886,6 +5886,8 @@ void editor_mode::on_scroll(double const Xoffset, double const Yoffset) return; if (ImGui::GetIO().WantCaptureMouse || Yoffset == 0.0) return; + // a fast swipe on a touchpad can come as one big step + auto const notches{std::clamp(Yoffset, -2.0, 2.0)}; if (false == Global.EditorOrtho) { // in 3D the camera flies along the ray under the cursor, by a share of the distance to what it points at; @@ -5894,20 +5896,26 @@ void editor_mode::on_scroll(double const Xoffset, double const Yoffset) glm::dvec3 origin; glm::dvec3 direction; projection.ray(ImGui::GetIO().MousePos, origin, direction); + // with nothing under the cursor, the ground under the camera stands for it: the level 0 can lie far below the terrain + std::vector found; + auto const below{ground_heights({Camera.Pos}, false, &found)}; + auto const ground{found[0] != 0 ? below[0] : 0.0}; + auto const height{std::max(2.0, std::abs(Camera.Pos.y - ground))}; glm::dvec3 level; - auto const distance{std::min( - GfxRenderer->Mouse_Hit() ? glm::length(GfxRenderer->Mouse_Position()) : projection.on_level(ImGui::GetIO().MousePos, 0.0, level) ? glm::distance(level, origin) : std::max(20.0, std::abs(Camera.Pos.y) * 2.0), - 20000.0)}; - auto step{distance * (1.0 - std::pow(0.85, Yoffset))}; + auto distance{std::min(GfxRenderer->Mouse_Hit() ? glm::length(GfxRenderer->Mouse_Position()) : projection.on_level(ImGui::GetIO().MousePos, ground, level) ? glm::distance(level, origin) : 2.0 * height, 20000.0)}; + // backing away from a point on the horizon would throw the camera kilometres up + if (notches < 0.0) + distance = std::min(distance, std::max(100.0, 4.0 * height)); + auto step{distance * (1.0 - std::pow(0.85, notches))}; // no nearer than 2 m to the point, not through it - if (Yoffset > 0.0) + if (notches > 0.0) step = std::max(0.0, std::min(step, distance - 2.0)); Camera.Pos += direction * step; Global.pCamera = Camera; return; } auto const before{Global.EditorOrthoExtent}; - auto const after{std::clamp(before * static_cast(std::pow(0.85, Yoffset)), 5.0f, 50000.0f)}; + auto const after{std::clamp(before * static_cast(std::pow(0.85, notches)), 5.0f, 50000.0f)}; screen_projection const projection; glm::dvec3 origin; glm::dvec3 direction; diff --git a/application/editormode.h b/application/editormode.h index 42040efa..f154adad 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1476,6 +1476,17 @@ class editor_mode : public application_mode, private editor_track::observer editor_track::point_ref m_join_point; mutable alignment::join_result m_join_last; bool track_wheel(double const Offset); + // a touchpad sends fractions of a notch: a choice moves a step for each whole notch gathered, and only one in a gesture when + // Gesture; a pause, another key or the other way starts over + struct wheel_state + { + double rest{0.0}; + double time{-10.0}; + int keys{0}; + bool spent{false}; + }; + wheel_state m_wheel; + int wheel_step(double const Offset, bool const Gesture); std::string track_shaping(bool const Joined) const; void render_layout_combo(); double design_speed(TTrack const *From, TTrack const *To) const; diff --git a/application/editormodelay.cpp b/application/editormodelay.cpp index 40155f80..d66b7608 100644 --- a/application/editormodelay.cpp +++ b/application/editormodelay.cpp @@ -696,13 +696,37 @@ void editor_mode::render_layout_combo() ImGui::TextDisabled("%s", STR_C("(Shift+wheel)")); } +int editor_mode::wheel_step(double const Offset, bool const Gesture) +{ + auto const &io{ImGui::GetIO()}; + auto const now{ImGui::GetTime()}; + int const keys{(io.KeyCtrl ? 1 : 0) | (io.KeyShift ? 2 : 0) | (io.KeyAlt ? 4 : 0)}; + auto &wheel{m_wheel}; + if (now - wheel.time > 0.3 || keys != wheel.keys || (wheel.rest != 0.0 && (wheel.rest > 0.0) != (Offset > 0.0))) + { + wheel.rest = 0.0; + wheel.spent = false; + } + wheel.time = now; + wheel.keys = keys; + wheel.rest += Offset; + if ((Gesture && wheel.spent) || std::abs(wheel.rest) < 1.0 - 1e-6) + return 0; + int const step{wheel.rest > 0.0 ? 1 : -1}; + wheel.rest -= step; + wheel.spent = true; + return step; +} + bool editor_mode::track_wheel(double const Offset) { auto const &io{ImGui::GetIO()}; if (m_switch.placing && m_switch.connection >= 0 && Offset != 0.0 && (io.KeyCtrl || io.KeyShift || io.KeyAlt)) { auto &tool{m_switch}; - int const step{Offset > 0.0 ? 1 : -1}; + int const step{wheel_step(Offset, false == io.KeyCtrl)}; + if (step == 0) + return true; if (io.KeyShift) tool.connection = (tool.connection + step + connection::kind_count) % connection::kind_count; else if (io.KeyCtrl) @@ -723,7 +747,9 @@ bool editor_mode::track_wheel(double const Offset) bool const laying{m_lay.active && false == m_lay.points.empty()}; if (false == m_extend.active && false == laying) return false; - int const step{Offset > 0.0 ? 1 : -1}; + int const step{wheel_step(Offset, false == io.KeyCtrl)}; + if (step == 0) + return true; editor_track::snap_target target; bool const joined{m_extend.active ? extend_snap(target) : m_lay.mouse_snap.track != nullptr && m_lay.start.track != nullptr}; static double const radii[]{100.0, 150.0, 190.0, 250.0, 300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1200.0, 1500.0, 2000.0, 3000.0, 4000.0, 6000.0}; diff --git a/application/editormodeselftestjoin.cpp b/application/editormodeselftestjoin.cpp index 0ff46612..17f1791d 100644 --- a/application/editormodeselftestjoin.cpp +++ b/application/editormodeselftestjoin.cpp @@ -169,6 +169,12 @@ editor_mode::selftest_result editor_mode::selftest_join(std::string const &comma lay_cancel(); return selftest_result::handled; } + if (command == "laystate") + { + WriteLog(format("SELFTEST laystate: layout %d, join variant %d, R %.0f m, join R %.0f m, camera %.1f %.1f %.1f, extent %.1f", static_cast(m_track_layout), m_join_variant, m_lay.radius, m_join_radius, Camera.Pos.x, + Camera.Pos.y, Camera.Pos.z, static_cast(Global.EditorOrthoExtent))); + return selftest_result::handled; + } if (command == "layclicks") { int layout{0};