annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 1346:75ad55315db4 3.0-integration

More work on getting tests (especially file encoding ones) running on Windows. Various problems here to do with interaction with test filenames in Hg repos
author Chris Cannam
date Fri, 06 Jan 2017 15:44:55 +0000
parents 2ff5e411151d
children 667e369cfeab
rev   line source
Chris@152 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@152 2
Chris@152 3 /*
Chris@152 4 Sonic Visualiser
Chris@152 5 An audio file viewer and annotation editor.
Chris@152 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@152 8
Chris@152 9 This program is free software; you can redistribute it and/or
Chris@152 10 modify it under the terms of the GNU General Public License as
Chris@152 11 published by the Free Software Foundation; either version 2 of the
Chris@152 12 License, or (at your option) any later version. See the file
Chris@152 13 COPYING included with this distribution for more information.
Chris@152 14 */
Chris@152 15
Chris@152 16 #include "EditableDenseThreeDimensionalModel.h"
Chris@152 17
Chris@478 18 #include "base/LogRange.h"
Chris@478 19
Chris@152 20 #include <QTextStream>
Chris@387 21 #include <QStringList>
Chris@536 22 #include <QReadLocker>
Chris@536 23 #include <QWriteLocker>
Chris@387 24
Chris@181 25 #include <iostream>
Chris@181 26
Chris@256 27 #include <cmath>
Chris@534 28 #include <cassert>
Chris@256 29
Chris@1044 30 using std::vector;
Chris@1044 31
Chris@607 32 #include "system/System.h"
Chris@607 33
Chris@1040 34 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(sv_samplerate_t sampleRate,
Chris@929 35 int resolution,
Chris@929 36 int yBinCount,
Chris@535 37 CompressionType compression,
Chris@152 38 bool notifyOnAdd) :
Chris@611 39 m_startFrame(0),
Chris@152 40 m_sampleRate(sampleRate),
Chris@152 41 m_resolution(resolution),
Chris@152 42 m_yBinCount(yBinCount),
Chris@535 43 m_compression(compression),
Chris@152 44 m_minimum(0.0),
Chris@152 45 m_maximum(0.0),
Chris@256 46 m_haveExtents(false),
Chris@152 47 m_notifyOnAdd(notifyOnAdd),
Chris@152 48 m_sinceLastNotifyMin(-1),
Chris@152 49 m_sinceLastNotifyMax(-1),
Chris@152 50 m_completion(100)
Chris@152 51 {
Chris@152 52 }
Chris@152 53
Chris@152 54 bool
Chris@152 55 EditableDenseThreeDimensionalModel::isOK() const
Chris@152 56 {
Chris@152 57 return true;
Chris@152 58 }
Chris@152 59
Chris@1040 60 sv_samplerate_t
Chris@152 61 EditableDenseThreeDimensionalModel::getSampleRate() const
Chris@152 62 {
Chris@152 63 return m_sampleRate;
Chris@152 64 }
Chris@152 65
Chris@1038 66 sv_frame_t
Chris@152 67 EditableDenseThreeDimensionalModel::getStartFrame() const
Chris@152 68 {
Chris@611 69 return m_startFrame;
Chris@611 70 }
Chris@611 71
Chris@611 72 void
Chris@1038 73 EditableDenseThreeDimensionalModel::setStartFrame(sv_frame_t f)
Chris@611 74 {
Chris@611 75 m_startFrame = f;
Chris@152 76 }
Chris@152 77
Chris@1038 78 sv_frame_t
Chris@152 79 EditableDenseThreeDimensionalModel::getEndFrame() const
Chris@152 80 {
Chris@152 81 return m_resolution * m_data.size() + (m_resolution - 1);
Chris@152 82 }
Chris@152 83
Chris@929 84 int
Chris@152 85 EditableDenseThreeDimensionalModel::getResolution() const
Chris@152 86 {
Chris@152 87 return m_resolution;
Chris@152 88 }
Chris@152 89
Chris@152 90 void
Chris@929 91 EditableDenseThreeDimensionalModel::setResolution(int sz)
Chris@152 92 {
Chris@152 93 m_resolution = sz;
Chris@152 94 }
Chris@152 95
Chris@929 96 int
Chris@182 97 EditableDenseThreeDimensionalModel::getWidth() const
Chris@182 98 {
Chris@1154 99 return int(m_data.size());
Chris@182 100 }
Chris@182 101
Chris@929 102 int
Chris@182 103 EditableDenseThreeDimensionalModel::getHeight() const
Chris@152 104 {
Chris@152 105 return m_yBinCount;
Chris@152 106 }
Chris@152 107
Chris@152 108 void
Chris@929 109 EditableDenseThreeDimensionalModel::setHeight(int sz)
Chris@152 110 {
Chris@152 111 m_yBinCount = sz;
Chris@152 112 }
Chris@152 113
Chris@152 114 float
Chris@152 115 EditableDenseThreeDimensionalModel::getMinimumLevel() const
Chris@152 116 {
Chris@152 117 return m_minimum;
Chris@152 118 }
Chris@152 119
Chris@152 120 void
Chris@152 121 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
Chris@152 122 {
Chris@152 123 m_minimum = level;
Chris@152 124 }
Chris@152 125
Chris@152 126 float
Chris@152 127 EditableDenseThreeDimensionalModel::getMaximumLevel() const
Chris@152 128 {
Chris@152 129 return m_maximum;
Chris@152 130 }
Chris@152 131
Chris@152 132 void
Chris@152 133 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
Chris@152 134 {
Chris@152 135 m_maximum = level;
Chris@152 136 }
Chris@152 137
Chris@533 138 EditableDenseThreeDimensionalModel::Column
Chris@929 139 EditableDenseThreeDimensionalModel::getColumn(int index) const
Chris@152 140 {
Chris@536 141 QReadLocker locker(&m_lock);
Chris@1154 142 if (in_range_for(m_data, index)) return expandAndRetrieve(index);
Chris@1154 143 else return Column();
Chris@152 144 }
Chris@152 145
Chris@152 146 float
Chris@929 147 EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const
Chris@152 148 {
Chris@534 149 Column c = getColumn(index);
Chris@1154 150 if (in_range_for(c, n)) return c.at(n);
Chris@534 151 return m_minimum;
Chris@534 152 }
Chris@152 153
Chris@535 154 //static int given = 0, stored = 0;
Chris@534 155
Chris@534 156 void
Chris@929 157 EditableDenseThreeDimensionalModel::truncateAndStore(int index,
Chris@534 158 const Column &values)
Chris@534 159 {
Chris@1154 160 assert(in_range_for(m_data, index));
Chris@534 161
Chris@843 162 //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
Chris@534 163
Chris@535 164 // The default case is to store the entire column at m_data[index]
Chris@535 165 // and place 0 at m_trunc[index] to indicate that it has not been
Chris@535 166 // truncated. We only do clever stuff if one of the clever-stuff
Chris@535 167 // tests works out.
Chris@535 168
Chris@534 169 m_trunc[index] = 0;
Chris@535 170 if (index == 0 ||
Chris@535 171 m_compression == NoCompression ||
Chris@1154 172 int(values.size()) != m_yBinCount) {
Chris@535 173 // given += values.size();
Chris@535 174 // stored += values.size();
Chris@534 175 m_data[index] = values;
Chris@534 176 return;
Chris@152 177 }
Chris@152 178
Chris@535 179 // Maximum distance between a column and the one we refer to as
Chris@535 180 // the source of its truncated values. Limited by having to fit
Chris@535 181 // in a signed char, but in any case small values are usually
Chris@535 182 // better
Chris@535 183 static int maxdist = 6;
Chris@534 184
Chris@535 185 bool known = false; // do we know whether to truncate at top or bottom?
Chris@535 186 bool top = false; // if we do know, will we truncate at top?
Chris@534 187
Chris@535 188 // If the previous column is not truncated, then it is the only
Chris@535 189 // candidate for comparison. If it is truncated, then the column
Chris@535 190 // that it refers to is the only candidate. Either way, we only
Chris@535 191 // have one possible column to compare against here, and we are
Chris@535 192 // being careful to ensure it is not a truncated one (to avoid
Chris@535 193 // doing more work recursively when uncompressing).
Chris@534 194 int tdist = 1;
Chris@534 195 int ptrunc = m_trunc[index-1];
Chris@534 196 if (ptrunc < 0) {
Chris@534 197 top = false;
Chris@534 198 known = true;
Chris@534 199 tdist = -ptrunc + 1;
Chris@534 200 } else if (ptrunc > 0) {
Chris@534 201 top = true;
Chris@534 202 known = true;
Chris@534 203 tdist = ptrunc + 1;
Chris@534 204 }
Chris@534 205
Chris@534 206 Column p = expandAndRetrieve(index - tdist);
Chris@534 207 int h = m_yBinCount;
Chris@534 208
Chris@1154 209 if (int(p.size()) == h && tdist <= maxdist) {
Chris@534 210
Chris@534 211 int bcount = 0, tcount = 0;
Chris@534 212 if (!known || !top) {
Chris@535 213 // count how many identical values there are at the bottom
Chris@534 214 for (int i = 0; i < h; ++i) {
Chris@534 215 if (values.at(i) == p.at(i)) ++bcount;
Chris@534 216 else break;
Chris@534 217 }
Chris@534 218 }
Chris@534 219 if (!known || top) {
Chris@535 220 // count how many identical values there are at the top
Chris@534 221 for (int i = h; i > 0; --i) {
Chris@534 222 if (values.at(i-1) == p.at(i-1)) ++tcount;
Chris@534 223 else break;
Chris@534 224 }
Chris@534 225 }
Chris@534 226 if (!known) top = (tcount > bcount);
Chris@534 227
Chris@535 228 int limit = h / 4; // don't bother unless we have at least this many
Chris@534 229 if ((top ? tcount : bcount) > limit) {
Chris@534 230
Chris@534 231 if (!top) {
Chris@535 232 // create a new column with h - bcount values from bcount up
Chris@534 233 Column tcol(h - bcount);
Chris@535 234 // given += values.size();
Chris@535 235 // stored += h - bcount;
Chris@534 236 for (int i = bcount; i < h; ++i) {
Chris@534 237 tcol[i - bcount] = values.at(i);
Chris@534 238 }
Chris@534 239 m_data[index] = tcol;
Chris@1038 240 m_trunc[index] = (signed char)(-tdist);
Chris@534 241 return;
Chris@534 242 } else {
Chris@535 243 // create a new column with h - tcount values from 0 up
Chris@534 244 Column tcol(h - tcount);
Chris@535 245 // given += values.size();
Chris@535 246 // stored += h - tcount;
Chris@534 247 for (int i = 0; i < h - tcount; ++i) {
Chris@534 248 tcol[i] = values.at(i);
Chris@534 249 }
Chris@534 250 m_data[index] = tcol;
Chris@1038 251 m_trunc[index] = (signed char)(tdist);
Chris@534 252 return;
Chris@534 253 }
Chris@534 254 }
Chris@534 255 }
Chris@534 256
Chris@535 257 // given += values.size();
Chris@535 258 // stored += values.size();
Chris@843 259 // cout << "given: " << given << ", stored: " << stored << " ("
Chris@843 260 // << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
Chris@534 261
Chris@535 262 // default case if nothing wacky worked out
Chris@534 263 m_data[index] = values;
Chris@534 264 return;
Chris@534 265 }
Chris@534 266
Chris@534 267 EditableDenseThreeDimensionalModel::Column
Chris@1252 268 EditableDenseThreeDimensionalModel::rightHeight(const Column &c) const
Chris@1252 269 {
Chris@1252 270 if (int(c.size()) == m_yBinCount) return c;
Chris@1252 271 else {
Chris@1252 272 Column cc(c);
Chris@1252 273 cc.resize(m_yBinCount, 0.0);
Chris@1252 274 return cc;
Chris@1252 275 }
Chris@1252 276 }
Chris@1252 277
Chris@1252 278 EditableDenseThreeDimensionalModel::Column
Chris@929 279 EditableDenseThreeDimensionalModel::expandAndRetrieve(int index) const
Chris@534 280 {
Chris@535 281 // See comment above m_trunc declaration in header
Chris@535 282
Chris@941 283 assert(index >= 0 && index < int(m_data.size()));
Chris@534 284 Column c = m_data.at(index);
Chris@534 285 if (index == 0) {
Chris@1252 286 return rightHeight(c);
Chris@534 287 }
Chris@534 288 int trunc = (int)m_trunc[index];
Chris@534 289 if (trunc == 0) {
Chris@1252 290 return rightHeight(c);
Chris@534 291 }
Chris@534 292 bool top = true;
Chris@534 293 int tdist = trunc;
Chris@534 294 if (trunc < 0) { top = false; tdist = -trunc; }
Chris@534 295 Column p = expandAndRetrieve(index - tdist);
Chris@1154 296 int psize = int(p.size()), csize = int(c.size());
Chris@941 297 if (psize != m_yBinCount) {
Chris@843 298 cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl;
Chris@534 299 }
Chris@534 300 if (top) {
Chris@537 301 for (int i = csize; i < psize; ++i) {
Chris@534 302 c.push_back(p.at(i));
Chris@534 303 }
Chris@534 304 } else {
Chris@593 305 Column cc(psize);
Chris@593 306 for (int i = 0; i < psize - csize; ++i) {
Chris@593 307 cc[i] = p.at(i);
Chris@534 308 }
Chris@593 309 for (int i = 0; i < csize; ++i) {
Chris@593 310 cc[i + (psize - csize)] = c.at(i);
Chris@593 311 }
Chris@593 312 return cc;
Chris@534 313 }
Chris@534 314 return c;
Chris@152 315 }
Chris@152 316
Chris@152 317 void
Chris@929 318 EditableDenseThreeDimensionalModel::setColumn(int index,
Chris@182 319 const Column &values)
Chris@152 320 {
Chris@536 321 QWriteLocker locker(&m_lock);
Chris@152 322
Chris@1154 323 while (index >= int(m_data.size())) {
Chris@182 324 m_data.push_back(Column());
Chris@534 325 m_trunc.push_back(0);
Chris@152 326 }
Chris@152 327
Chris@152 328 bool allChange = false;
Chris@152 329
Chris@1154 330 for (int i = 0; in_range_for(values, i); ++i) {
Chris@256 331 float value = values[i];
Chris@606 332 if (ISNAN(value) || ISINF(value)) {
Chris@256 333 continue;
Chris@256 334 }
Chris@256 335 if (!m_haveExtents || value < m_minimum) {
Chris@256 336 m_minimum = value;
Chris@152 337 allChange = true;
Chris@152 338 }
Chris@256 339 if (!m_haveExtents || value > m_maximum) {
Chris@256 340 m_maximum = value;
Chris@152 341 allChange = true;
Chris@152 342 }
Chris@256 343 m_haveExtents = true;
Chris@152 344 }
Chris@152 345
Chris@534 346 truncateAndStore(index, values);
Chris@534 347
Chris@593 348 // assert(values == expandAndRetrieve(index));
Chris@152 349
Chris@1110 350 sv_frame_t windowStart = index;
Chris@182 351 windowStart *= m_resolution;
Chris@182 352
Chris@152 353 if (m_notifyOnAdd) {
Chris@152 354 if (allChange) {
Chris@152 355 emit modelChanged();
Chris@152 356 } else {
Chris@931 357 emit modelChangedWithin(windowStart, windowStart + m_resolution);
Chris@152 358 }
Chris@152 359 } else {
Chris@152 360 if (allChange) {
Chris@152 361 m_sinceLastNotifyMin = -1;
Chris@152 362 m_sinceLastNotifyMax = -1;
Chris@152 363 emit modelChanged();
Chris@152 364 } else {
Chris@152 365 if (m_sinceLastNotifyMin == -1 ||
Chris@152 366 windowStart < m_sinceLastNotifyMin) {
Chris@152 367 m_sinceLastNotifyMin = windowStart;
Chris@152 368 }
Chris@152 369 if (m_sinceLastNotifyMax == -1 ||
Chris@152 370 windowStart > m_sinceLastNotifyMax) {
Chris@152 371 m_sinceLastNotifyMax = windowStart;
Chris@152 372 }
Chris@152 373 }
Chris@152 374 }
Chris@152 375 }
Chris@152 376
Chris@152 377 QString
Chris@929 378 EditableDenseThreeDimensionalModel::getBinName(int n) const
Chris@152 379 {
Chris@939 380 if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
Chris@152 381 else return "";
Chris@152 382 }
Chris@152 383
Chris@152 384 void
Chris@929 385 EditableDenseThreeDimensionalModel::setBinName(int n, QString name)
Chris@152 386 {
Chris@929 387 while ((int)m_binNames.size() <= n) m_binNames.push_back("");
Chris@152 388 m_binNames[n] = name;
Chris@152 389 emit modelChanged();
Chris@152 390 }
Chris@152 391
Chris@152 392 void
Chris@152 393 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
Chris@152 394 {
Chris@152 395 m_binNames = names;
Chris@152 396 emit modelChanged();
Chris@152 397 }
Chris@152 398
Chris@478 399 bool
Chris@886 400 EditableDenseThreeDimensionalModel::hasBinValues() const
Chris@886 401 {
Chris@886 402 return !m_binValues.empty();
Chris@886 403 }
Chris@886 404
Chris@886 405 float
Chris@929 406 EditableDenseThreeDimensionalModel::getBinValue(int n) const
Chris@886 407 {
Chris@929 408 if (n < (int)m_binValues.size()) return m_binValues[n];
Chris@886 409 else return 0.f;
Chris@886 410 }
Chris@886 411
Chris@886 412 void
Chris@886 413 EditableDenseThreeDimensionalModel::setBinValues(std::vector<float> values)
Chris@886 414 {
Chris@886 415 m_binValues = values;
Chris@886 416 }
Chris@886 417
Chris@886 418 QString
Chris@886 419 EditableDenseThreeDimensionalModel::getBinValueUnit() const
Chris@886 420 {
Chris@886 421 return m_binValueUnit;
Chris@886 422 }
Chris@886 423
Chris@886 424 void
Chris@886 425 EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit)
Chris@886 426 {
Chris@886 427 m_binValueUnit = unit;
Chris@886 428 }
Chris@886 429
Chris@886 430 bool
Chris@478 431 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
Chris@478 432 {
Chris@536 433 QReadLocker locker(&m_lock);
Chris@534 434
Chris@1044 435 vector<double> sample;
Chris@1044 436 vector<int> n;
Chris@478 437
Chris@478 438 for (int i = 0; i < 10; ++i) {
Chris@929 439 int index = i * 10;
Chris@1154 440 if (in_range_for(m_data, index)) {
Chris@533 441 const Column &c = m_data.at(index);
Chris@1154 442 while (c.size() > sample.size()) {
Chris@1044 443 sample.push_back(0.0);
Chris@478 444 n.push_back(0);
Chris@478 445 }
Chris@1154 446 for (int j = 0; in_range_for(c, j); ++j) {
Chris@533 447 sample[j] += c.at(j);
Chris@478 448 ++n[j];
Chris@478 449 }
Chris@478 450 }
Chris@478 451 }
Chris@478 452
Chris@478 453 if (sample.empty()) return false;
Chris@1044 454 for (decltype(sample)::size_type j = 0; j < sample.size(); ++j) {
Chris@1044 455 if (n[j]) sample[j] /= n[j];
Chris@478 456 }
Chris@478 457
Chris@1044 458 return LogRange::useLogScale(sample);
Chris@478 459 }
Chris@478 460
Chris@152 461 void
Chris@333 462 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
Chris@152 463 {
Chris@152 464 if (m_completion != completion) {
Chris@152 465 m_completion = completion;
Chris@152 466
Chris@152 467 if (completion == 100) {
Chris@152 468
Chris@152 469 m_notifyOnAdd = true; // henceforth
Chris@152 470 emit modelChanged();
Chris@152 471
Chris@152 472 } else if (!m_notifyOnAdd) {
Chris@152 473
Chris@333 474 if (update &&
Chris@333 475 m_sinceLastNotifyMin >= 0 &&
Chris@152 476 m_sinceLastNotifyMax >= 0) {
Chris@931 477 emit modelChangedWithin(m_sinceLastNotifyMin,
Chris@931 478 m_sinceLastNotifyMax + m_resolution);
Chris@152 479 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@152 480 } else {
Chris@152 481 emit completionChanged();
Chris@152 482 }
Chris@152 483 } else {
Chris@152 484 emit completionChanged();
Chris@152 485 }
Chris@152 486 }
Chris@152 487 }
Chris@152 488
Chris@318 489 QString
Chris@318 490 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const
Chris@318 491 {
Chris@536 492 QReadLocker locker(&m_lock);
Chris@318 493 QString s;
Chris@1154 494 for (int i = 0; in_range_for(m_data, i); ++i) {
Chris@318 495 QStringList list;
Chris@1154 496 for (int j = 0; in_range_for(m_data.at(i), j); ++j) {
Chris@533 497 list << QString("%1").arg(m_data.at(i).at(j));
Chris@318 498 }
Chris@318 499 s += list.join(delimiter) + "\n";
Chris@318 500 }
Chris@318 501 return s;
Chris@318 502 }
Chris@318 503
Chris@838 504 QString
Chris@1038 505 EditableDenseThreeDimensionalModel::toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const
Chris@838 506 {
Chris@838 507 QReadLocker locker(&m_lock);
Chris@838 508 QString s;
Chris@1154 509 for (int i = 0; in_range_for(m_data, i); ++i) {
Chris@1038 510 sv_frame_t fr = m_startFrame + i * m_resolution;
Chris@1038 511 if (fr >= f0 && fr < f1) {
Chris@838 512 QStringList list;
Chris@1154 513 for (int j = 0; in_range_for(m_data.at(i), j); ++j) {
Chris@838 514 list << QString("%1").arg(m_data.at(i).at(j));
Chris@838 515 }
Chris@838 516 s += list.join(delimiter) + "\n";
Chris@838 517 }
Chris@838 518 }
Chris@838 519 return s;
Chris@838 520 }
Chris@838 521
Chris@152 522 void
Chris@152 523 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
Chris@314 524 QString indent,
Chris@314 525 QString extraAttributes) const
Chris@152 526 {
Chris@536 527 QReadLocker locker(&m_lock);
Chris@534 528
Chris@152 529 // For historical reasons we read and write "resolution" as "windowSize"
Chris@152 530
Chris@690 531 SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
Chris@318 532
Chris@314 533 Model::toXml
Chris@314 534 (out, indent,
Chris@611 535 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
Chris@152 536 .arg(m_resolution)
Chris@152 537 .arg(m_yBinCount)
Chris@152 538 .arg(m_minimum)
Chris@152 539 .arg(m_maximum)
Chris@152 540 .arg(getObjectExportId(&m_data))
Chris@611 541 .arg(m_startFrame)
Chris@152 542 .arg(extraAttributes));
Chris@152 543
Chris@152 544 out << indent;
Chris@152 545 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
Chris@152 546 .arg(getObjectExportId(&m_data));
Chris@152 547
Chris@929 548 for (int i = 0; i < (int)m_binNames.size(); ++i) {
Chris@152 549 if (m_binNames[i] != "") {
Chris@152 550 out << indent + " ";
Chris@152 551 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
Chris@152 552 .arg(i).arg(m_binNames[i]);
Chris@152 553 }
Chris@152 554 }
Chris@152 555
Chris@929 556 for (int i = 0; i < (int)m_data.size(); ++i) {
Chris@152 557 out << indent + " ";
Chris@152 558 out << QString("<row n=\"%1\">").arg(i);
Chris@929 559 for (int j = 0; j < (int)m_data.at(i).size(); ++j) {
Chris@152 560 if (j > 0) out << " ";
Chris@533 561 out << m_data.at(i).at(j);
Chris@152 562 }
Chris@152 563 out << QString("</row>\n");
Chris@318 564 out.flush();
Chris@152 565 }
Chris@152 566
Chris@152 567 out << indent + "</dataset>\n";
Chris@152 568 }
Chris@152 569
Chris@152 570