Updated with native ui (imgui + qt)
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.h"
|
||||
#include "signal_buffer.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QPair>
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "qcustomplot.h"
|
||||
|
||||
/**
|
||||
* A single plot panel backed by QCustomPlot.
|
||||
*
|
||||
* Rendering pipeline per trace each timer tick:
|
||||
* 1. select ring buffer or archive depending on window coverage
|
||||
* 2. read_window_decimated → kRawPts (uniform stride)
|
||||
* 3. lttb(kRawPts → kDisplayPts) — visually optimal 3000-point line
|
||||
*
|
||||
* Zoom behaviour:
|
||||
* - Live mode: rolling window updated each tick UNLESS userZoomed_ is set.
|
||||
* - Zoomed (userZoomed_): axis stays at user range; re-queries buffer with
|
||||
* that range → zooming in gives higher resolution from the ring buffer.
|
||||
* - "Reset Zoom" context menu clears userZoomed_ and returns to live mode.
|
||||
*
|
||||
* x-range adapts to actual available data so the plot never shows empty space
|
||||
* on the left when the signal has just started.
|
||||
*/
|
||||
class PlotWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PlotWidget(int plotId, QWidget* parent = nullptr);
|
||||
|
||||
int plotId() const { return plotId_; }
|
||||
|
||||
// ── Signal management ─────────────────────────────────────────────
|
||||
void addSignal(std::shared_ptr<Source> src, int sigIdx);
|
||||
void removeSignal(std::shared_ptr<Source> src, int sigIdx);
|
||||
bool hasSignal(const std::shared_ptr<Source>& src, int sigIdx) const;
|
||||
|
||||
/** For saving/restoring traces across layout changes. */
|
||||
struct SignalId { std::shared_ptr<Source> src; int sigIdx; };
|
||||
std::vector<SignalId> getSignals() const;
|
||||
|
||||
// ── View control (called from MainWindow timer) ───────────────────
|
||||
void refresh(double viewTMax, double windowSec, bool paused);
|
||||
|
||||
/** Force the visible range (e.g. when a trigger fires). */
|
||||
void setViewRange(double tMin, double tMax);
|
||||
|
||||
/** Return to live rolling window. */
|
||||
void resetZoom();
|
||||
|
||||
/** Apply a specific x range (for zoom-back; suppresses history emission). */
|
||||
void applyZoomRange(double lo, double hi);
|
||||
|
||||
/** Fit Y axis to currently visible data. */
|
||||
void fitYAxis();
|
||||
|
||||
/** Current x axis range. */
|
||||
QPair<double,double> currentXRange() const;
|
||||
|
||||
/** Sync X range from another plot (cross-plot zoom). */
|
||||
void syncXRange(double lo, double hi);
|
||||
|
||||
/** Push current cursor state to display (called from MainWindow). */
|
||||
void refreshCursors(double tA, double tB, bool aActive, bool bActive);
|
||||
|
||||
signals:
|
||||
void signalDropped(int srcIdx, int sigIdx);
|
||||
void xRangeChanged(double lo, double hi); // emitted on user zoom/pan
|
||||
void cursorAPlaced(double t); // emitted on Ctrl+Left click
|
||||
void cursorBPlaced(double t); // emitted on Ctrl+Right click
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent* e) override;
|
||||
void dropEvent(QDropEvent* e) override;
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onLegendDoubleClick(QCPLegend* legend, QCPAbstractLegendItem* item);
|
||||
void onContextMenuRequest(QPoint pos);
|
||||
|
||||
private:
|
||||
struct Trace {
|
||||
std::shared_ptr<Source> src;
|
||||
int sigIdx;
|
||||
QCPGraph* graphMin = nullptr;
|
||||
QCPGraph* graphMax = nullptr;
|
||||
QCPGraph* graphLine = nullptr;
|
||||
size_t lastHead = 0;
|
||||
double lastTMin = 0.0;
|
||||
double lastTMax = 0.0;
|
||||
QColor color; // persistent user color (invalid = use palette)
|
||||
qreal lineWidth = 1.5;
|
||||
};
|
||||
|
||||
void applyDarkTheme();
|
||||
void rebuildTrace(Trace& tr);
|
||||
|
||||
void updateCursorItems(); // update QCPItemLine positions + visibility
|
||||
|
||||
int plotId_;
|
||||
QCustomPlot* qcp_ = nullptr;
|
||||
std::vector<Trace> traces_;
|
||||
|
||||
// Zoom / sync state
|
||||
bool userZoomed_ = false;
|
||||
bool zoomGuard_ = false;
|
||||
bool syncGuard_ = false; // prevent re-entrant xRangeChanged during syncXRange
|
||||
|
||||
// A/B cursor items (owned by qcp_, positions set via refreshCursors)
|
||||
QCPItemLine* cursorA_ = nullptr;
|
||||
QCPItemLine* cursorB_ = nullptr;
|
||||
double cursorATime_ = 0.0;
|
||||
double cursorBTime_ = 0.0;
|
||||
bool cursorAActive_ = false;
|
||||
bool cursorBActive_ = false;
|
||||
|
||||
// Rendering pipeline scratch buffers
|
||||
// kFullResPts: full-res read budget (light path). Must be >= 4*kMaxBuckets so
|
||||
// the light-path threshold never exceeds what read_window can return.
|
||||
static constexpr size_t kFullResPts = 65'536;
|
||||
// kRawPts: stride-decimated read budget (heavy path, >kModerateThreshold samples)
|
||||
static constexpr size_t kRawPts = 8'192;
|
||||
// kMaxBuckets: number of min-max buckets (moderate path + LTTB output)
|
||||
static constexpr size_t kMaxBuckets = 4'096;
|
||||
std::vector<double> tmpT_, tmpV_; // full-res / stride-decimated intermediary
|
||||
std::vector<double> tmpMinV_, tmpMaxV_; // min-max decimation intermediary
|
||||
std::vector<double> outT_, outV_; // LTTB output (resized as needed)
|
||||
};
|
||||
Reference in New Issue
Block a user