53 const QRect &paneRect,
54 const QMatrix4x4 &pvm,
58 bool invertible =
false;
59 QMatrix4x4 invPVM = pvm.inverted(&invertible);
60 if (!invertible)
return false;
62 const float localX =
static_cast<float>(screenPos.x() - paneRect.x());
63 const float localY =
static_cast<float>(screenPos.y() - paneRect.y());
64 const float paneW =
static_cast<float>(std::max(1, paneRect.width()));
65 const float paneH =
static_cast<float>(std::max(1, paneRect.height()));
67 const float ndcX = (2.0f * localX) / paneW - 1.0f;
68 const float ndcY = 1.0f - (2.0f * localY) / paneH;
70 QVector4D vNear(ndcX, ndcY, -1.0f, 1.0f);
71 QVector4D vFar (ndcX, ndcY, 1.0f, 1.0f);
73 QVector4D pNear = invPVM * vNear;
74 QVector4D pFar = invPVM * vFar;
78 rayOrigin = pNear.toVector3D();
79 rayDir = (pFar.toVector3D() - pNear.toVector3D()).normalized();
86 const QVector3D &rayDir,
88 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
89 const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>> &itemSurfaceMap,
90 const QMap<
const QStandardItem*, std::shared_ptr<DipoleObject>> &itemDipoleMap)
93 float closestDist = std::numeric_limits<float>::max();
96 for (
auto it = surfaces.cbegin(); it != surfaces.cend(); ++it) {
97 const QString &key = it.key();
98 const auto &surf = it.value();
100 if (!surf->isVisible())
continue;
102 if (key.startsWith(
"srcsp_"))
continue;
104 const bool isSensor = key.startsWith(
"sens_");
105 const bool isBem = key.startsWith(
"bem_");
106 const bool isDig = key.startsWith(
"dig_");
109 if (!isSensor && !isBem && !isDig) {
115 if (surf->intersects(rayOrigin, rayDir, dist, vertexIdx)) {
116 if (dist < closestDist) {
120 result.
hitPoint = rayOrigin + dist * rayDir;
127 result.
item =
nullptr;
128 for (
auto i = itemSurfaceMap.cbegin(); i != itemSurfaceMap.cend(); ++i) {
129 if (i.value() == surf) {
130 result.
item =
const_cast<QStandardItem*
>(i.key());
136 if (result.
item && itemSurfaceMap.contains(result.
item)) {
137 result.
regionName = itemSurfaceMap[result.
item]->getAnnotationLabel(vertexIdx);
138 result.
regionId = itemSurfaceMap[result.
item]->getAnnotationLabelId(vertexIdx);
148 for (
auto it = itemDipoleMap.cbegin(); it != itemDipoleMap.cend(); ++it) {
150 if (!it.value()->isVisible())
continue;
153 int dipIdx = it.value()->intersect(rayOrigin, rayDir, dist);
154 if (dipIdx != -1 && dist < closestDist) {
158 result.
hitPoint = rayOrigin + dist * rayDir;
159 result.
item =
const_cast<QStandardItem*
>(it.key());
175 const QMap<
const QStandardItem*, std::shared_ptr<BrainSurface>> &itemSurfaceMap,
176 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces)
178 if (!result.
hit)
return QString();
185 if (key.startsWith(
"lh")) hemi =
"lh";
186 else if (key.startsWith(
"rh")) hemi =
"rh";
188 return hemi.isEmpty()
189 ? QString(
"Region: %1").arg(result.
regionName)
190 : QString(
"Region: %1 (%2)").arg(result.
regionName, hemi);
195 QString name = result.
item ? result.
item->text() : QStringLiteral(
"Dipole");
196 return QString(
"%1 (Dipole %2)").arg(name).arg(result.
dipoleIndex);
200 if (key.startsWith(
"sens_surface_meg")) {
201 return QStringLiteral(
"MEG Helmet");
203 if (key.startsWith(
"sens_meg_")) {
204 return QString(
"MEG: %1").arg(result.
item ? result.
item->text() : key);
206 if (key.startsWith(
"sens_eeg_")) {
207 return QString(
"EEG: %1").arg(result.
item ? result.
item->text() : key);
209 if (key.startsWith(
"dig_")) {
216 constexpr int vertsPerSphere = 42;
218 const QStringList &names = digItem->pointNames();
219 if (ptIdx >= 0 && ptIdx < names.size())
220 pointName = names[ptIdx];
223 QString category = key.mid(4);
224 if (!category.isEmpty()) category[0] = category[0].toUpper();
225 return pointName.isEmpty()
226 ? QString(
"Digitizer (%1)").arg(category)
227 : QString(
"Digitizer: %1 (%2)").arg(pointName, category);
229 if (key.startsWith(
"bem_")) {
230 QString compartment = key.mid(4);
231 if (!compartment.isEmpty()) compartment[0] = compartment[0].toUpper();
232 compartment.replace(
"_",
" ");
233 return QString(
"BEM: %1").arg(compartment);
237 if (key.startsWith(
"lh_"))
return QStringLiteral(
"Left Hemisphere");
238 if (key.startsWith(
"rh_"))
return QStringLiteral(
"Right Hemisphere");
249 if (!
hit)
return QString();
254 else if (
surfaceKey.startsWith(
"rh")) hemi =
"rh";
255 return hemi.isEmpty()
257 : QString(
"Region: %1 (%2)").arg(
regionName, hemi);
RayPicker class declaration — ray casting and intersection testing.
BrainSurface class declaration.
DipoleObject class declaration.
AbstractTreeItem class declaration.
DigitizerTreeItem class declaration.
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
Surface map key of the hit surface.
QVector3D hitPoint
World-space intersection point.
QString displayLabel() const
QString regionName
Annotation 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
Annotation 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.
int type() const override
Digitizer point group tree item.