mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 09:03:46 +08:00
jp: zoom improvements
(cherry picked from commit f804d3468e05820f11e3580d6aa797bc958c7b82)
This commit is contained in:
committed by
firestar5683
parent
18860c3d6c
commit
dd6b26bbba
@@ -663,6 +663,11 @@ struct UndoStack {
|
||||
}
|
||||
};
|
||||
|
||||
struct PlotView {
|
||||
ImGuiID plot_id = 0;
|
||||
PlotRange range;
|
||||
};
|
||||
|
||||
struct UiState {
|
||||
bool open_open_route = false;
|
||||
bool open_stream = false;
|
||||
@@ -734,6 +739,12 @@ struct UiState {
|
||||
bool init_data_selected = false;
|
||||
std::string init_data_search;
|
||||
UndoStack undo;
|
||||
std::vector<PlotView> plot_view_history;
|
||||
std::optional<PlotView> restore_plot_view;
|
||||
ImGuiID last_plot_view_id = 0;
|
||||
double last_plot_view_change = -1.0;
|
||||
bool last_plot_view_wheel = false;
|
||||
double last_plot_pan_click = -1.0;
|
||||
};
|
||||
|
||||
// app.cc public API
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "tools/jotpluggler/internal.h"
|
||||
|
||||
#include "implot.h"
|
||||
#include "implot_internal.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#include <cmath>
|
||||
@@ -801,7 +802,13 @@ void draw_plot(const AppSession &session, Pane *pane, UiState *state) {
|
||||
plot_flags |= ImPlotFlags_NoLegend;
|
||||
}
|
||||
|
||||
const ImPlotAxisFlags x_axis_flags = ImPlotAxisFlags_NoMenus | ImPlotAxisFlags_NoHighlight;
|
||||
ImPlotAxisFlags x_axis_flags = ImPlotAxisFlags_NoMenus | ImPlotAxisFlags_NoHighlight;
|
||||
const ImGuiIO &input = ImGui::GetIO();
|
||||
const bool at_zoom_limit = state->x_view_max - state->x_view_min <= MIN_HORIZONTAL_ZOOM_SECONDS + 1.0e-6;
|
||||
if (at_zoom_limit && (input.MouseWheel > 0.0f || input.MouseReleased[ImGuiMouseButton_Left])) {
|
||||
// Further zoom-in must not move the time window; panning and zoom-out remain available.
|
||||
x_axis_flags |= ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax;
|
||||
}
|
||||
ImPlotAxisFlags y_axis_flags = ImPlotAxisFlags_NoMenus | ImPlotAxisFlags_NoHighlight;
|
||||
if (state_block_mode) {
|
||||
y_axis_flags |= ImPlotAxisFlags_NoDecorations;
|
||||
@@ -817,6 +824,8 @@ void draw_plot(const AppSession &session, Pane *pane, UiState *state) {
|
||||
if (ImPlot::BeginPlot("##plot", plot_size, plot_flags)) {
|
||||
ImPlot::SetupAxes(nullptr, nullptr, x_axis_flags, y_axis_flags);
|
||||
ImPlot::SetupAxisFormat(ImAxis_X1, "%.1f");
|
||||
// ImPlot updates pan endpoints separately; a minimum span would distort the pan.
|
||||
ImPlot::SetupAxisZoomConstraints(ImAxis_X1, input.MouseDown[ImGuiMouseButton_Right] ? 0.0 : MIN_HORIZONTAL_ZOOM_SECONDS, INFINITY);
|
||||
if (state_block_mode) {
|
||||
ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 1.0, ImPlotCond_Always);
|
||||
} else if (pane_value_format.valid) {
|
||||
@@ -841,6 +850,28 @@ void draw_plot(const AppSession &session, Pane *pane, UiState *state) {
|
||||
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
||||
}
|
||||
|
||||
ImPlotPlot *plot = ImPlot::GetCurrentPlot();
|
||||
const PlotRange previous_view = {.left = previous_x_min, .right = previous_x_max,
|
||||
.bottom = plot->Axes[ImAxis_Y1].Range.Min, .top = plot->Axes[ImAxis_Y1].Range.Max};
|
||||
const bool was_selecting = plot->Selecting;
|
||||
const bool restoring_view = state->restore_plot_view && state->restore_plot_view->plot_id == plot->ID;
|
||||
if (restoring_view) {
|
||||
ImPlot::SetupAxisLimits(ImAxis_Y1, state->restore_plot_view->range.bottom, state->restore_plot_view->range.top, ImPlotCond_Always);
|
||||
state->restore_plot_view.reset();
|
||||
}
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
const ImVec2 drag(io.MousePos.x - plot->SelectStart.x, io.MousePos.y - plot->SelectStart.y);
|
||||
const bool zoom_ready = ImGui::GetTime() - io.MouseClickedTime[ImGuiMouseButton_Left] >= 0.09
|
||||
&& (std::abs(drag.x) > 12.0f || std::abs(drag.y) > 12.0f);
|
||||
// Cancel before ImPlot processes release; hide the box until both thresholds are met.
|
||||
if (io.MouseReleased[ImGuiMouseButton_Left] && !zoom_ready) {
|
||||
plot->Selecting = false;
|
||||
}
|
||||
ImPlot::SetupFinish();
|
||||
if (!zoom_ready) {
|
||||
plot->Selected = false;
|
||||
}
|
||||
|
||||
if (state_block_mode) {
|
||||
draw_state_blocks_pane(prepared_curves, state);
|
||||
} else {
|
||||
@@ -873,6 +904,28 @@ void draw_plot(const AppSession &session, Pane *pane, UiState *state) {
|
||||
state->tracker_time = std::clamp(ImPlot::GetPlotMousePos().x, state->route_x_min, state->route_x_max);
|
||||
state->has_tracker_time = true;
|
||||
}
|
||||
const ImPlotRect view = ImPlot::GetPlotLimits();
|
||||
const bool changed = std::abs(view.X.Min - previous_view.left) > 1.0e-6
|
||||
|| std::abs(view.X.Max - previous_view.right) > 1.0e-6
|
||||
|| std::abs(view.Y.Min - previous_view.bottom) > 1.0e-6
|
||||
|| std::abs(view.Y.Max - previous_view.top) > 1.0e-6;
|
||||
const bool wheel = io.MouseWheel != 0.0f;
|
||||
const bool pan = ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
||||
if (changed && (ImPlot::IsPlotHovered() || was_selecting || plot->Held) && (wheel || pan || io.MouseReleased[ImGuiMouseButton_Left])
|
||||
&& !restoring_view && !state->suppress_range_side_effects && !plot->JustCreated) {
|
||||
// Keep continuous pans and wheel bursts as one view-history entry.
|
||||
const bool continuing = state->last_plot_view_id == plot->ID
|
||||
&& ((pan && state->last_plot_pan_click == io.MouseClickedTime[ImGuiMouseButton_Right])
|
||||
|| (wheel && state->last_plot_view_wheel && ImGui::GetTime() - state->last_plot_view_change < 0.3));
|
||||
if (!continuing) {
|
||||
if (state->plot_view_history.size() == 50) state->plot_view_history.erase(state->plot_view_history.begin());
|
||||
state->plot_view_history.push_back({plot->ID, previous_view});
|
||||
}
|
||||
state->last_plot_view_id = plot->ID;
|
||||
state->last_plot_view_change = ImGui::GetTime();
|
||||
state->last_plot_view_wheel = wheel;
|
||||
state->last_plot_pan_click = io.MouseClickedTime[ImGuiMouseButton_Right];
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
app_pop_mono_font();
|
||||
|
||||
@@ -68,6 +68,18 @@ void render_layout(AppSession *session, UiState *state, bool show_camera_feed) {
|
||||
if (!ImGui::GetIO().WantTextInput && ImGui::IsKeyPressed(ImGuiKey_Space, false)) {
|
||||
state->playback_playing = !state->playback_playing;
|
||||
}
|
||||
// GLFW/ImGui mouse button 4 is the conventional mouse Back button.
|
||||
if (ImGui::IsMouseClicked(3) && !state->plot_view_history.empty()) {
|
||||
const PlotView view = state->plot_view_history.back();
|
||||
state->plot_view_history.pop_back();
|
||||
state->x_view_min = view.range.left;
|
||||
state->x_view_max = view.range.right;
|
||||
state->restore_plot_view = view;
|
||||
state->follow_latest = false;
|
||||
state->playback_playing = false;
|
||||
state->suppress_range_side_effects = true;
|
||||
state->last_plot_view_id = 0;
|
||||
}
|
||||
advance_playback(state, *session);
|
||||
CameraFeedView *sidebar_camera = session->pane_camera_feeds[static_cast<size_t>(sidebar_preview_camera_view(*session))].get();
|
||||
if (show_camera_feed && sidebar_camera != nullptr && state->has_tracker_time) {
|
||||
|
||||
@@ -31,6 +31,9 @@ void apply_route_data(AppSession *session, UiState *state, RouteData route_data)
|
||||
} else if (session->route_name.empty() && session->data_mode == SessionDataMode::Route) {
|
||||
session->route_id = {};
|
||||
}
|
||||
state->plot_view_history.clear();
|
||||
state->last_plot_view_id = 0;
|
||||
state->restore_plot_view.reset();
|
||||
session->route_data = std::move(route_data);
|
||||
rebuild_route_index(session);
|
||||
rebuild_browser_nodes(session, state);
|
||||
|
||||
Reference in New Issue
Block a user