85 const Eigen::MatrixXf &rr = surf.
rr();
86 const Eigen::MatrixXf &nn = surf.
nn();
87 const Eigen::MatrixXi &tris = surf.
tris();
88 const Eigen::VectorXf &curv = surf.
curv();
89 m_curvature.resize(curv.size());
90 for(
int i=0; i<curv.size(); ++i) m_curvature[i] = curv[i];
93 m_vertexData.reserve(rr.rows());
95 for (
int i = 0; i < rr.rows(); ++i) {
97 v.
pos = QVector3D(rr(i, 0), rr(i, 1), rr(i, 2));
98 v.
norm = QVector3D(nn(i, 0), nn(i, 1), nn(i, 2));
101 m_vertexData.append(v);
104 m_indexData.reserve(tris.rows() * 3);
105 for (
int i = 0; i < tris.rows(); ++i) {
106 m_indexData.append(tris(i, 0));
107 m_indexData.append(tris(i, 1));
108 m_indexData.append(tris(i, 2));
110 m_indexCount = m_indexData.size();
116 updateVertexColors();
118 m_originalVertexData = m_vertexData;
127 m_vertexData.clear();
131 int nVerts = surf.
rr.rows();
132 m_vertexData.reserve(nVerts);
135 Eigen::MatrixX3f nn = surf.
nn;
136 if (nn.rows() != nVerts) {
140 m_defaultColor = color;
142 uint32_t colorVal =
packABGR(color.red(), color.green(), color.blue(), color.alpha());
144 for (
int i = 0; i < nVerts; ++i) {
146 v.
pos = QVector3D(surf.
rr(i, 0), surf.
rr(i, 1), surf.
rr(i, 2));
147 v.
norm = QVector3D(nn(i, 0), nn(i, 1), nn(i, 2));
150 m_vertexData.append(v);
153 int nTris = surf.
itris.rows();
154 m_indexData.reserve(nTris * 3);
155 for (
int i = 0; i < nTris; ++i) {
156 m_indexData.append(surf.
itris(i, 0));
157 m_indexData.append(surf.
itris(i, 1));
158 m_indexData.append(surf.
itris(i, 2));
160 m_indexCount = m_indexData.size();
162 m_originalVertexData = m_vertexData;
170 Eigen::MatrixX3f normals(vertices.rows(), 3);
171 for(
int i=0; i<vertices.rows(); ++i) {
172 QVector3D p(vertices(i, 0), vertices(i, 1), vertices(i, 2));
173 QVector3D n = p.normalized();
174 normals(i, 0) = n.x();
175 normals(i, 1) = n.y();
176 normals(i, 2) = n.z();
181void BrainSurface::createFromData(
const Eigen::MatrixX3f &vertices,
const Eigen::MatrixX3f &normals,
const Eigen::MatrixX3i &triangles,
const QColor &color)
183 m_vertexData.clear();
187 int nVerts = vertices.rows();
188 m_vertexData.reserve(nVerts);
190 m_defaultColor = color;
192 uint32_t colorVal =
packABGR(color.red(), color.green(), color.blue(), color.alpha());
194 for (
int i = 0; i < nVerts; ++i) {
196 v.
pos = QVector3D(vertices(i, 0), vertices(i, 1), vertices(i, 2));
197 v.
norm = QVector3D(normals(i, 0), normals(i, 1), normals(i, 2));
200 m_vertexData.append(v);
203 int nTris = triangles.rows();
204 m_indexData.reserve(nTris * 3);
205 for (
int i = 0; i < nTris; ++i) {
206 m_indexData.append(triangles(i, 0));
207 m_indexData.append(triangles(i, 1));
208 m_indexData.append(triangles(i, 2));
210 m_indexCount = m_indexData.size();
212 m_originalVertexData = m_vertexData;
220 Eigen::MatrixX3f rr(m_vertexData.size(), 3);
221 for (
int i = 0; i < m_vertexData.size(); ++i) {
222 rr(i, 0) = m_vertexData[i].pos.x();
223 rr(i, 1) = m_vertexData[i].pos.y();
224 rr(i, 2) = m_vertexData[i].pos.z();
233 Eigen::MatrixX3f nn(m_vertexData.size(), 3);
234 for (
int i = 0; i < m_vertexData.size(); ++i) {
235 nn(i, 0) = m_vertexData[i].norm.x();
236 nn(i, 1) = m_vertexData[i].norm.y();
237 nn(i, 2) = m_vertexData[i].norm.z();
247 qWarning() <<
"BrainSurface: Failed to load annotation from" << path;
250 m_hasAnnotation =
true;
251 updateVertexColors();
258 m_annotation = annotation;
259 m_hasAnnotation =
true;
260 updateVertexColors();
276 if (m_visMode == mode)
285 updateVertexColors();
294 m_stcColors = colors;
297 for (
int i = 0; i < qMin(colors.size(), m_vertexData.size()); ++i) {
298 m_vertexData[i].color = colors[i];
308 m_baseColor = useDefault ? m_defaultColor : Qt::white;
309 updateVertexColors();
312void BrainSurface::updateVertexColors()
319 if (!m_curvature.isEmpty() && m_curvature.size() == m_vertexData.size()) {
320 for (
int i = 0; i < m_vertexData.size(); ++i) {
321 const uint32_t val = (m_curvature[i] > 0.0f) ? 0x40u : 0xAAu;
322 m_vertexData[i].color =
packABGR(val, val, val);
327 const uint32_t baseVal =
packABGR(m_baseColor.red(), m_baseColor.green(),
328 m_baseColor.blue(), m_baseColor.alpha());
329 for (
int i = 0; i < m_vertexData.size(); ++i) {
330 m_vertexData[i].color = baseVal;
340 if (!m_stcColors.isEmpty()) {
341 for (
int i = 0; i < qMin(m_stcColors.size(), m_vertexData.size()); ++i) {
342 m_vertexData[i].color = m_stcColors[i];
347 for (
auto &v : m_vertexData) {
348 v.colorAnnotation = 0x00000000;
351 if (m_hasAnnotation && !m_vertexData.isEmpty()) {
352 const Eigen::VectorXi &vertices = m_annotation.getVertices();
353 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
354 const FSLIB::Colortable &ct = m_annotation.getColortable();
356 for (
int i = 0; i < labelIds.rows(); ++i) {
357 int vertexIdx = vertices(i);
358 if (vertexIdx >= 0 && vertexIdx < m_vertexData.size()) {
361 if (ct.
table(c, 4) == labelIds(i)) {
367 uint32_t r = ct.
table(colorIdx, 0);
368 uint32_t g = ct.
table(colorIdx, 1);
369 uint32_t b = ct.
table(colorIdx, 2);
370 m_vertexData[vertexIdx].colorAnnotation =
378 if (m_selected || m_selectedRegionId != -1) {
379 const uint32_t goldR = 255, goldG = 200, goldB = 80;
380 const float blendFactor = 0.4f;
382 auto blendToGold = [&](uint32_t &c) {
383 uint32_t a = (c >> 24) & 0xFF;
384 uint32_t bv = (c >> 16) & 0xFF;
385 uint32_t gv = (c >> 8) & 0xFF;
386 uint32_t rv = c & 0xFF;
387 rv =
static_cast<uint32_t
>(rv * (1.0f - blendFactor) + goldR * blendFactor);
388 gv =
static_cast<uint32_t
>(gv * (1.0f - blendFactor) + goldG * blendFactor);
389 bv =
static_cast<uint32_t
>(bv * (1.0f - blendFactor) + goldB * blendFactor);
393 if (m_hasAnnotation && m_selectedRegionId != -1) {
394 const Eigen::VectorXi &vertices = m_annotation.getVertices();
395 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
396 for (
int i = 0; i < labelIds.rows(); ++i) {
397 if (labelIds(i) == m_selectedRegionId) {
398 int vertexIdx = vertices(i);
399 if (vertexIdx >= 0 && vertexIdx < m_vertexData.size()) {
400 blendToGold(m_vertexData[vertexIdx].color);
401 blendToGold(m_vertexData[vertexIdx].colorAnnotation);
405 }
else if (m_selected) {
406 for (
auto &v : m_vertexData) {
407 blendToGold(v.color);
408 blendToGold(v.colorAnnotation);
414 if (m_selectedVertexStart >= 0 && m_selectedVertexCount > 0) {
415 const uint32_t hlR = 255, hlG = 255, hlB = 180;
416 const float blendFactor = 0.85f;
417 int end = qMin(m_selectedVertexStart + m_selectedVertexCount, (
int)m_vertexData.size());
418 for (
int i = m_selectedVertexStart; i < end; ++i) {
419 for (uint32_t *c : { &m_vertexData[i].color, &m_vertexData[i].colorAnnotation }) {
420 uint32_t a = (*c >> 24) & 0xFF;
421 uint32_t bv = (*c >> 16) & 0xFF;
422 uint32_t gv = (*c >> 8) & 0xFF;
423 uint32_t rv = *c & 0xFF;
424 rv =
static_cast<uint32_t
>(rv * (1.0f - blendFactor) + hlR * blendFactor);
425 gv =
static_cast<uint32_t
>(gv * (1.0f - blendFactor) + hlG * blendFactor);
426 bv =
static_cast<uint32_t
>(bv * (1.0f - blendFactor) + hlB * blendFactor);
437 float minVal = std::numeric_limits<float>::max();
438 for (
const auto &v : m_vertexData) {
439 if (v.pos.x() < minVal) minVal = v.pos.x();
446 float maxVal = std::numeric_limits<float>::lowest();
447 for (
const auto &v : m_vertexData) {
448 if (v.pos.x() > maxVal) maxVal = v.pos.x();
455 for (
auto &v : m_vertexData) {
456 v.pos.setX(v.pos.x() + offset);
468 QMatrix3x3 normalMat = m.normalMatrix();
470 for (
auto &v : m_vertexData) {
472 v.pos = m.map(v.pos);
485 const float *d = normalMat.constData();
486 float nx = d[0]*v.norm.x() + d[3]*v.norm.y() + d[6]*v.norm.z();
487 float ny = d[1]*v.norm.x() + d[4]*v.norm.y() + d[7]*v.norm.z();
488 float nz = d[2]*v.norm.x() + d[5]*v.norm.y() + d[8]*v.norm.z();
489 v.norm = QVector3D(nx, ny, nz).normalized();
499 m_vertexData = m_originalVertexData;
500 if (!m.isIdentity()) {
512 if (!m_gpu->dirty && m_gpu->vertexBuffer && m_gpu->indexBuffer)
return;
514 const quint32 vbufSize = m_vertexData.size() *
sizeof(
VertexData);
515 const quint32 ibufSize = m_indexData.size() *
sizeof(uint32_t);
517 if (!m_gpu->vertexBuffer) {
518 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, vbufSize));
519 m_gpu->vertexBuffer->create();
521 if (!m_gpu->indexBuffer) {
522 m_gpu->indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, ibufSize));
523 m_gpu->indexBuffer->create();
526 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
527 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
528 m_gpu->dirty =
false;
536 std::vector<std::set<int>> tempNeighbors(m_vertexData.size());
539 for (
int i = 0; i + 2 < m_indexData.size(); i += 3) {
540 int v0 = m_indexData[i];
541 int v1 = m_indexData[i + 1];
542 int v2 = m_indexData[i + 2];
545 tempNeighbors[v0].insert(v1);
546 tempNeighbors[v0].insert(v2);
547 tempNeighbors[v1].insert(v0);
548 tempNeighbors[v1].insert(v2);
549 tempNeighbors[v2].insert(v0);
550 tempNeighbors[v2].insert(v1);
554 std::vector<Eigen::VectorXi> neighbors(tempNeighbors.size());
555 for (
size_t k = 0; k < tempNeighbors.size(); ++k) {
556 const auto& s = tempNeighbors[k];
557 neighbors[k].resize(
static_cast<Eigen::Index
>(s.size()));
558 Eigen::Index idx = 0;
560 neighbors[k][idx++] = val;
571 Eigen::MatrixX3f mat(m_vertexData.size(), 3);
572 for (
int i = 0; i < m_vertexData.size(); ++i) {
573 mat(i, 0) = m_vertexData[i].pos.x();
574 mat(i, 1) = m_vertexData[i].pos.y();
575 mat(i, 2) = m_vertexData[i].pos.z();
591 if (m_vertexData.isEmpty()) {
592 min = QVector3D(0,0,0);
593 max = QVector3D(0,0,0);
597 min = m_vertexData[0].pos;
598 max = m_vertexData[0].pos;
600 for (
const auto &v : m_vertexData) {
601 min.setX(std::min(min.x(), v.pos.x()));
602 min.setY(std::min(min.y(), v.pos.y()));
603 min.setZ(std::min(min.z(), v.pos.z()));
605 max.setX(std::max(max.x(), v.pos.x()));
606 max.setY(std::max(max.y(), v.pos.y()));
607 max.setZ(std::max(max.z(), v.pos.z()));
612 m_bAABBDirty =
false;
618 if (m_vertexData.isEmpty())
return false;
626 double origin[3] = {rayOrigin.x(), rayOrigin.y(), rayOrigin.z()};
627 double dir[3] = {rayDir.x(), rayDir.y(), rayDir.z()};
628 double minB[3] = {min.x() - eps, min.y() - eps, min.z() - eps};
629 double maxB[3] = {max.x() + eps, max.y() + eps, max.z() + eps};
632 double originX = origin[0], originY = origin[1], originZ = origin[2];
633 double dirX = dir[0], dirY = dir[1], dirZ = dir[2];
635 double tmin = -std::numeric_limits<double>::max();
636 double tmax = std::numeric_limits<double>::max();
638 for (
int i = 0; i < 3; ++i) {
639 if (std::abs(dir[i]) < 1e-15) {
640 if (origin[i] < minB[i] || origin[i] > maxB[i])
return false;
642 double t1 = (minB[i] - origin[i]) / dir[i];
643 double t2 = (maxB[i] - origin[i]) / dir[i];
644 if (t1 > t2) std::swap(t1, t2);
645 if (t1 > tmin) tmin = t1;
646 if (t2 < tmax) tmax = t2;
647 if (tmin > tmax)
return false;
651 if (tmax < 1e-7)
return false;
654 double closestDist = std::numeric_limits<double>::max();
656 int closestVert = -1;
661 for (
int i = 0; i < m_indexData.size(); i += 3) {
662 int i0 = m_indexData[i];
663 int i1 = m_indexData[i+1];
664 int i2 = m_indexData[i+2];
665 const QVector3D &v0q = m_vertexData[i0].pos;
666 const QVector3D &v1q = m_vertexData[i1].pos;
667 const QVector3D &v2q = m_vertexData[i2].pos;
669 double v0x = v0q.x(), v0y = v0q.y(), v0z = v0q.z();
670 double v1x = v1q.x(), v1y = v1q.y(), v1z = v1q.z();
671 double v2x = v2q.x(), v2y = v2q.y(), v2z = v2q.z();
673 double edge1x = v1x - v0x, edge1y = v1y - v0y, edge1z = v1z - v0z;
674 double edge2x = v2x - v0x, edge2y = v2y - v0y, edge2z = v2z - v0z;
676 double hx = dirY * edge2z - dirZ * edge2y;
677 double hy = dirZ * edge2x - dirX * edge2z;
678 double hz = dirX * edge2y - dirY * edge2x;
680 double a = edge1x * hx + edge1y * hy + edge1z * hz;
681 if (std::abs(a) < 1e-18)
continue;
684 double sx = originX - v0x, sy = originY - v0y, sz = originZ - v0z;
685 double u = f * (sx * hx + sy * hy + sz * hz);
686 if (u < -1e-7 || u > 1.0000001)
continue;
688 double qx = sy * edge1z - sz * edge1y;
689 double qy = sz * edge1x - sx * edge1z;
690 double qz = sx * edge1y - sy * edge1x;
692 double v = f * (dirX * qx + dirY * qy + dirZ * qz);
693 if (v < -1e-7 || u + v > 1.0000001)
continue;
695 double t = f * (edge2x * qx + edge2y * qy + edge2z * qz);
696 if (t > 1e-7 && t < closestDist) {
702 constexpr double tol = 0.25;
704 if (u >= -tol && v >= -tol && u + v <= 1.0 + tol) {
710 double hitX = originX + t * dirX;
711 double hitY = originY + t * dirY;
712 double hitZ = originZ + t * dirZ;
714 double d0 = (v0x - hitX)*(v0x - hitX) + (v0y - hitY)*(v0y - hitY) + (v0z - hitZ)*(v0z - hitZ);
715 double d1 = (v1x - hitX)*(v1x - hitX) + (v1y - hitY)*(v1y - hitY) + (v1z - hitZ)*(v1z - hitZ);
716 double d2 = (v2x - hitX)*(v2x - hitX) + (v2y - hitY)*(v2y - hitY) + (v2z - hitZ)*(v2z - hitZ);
718 if (d0 < d1 && d0 < d2) closestVert = i0;
719 else if (d1 < d2) closestVert = i1;
720 else closestVert = i2;
726 dist =
static_cast<float>(closestDist);
727 vertexIdx = closestVert;
738 if (!m_hasAnnotation || vertexIdx < 0 || vertexIdx >= m_vertexData.size()) {
742 const Eigen::VectorXi &vertices = m_annotation.getVertices();
743 const Eigen::VectorXi &labelIds = m_annotation.getLabelIds();
750 for (
int i = 0; i < vertices.rows(); ++i) {
751 if (vertices(i) == vertexIdx) {
752 labelId = labelIds(i);
757 if (labelId == -1)
return "Unknown";
761 if (ct.
table(i, 4) == labelId) {
764 while (!name.isEmpty() && (name.endsWith(
'\0') || name.endsWith(
' '))) {
776 if (!m_hasAnnotation || vertexIdx < 0)
return -1;
778 const Eigen::VectorXi &vertices = m_annotation.getVertices();
779 const Eigen::VectorXi &labelIds = m_annotation.
getLabelIds();
781 for (
int i = 0; i < vertices.rows(); ++i) {
782 if (vertices(i) == vertexIdx) {
792 if (m_selectedRegionId != regionId) {
793 m_selectedRegionId = regionId;
794 updateVertexColors();
801 if (m_selected != selected) {
802 m_selected = selected;
803 updateVertexColors();
810 if (m_selectedVertexStart != start || m_selectedVertexCount != count) {
811 m_selectedVertexStart = start;
812 m_selectedVertexCount = count;
813 updateVertexColors();
BrainSurface class declaration.
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
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 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)
void fromSurface(const FSLIB::Surface &surf)
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 updateBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
Eigen::MatrixX3f vertexPositions() const
int getAnnotationLabelId(int vertexIdx) const
void applyTransform(const QMatrix4x4 &m)
void setSelected(bool selected)
void addAnnotation(const FSLIB::Annotation &annotation)
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, Annotation &p_Annotation)
Vertices label based lookup table.
Eigen::VectorXi getLabelIds() const
const Eigen::MatrixX3f & nn() const
const Eigen::MatrixX3f & rr() const
const Eigen::VectorXf & curv() const
const Eigen::MatrixX3i & tris() const
static Eigen::MatrixX3f compute_normals(const Eigen::MatrixX3f &rr, const Eigen::MatrixX3i &tris)
BEM surface provides geometry information.