59void BrainSurface::markVertexDirty()
94 const Eigen::MatrixXf &rr = surf.
rr();
95 const Eigen::MatrixXf &nn = surf.
nn();
96 const Eigen::MatrixXi &tris = surf.
tris();
97 const Eigen::VectorXf &curv = surf.
curv();
98 m_curvature.resize(curv.size());
99 for(
int i=0; i<curv.size(); ++i) m_curvature[i] = curv[i];
102 m_vertexData.reserve(rr.rows());
104 for (
int i = 0; i < rr.rows(); ++i) {
106 v.
pos = QVector3D(rr(i, 0), rr(i, 1), rr(i, 2));
107 v.
norm = QVector3D(nn(i, 0), nn(i, 1), nn(i, 2));
108 v.
color = 0xFFFFFFFF;
110 m_vertexData.append(v);
113 m_indexData.reserve(tris.rows() * 3);
114 for (
int i = 0; i < tris.rows(); ++i) {
115 m_indexData.append(tris(i, 0));
116 m_indexData.append(tris(i, 1));
117 m_indexData.append(tris(i, 2));
119 m_indexCount = m_indexData.size();
125 updateVertexColors();
127 m_originalVertexData = m_vertexData;
136 m_vertexData.clear();
140 int nVerts = surf.
rr.rows();
141 m_vertexData.reserve(nVerts);
144 Eigen::MatrixX3f nn = surf.
nn;
145 if (nn.rows() != nVerts) {
149 m_defaultColor = color;
151 uint32_t colorVal =
packABGR(color.red(), color.green(), color.blue(), color.alpha());
153 for (
int i = 0; i < nVerts; ++i) {
155 v.
pos = QVector3D(surf.
rr(i, 0), surf.
rr(i, 1), surf.
rr(i, 2));
156 v.
norm = QVector3D(nn(i, 0), nn(i, 1), nn(i, 2));
159 m_vertexData.append(v);
162 int nTris = surf.
itris.rows();
163 m_indexData.reserve(nTris * 3);
164 for (
int i = 0; i < nTris; ++i) {
165 m_indexData.append(surf.
itris(i, 0));
166 m_indexData.append(surf.
itris(i, 1));
167 m_indexData.append(surf.
itris(i, 2));
169 m_indexCount = m_indexData.size();
171 m_originalVertexData = m_vertexData;
179 Eigen::MatrixX3f normals(vertices.rows(), 3);
180 for(
int i=0; i<vertices.rows(); ++i) {
181 QVector3D p(vertices(i, 0), vertices(i, 1), vertices(i, 2));
182 QVector3D n = p.normalized();
183 normals(i, 0) = n.x();
184 normals(i, 1) = n.y();
185 normals(i, 2) = n.z();
190void BrainSurface::createFromData(
const Eigen::MatrixX3f &vertices,
const Eigen::MatrixX3f &normals,
const Eigen::MatrixX3i &triangles,
const QColor &color)
192 m_vertexData.clear();
196 int nVerts = vertices.rows();
197 m_vertexData.reserve(nVerts);
199 m_defaultColor = color;
201 uint32_t colorVal =
packABGR(color.red(), color.green(), color.blue(), color.alpha());
203 for (
int i = 0; i < nVerts; ++i) {
205 v.
pos = QVector3D(vertices(i, 0), vertices(i, 1), vertices(i, 2));
206 v.
norm = QVector3D(normals(i, 0), normals(i, 1), normals(i, 2));
209 m_vertexData.append(v);
212 int nTris = triangles.rows();
213 m_indexData.reserve(nTris * 3);
214 for (
int i = 0; i < nTris; ++i) {
215 m_indexData.append(triangles(i, 0));
216 m_indexData.append(triangles(i, 1));
217 m_indexData.append(triangles(i, 2));
219 m_indexCount = m_indexData.size();
221 m_originalVertexData = m_vertexData;
229 Eigen::MatrixX3f rr(m_vertexData.size(), 3);
230 for (
int i = 0; i < m_vertexData.size(); ++i) {
231 rr(i, 0) = m_vertexData[i].pos.x();
232 rr(i, 1) = m_vertexData[i].pos.y();
233 rr(i, 2) = m_vertexData[i].pos.z();
242 Eigen::MatrixX3f nn(m_vertexData.size(), 3);
243 for (
int i = 0; i < m_vertexData.size(); ++i) {
244 nn(i, 0) = m_vertexData[i].norm.x();
245 nn(i, 1) = m_vertexData[i].norm.y();
246 nn(i, 2) = m_vertexData[i].norm.z();
256 qWarning() <<
"BrainSurface: Failed to load annotation from" << path;
259 m_hasAnnotation =
true;
260 updateVertexColors();
267 m_annotation = annotation;
268 m_hasAnnotation =
true;
269 updateVertexColors();
285 if (m_visMode == mode)
294 updateVertexColors();
303 m_stcColors = colors;
306 for (
int i = 0; i < qMin(colors.size(), m_vertexData.size()); ++i) {
307 m_vertexData[i].color = colors[i];
319 updateVertexColors();
327 m_baseColor = useDefault ? m_defaultColor : Qt::white;
328 updateVertexColors();
331void BrainSurface::updateVertexColors()
338 if (!m_curvature.isEmpty() && m_curvature.size() == m_vertexData.size()) {
339 for (
int i = 0; i < m_vertexData.size(); ++i) {
340 const uint32_t val = (m_curvature[i] > 0.0f) ? 0x40u : 0xAAu;
341 m_vertexData[i].color =
packABGR(val, val, val);
346 const uint32_t baseVal =
packABGR(m_baseColor.red(), m_baseColor.green(),
347 m_baseColor.blue(), m_baseColor.alpha());
348 for (
int i = 0; i < m_vertexData.size(); ++i) {
349 m_vertexData[i].color = baseVal;
359 if (!m_stcColors.isEmpty()) {
360 for (
int i = 0; i < qMin(m_stcColors.size(), m_vertexData.size()); ++i) {
361 m_vertexData[i].color = m_stcColors[i];
366 for (
auto &v : m_vertexData) {
367 v.colorAnnotation = 0x00000000;
370 if (m_hasAnnotation && !m_vertexData.isEmpty()) {
371 const Eigen::VectorXi &vertices = m_annotation.getVertices();
372 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
373 const FSLIB::FsColortable &ct = m_annotation.getColortable();
375 for (
int i = 0; i < labelIds.rows(); ++i) {
376 int vertexIdx = vertices(i);
377 if (vertexIdx >= 0 && vertexIdx < m_vertexData.size()) {
380 if (ct.
table(c, 4) == labelIds(i)) {
386 uint32_t r = ct.
table(colorIdx, 0);
387 uint32_t g = ct.
table(colorIdx, 1);
388 uint32_t b = ct.
table(colorIdx, 2);
389 m_vertexData[vertexIdx].colorAnnotation =
402 const uint32_t gold =
packABGR(255, 200, 60);
403 if (m_selectedRegionId != -1 && m_hasAnnotation) {
405 const Eigen::VectorXi &vertices = m_annotation.getVertices();
406 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
407 for (
int i = 0; i < labelIds.rows(); ++i) {
408 if (labelIds(i) == m_selectedRegionId) {
409 int idx = vertices(i);
410 if (idx >= 0 && idx < m_vertexData.size()) {
411 m_vertexData[idx].color = gold;
412 m_vertexData[idx].colorAnnotation = gold;
416 }
else if (m_selectedVertexStart >= 0 && m_selectedVertexCount > 0) {
418 const int end = qMin(m_selectedVertexStart + m_selectedVertexCount,
419 m_vertexData.size());
420 for (
int i = m_selectedVertexStart; i < end; ++i) {
421 m_vertexData[i].color = gold;
433 float minVal = std::numeric_limits<float>::max();
434 for (
const auto &v : m_vertexData) {
435 if (v.pos.x() < minVal) minVal = v.pos.x();
442 float maxVal = std::numeric_limits<float>::lowest();
443 for (
const auto &v : m_vertexData) {
444 if (v.pos.x() > maxVal) maxVal = v.pos.x();
451 for (
auto &v : m_vertexData) {
452 v.pos.setX(v.pos.x() + offset);
464 QMatrix3x3 normalMat = m.normalMatrix();
466 for (
auto &v : m_vertexData) {
468 v.pos = m.map(v.pos);
481 const float *d = normalMat.constData();
482 float nx = d[0]*v.norm.x() + d[3]*v.norm.y() + d[6]*v.norm.z();
483 float ny = d[1]*v.norm.x() + d[4]*v.norm.y() + d[7]*v.norm.z();
484 float nz = d[2]*v.norm.x() + d[5]*v.norm.y() + d[8]*v.norm.z();
485 v.norm = QVector3D(nx, ny, nz).normalized();
495 m_vertexData = m_originalVertexData;
496 if (!m.isIdentity()) {
508 const bool needsCreate = !m_gpu->vertexBuffer || !m_gpu->indexBuffer;
516 if (!needsCreate && !m_gpu->dirty) {
518 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
519 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
523 if (!m_gpu->dirty && !needsCreate)
return;
526 const quint32 vbufSize = m_vertexData.size() *
sizeof(
VertexData);
527 const quint32 ibufSize = m_indexData.size() *
sizeof(uint32_t);
529 if (!m_gpu->vertexBuffer) {
535 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, vbufSize));
537 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, vbufSize));
539 m_gpu->vertexBuffer->create();
540 m_gpu->indexDirty =
true;
542 if (!m_gpu->indexBuffer) {
543 m_gpu->indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, ibufSize));
544 m_gpu->indexBuffer->create();
545 m_gpu->indexDirty =
true;
549 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
550 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
555 u->updateDynamicBuffer(m_gpu->vertexBuffer.get(), 0, vbufSize, m_vertexData.constData());
556 if (m_gpu->indexDirty) {
557 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
558 m_gpu->indexDirty =
false;
561 m_gpu->dirty =
false;
569 std::vector<std::set<int>> tempNeighbors(m_vertexData.size());
572 for (
int i = 0; i + 2 < m_indexData.size(); i += 3) {
573 int v0 = m_indexData[i];
574 int v1 = m_indexData[i + 1];
575 int v2 = m_indexData[i + 2];
578 tempNeighbors[v0].insert(v1);
579 tempNeighbors[v0].insert(v2);
580 tempNeighbors[v1].insert(v0);
581 tempNeighbors[v1].insert(v2);
582 tempNeighbors[v2].insert(v0);
583 tempNeighbors[v2].insert(v1);
587 std::vector<Eigen::VectorXi> neighbors(tempNeighbors.size());
588 for (
size_t k = 0; k < tempNeighbors.size(); ++k) {
589 const auto& s = tempNeighbors[k];
590 neighbors[k].resize(
static_cast<Eigen::Index
>(s.size()));
591 Eigen::Index idx = 0;
593 neighbors[k][idx++] = val;
604 Eigen::MatrixX3f mat(m_vertexData.size(), 3);
605 for (
int i = 0; i < m_vertexData.size(); ++i) {
606 mat(i, 0) = m_vertexData[i].pos.x();
607 mat(i, 1) = m_vertexData[i].pos.y();
608 mat(i, 2) = m_vertexData[i].pos.z();
624 if (m_vertexData.isEmpty()) {
625 min = QVector3D(0,0,0);
626 max = QVector3D(0,0,0);
630 min = m_vertexData[0].pos;
631 max = m_vertexData[0].pos;
633 for (
const auto &v : m_vertexData) {
634 min.setX(std::min(min.x(), v.pos.x()));
635 min.setY(std::min(min.y(), v.pos.y()));
636 min.setZ(std::min(min.z(), v.pos.z()));
638 max.setX(std::max(max.x(), v.pos.x()));
639 max.setY(std::max(max.y(), v.pos.y()));
640 max.setZ(std::max(max.z(), v.pos.z()));
645 m_bAABBDirty =
false;
651 if (m_vertexData.isEmpty())
return false;
659 double origin[3] = {rayOrigin.x(), rayOrigin.y(), rayOrigin.z()};
660 double dir[3] = {rayDir.x(), rayDir.y(), rayDir.z()};
661 double minB[3] = {min.x() - eps, min.y() - eps, min.z() - eps};
662 double maxB[3] = {max.x() + eps, max.y() + eps, max.z() + eps};
665 double originX = origin[0], originY = origin[1], originZ = origin[2];
666 double dirX = dir[0], dirY = dir[1], dirZ = dir[2];
668 double tmin = -std::numeric_limits<double>::max();
669 double tmax = std::numeric_limits<double>::max();
671 for (
int i = 0; i < 3; ++i) {
672 if (std::abs(dir[i]) < 1e-15) {
673 if (origin[i] < minB[i] || origin[i] > maxB[i])
return false;
675 double t1 = (minB[i] - origin[i]) / dir[i];
676 double t2 = (maxB[i] - origin[i]) / dir[i];
677 if (t1 > t2) std::swap(t1, t2);
678 if (t1 > tmin) tmin = t1;
679 if (t2 < tmax) tmax = t2;
680 if (tmin > tmax)
return false;
684 if (tmax < 1e-7)
return false;
687 double closestDist = std::numeric_limits<double>::max();
689 int closestVert = -1;
694 for (
int i = 0; i < m_indexData.size(); i += 3) {
695 int i0 = m_indexData[i];
696 int i1 = m_indexData[i+1];
697 int i2 = m_indexData[i+2];
698 const QVector3D &v0q = m_vertexData[i0].pos;
699 const QVector3D &v1q = m_vertexData[i1].pos;
700 const QVector3D &v2q = m_vertexData[i2].pos;
702 double v0x = v0q.x(), v0y = v0q.y(), v0z = v0q.z();
703 double v1x = v1q.x(), v1y = v1q.y(), v1z = v1q.z();
704 double v2x = v2q.x(), v2y = v2q.y(), v2z = v2q.z();
706 double edge1x = v1x - v0x, edge1y = v1y - v0y, edge1z = v1z - v0z;
707 double edge2x = v2x - v0x, edge2y = v2y - v0y, edge2z = v2z - v0z;
709 double hx = dirY * edge2z - dirZ * edge2y;
710 double hy = dirZ * edge2x - dirX * edge2z;
711 double hz = dirX * edge2y - dirY * edge2x;
713 double a = edge1x * hx + edge1y * hy + edge1z * hz;
714 if (std::abs(a) < 1e-18)
continue;
717 double sx = originX - v0x, sy = originY - v0y, sz = originZ - v0z;
718 double u = f * (sx * hx + sy * hy + sz * hz);
719 if (u < -1e-7 || u > 1.0000001)
continue;
721 double qx = sy * edge1z - sz * edge1y;
722 double qy = sz * edge1x - sx * edge1z;
723 double qz = sx * edge1y - sy * edge1x;
725 double v = f * (dirX * qx + dirY * qy + dirZ * qz);
726 if (v < -1e-7 || u + v > 1.0000001)
continue;
728 double t = f * (edge2x * qx + edge2y * qy + edge2z * qz);
729 if (t > 1e-7 && t < closestDist) {
735 constexpr double tol = 0.25;
737 if (u >= -tol && v >= -tol && u + v <= 1.0 + tol) {
743 double hitX = originX + t * dirX;
744 double hitY = originY + t * dirY;
745 double hitZ = originZ + t * dirZ;
747 double d0 = (v0x - hitX)*(v0x - hitX) + (v0y - hitY)*(v0y - hitY) + (v0z - hitZ)*(v0z - hitZ);
748 double d1 = (v1x - hitX)*(v1x - hitX) + (v1y - hitY)*(v1y - hitY) + (v1z - hitZ)*(v1z - hitZ);
749 double d2 = (v2x - hitX)*(v2x - hitX) + (v2y - hitY)*(v2y - hitY) + (v2z - hitZ)*(v2z - hitZ);
751 if (d0 < d1 && d0 < d2) closestVert = i0;
752 else if (d1 < d2) closestVert = i1;
753 else closestVert = i2;
759 dist =
static_cast<float>(closestDist);
760 vertexIdx = closestVert;
771 if (!m_hasAnnotation || vertexIdx < 0 || vertexIdx >= m_vertexData.size()) {
775 const Eigen::VectorXi &vertices = m_annotation.getVertices();
776 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
783 for (
int i = 0; i < vertices.rows(); ++i) {
784 if (vertices(i) == vertexIdx) {
785 labelId = labelIds(i);
790 if (labelId == -1)
return "Unknown";
794 if (ct.
table(i, 4) == labelId) {
797 while (!name.isEmpty() && (name.endsWith(
'\0') || name.endsWith(
' '))) {
809 if (!m_hasAnnotation || vertexIdx < 0)
return -1;
811 const Eigen::VectorXi &vertices = m_annotation.getVertices();
812 const Eigen::VectorXi &labelIds = m_annotation.
getLabelIds();
814 for (
int i = 0; i < vertices.rows(); ++i) {
815 if (vertices(i) == vertexIdx) {
825 m_selectedRegionId = regionId;
826 updateVertexColors();
831 m_selected = selected;
832 updateVertexColors();
837 m_selectedVertexStart = start;
838 m_selectedVertexCount = count;
839 updateVertexColors();
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
BrainSurface class declaration.
std::unique_ptr< QRhiBuffer > indexBuffer
std::unique_ptr< QRhiBuffer > vertexBuffer
Interleaved vertex attributes (position, normal, color, curvature) for brain surface GPU upload.
void translateX(float offset)
void fromSurface(const FSLIB::FsSurface &surf)
void setVisualizationMode(VisualizationMode mode)
Eigen::MatrixX3f vertexNormals() const
void setVisible(bool visible)
void boundingBox(QVector3D &min, QVector3D &max) const
QRhiBuffer * vertexBuffer() const
bool loadAnnotation(const QString &path)
void setSelectedRegion(int regionId)
void transform(const QMatrix4x4 &m)
::VisualizationMode VisualizationMode
void setSelectedVertexRange(int start, int count)
static constexpr VisualizationMode ModeSourceEstimate
QRhiBuffer * indexBuffer() const
bool intersects(const QVector3D &rayOrigin, const QVector3D &rayDir, float &dist, int &vertexIdx) const
void applySourceEstimateColors(const QVector< uint32_t > &colors)
void setUseDefaultColor(bool useDefault)
QString getAnnotationLabel(int vertexIdx) const
void clearSourceEstimateColors()
void updateBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
Eigen::MatrixX3f vertexPositions() const
int getAnnotationLabelId(int vertexIdx) const
void addAnnotation(const FSLIB::FsAnnotation &annotation)
void applyTransform(const QMatrix4x4 &m)
void setSelected(bool selected)
static constexpr VisualizationMode ModeSurface
Eigen::MatrixX3f verticesAsMatrix() const
void fromBemSurface(const MNELIB::MNEBemSurface &surf, const QColor &color=Qt::white)
void createFromData(const Eigen::MatrixX3f &vertices, const Eigen::MatrixX3i &triangles, const QColor &color)
std::vector< Eigen::VectorXi > computeNeighbors() const
static bool read(const QString &subject_id, qint32 hemi, const QString &atlas, const QString &subjects_dir, FsAnnotation &p_Annotation)
Vertices label based lookup table.
Eigen::VectorXi getLabelIds() const
const Eigen::MatrixX3f & nn() const
const Eigen::MatrixX3i & tris() const
const Eigen::MatrixX3f & rr() const
const Eigen::VectorXf & curv() const
static Eigen::MatrixX3f compute_normals(const Eigen::MatrixX3f &rr, const Eigen::MatrixX3i &tris)
BEM surface provides geometry information.