70 QHash<QString, int> leadIndex;
71 QVector<QVector<QPair<int, int>>> members;
72 for (
int i = 0; i < m_contacts.size(); ++i) {
74 const QRegularExpressionMatch m = splitName(contact.
name);
75 if (contact.
lead.isEmpty())
76 contact.
lead = (m.hasMatch() && !m.captured(1).isEmpty()) ? m.captured(1) : contact.
name;
77 if (!leadIndex.contains(contact.
lead)) {
78 leadIndex.insert(contact.
lead,
int(m_leads.size()));
82 members.append(QVector<QPair<int, int>>());
84 members[leadIndex.value(contact.
lead)].append({m.hasMatch() ? m.captured(2).toInt() : 0, i});
87 for (
int l = 0; l < m_leads.size(); ++l) {
88 QVector<QPair<int, int>>& lead = members[l];
89 std::stable_sort(lead.begin(), lead.end(), [](
const auto& a,
const auto& b) { return a.first < b.first; });
90 float spacing = std::numeric_limits<float>::max();
91 for (
int k = 0; k < lead.size(); ++k) {
92 m_leads[l].contacts.append(lead[k].second);
94 spacing = std::min(spacing, (m_contacts[lead[k].second].position - m_contacts[lead[k - 1].second].position).norm());
104 const QFileInfo info(electrodesTsv);
108 const QStringList candidates = info.dir().entryList({QStringLiteral(
"*_coordsystem.json")}, QDir::Files);
109 for (
const QString& candidate : candidates) {
111 while (prefix < candidate.size() && prefix < info.fileName().size() && candidate[prefix] == info.fileName()[prefix])
113 if (prefix > bestPrefix) {
115 coordPath = info.dir().filePath(candidate);
118 if (coordPath.isEmpty()) {
119 setError(error, QStringLiteral(
"no *_coordsystem.json next to %1, so the units are unknown").arg(electrodesTsv));
123 const float scale = coordSystem.
units == QLatin1String(
"m") ? 1.0f
124 : coordSystem.
units == QLatin1String(
"cm") ? 0.01f
125 : coordSystem.
units == QLatin1String(
"mm") ? 0.001f
128 setError(error, QStringLiteral(
"unsupported iEEGCoordinateUnits \"%1\" in %2").arg(coordSystem.
units, coordPath));
133 QHash<QString, QString> declaredTypes;
134 QHash<QString, QString> declaredDimensions;
136 bool okX =
false, okY =
false, okZ =
false;
137 const Eigen::Vector3f position(e.x.toFloat(&okX), e.y.toFloat(&okY), e.z.toFloat(&okZ));
138 if (!okX || !okY || !okZ)
141 contact.
name = e.name;
142 contact.
position = position * scale;
143 contact.
lead = e.group;
146 const QString key = e.group.isEmpty() ? e.name : e.group;
147 if (!e.type.isEmpty())
148 declaredTypes.insert(key, e.type.toLower());
149 if (!e.dimension.isEmpty())
150 declaredDimensions.insert(key, e.dimension);
153 setError(error, QStringLiteral(
"no contacts with coordinates in %1").arg(electrodesTsv));
158 static const QRegularExpression dimension(QStringLiteral(
"^\\[?\\s*(\\d+)\\s*x\\s*(\\d+)\\s*\\]?$"));
159 for (
int l = 0; l < implantation.m_leads.size(); ++l) {
160 const IeegLead lead = implantation.m_leads[l];
162 QString type = declaredTypes.value(lead.
name);
163 QString shape = declaredDimensions.value(lead.
name);
164 for (
const int c : std::as_const(lead.
contacts)) {
165 const QString& name = implantation.m_contacts[c].name;
167 type = declaredTypes.value(name);
169 shape = declaredDimensions.value(name);
171 const QRegularExpressionMatch m = dimension.match(shape);
172 const int rows = m.hasMatch() ? m.captured(1).toInt() : 0;
173 const int cols = m.hasMatch() ? m.captured(2).toInt() : 0;
175 if (type.contains(QLatin1String(
"depth")) || type.contains(QLatin1String(
"seeg")))
177 else if (type.contains(QLatin1String(
"strip")))
179 else if (type.contains(QLatin1String(
"grid")))