v2.0.0
Loading...
Searching...
No Matches
channelrhiview.h
Go to the documentation of this file.
1//=============================================================================================================
34
35#ifndef CHANNELRHIVIEW_H
36#define CHANNELRHIVIEW_H
37
38//=============================================================================================================
39// INCLUDES
40//=============================================================================================================
41
42#include "../../disp_global.h"
43#include "channeldatamodel.h"
44
45//=============================================================================================================
46// QT INCLUDES
47//=============================================================================================================
48
49#include <QColor>
50#include <QElapsedTimer>
51#include <QFutureWatcher>
52#include <QImage>
53#include <QMouseEvent>
54#include <QPaintEvent>
55#include <QPointer>
56#include <QPropertyAnimation>
57#include <QRhiWidget>
58#include <QResizeEvent>
59#include <QWheelEvent>
60#include <QtConcurrent>
61
62#include <memory>
63#include <vector>
64
65//=============================================================================================================
66// DEFINE NAMESPACE DISPLIB
67//=============================================================================================================
68
69class QRhi;
70class QRhiBuffer;
71class QRhiCommandBuffer;
72class QRhiGraphicsPipeline;
73class QRhiResourceUpdateBatch;
74class QRhiSampler;
75class QRhiShaderResourceBindings;
76class QRhiTexture;
77
78class CrosshairOverlay; // defined in .cpp
79
80namespace DISPLIB
81{
82
83//=============================================================================================================
92class DISPSHARED_EXPORT ChannelRhiView : public QRhiWidget
93{
94 Q_OBJECT
95
98
99public:
100 //=========================================================================================================
104 struct EventMarker {
105 int sample = 0;
106 int type = 1;
107 QColor color;
108 QString label;
109 };
110
111 //=========================================================================================================
116 int startSample = 0;
117 int endSample = 0;
118 QColor color;
119 QString label;
120 };
121
122 explicit ChannelRhiView(QWidget *parent = nullptr);
123 ~ChannelRhiView() override;
124
125 //=========================================================================================================
131 void setModel(ChannelDataModel *model);
132
133 //=========================================================================================================
142 void setEvents(const QVector<EventMarker> &events);
143
144 void setEpochMarkers(const QVector<int> &triggerSamples);
145 void setEpochMarkersVisible(bool visible);
146 bool epochMarkersVisible() const { return m_bShowEpochMarkers; }
147
148 void setClippingVisible(bool visible);
149 bool clippingVisible() const { return m_bShowClipping; }
150
151 void setZScoreMode(bool enabled);
152 bool zScoreMode() const { return m_bZScoreMode; }
153
154 //=========================================================================================================
160 void setAnnotations(const QVector<AnnotationSpan> &annotations);
161
162 //=========================================================================================================
167 void setAnnotationSelectionEnabled(bool enabled);
168
169 void setEventsVisible(bool visible);
170 bool eventsVisible() const;
171
172 void setAnnotationsVisible(bool visible);
173 bool annotationsVisible() const;
174
175 // ── Scroll / zoom ─────────────────────────────────────────────────
176
177 float scrollSample() const { return m_scrollSample; }
178 float samplesPerPixel() const { return m_samplesPerPixel; }
179
180 //=========================================================================================================
187 void setScrollSample(float sample);
188
189 //=========================================================================================================
196 void setSamplesPerPixel(float spp);
197
198 //=========================================================================================================
205 void scrollTo(float targetSample, int durationMs = 200);
206
207 //=========================================================================================================
214 void zoomTo(float targetSpp, int durationMs = 200);
215
216 //=========================================================================================================
222 void setBackgroundColor(const QColor &color);
223 QColor backgroundColor() const { return m_bgColor; }
224
225 //=========================================================================================================
232 void setPrefetchFactor(float factor);
233
234 //=========================================================================================================
239 int visibleFirstSample() const;
240
241 //=========================================================================================================
245 int visibleSampleCount() const;
246
247 //=========================================================================================================
256 void setChannelIndices(const QVector<int> &indices);
257
258 //=========================================================================================================
262 int totalLogicalChannels() const;
263
264 //=========================================================================================================
270 void setFirstVisibleChannel(int ch);
271 int firstVisibleChannel() const { return m_firstVisibleChannel; }
272
273 //=========================================================================================================
279 void setVisibleChannelCount(int count);
280 int visibleChannelCount() const { return m_visibleChannelCount; }
281
282 //=========================================================================================================
287 void setFrozen(bool frozen);
288 bool isFrozen() const { return m_frozen; }
289
290 //=========================================================================================================
294 void setGridVisible(bool visible);
295 bool gridVisible() const { return m_gridVisible; }
296
297 //=========================================================================================================
302 void setSfreq(float sfreq);
303
304 //=========================================================================================================
311 void setFirstFileSample(int first);
312
313 //=========================================================================================================
321 void setLastFileSample(int last);
322 int lastFileSample() const { return m_lastFileSample; }
323
324 //=========================================================================================================
332 void setWheelScrollsChannels(bool channelsMode);
333 bool wheelScrollsChannels() const { return m_wheelScrollsChannels; }
334
335 void setScrollSpeedFactor(float factor);
336 float scrollSpeedFactor() const { return m_scrollSpeedFactor; }
337
338 //=========================================================================================================
347 void setHideBadChannels(bool hide);
348 bool hideBadChannels() const { return m_hideBadChannels; }
349
350 // ── Crosshair ─────────────────────────────────────────────────────
351
352 //=========================================================================================================
358 void setCrosshairEnabled(bool enabled);
359 bool crosshairEnabled() const { return m_crosshairEnabled; }
360
364 void setClockTimeFormat(bool useClock) { m_useClockTime = useClock; update(); }
365 bool clockTimeFormat() const { return m_useClockTime; }
366
367 // ── Scalebars ─────────────────────────────────────────────────────
368
369 //=========================================================================================================
373 void setScalebarsVisible(bool visible);
374 bool scalebarsVisible() const { return m_scalebarsVisible; }
375
376 // ── Butterfly mode ────────────────────────────────────────────────
377
378 //=========================================================================================================
382 void setButterflyMode(bool enabled);
383 bool butterflyMode() const { return m_butterflyMode; }
384
385signals:
386 void scrollSampleChanged(float sample);
387 void samplesPerPixelChanged(float spp);
388 void viewResized(int newWidth, int newHeight);
389
390 //=========================================================================================================
394 void channelOffsetChanged(int firstChannel);
395
396 //=========================================================================================================
400 void sampleClicked(int sample);
401
402 //=========================================================================================================
406 void sampleRangeSelected(int startSample, int endSample);
407
408 //=========================================================================================================
416 void annotationBoundaryMoved(int annotationIndex, bool isStartBoundary, int newSample);
417
418 //=========================================================================================================
427 void cursorDataChanged(float timeSec, float amplitude,
428 const QString &channelName, const QString &unitLabel);
429
430protected:
431 void initialize(QRhiCommandBuffer *cb) override;
432 void render(QRhiCommandBuffer *cb) override;
433 void releaseResources() override;
434 void paintEvent(QPaintEvent *event) override;
435
436 // Overlay access — called by CrosshairOverlay::paintEvent
437 friend class ::CrosshairOverlay;
438 void drawCrosshair(QPainter &p);
439 void drawScalebars(QPainter &p);
440 void drawRulerOverlay(QPainter &p);
441 void drawAnnotationSelectionOverlay(QPainter &p);
442 void emitCursorData();
443 bool rulerActive() const { return m_rulerActive; }
444 bool annotationSelecting() const { return m_annSelecting; }
445
446 void resizeEvent(QResizeEvent *event) override;
447 void wheelEvent(QWheelEvent *event) override;
448 void mousePressEvent(QMouseEvent *event) override;
449 void mouseMoveEvent(QMouseEvent *event) override;
450 void mouseReleaseEvent(QMouseEvent *event) override;
451 void mouseDoubleClickEvent(QMouseEvent *event) override;
452
453private:
454 // ── GPU resource management ────────────────────────────────────────
455 struct ChannelGpuData {
456 std::unique_ptr<QRhiBuffer> vbo;
457 int vertexCount = 0;
458 int vboFirstSample = 0; // absolute first sample in VBO
459 };
460
461 void ensurePipeline();
462 void rebuildVBOs(QRhiResourceUpdateBatch *batch);
463 void updateUBO(QRhiResourceUpdateBatch *batch);
464 bool isVboDirty() const;
465
466 // ── Overlay blit (annotations/events baked into texture, bands in shader) ──
467 void ensureOverlayPipeline();
468 void rebuildOverlayImage(int logicalWidth, int logicalHeight, qreal devicePixelRatio);
469
470 std::unique_ptr<QRhiBuffer> m_overlayVbo; // Static quad
471 std::unique_ptr<QRhiTexture> m_overlayTex;
472 std::unique_ptr<QRhiSampler> m_overlaySampler;
473 std::unique_ptr<QRhiShaderResourceBindings> m_overlaySrb;
474 std::unique_ptr<QRhiGraphicsPipeline> m_overlayPipeline;
475 std::unique_ptr<QRhiBuffer> m_overlayUbo; // OverlayParams UBO
476 bool m_overlayVboNeedsUpload = false;
477 QImage m_overlayImage;
478 bool m_overlayDirty = true;
479 QSize m_overlayTexSize;
480
481 // Overlay prefetch: the texture covers a wider sample range than the viewport.
482 // During scroll, the shader maps screen UVs into this wider texture via uniforms.
483 // Rebuild is only needed when scroll exceeds the prefetch window.
484 static constexpr float kOverlayPrefetchFactor = 1.0f; // extra viewport widths each side
485 float m_overlayFirstSample = 0.f; // first sample covered by overlay tex
486 float m_overlayTotalSamples = 0.f; // total sample span of overlay tex
487
488 // ── Legacy async tile helpers retained for staging/reuse ──────────
489 struct TileResult {
490 QImage image;
491 float sampleFirst = 0.f;
492 float samplesPerPixel = 0.f;
493 int firstChannel = 0;
494 int visibleCount = 0;
495 };
496
497 void scheduleTileRebuild();
498 static TileResult buildTile(ChannelDataModel *model,
499 float scrollSample,
500 float samplesPerPixel,
501 int firstVisibleChannel,
502 int visibleChannelCount,
503 int viewWidth, int viewHeight,
504 QColor bgColor,
505 bool gridVisible,
506 float sfreq,
507 int firstFileSample,
508 bool hideBadChannels,
509 const QVector<int> &channelIndices,
510 const QVector<EventMarker> &events,
511 const QVector<AnnotationSpan> &annotations,
512 const QVector<int> &epochMarkers,
513 bool showClipping,
514 bool zScoreMode);
515 bool isTileFresh() const;
516
517 QImage m_tileImage;
518 float m_tileSampleFirst = 0.f;
519 float m_tileSamplesPerPixel = 0.f;
520 int m_tileFirstChannel = -1;
521 int m_tileVisibleCount = 0;
522 bool m_tileDirty = true;
523
524 QFutureWatcher<TileResult> m_tileWatcher;
525 bool m_tileRebuildPending = false;
526
527 std::unique_ptr<QRhiBuffer> m_ubo;
528 std::unique_ptr<QRhiShaderResourceBindings> m_srb;
529 std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
530 std::vector<ChannelGpuData> m_gpuChannels;
531 int m_uboStride = 256;
532 bool m_pipelineDirty = true;
533 bool m_vboDirty = true;
534
535 // ── Shared browser state ───────────────────────────────────────────
536 QPointer<ChannelDataModel> m_model;
537 float m_scrollSample = 0.f;
538 float m_samplesPerPixel = 1.f;
539 float m_prefetchFactor = 1.0f;
540 QColor m_bgColor { 250, 250, 250 }; // light default
541
542 bool m_frozen = false;
543 bool m_gridVisible = true;
544 float m_sfreq = 1000.f;
545 int m_firstFileSample = 0;
546 int m_lastFileSample = -1; // -1 = no limit (file not yet known)
547 bool m_wheelScrollsChannels = true; // default: vertical wheel → channels
548 float m_scrollSpeedFactor = 1.0f; // multiplier for wheel/keyboard scroll
549 bool m_hideBadChannels = false;
550
551 // ── Vertical channel windowing ────────────────────────────────────
552 int m_firstVisibleChannel = 0;
553 int m_visibleChannelCount = 12;
554
555 // ── Drag scroll support ────────────────────────────────────────────
556 bool m_dragging = false;
557 int m_dragStartX = 0;
558 float m_dragStartScroll = 0.f;
559
560 // ── Left-button drag (panning) + inertial scroll ──────────────────
561 bool m_leftButtonDown = false;
562 int m_leftDownX = 0;
563 float m_leftDownScroll = 0.f;
564 bool m_leftDragActivated = false;
565
566 struct VelocitySample { int x; qint64 t; };
567 QVector<VelocitySample> m_velocityHistory;
568 QElapsedTimer m_dragTimer;
569 QPropertyAnimation* m_pInertialAnim = nullptr;
570
571 // ── Channel index filter ──────────────────────────────────────────
572 // When non-empty, only these model channel indices are rendered/scrolled.
573 QVector<int> m_filteredChannels;
574
575 // ── Event / stimulus markers ──────────────────────────────────────
576 QVector<EventMarker> m_events;
577 QVector<int> m_epochTriggerSamples;
578 QVector<AnnotationSpan> m_annotations;
579 bool m_annotationSelectionEnabled = false;
580 bool m_bShowEvents = true;
581 bool m_bShowAnnotations = true;
582 bool m_bShowEpochMarkers = true;
583 bool m_bShowClipping = true;
584 bool m_bZScoreMode = false;
585
586 // ── Annotation boundary drag-resize ───────────────────────────────
587 bool m_annDragging = false;
588 int m_annDragIndex = -1;
589 bool m_annDragIsStart = true;
590 int m_annHoverIndex = -1;
591 bool m_annHoverIsStart = true;
592 static constexpr int kAnnBoundaryHitPx = 5;
593
594 // ── Annotation range selection (right-drag when annotation mode ON) ─
595 bool m_annSelecting = false;
596 int m_annSelX0 = 0;
597 int m_annSelX1 = 0;
598
599 int hitTestAnnotationBoundary(int px, bool &isStart) const;
600
601 // Helper — use instead of firstCh+i for actual model channel index
602 int actualChannelAt(int logicalIdx) const; // maps logical → model channel index
603 QVector<int> effectiveChannelIndices() const;
604
605 // ── Prefetch window tracking ───────────────────────────────────────
606 int m_vboWindowFirst = 0;
607 int m_vboWindowLast = 0;
608
609 // ── Ruler / measurement overlay ───────────────────────────────────
610 // Active while right-button is held.
611 enum class RulerSnap { Free, Horizontal, Vertical };
612 bool m_rulerActive = false;
613 RulerSnap m_rulerSnap = RulerSnap::Free;
614 int m_rulerX0 = 0; // press position (screen px)
615 int m_rulerY0 = 0;
616 int m_rulerX1 = 0; // current cursor position (may be snapped)
617 int m_rulerY1 = 0;
618 int m_rulerRawX1 = 0; // raw cursor position (unsnapped)
619 int m_rulerRawY1 = 0;
620
621 // ── Crosshair cursor ──────────────────────────────────────────────
622 bool m_crosshairEnabled = false;
623 int m_crosshairX = -1; // screen px, -1 = not tracking
624 int m_crosshairY = -1;
625 bool m_useClockTime = false; // mirror of TimeRulerWidget time format
626 CrosshairOverlay* m_overlay = nullptr; // lightweight overlay for crosshair/scalebars
627
628 // ── Scalebars ─────────────────────────────────────────────────────
629 bool m_scalebarsVisible = false;
630
631 // ── Butterfly mode ────────────────────────────────────────────────
632 bool m_butterflyMode = false;
633
634 struct ButterflyTypeGroup {
635 QString typeLabel; // e.g. "MEG", "EEG"
636 QColor color; // representative type colour
637 float amplitudeMax; // per-type amplitude scale
638 QVector<int> channelIndices; // model channel indices in this group
639 };
640
641 QVector<ButterflyTypeGroup> butterflyTypeGroups() const;
642 int butterflyLaneCount() const;
643
644 void drawOverlays(); // QPainter-based overlays on top of the QRHI-rendered traces
645};
646
647} // namespace DISPLIB
648
649#endif // CHANNELRHIVIEW_H
disp library export/import macros.
#define DISPSHARED_EXPORT
Definition disp_global.h:51
Declaration of the ChannelDataModel class.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
ChannelDataModel – lightweight data container for ChannelDataView / ChannelRhiView.
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.