v2.0.0
Loading...
Searching...
No Matches
channelrhiview.h
Go to the documentation of this file.
1//=============================================================================================================
20
21#ifndef CHANNELRHIVIEW_H
22#define CHANNELRHIVIEW_H
23
24//=============================================================================================================
25// INCLUDES
26//=============================================================================================================
27
28#include "../../disp_global.h"
29#include "channeldatamodel.h"
30
31//=============================================================================================================
32// QT INCLUDES
33//=============================================================================================================
34
35#include <QColor>
36#include <QElapsedTimer>
37#include <QFutureWatcher>
38#include <QImage>
39#include <QMouseEvent>
40#include <QPaintEvent>
41#include <QPointer>
42#include <QPropertyAnimation>
43#include <QRhiWidget>
44#include <QResizeEvent>
45#include <QWheelEvent>
46#include <QtConcurrent>
47
48#include <memory>
49#include <vector>
50
51//=============================================================================================================
52// DEFINE NAMESPACE DISPLIB
53//=============================================================================================================
54
55class QRhi;
56class QRhiBuffer;
57class QRhiCommandBuffer;
58class QRhiGraphicsPipeline;
59class QRhiResourceUpdateBatch;
60class QRhiSampler;
61class QRhiShaderResourceBindings;
62class QRhiTexture;
63
64class CrosshairOverlay; // defined in .cpp
65
66namespace DISPLIB
67{
68
69//=============================================================================================================
78class DISPSHARED_EXPORT ChannelRhiView : public QRhiWidget
79{
80 Q_OBJECT
81
84
85public:
86 //=========================================================================================================
90 struct EventMarker {
91 int sample = 0;
92 int type = 1;
93 QColor color;
94 QString label;
95 };
96
97 //=========================================================================================================
102 int startSample = 0;
103 int endSample = 0;
104 QColor color;
105 QString label;
106 };
107
108 explicit ChannelRhiView(QWidget *parent = nullptr);
109 ~ChannelRhiView() override;
110
111 //=========================================================================================================
117 void setModel(ChannelDataModel *model);
118
119 //=========================================================================================================
128 void setEvents(const QVector<EventMarker> &events);
129
130 void setEpochMarkers(const QVector<int> &triggerSamples);
131 void setEpochMarkersVisible(bool visible);
132 bool epochMarkersVisible() const { return m_bShowEpochMarkers; }
133
134 void setClippingVisible(bool visible);
135 bool clippingVisible() const { return m_bShowClipping; }
136
137 void setZScoreMode(bool enabled);
138 bool zScoreMode() const { return m_bZScoreMode; }
139
140 //=========================================================================================================
146 void setAnnotations(const QVector<AnnotationSpan> &annotations);
147
148 //=========================================================================================================
153 void setAnnotationSelectionEnabled(bool enabled);
154
155 void setEventsVisible(bool visible);
156 bool eventsVisible() const;
157
158 void setAnnotationsVisible(bool visible);
159 bool annotationsVisible() const;
160
161 // ── Scroll / zoom ─────────────────────────────────────────────────
162
163 float scrollSample() const { return m_scrollSample; }
164 float samplesPerPixel() const { return m_samplesPerPixel; }
165
166 //=========================================================================================================
173 void setScrollSample(float sample);
174
175 //=========================================================================================================
182 void setSamplesPerPixel(float spp);
183
184 //=========================================================================================================
191 void scrollTo(float targetSample, int durationMs = 200);
192
193 //=========================================================================================================
200 void zoomTo(float targetSpp, int durationMs = 200);
201
202 //=========================================================================================================
208 void setBackgroundColor(const QColor &color);
209 QColor backgroundColor() const { return m_bgColor; }
210
211 //=========================================================================================================
218 void setPrefetchFactor(float factor);
219
220 //=========================================================================================================
225 int visibleFirstSample() const;
226
227 //=========================================================================================================
231 int visibleSampleCount() const;
232
233 //=========================================================================================================
242 void setChannelIndices(const QVector<int> &indices);
243
244 //=========================================================================================================
248 int totalLogicalChannels() const;
249
250 //=========================================================================================================
256 void setFirstVisibleChannel(int ch);
257 int firstVisibleChannel() const { return m_firstVisibleChannel; }
258
259 //=========================================================================================================
265 void setVisibleChannelCount(int count);
266 int visibleChannelCount() const { return m_visibleChannelCount; }
267
268 //=========================================================================================================
273 void setFrozen(bool frozen);
274 bool isFrozen() const { return m_frozen; }
275
276 //=========================================================================================================
280 void setGridVisible(bool visible);
281 bool gridVisible() const { return m_gridVisible; }
282
283 //=========================================================================================================
288 void setSfreq(float sfreq);
289
290 //=========================================================================================================
297 void setFirstFileSample(int first);
298
299 //=========================================================================================================
307 void setLastFileSample(int last);
308 int lastFileSample() const { return m_lastFileSample; }
309
310 //=========================================================================================================
318 void setWheelScrollsChannels(bool channelsMode);
319 bool wheelScrollsChannels() const { return m_wheelScrollsChannels; }
320
321 void setScrollSpeedFactor(float factor);
322 float scrollSpeedFactor() const { return m_scrollSpeedFactor; }
323
324 //=========================================================================================================
333 void setHideBadChannels(bool hide);
334 bool hideBadChannels() const { return m_hideBadChannels; }
335
336 // ── Crosshair ─────────────────────────────────────────────────────
337
338 //=========================================================================================================
344 void setCrosshairEnabled(bool enabled);
345 bool crosshairEnabled() const { return m_crosshairEnabled; }
346
350 void setClockTimeFormat(bool useClock) { m_useClockTime = useClock; update(); }
351 bool clockTimeFormat() const { return m_useClockTime; }
352
353 // ── Scalebars ─────────────────────────────────────────────────────
354
355 //=========================================================================================================
359 void setScalebarsVisible(bool visible);
360 bool scalebarsVisible() const { return m_scalebarsVisible; }
361
362 // ── Butterfly mode ────────────────────────────────────────────────
363
364 //=========================================================================================================
368 void setButterflyMode(bool enabled);
369 bool butterflyMode() const { return m_butterflyMode; }
370
371signals:
372 void scrollSampleChanged(float sample);
373 void samplesPerPixelChanged(float spp);
374 void viewResized(int newWidth, int newHeight);
375
376 //=========================================================================================================
380 void channelOffsetChanged(int firstChannel);
381
382 //=========================================================================================================
386 void sampleClicked(int sample);
387
388 //=========================================================================================================
392 void sampleRangeSelected(int startSample, int endSample);
393
394 //=========================================================================================================
402 void annotationBoundaryMoved(int annotationIndex, bool isStartBoundary, int newSample);
403
404 //=========================================================================================================
413 void cursorDataChanged(float timeSec, float amplitude,
414 const QString &channelName, const QString &unitLabel);
415
416protected:
417 void initialize(QRhiCommandBuffer *cb) override;
418 void render(QRhiCommandBuffer *cb) override;
419 void releaseResources() override;
420 void paintEvent(QPaintEvent *event) override;
421
422 // Overlay access — called by CrosshairOverlay::paintEvent
423 friend class ::CrosshairOverlay;
424 void drawCrosshair(QPainter &p);
425 void drawScalebars(QPainter &p);
426 void drawRulerOverlay(QPainter &p);
427 void drawAnnotationSelectionOverlay(QPainter &p);
428 void emitCursorData();
429 bool rulerActive() const { return m_rulerActive; }
430 bool annotationSelecting() const { return m_annSelecting; }
431
432 void resizeEvent(QResizeEvent *event) override;
433 void wheelEvent(QWheelEvent *event) override;
434 void mousePressEvent(QMouseEvent *event) override;
435 void mouseMoveEvent(QMouseEvent *event) override;
436 void mouseReleaseEvent(QMouseEvent *event) override;
437 void mouseDoubleClickEvent(QMouseEvent *event) override;
438
439private:
440 // ── GPU resource management ────────────────────────────────────────
441 struct ChannelGpuData {
442 std::unique_ptr<QRhiBuffer> vbo;
443 int vertexCount = 0;
444 int vboFirstSample = 0; // absolute first sample in VBO
445 };
446
447 void ensurePipeline();
448 void rebuildVBOs(QRhiResourceUpdateBatch *batch);
449 void updateUBO(QRhiResourceUpdateBatch *batch);
450 bool isVboDirty() const;
451
452 // ── Overlay blit (annotations/events baked into texture, bands in shader) ──
453 void ensureOverlayPipeline();
454 void rebuildOverlayImage(int logicalWidth, int logicalHeight, qreal devicePixelRatio);
455
456 std::unique_ptr<QRhiBuffer> m_overlayVbo; // Static quad
457 std::unique_ptr<QRhiTexture> m_overlayTex;
458 std::unique_ptr<QRhiSampler> m_overlaySampler;
459 std::unique_ptr<QRhiShaderResourceBindings> m_overlaySrb;
460 std::unique_ptr<QRhiGraphicsPipeline> m_overlayPipeline;
461 std::unique_ptr<QRhiBuffer> m_overlayUbo; // OverlayParams UBO
462 bool m_overlayVboNeedsUpload = false;
463 QImage m_overlayImage;
464 bool m_overlayDirty = true;
465 QSize m_overlayTexSize;
466
467 // Overlay prefetch: the texture covers a wider sample range than the viewport.
468 // During scroll, the shader maps screen UVs into this wider texture via uniforms.
469 // Rebuild is only needed when scroll exceeds the prefetch window.
470 static constexpr float kOverlayPrefetchFactor = 1.0f; // extra viewport widths each side
471 float m_overlayFirstSample = 0.f; // first sample covered by overlay tex
472 float m_overlayTotalSamples = 0.f; // total sample span of overlay tex
473
474 // ── Legacy async tile helpers retained for staging/reuse ──────────
475 struct TileResult {
476 QImage image;
477 float sampleFirst = 0.f;
478 float samplesPerPixel = 0.f;
479 int firstChannel = 0;
480 int visibleCount = 0;
481 };
482
483 void scheduleTileRebuild();
484 static TileResult buildTile(ChannelDataModel *model,
485 float scrollSample,
486 float samplesPerPixel,
487 int firstVisibleChannel,
488 int visibleChannelCount,
489 int viewWidth, int viewHeight,
490 QColor bgColor,
491 bool gridVisible,
492 float sfreq,
493 int firstFileSample,
494 bool hideBadChannels,
495 const QVector<int> &channelIndices,
496 const QVector<EventMarker> &events,
497 const QVector<AnnotationSpan> &annotations,
498 const QVector<int> &epochMarkers,
499 bool showClipping,
500 bool zScoreMode);
501 bool isTileFresh() const;
502
503 QImage m_tileImage;
504 float m_tileSampleFirst = 0.f;
505 float m_tileSamplesPerPixel = 0.f;
506 int m_tileFirstChannel = -1;
507 int m_tileVisibleCount = 0;
508 bool m_tileDirty = true;
509
510 QFutureWatcher<TileResult> m_tileWatcher;
511 bool m_tileRebuildPending = false;
512
513 std::unique_ptr<QRhiBuffer> m_ubo;
514 std::unique_ptr<QRhiShaderResourceBindings> m_srb;
515 std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
516 std::vector<ChannelGpuData> m_gpuChannels;
517 int m_uboStride = 256;
518 bool m_pipelineDirty = true;
519 bool m_vboDirty = true;
520
521 // ── Shared browser state ───────────────────────────────────────────
522 QPointer<ChannelDataModel> m_model;
523 float m_scrollSample = 0.f;
524 float m_samplesPerPixel = 1.f;
525 float m_prefetchFactor = 1.0f;
526 QColor m_bgColor { 250, 250, 250 }; // light default
527
528 bool m_frozen = false;
529 bool m_gridVisible = true;
530 float m_sfreq = 1000.f;
531 int m_firstFileSample = 0;
532 int m_lastFileSample = -1; // -1 = no limit (file not yet known)
533 bool m_wheelScrollsChannels = true; // default: vertical wheel → channels
534 float m_scrollSpeedFactor = 1.0f; // multiplier for wheel/keyboard scroll
535 bool m_hideBadChannels = false;
536
537 // ── Vertical channel windowing ────────────────────────────────────
538 int m_firstVisibleChannel = 0;
539 int m_visibleChannelCount = 12;
540
541 // ── Drag scroll support ────────────────────────────────────────────
542 bool m_dragging = false;
543 int m_dragStartX = 0;
544 float m_dragStartScroll = 0.f;
545
546 // ── Left-button drag (panning) + inertial scroll ──────────────────
547 bool m_leftButtonDown = false;
548 int m_leftDownX = 0;
549 float m_leftDownScroll = 0.f;
550 bool m_leftDragActivated = false;
551
552 struct VelocitySample { int x; qint64 t; };
553 QVector<VelocitySample> m_velocityHistory;
554 QElapsedTimer m_dragTimer;
555 QPropertyAnimation* m_pInertialAnim = nullptr;
556
557 // ── Channel index filter ──────────────────────────────────────────
558 // When non-empty, only these model channel indices are rendered/scrolled.
559 QVector<int> m_filteredChannels;
560
561 // ── Event / stimulus markers ──────────────────────────────────────
562 QVector<EventMarker> m_events;
563 QVector<int> m_epochTriggerSamples;
564 QVector<AnnotationSpan> m_annotations;
565 bool m_annotationSelectionEnabled = false;
566 bool m_bShowEvents = true;
567 bool m_bShowAnnotations = true;
568 bool m_bShowEpochMarkers = true;
569 bool m_bShowClipping = true;
570 bool m_bZScoreMode = false;
571
572 // ── Annotation boundary drag-resize ───────────────────────────────
573 bool m_annDragging = false;
574 int m_annDragIndex = -1;
575 bool m_annDragIsStart = true;
576 int m_annHoverIndex = -1;
577 bool m_annHoverIsStart = true;
578 static constexpr int kAnnBoundaryHitPx = 5;
579
580 // ── Annotation range selection (right-drag when annotation mode ON) ─
581 bool m_annSelecting = false;
582 int m_annSelX0 = 0;
583 int m_annSelX1 = 0;
584
585 int hitTestAnnotationBoundary(int px, bool &isStart) const;
586
587 // Helper — use instead of firstCh+i for actual model channel index
588 int actualChannelAt(int logicalIdx) const; // maps logical → model channel index
589 QVector<int> effectiveChannelIndices() const;
590
591 // ── Prefetch window tracking ───────────────────────────────────────
592 int m_vboWindowFirst = 0;
593 int m_vboWindowLast = 0;
594
595 // ── Ruler / measurement overlay ───────────────────────────────────
596 // Active while right-button is held.
597 enum class RulerSnap { Free, Horizontal, Vertical };
598 bool m_rulerActive = false;
599 RulerSnap m_rulerSnap = RulerSnap::Free;
600 int m_rulerX0 = 0; // press position (screen px)
601 int m_rulerY0 = 0;
602 int m_rulerX1 = 0; // current cursor position (may be snapped)
603 int m_rulerY1 = 0;
604 int m_rulerRawX1 = 0; // raw cursor position (unsnapped)
605 int m_rulerRawY1 = 0;
606
607 // ── Crosshair cursor ──────────────────────────────────────────────
608 bool m_crosshairEnabled = false;
609 int m_crosshairX = -1; // screen px, -1 = not tracking
610 int m_crosshairY = -1;
611 bool m_useClockTime = false; // mirror of TimeRulerWidget time format
612 CrosshairOverlay* m_overlay = nullptr; // lightweight overlay for crosshair/scalebars
613
614 // ── Scalebars ─────────────────────────────────────────────────────
615 bool m_scalebarsVisible = false;
616
617 // ── Butterfly mode ────────────────────────────────────────────────
618 bool m_butterflyMode = false;
619
620 struct ButterflyTypeGroup {
621 QString typeLabel; // e.g. "MEG", "EEG"
622 QColor color; // representative type colour
623 float amplitudeMax; // per-type amplitude scale
624 QVector<int> channelIndices; // model channel indices in this group
625 };
626
627 QVector<ButterflyTypeGroup> butterflyTypeGroups() const;
628 int butterflyLaneCount() const;
629
630 void drawOverlays(); // QPainter-based overlays on top of the QRHI-rendered traces
631};
632
633} // namespace DISPLIB
634
635#endif // CHANNELRHIVIEW_H
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
Export macros and build-info hooks for the DISPLIB shared library.
#define DISPSHARED_EXPORT
Definition disp_global.h:38
2-D display widgets and visualisation helpers (charts, topography, colour maps).
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
void viewResized(int newWidth, int newHeight)
bool wheelScrollsChannels() const
ChannelRhiView(QWidget *parent=nullptr)
bool epochMarkersVisible() const
void channelOffsetChanged(int firstChannel)
void samplesPerPixelChanged(float spp)
void sampleRangeSelected(int startSample, int endSample)
void setEvents(const QVector< EventMarker > &events)
void render(QRhiCommandBuffer *cb) override
void drawScalebars(QPainter &p)
bool annotationSelecting() const
void setSamplesPerPixel(float spp)
void annotationBoundaryMoved(int annotationIndex, bool isStartBoundary, int newSample)
void setModel(ChannelDataModel *model)
void initialize(QRhiCommandBuffer *cb) override
QColor backgroundColor() const
void setEpochMarkersVisible(bool visible)
void drawCrosshair(QPainter &p)
void releaseResources() override
void setClockTimeFormat(bool useClock)
void setEpochMarkers(const QVector< int > &triggerSamples)
void drawAnnotationSelectionOverlay(QPainter &p)
void sampleClicked(int sample)
void setScrollSample(float sample)
void paintEvent(QPaintEvent *event) override
float samplesPerPixel() const
void cursorDataChanged(float timeSec, float amplitude, const QString &channelName, const QString &unitLabel)
float scrollSpeedFactor() const
void drawRulerOverlay(QPainter &p)
void scrollSampleChanged(float sample)
Stimulus / event marker — a coloured vertical line at a given sample.
QString label
Short text label (shown at the bottom strip); defaults to type number.
QColor color
Display colour of the line and label chip.
int type
Numeric event type (stimulus code).
int sample
Absolute sample index of the event onset.
Time-span annotation overlay.
QColor color
Fill / border colour for the highlighted span.
QString label
Annotation label shown near the top edge.
int endSample
Absolute last sample covered by the annotation.
int startSample
Absolute first sample covered by the annotation.