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 <QImage>
38#include <QMouseEvent>
39#include <QPaintEvent>
40#include <QPointer>
41#include <QPropertyAnimation>
42#include <QRhiWidget>
43#include <QResizeEvent>
44#include <QWheelEvent>
45
46#include <memory>
47#include <vector>
48
49//=============================================================================================================
50// DEFINE NAMESPACE DISPLIB
51//=============================================================================================================
52
53class QRhi;
54class QRhiBuffer;
55class QRhiCommandBuffer;
56class QRhiGraphicsPipeline;
57class QRhiResourceUpdateBatch;
58class QRhiSampler;
59class QRhiShaderResourceBindings;
60class QRhiTexture;
61
62class CrosshairOverlay; // defined in .cpp
63
64namespace DISPLIB
65{
66
67//=============================================================================================================
76class DISPSHARED_EXPORT ChannelRhiView : public QRhiWidget
77{
78 Q_OBJECT
79
82
83public:
84 //=========================================================================================================
89 {
90 int sample = 0;
91 int type = 1;
92 QColor color;
93 QString label;
94 };
95
96 //=========================================================================================================
101 {
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);
133 {
134 return m_bShowEpochMarkers;
135 }
136
137 void setClippingVisible(bool visible);
138 bool clippingVisible() const
139 {
140 return m_bShowClipping;
141 }
142
143 void setZScoreMode(bool enabled);
144 bool zScoreMode() const
145 {
146 return m_bZScoreMode;
147 }
148
149 //=========================================================================================================
155 void setAnnotations(const QVector<AnnotationSpan>& annotations);
156
157 //=========================================================================================================
164 void setAnnotationSelectionEnabled(bool enabled);
165
166 void setEventsVisible(bool visible);
167 bool eventsVisible() const;
168
169 void setAnnotationsVisible(bool visible);
170 bool annotationsVisible() const;
171
172 // ── Scroll / zoom ─────────────────────────────────────────────────
173
174 float scrollSample() const
175 {
176 return m_scrollSample;
177 }
178 float samplesPerPixel() const
179 {
180 return m_samplesPerPixel;
181 }
182
183 //=========================================================================================================
190 void setScrollSample(float sample);
191
192 //=========================================================================================================
199 void setSamplesPerPixel(float spp);
200
201 //=========================================================================================================
208 void scrollTo(float targetSample, int durationMs = 200);
209
210 //=========================================================================================================
218 void scrollBy(float deltaSamples, int durationMs = 200);
219
220 //=========================================================================================================
227 void zoomTo(float targetSpp, int durationMs = 200);
228
229 //=========================================================================================================
235 void setBackgroundColor(const QColor& color);
236 QColor backgroundColor() const
237 {
238 return m_bgColor;
239 }
240
241 //=========================================================================================================
248 void setPrefetchFactor(float factor);
249
250 //=========================================================================================================
257 int visibleFirstSample() const;
258
259 //=========================================================================================================
265 int visibleSampleCount() const;
266
267 //=========================================================================================================
276 void setChannelIndices(const QVector<int>& indices);
277
278 //=========================================================================================================
284 int totalLogicalChannels() const;
285
286 //=========================================================================================================
292 void setFirstVisibleChannel(int ch);
294 {
295 return m_firstVisibleChannel;
296 }
297
298 //=========================================================================================================
304 void setVisibleChannelCount(int count);
306 {
307 return m_visibleChannelCount;
308 }
309
310 //=========================================================================================================
317 void setFrozen(bool frozen);
318 bool isFrozen() const
319 {
320 return m_frozen;
321 }
322
323 //=========================================================================================================
329 void setGridVisible(bool visible);
330 bool gridVisible() const
331 {
332 return m_gridVisible;
333 }
334
335 //=========================================================================================================
342 void setSfreq(float sfreq);
343
344 //=========================================================================================================
351 void setFirstFileSample(int first);
352
353 //=========================================================================================================
361 void setLastFileSample(int last);
362 int lastFileSample() const
363 {
364 return m_lastFileSample;
365 }
366
367 //=========================================================================================================
375 void setWheelScrollsChannels(bool channelsMode);
377 {
378 return m_wheelScrollsChannels;
379 }
380
381 void setScrollSpeedFactor(float factor);
382 float scrollSpeedFactor() const
383 {
384 return m_scrollSpeedFactor;
385 }
386
387 //=========================================================================================================
396 void setHideBadChannels(bool hide);
397 bool hideBadChannels() const
398 {
399 return m_hideBadChannels;
400 }
401
402 // ── Crosshair ─────────────────────────────────────────────────────
403
404 //=========================================================================================================
412 void setCrosshairEnabled(bool enabled);
413 bool crosshairEnabled() const
414 {
415 return m_crosshairEnabled;
416 }
417
423 void setClockTimeFormat(bool useClock)
424 {
425 m_useClockTime = useClock;
426 update();
427 }
428 bool clockTimeFormat() const
429 {
430 return m_useClockTime;
431 }
432
433 // ── Export ────────────────────────────────────────────────────────
434
435 //=========================================================================================================
448 QImage renderToImage(const QSize& size = QSize()) const;
449
450 // ── Scalebars ─────────────────────────────────────────────────────
451
452 //=========================================================================================================
458 void setScalebarsVisible(bool visible);
459 bool scalebarsVisible() const
460 {
461 return m_scalebarsVisible;
462 }
463
464 // ── Butterfly mode ────────────────────────────────────────────────
465
466 //=========================================================================================================
472 void setButterflyMode(bool enabled);
473 bool butterflyMode() const
474 {
475 return m_butterflyMode;
476 }
477
478signals:
479 void scrollSampleChanged(float sample);
480 void samplesPerPixelChanged(float spp);
481 void viewResized(int newWidth, int newHeight);
482
483 //=========================================================================================================
487 void channelOffsetChanged(int firstChannel);
488
489 //=========================================================================================================
493 void sampleClicked(int sample);
494
495 //=========================================================================================================
499 void sampleRangeSelected(int startSample, int endSample);
500
501 //=========================================================================================================
509 void annotationBoundaryMoved(int annotationIndex, bool isStartBoundary, int newSample);
510
511 //=========================================================================================================
520 void cursorDataChanged(float timeSec, float amplitude,
521 const QString& channelName, const QString& unitLabel);
522
523protected:
524 void initialize(QRhiCommandBuffer* cb) override;
525 void render(QRhiCommandBuffer* cb) override;
526 void releaseResources() override;
527 void paintEvent(QPaintEvent* event) override;
528
529 //=========================================================================================================
537 void renderFrame(QRhi* rhi, QRhiRenderTarget* target, QRhiCommandBuffer* cb);
538
539 // Overlay access — called by CrosshairOverlay::paintEvent
540 friend class ::CrosshairOverlay;
541 void drawCrosshair(QPainter& p);
542 void drawScalebars(QPainter& p);
543 void drawRulerOverlay(QPainter& p);
544 void drawAnnotationSelectionOverlay(QPainter& p);
545 void emitCursorData();
546 bool rulerActive() const
547 {
548 return m_rulerActive;
549 }
551 {
552 return m_annSelecting;
553 }
554
555 void resizeEvent(QResizeEvent* event) override;
556 void wheelEvent(QWheelEvent* event) override;
557 void mousePressEvent(QMouseEvent* event) override;
558 void mouseMoveEvent(QMouseEvent* event) override;
559 void mouseReleaseEvent(QMouseEvent* event) override;
560 void mouseDoubleClickEvent(QMouseEvent* event) override;
561
562private:
563 float clampScrollSample(float sample) const;
564 void stopScrollAnimations();
565
566 // ── GPU resource management ────────────────────────────────────────
567 struct ChannelGpuData
568 {
569 std::unique_ptr<QRhiBuffer> vbo;
570 int vertexCount = 0;
571 int vboFirstSample = 0; // absolute first sample in VBO
572 };
573
574 void ensurePipeline(QRhi* rhi, QRhiRenderTarget* target);
575 void rebuildVBOs(QRhi* rhi, QRhiResourceUpdateBatch* batch);
576 void updateUBO(QRhiResourceUpdateBatch* batch);
577 bool isVboDirty() const;
578
579 // ── Overlay blit (annotations/events baked into texture, bands in shader) ──
580 void ensureOverlayPipeline(QRhi* rhi, QRhiRenderTarget* target);
581 void rebuildOverlayImage(int logicalWidth, int logicalHeight, qreal devicePixelRatio);
582
583 std::unique_ptr<QRhiBuffer> m_overlayVbo; // Static quad
584 std::unique_ptr<QRhiTexture> m_overlayTex;
585 std::unique_ptr<QRhiSampler> m_overlaySampler;
586 std::unique_ptr<QRhiShaderResourceBindings> m_overlaySrb;
587 std::unique_ptr<QRhiGraphicsPipeline> m_overlayPipeline;
588 std::unique_ptr<QRhiBuffer> m_overlayUbo; // OverlayParams UBO
589 bool m_overlayVboNeedsUpload = false;
590 QImage m_overlayImage;
591 bool m_overlayDirty = true;
592 QSize m_overlayTexSize;
593
594 // Overlay prefetch: the texture covers a wider sample range than the viewport.
595 // During scroll, the shader maps screen UVs into this wider texture via uniforms.
596 // Rebuild is only needed when scroll exceeds the prefetch window.
597 static constexpr float kOverlayPrefetchFactor = 1.0f; // extra viewport widths each side
598 float m_overlayFirstSample = 0.f; // first sample covered by overlay tex
599 float m_overlayTotalSamples = 0.f; // total sample span of overlay tex
600
601
602 std::unique_ptr<QRhiBuffer> m_ubo;
603 std::unique_ptr<QRhiShaderResourceBindings> m_srb;
604 std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
605 std::vector<ChannelGpuData> m_gpuChannels;
606 int m_uboStride = 256;
607 bool m_pipelineDirty = true;
608 bool m_vboDirty = true;
609
610 // ── Shared browser state ───────────────────────────────────────────
611 QPointer<ChannelDataModel> m_model;
612 float m_scrollSample = 0.f;
613 float m_samplesPerPixel = 1.f;
614 float m_prefetchFactor = 1.0f;
615 QColor m_bgColor{250, 250, 250}; // light default
616
617 bool m_frozen = false;
618 bool m_gridVisible = true;
619 float m_sfreq = 1000.f;
620 int m_firstFileSample = 0;
621 int m_lastFileSample = -1; // -1 = no limit (file not yet known)
622 bool m_wheelScrollsChannels = true; // default: vertical wheel → channels
623 float m_scrollSpeedFactor = 1.0f; // multiplier for wheel/keyboard scroll
624 bool m_hideBadChannels = false;
625
626 // ── Vertical channel windowing ────────────────────────────────────
627 int m_firstVisibleChannel = 0;
628 int m_visibleChannelCount = 12;
629
630 // ── Drag scroll support ────────────────────────────────────────────
631 bool m_dragging = false;
632 int m_dragStartX = 0;
633 float m_dragStartScroll = 0.f;
634
635 // ── Left-button drag (panning) + inertial scroll ──────────────────
636 bool m_leftButtonDown = false;
637 int m_leftDownX = 0;
638 float m_leftDownScroll = 0.f;
639 bool m_leftDragActivated = false;
640
641 struct VelocitySample
642 {
643 int x;
644 qint64 t;
645 };
646 QVector<VelocitySample> m_velocityHistory;
647 QElapsedTimer m_dragTimer;
648 QPropertyAnimation* m_pInertialAnim = nullptr;
649 QPointer<QPropertyAnimation> m_pScrollAnim;
650 float m_scrollTarget = 0.f;
651
652 // ── Channel index filter ──────────────────────────────────────────
653 // When non-empty, only these model channel indices are rendered/scrolled.
654 QVector<int> m_filteredChannels;
655
656 // ── Event / stimulus markers ──────────────────────────────────────
657 QVector<EventMarker> m_events;
658 QVector<int> m_epochTriggerSamples;
659 QVector<AnnotationSpan> m_annotations;
660 bool m_annotationSelectionEnabled = false;
661 bool m_bShowEvents = true;
662 bool m_bShowAnnotations = true;
663 bool m_bShowEpochMarkers = true;
664 bool m_bShowClipping = true;
665 bool m_bZScoreMode = false;
666
667 // ── Annotation boundary drag-resize ───────────────────────────────
668 bool m_annDragging = false;
669 int m_annDragIndex = -1;
670 bool m_annDragIsStart = true;
671 int m_annHoverIndex = -1;
672 bool m_annHoverIsStart = true;
673 static constexpr int kAnnBoundaryHitPx = 5;
674
675 // ── Annotation range selection (right-drag when annotation mode ON) ─
676 bool m_annSelecting = false;
677 int m_annSelX0 = 0;
678 int m_annSelX1 = 0;
679
680 int hitTestAnnotationBoundary(int px, bool& isStart) const;
681
682 // Helper — use instead of firstCh+i for actual model channel index
683 int actualChannelAt(int logicalIdx) const; // maps logical → model channel index
684 QVector<int> effectiveChannelIndices() const;
685
686 // ── Prefetch window tracking ───────────────────────────────────────
687 int m_vboWindowFirst = 0;
688 int m_vboWindowLast = 0;
689
690 // ── Ruler / measurement overlay ───────────────────────────────────
691 // Active while right-button is held.
692 enum class RulerSnap
693 {
694 Free,
697 };
698 bool m_rulerActive = false;
699 RulerSnap m_rulerSnap = RulerSnap::Free;
700 int m_rulerX0 = 0; // press position (screen px)
701 int m_rulerY0 = 0;
702 int m_rulerX1 = 0; // current cursor position (may be snapped)
703 int m_rulerY1 = 0;
704 int m_rulerRawX1 = 0; // raw cursor position (unsnapped)
705 int m_rulerRawY1 = 0;
706
707 // ── Crosshair cursor ──────────────────────────────────────────────
708 bool m_crosshairEnabled = false;
709 int m_crosshairX = -1; // screen px, -1 = not tracking
710 int m_crosshairY = -1;
711 bool m_useClockTime = false; // mirror of TimeRulerWidget time format
712 CrosshairOverlay* m_overlay = nullptr; // lightweight overlay for crosshair/scalebars
713
714 // ── Scalebars ─────────────────────────────────────────────────────
715 bool m_scalebarsVisible = false;
716
717 // ── Butterfly mode ────────────────────────────────────────────────
718 bool m_butterflyMode = false;
719
720 struct ButterflyTypeGroup
721 {
722 QString typeLabel; // e.g. "MEG", "EEG"
723 QColor color; // representative type colour
724 float amplitudeMax; // per-type amplitude scale
725 QVector<int> channelIndices; // model channel indices in this group
726 };
727
728 QVector<ButterflyTypeGroup> butterflyTypeGroups() const;
729 int butterflyLaneCount() const;
730
731 void drawOverlays(); // QPainter-based overlays on top of the QRHI-rendered traces
732};
733
734} // namespace DISPLIB
735
736#endif // CHANNELRHIVIEW_H
Export macros and build-info hooks for the DISPLIB shared library.
#define DISPSHARED_EXPORT
Definition disp_global.h:38
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
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 renderFrame(QRhi *rhi, QRhiRenderTarget *target, QRhiCommandBuffer *cb)
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.