31 const QRect &paneRect,
32 const QMatrix4x4 &pvm,
36 bool invertible =
false;
37 QMatrix4x4 invPVM = pvm.inverted(&invertible);
38 if (!invertible)
return false;
40 const float localX =
static_cast<float>(screenPos.x() - paneRect.x());
41 const float localY =
static_cast<float>(screenPos.y() - paneRect.y());
42 const float paneW =
static_cast<float>(std::max(1, paneRect.width()));
43 const float paneH =
static_cast<float>(std::max(1, paneRect.height()));
45 const float ndcX = (2.0f * localX) / paneW - 1.0f;
46 const float ndcY = 1.0f - (2.0f * localY) / paneH;
48 QVector4D vNear(ndcX, ndcY, -1.0f, 1.0f);
49 QVector4D vFar (ndcX, ndcY, 1.0f, 1.0f);
51 QVector4D pNear = invPVM * vNear;
52 QVector4D pFar = invPVM * vFar;
56 rayOrigin = pNear.toVector3D();
57 rayDir = (pFar.toVector3D() - pNear.toVector3D()).normalized();
64 const QVector3D &rayDir,
66 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
67 const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>> &itemSurfaceMap,
68 const QMap<
const QStandardItem*, std::shared_ptr<DipoleObject>> &itemDipoleMap)
71 float closestDist = std::numeric_limits<float>::max();
74 for (
auto it = surfaces.cbegin(); it != surfaces.cend(); ++it) {
75 const QString &key = it.key();
76 const auto &surf = it.value();
78 if (!surf->isVisible())
continue;
80 if (key.startsWith(
"srcsp_"))
continue;
82 const bool isSensor = key.startsWith(
"sens_");
83 const bool isBem = key.startsWith(
"bem_");
84 const bool isDig = key.startsWith(
"dig_");
87 if (!isSensor && !isBem && !isDig) {
93 if (surf->intersects(rayOrigin, rayDir, dist, vertexIdx)) {
94 if (dist < closestDist) {
98 result.
hitPoint = rayOrigin + dist * rayDir;
105 result.
item =
nullptr;
106 for (
auto i = itemSurfaceMap.cbegin(); i != itemSurfaceMap.cend(); ++i) {
107 if (i.value() == surf) {
108 result.
item =
const_cast<QStandardItem*
>(i.key());
114 if (result.
item && itemSurfaceMap.contains(result.
item)) {
115 result.
regionName = itemSurfaceMap[result.
item]->getAnnotationLabel(vertexIdx);
116 result.
regionId = itemSurfaceMap[result.
item]->getAnnotationLabelId(vertexIdx);
126 for (
auto it = itemDipoleMap.cbegin(); it != itemDipoleMap.cend(); ++it) {
128 if (!it.value()->isVisible())
continue;
131 int dipIdx = it.value()->intersect(rayOrigin, rayDir, dist);
132 if (dipIdx != -1 && dist < closestDist) {
136 result.
hitPoint = rayOrigin + dist * rayDir;
137 result.
item =
const_cast<QStandardItem*
>(it.key());
153 const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>> &itemSurfaceMap,
154 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces)
156 if (!result.
hit)
return QString();
163 if (key.startsWith(
"lh")) hemi =
"lh";
164 else if (key.startsWith(
"rh")) hemi =
"rh";
166 return hemi.isEmpty()
167 ? QString(
"Region: %1").arg(result.
regionName)
168 : QString(
"Region: %1 (%2)").arg(result.
regionName, hemi);
173 QString name = result.
item ? result.
item->text() : QStringLiteral(
"Dipole");
174 return QString(
"%1 (Dipole %2)").arg(name).arg(result.
dipoleIndex);
178 if (key.startsWith(
"sens_surface_meg")) {
179 return QStringLiteral(
"MEG Helmet");
181 if (key.startsWith(
"sens_meg_")) {
182 return QString(
"MEG: %1").arg(result.
item ? result.
item->text() : key);
184 if (key.startsWith(
"sens_eeg_")) {
185 return QString(
"EEG: %1").arg(result.
item ? result.
item->text() : key);
187 if (key.startsWith(
"dig_")) {
194 constexpr int vertsPerSphere = 42;
196 const QStringList &names = digItem->pointNames();
197 if (ptIdx >= 0 && ptIdx < names.size())
198 pointName = names[ptIdx];
201 QString category = key.mid(4);
202 if (!category.isEmpty()) category[0] = category[0].toUpper();
203 return pointName.isEmpty()
204 ? QString(
"Digitizer (%1)").arg(category)
205 : QString(
"Digitizer: %1 (%2)").arg(pointName, category);
207 if (key.startsWith(
"bem_")) {
208 QString compartment = key.mid(4);
209 if (!compartment.isEmpty()) compartment[0] = compartment[0].toUpper();
210 compartment.replace(
"_",
" ");
211 return QString(
"BEM: %1").arg(compartment);
215 if (key.startsWith(
"lh_"))
return QStringLiteral(
"Left Hemisphere");
216 if (key.startsWith(
"rh_"))
return QStringLiteral(
"Right Hemisphere");
227 if (!
hit)
return QString();
232 else if (
surfaceKey.startsWith(
"rh")) hemi =
"rh";
233 return hemi.isEmpty()
235 : QString(
"Region: %1 (%2)").arg(
regionName, hemi);
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
Instanced-arrow renderable for fitted equivalent current dipoles, driven by QRhi instancing.
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.
Viewport subdivision holding its own camera, projection, and scissor rectangle.
bool matchesSurfaceType(const QString &key) const
ViewVisibilityProfile visibility
bool shouldRenderSurface(const QString &key) const
Result of a ray–mesh intersection test containing the hit point, triangle index, and distance.
int dipoleIndex
Index within the dipole set.
int vertexIndex
Vertex or element index at hit.
bool hit
True if something was hit.
QString surfaceKey
FsSurface map key of the hit surface.
QVector3D hitPoint
World-space intersection point.
QString displayLabel() const
QString regionName
FsAnnotation region label (if available).
QStandardItem * item
Tree item that was hit (nullable).
float distance
Distance along ray to hit point.
bool isDipole
True if a dipole was hit.
int regionId
FsAnnotation label ID.
static bool unproject(const QPoint &screenPos, const QRect &paneRect, const QMatrix4x4 &pvm, QVector3D &rayOrigin, QVector3D &rayDir)
static QString buildLabel(const PickResult &result, const QMap< const QStandardItem *, std::shared_ptr< BrainSurface > > &itemSurfaceMap, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces)
static PickResult 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)
Base tree item providing check-state, visibility, and data-role storage for all 3-D scene items.
static constexpr int itemTypeId(ItemType type)
int type() const override
Digitizer point group tree item.