v2.0.0
Loading...
Searching...
No Matches
mne_layout.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "mne_layout.h"
18
19//=============================================================================================================
20// QT INCLUDES
21//=============================================================================================================
22
23#include <QFile>
24#include <QRegularExpression>
25#include <QTextStream>
26
27//=============================================================================================================
28// STL INCLUDES
29//=============================================================================================================
30
31#include <algorithm>
32#include <cmath>
33
34//=============================================================================================================
35// USED NAMESPACES
36//=============================================================================================================
37
38using namespace MNELIB;
39
40//=============================================================================================================
41// DEFINE STATIC METHODS
42//=============================================================================================================
43
44namespace
45{
46
47bool isInside(const MNELayoutPort& port, const QRectF& area)
48{
49 return port.xmin >= area.left() && port.xmax <= area.right() && port.ymin >= area.top() && port.ymax <= area.bottom();
50}
51
52} // namespace
53
54//=============================================================================================================
55// DEFINE MEMBER METHODS
56//=============================================================================================================
57
58QString MNELayoutPort::cleanName(const QString& name)
59{
60 return name.section('-', 0, 0).remove(' ').toLower();
61}
62
63//=============================================================================================================
64
65bool MNELayoutPort::contains(const QString& channel) const
66{
67 return names.contains(cleanName(channel));
68}
69
70//=============================================================================================================
71
72std::optional<MNELayout> MNELayout::read(const QString& path)
73{
74 QFile file(path);
75 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
76 qWarning("MNELayout::read - Cannot open %s", qPrintable(path));
77 return std::nullopt;
78 }
79
80 MNELayout layout;
81 layout.fileName = path;
82 bool haveExtent = false;
83 QTextStream in(&file);
84 static const QRegularExpression whitespace(QStringLiteral("\\s+"));
85 while (!in.atEnd()) {
86 const QString line = in.readLine().trimmed();
87 if (line.isEmpty() || line.startsWith('#')) {
88 continue;
89 }
90 QStringList fields = line.split(whitespace);
91 if (!haveExtent) {
92 bool ok[4];
93 const double v[4] = {fields.value(0).toDouble(&ok[0]), fields.value(1).toDouble(&ok[1]), fields.value(2).toDouble(&ok[2]), fields.value(3).toDouble(&ok[3])};
94 if (!(ok[0] && ok[1] && ok[2] && ok[3])) {
95 qWarning("MNELayout::read - Illegal extent in %s", qPrintable(path));
96 return std::nullopt;
97 }
98 layout.m_extent = QRectF(QPointF(v[0], v[2]), QPointF(v[1], v[3]));
99 haveExtent = true;
100 continue;
101 }
102 bool ok[5];
103 MNELayoutPort port;
104 port.portno = fields.value(0).toInt(&ok[0]);
105 const double x0 = fields.value(1).toDouble(&ok[1]);
106 const double y0 = fields.value(2).toDouble(&ok[2]);
107 const double w = fields.value(3).toDouble(&ok[3]);
108 const double h = fields.value(4).toDouble(&ok[4]);
109 if (fields.size() < 5 || !(ok[0] && ok[1] && ok[2] && ok[3] && ok[4])) {
110 qWarning("MNELayout::read - Bad layout item in %s: %s", qPrintable(path), qPrintable(line));
111 return std::nullopt;
112 }
113 port.xmin = static_cast<float>(x0);
114 port.xmax = static_cast<float>(x0 + std::fabs(w));
115 port.ymin = static_cast<float>(y0);
116 port.ymax = static_cast<float>(y0 + std::fabs(h));
117 port.invert = h < 0;
118 // The names are the rest of the line and may contain spaces ("MEG 0113:MEG 0112").
119 port.label = line.section(whitespace, 5).trimmed();
120 for (const QString& name : port.label.split(':', Qt::SkipEmptyParts)) {
121 port.names << MNELayoutPort::cleanName(name);
122 }
123 layout.ports.push_back(port);
124 }
125 if (!haveExtent) {
126 qWarning("MNELayout::read - No extent in %s", qPrintable(path));
127 return std::nullopt;
128 }
129 layout.m_visible = layout.m_extent;
130 return layout;
131}
132
133//=============================================================================================================
134
135QMap<QString, QPointF> MNELayout::channelPositions() const
136{
137 QMap<QString, QPointF> positions;
138 for (const MNELayoutPort& port : ports) {
139 for (const QString& name : port.label.split(':', Qt::SkipEmptyParts)) {
140 positions.insert(name.trimmed(), QPointF(port.xmin, port.ymin));
141 }
142 }
143 return positions;
144}
145
146//=============================================================================================================
147
148int MNELayout::matchPorts(const QString& channel)
149{
150 int nmatch = 0;
151 for (MNELayoutPort& port : ports) {
152 port.matched = !channel.isEmpty() && port.contains(channel) && isVisible(port);
153 nmatch += port.matched ? 1 : 0;
154 }
155 return nmatch;
156}
157
158//=============================================================================================================
159
160int MNELayout::matchPorts(const QStringList& channels)
161{
162 m_matches = Eigen::MatrixXi::Zero(static_cast<Eigen::Index>(ports.size()), channels.size());
163 int nmatch = 0;
164 for (std::size_t p = 0; p < ports.size(); ++p) {
165 MNELayoutPort& port = ports[p];
166 port.matched = false;
167 if (!isVisible(port)) {
168 continue;
169 }
170 for (int c = 0; c < channels.size(); ++c) {
171 if (port.contains(channels[c])) {
172 m_matches(static_cast<Eigen::Index>(p), c) = 1;
173 port.matched = true;
174 ++nmatch;
175 }
176 }
177 }
178 return nmatch;
179}
180
181//=============================================================================================================
182
183int MNELayout::portAt(const QPointF& point) const
184{
185 const auto it = std::find_if(ports.cbegin(), ports.cend(), [&point](const MNELayoutPort& port) {
186 return point.x() > port.xmin && point.x() < port.xmax && point.y() > port.ymin && point.y() < port.ymax;
187 });
188 return it == ports.cend() ? -1 : static_cast<int>(it - ports.cbegin());
189}
190
191//=============================================================================================================
192
193int MNELayout::confine(const QRectF& area)
194{
195 const QRectF requested = area.normalized();
196 QRectF covered;
197 int nmatch = 0;
198 for (const MNELayoutPort& port : ports) {
199 if (isInside(port, requested)) {
200 covered = nmatch == 0 ? port.rect() : covered.united(port.rect());
201 ++nmatch;
202 }
203 }
204 if (nmatch > 0) {
205 const double margin = 0.01 * covered.width();
206 m_visible = covered.adjusted(-margin, -margin, margin, margin);
207 }
208 return nmatch;
209}
210
211//=============================================================================================================
212
213bool MNELayout::isVisible(const MNELayoutPort& port) const
214{
215 return isInside(port, m_visible);
216}
2-D plotter layout read from an MNE-C .lout file: viewports, channel matching and zoom.
Core MNE data structures (source spaces, source estimates, hemispheres).
Plotter layout with viewports, channel matching and zoom.
Definition mne_layout.h:54
int matchPorts(const QString &channel)
int portAt(const QPointF &point) const
bool isVisible(const MNELayoutPort &port) const
static std::optional< MNELayout > read(const QString &path)
int confine(const QRectF &area)
std::vector< MNELayoutPort > ports
Definition mne_layout.h:170
QMap< QString, QPointF > channelPositions() const
Single viewport of a plotter layout (one line of an MNE-C .lout file).
bool contains(const QString &channel) const
static QString cleanName(const QString &name)