improved measuraments ,cursors, zooming and user interactions

This commit is contained in:
Martino Ferrari committed 2026-10-03 16:05:45 +02:00
1 parent 0eceaf537b
commit 02620ca946
12 files changed
+1488 -171

No files matched your search

+36
View File
@@ -157,6 +157,10 @@ enum class DisplayMode {
Capturing, ///< an edge was accepted: the live ring, edge-relative clock
Held, ///< the frozen snapshot of the last event
Waiting, ///< armed, nothing captured yet: blank
/// The user paused: the acquisition thread keeps writing the live ring, and the plot draws
/// the view that was frozen when the pause began -- a snapshot, exactly as `Held` is, so
/// resuming costs no data.
Paused,
};
/// The application's data model: the stream description, the two sample buffers,
@@ -238,6 +242,33 @@ class ScopeModel {
///
/// Caller holds `mutex()` (the acquisition thread already does when it latches).
void live_window_locked(double lo_s, double hi_s, u64& first, u64& last);
/// The frozen view taken when the user paused, or null while running. A pause is a
/// snapshot of the *visible window* in exactly the sense a trigger event is -- an immutable
/// series the plot binds to -- and the difference is the anchoring: an event's stamps are
/// edge-relative, a pause's are the stream's own, so the axis does not move when the pause
/// begins and the reader keeps the times they were already reading.
std::shared_ptr<const EventSnapshot> pause_view() const noexcept {
return pause_view_.load(std::memory_order_acquire);
}
/// Snapshots the live ring over `[lo_s, hi_s]` (stream seconds, the display epoch already
/// removed) as the pause view. The acquisition thread keeps writing, so this is a copy: what
/// is frozen is the picture, not the data.
void publish_pause_view(double lo_s, double hi_s);
void clear_pause_view() noexcept { pause_view_.store(nullptr, std::memory_order_release); }
/// Whether the user paused acquisition. Lives here rather than on `Acquisition` because the
/// bind, the measurement cut and the export all have to agree about what is on screen.
bool paused() const noexcept { return paused_.load(std::memory_order_relaxed); }
void set_paused(bool on) noexcept { paused_.store(on, std::memory_order_relaxed); }
/// The snapshot the plot should draw from, if any: the pause's frozen view while acquisition
/// is paused, else the trigger's held event. One accessor, so the bind, the measurements and
/// the export cannot disagree about which picture is on screen.
std::shared_ptr<const EventSnapshot> display_snapshot() const noexcept {
if (paused()) return pause_view();
return display_mode() == DisplayMode::Held ? event() : nullptr;
}
/// The event on screen, or null before the first latch.
std::shared_ptr<const EventSnapshot> event() const noexcept {
return event_.load(std::memory_order_acquire);
@@ -293,6 +324,9 @@ class ScopeModel {
/// answer around: calling it twice can straddle a latch, and then the bind and the
/// axis disagree about which clock they are on.
DisplayMode display_mode() const noexcept {
// A pause is the most recent instruction, so it wins over the trigger's own state: what the
// user last asked for is a still picture.
if (paused()) return DisplayMode::Paused;
if (!trigger_enabled()) return DisplayMode::Live;
if (trigger_pending()) return DisplayMode::Capturing;
return event() != nullptr ? DisplayMode::Held : DisplayMode::Waiting;
@@ -372,6 +406,8 @@ class ScopeModel {
std::atomic<bool> trigger_pending_{false};
std::atomic<double> capture_edge_{0.0};
std::atomic<std::shared_ptr<const EventSnapshot>> event_; ///< the event on screen
std::atomic<std::shared_ptr<const EventSnapshot>> pause_view_; ///< the view frozen at pause
std::atomic<bool> paused_{false};
/// Per-signal-name calibration (see `set_calibration`).
std::unordered_map<std::string, std::pair<double, double>> calibration_;
std::atomic<std::uint64_t> event_seq_{0};