34 const QRect& paneRect,
35 const QMatrix4x4& pvm,
39 bool invertible =
false;
40 QMatrix4x4 invPVM = pvm.inverted(&invertible);
44 const float localX =
static_cast<float>(screenPos.x() - paneRect.x());
45 const float localY =
static_cast<float>(screenPos.y() - paneRect.y());
46 const float paneW =
static_cast<float>(std::max(1, paneRect.width()));
47 const float paneH =
static_cast<float>(std::max(1, paneRect.height()));
49 const float ndcX = (2.0f * localX) / paneW - 1.0f;
50 const float ndcY = 1.0f - (2.0f * localY) / paneH;
52 QVector4D vNear(ndcX, ndcY, -1.0f, 1.0f);
53 QVector4D vFar(ndcX, ndcY, 1.0f, 1.0f);
55 QVector4D pNear = invPVM * vNear;
56 QVector4D pFar = invPVM * vFar;
60 rayOrigin = pNear.toVector3D();
61 rayDir = (pFar.toVector3D() - pNear.toVector3D()).normalized();
68 const QVector3D& rayDir,
70 const QMap<QString, std::shared_ptr<BrainSurface>>& surfaces,
71 const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>>& itemSurfaceMap,
72 const QMap<
const QStandardItem*, std::shared_ptr<DipoleObject>>& itemDipoleMap)
75 float closestDist = std::numeric_limits<float>::max();
78 for (
auto it = surfaces.cbegin(); it != surfaces.cend(); ++it) {
79 const QString& key = it.key();
80 const auto& surf = it.value();
82 if (!surf->isVisible())
86 if (key.startsWith(
"srcsp_"))
89 const bool isSensor = key.startsWith(
"sens_");
90 const bool isBem = key.startsWith(
"bem_");
91 const bool isDig = key.startsWith(
"dig_");
94 if (!isSensor && !isBem && !isDig) {
101 if (surf->intersects(rayOrigin, rayDir, dist, vertexIdx)) {
102 if (dist < closestDist) {
106 result.
hitPoint = rayOrigin + dist * rayDir;
113 result.
item =
nullptr;
114 for (
auto i = itemSurfaceMap.cbegin(); i != itemSurfaceMap.cend(); ++i) {
115 if (i.value() == surf) {
116 result.
item =
const_cast<QStandardItem*
>(i.key());
122 if (result.
item && itemSurfaceMap.contains(result.
item)) {
123 result.
regionName = itemSurfaceMap[result.
item]->getAnnotationLabel(vertexIdx);
124 result.
regionId = itemSurfaceMap[result.
item]->getAnnotationLabelId(vertexIdx);
134 for (
auto it = itemDipoleMap.cbegin(); it != itemDipoleMap.cend(); ++it) {
137 if (!it.value()->isVisible())
141 int dipIdx = it.value()->intersect(rayOrigin, rayDir, dist);
142 if (dipIdx != -1 && dist < closestDist) {
146 result.
hitPoint = rayOrigin + dist * rayDir;
147 result.
item =
const_cast<QStandardItem*
>(it.key());
163 [[maybe_unused]]
const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>>& itemSurfaceMap,
164 [[maybe_unused]]
const QMap<QString, std::shared_ptr<BrainSurface>>& surfaces)
174 if (key.startsWith(
"lh"))
176 else if (key.startsWith(
"rh"))
179 return hemi.isEmpty()
180 ? QString(
"Region: %1").arg(result.
regionName)
181 : QString(
"Region: %1 (%2)").arg(result.
regionName, hemi);
186 QString name = result.
item ? result.
item->text() : QStringLiteral(
"Dipole");
187 return QString(
"%1 (Dipole %2)").arg(name).arg(result.
dipoleIndex);
191 if (key.startsWith(
"sens_surface_meg")) {
192 return QStringLiteral(
"MEG Helmet");
194 if (key.startsWith(
"sens_meg_")) {
195 return QString(
"MEG: %1").arg(result.
item ? result.
item->text() : key);
197 if (key.startsWith(
"sens_eeg_")) {
198 return QString(
"EEG: %1").arg(result.
item ? result.
item->text() : key);
200 if (key.startsWith(
"dig_")) {
207 constexpr int vertsPerSphere = 42;
209 const QStringList& names = digItem->pointNames();
210 if (ptIdx >= 0 && ptIdx < names.size())
211 pointName = names[ptIdx];
214 QString category = key.mid(4);
215 if (!category.isEmpty())
216 category[0] = category[0].toUpper();
217 return pointName.isEmpty()
218 ? QString(
"Digitizer (%1)").arg(category)
219 : QString(
"Digitizer: %1 (%2)").arg(pointName, category);
221 if (key.startsWith(
"bem_")) {
222 QString compartment = key.mid(4);
223 if (!compartment.isEmpty())
224 compartment[0] = compartment[0].toUpper();
225 compartment.replace(
"_",
" ");
226 return QString(
"BEM: %1").arg(compartment);
230 if (key.startsWith(
"lh_"))
231 return QStringLiteral(
"Left Hemisphere");
232 if (key.startsWith(
"rh_"))
233 return QStringLiteral(
"Right Hemisphere");
253 return hemi.isEmpty()
255 : QString(
"Region: %1 (%2)").arg(
regionName, hemi);
Tree item holding a single category of digitizer points rendered as a batched-sphere mesh.
Base QStandardItem with check-state, visibility, transform, colour and alpha roles shared by every di...
Mouse-ray vs scene-object intersection for picking dipoles, electrodes and surfaces.
Instanced-arrow renderable for fitted equivalent current dipoles, driven by QRhi instancing.
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
3-D brain visualisation using the Qt RHI rendering backend.
Viewport subdivision holding its own camera, projection, and scissor rectangle.
bool matchesSurfaceType(const QString &key) const
bool shouldRenderSurface(const QString &key) const
ViewVisibilityProfile visibility
Result of a ray–mesh intersection test containing the hit point, triangle index, and distance.
QString surfaceKey
FsSurface map key of the hit surface.
float distance
Distance along ray to hit point.
QString displayLabel() const
int regionId
FsAnnotation label ID.
bool isDipole
True if a dipole was hit.
QStandardItem * item
Tree item that was hit (nullable).
int dipoleIndex
Index within the dipole set.
bool hit
True if something was hit.
QVector3D hitPoint
World-space intersection point.
int vertexIndex
Vertex or element index at hit.
QString regionName
FsAnnotation region label (if available).
static bool unproject(const QPoint &screenPos, const QRect &paneRect, const QMatrix4x4 &pvm, QVector3D &rayOrigin, QVector3D &rayDir)
static RayHit pick(const QVector3D &rayOrigin, const QVector3D &rayDir, const SubView &subView, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces, const QMap< const QStandardItem *, std::shared_ptr< BrainSurface > > &itemSurfaceMap, const QMap< const QStandardItem *, std::shared_ptr< DipoleObject > > &itemDipoleMap)
static QString buildLabel(const RayHit &result, const QMap< const QStandardItem *, std::shared_ptr< BrainSurface > > &itemSurfaceMap, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces)
Base tree item providing check-state, visibility, and data-role storage for all 3-D scene items.
int type() const override
static constexpr int itemTypeId(ItemType type)
Digitizer point group tree item.