annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents 78fe29adfd16
children d484490cdf69
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@1701 60 bool
Chris@1701 61 EditableDenseThreeDimensionalModel::isReady(int *completion) const
Chris@1701 62 {
Chris@1701 63 if (completion) *completion = getCompletion();
Chris@1701 64 return true;
Chris@1701 65 }
Chris@1701 66
Chris@1040 67 sv_samplerate_t
Chris@152 68 EditableDenseThreeDimensionalModel::getSampleRate() const
Chris@152 69 {
Chris@152 70 return m_sampleRate;
Chris@152 71 }
Chris@152 72
Chris@1038 73 sv_frame_t
Chris@152 74 EditableDenseThreeDimensionalModel::getStartFrame() const
Chris@152 75 {
Chris@611 76 return m_startFrame;
Chris@611 77 }
Chris@611 78
Chris@611 79 void
Chris@1038 80 EditableDenseThreeDimensionalModel::setStartFrame(sv_frame_t f)
Chris@611 81 {
Chris@611 82 m_startFrame = f;
Chris@152 83 }
Chris@152 84
Chris@1038 85 sv_frame_t
Chris@1725 86 EditableDenseThreeDimensionalModel::getTrueEndFrame() const
Chris@152 87 {
Chris@152 88 return m_resolution * m_data.size() + (m_resolution - 1);
Chris@152 89 }
Chris@152 90
Chris@929 91 int
Chris@152 92 EditableDenseThreeDimensionalModel::getResolution() const
Chris@152 93 {
Chris@152 94 return m_resolution;
Chris@152 95 }
Chris@152 96
Chris@152 97 void
Chris@929 98 EditableDenseThreeDimensionalModel::setResolution(int sz)
Chris@152 99 {
Chris@152 100 m_resolution = sz;
Chris@152 101 }
Chris@152 102
Chris@929 103 int
Chris@182 104 EditableDenseThreeDimensionalModel::getWidth() const
Chris@182 105 {
Chris@1154 106 return int(m_data.size());
Chris@182 107 }
Chris@182 108
Chris@929 109 int
Chris@182 110 EditableDenseThreeDimensionalModel::getHeight() const
Chris@152 111 {
Chris@152 112 return m_yBinCount;
Chris@152 113 }
Chris@152 114
Chris@152 115 void
Chris@929 116 EditableDenseThreeDimensionalModel::setHeight(int sz)
Chris@152 117 {
Chris@152 118 m_yBinCount = sz;
Chris@152 119 }
Chris@152 120
Chris@152 121 float
Chris@152 122 EditableDenseThreeDimensionalModel::getMinimumLevel() const
Chris@152 123 {
Chris@152 124 return m_minimum;
Chris@152 125 }
Chris@152 126
Chris@152 127 void
Chris@152 128 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
Chris@152 129 {
Chris@152 130 m_minimum = level;
Chris@152 131 }
Chris@152 132
Chris@152 133 float
Chris@152 134 EditableDenseThreeDimensionalModel::getMaximumLevel() const
Chris@152 135 {
Chris@152 136 return m_maximum;
Chris@152 137 }
Chris@152 138
Chris@152 139 void
Chris@152 140 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
Chris@152 141 {
Chris@152 142 m_maximum = level;
Chris@152 143 }
Chris@152 144
Chris@533 145 EditableDenseThreeDimensionalModel::Column
Chris@929 146 EditableDenseThreeDimensionalModel::getColumn(int index) const
Chris@152 147 {
Chris@536 148 QReadLocker locker(&m_lock);
Chris@1154 149 if (in_range_for(m_data, index)) return expandAndRetrieve(index);
Chris@1154 150 else return Column();
Chris@152 151 }
Chris@152 152
Chris@152 153 float
Chris@929 154 EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const
Chris@152 155 {
Chris@534 156 Column c = getColumn(index);
Chris@1154 157 if (in_range_for(c, n)) return c.at(n);
Chris@534 158 return m_minimum;
Chris@534 159 }
Chris@152 160
Chris@535 161 //static int given = 0, stored = 0;
Chris@534 162
Chris@534 163 void
Chris@929 164 EditableDenseThreeDimensionalModel::truncateAndStore(int index,
Chris@534 165 const Column &values)
Chris@534 166 {
Chris@1154 167 assert(in_range_for(m_data, index));
Chris@534 168
Chris@843 169 //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
Chris@534 170
Chris@535 171 // The default case is to store the entire column at m_data[index]
Chris@535 172 // and place 0 at m_trunc[index] to indicate that it has not been
Chris@535 173 // truncated. We only do clever stuff if one of the clever-stuff
Chris@535 174 // tests works out.
Chris@535 175
Chris@534 176 m_trunc[index] = 0;
Chris@535 177 if (index == 0 ||
Chris@535 178 m_compression == NoCompression ||
Chris@1154 179 int(values.size()) != m_yBinCount) {
Chris@535 180 // given += values.size();
Chris@535 181 // stored += values.size();
Chris@534 182 m_data[index] = values;
Chris@534 183 return;
Chris@152 184 }
Chris@152 185
Chris@535 186 // Maximum distance between a column and the one we refer to as
Chris@535 187 // the source of its truncated values. Limited by having to fit
Chris@535 188 // in a signed char, but in any case small values are usually
Chris@535 189 // better
Chris@535 190 static int maxdist = 6;
Chris@534 191
Chris@535 192 bool known = false; // do we know whether to truncate at top or bottom?
Chris@535 193 bool top = false; // if we do know, will we truncate at top?
Chris@534 194
Chris@535 195 // If the previous column is not truncated, then it is the only
Chris@535 196 // candidate for comparison. If it is truncated, then the column
Chris@535 197 // that it refers to is the only candidate. Either way, we only
Chris@535 198 // have one possible column to compare against here, and we are
Chris@535 199 // being careful to ensure it is not a truncated one (to avoid
Chris@535 200 // doing more work recursively when uncompressing).
Chris@534 201 int tdist = 1;
Chris@534 202 int ptrunc = m_trunc[index-1];
Chris@534 203 if (ptrunc < 0) {
Chris@534 204 top = false;
Chris@534 205 known = true;
Chris@534 206 tdist = -ptrunc + 1;
Chris@534 207 } else if (ptrunc > 0) {
Chris@534 208 top = true;
Chris@534 209 known = true;
Chris@534 210 tdist = ptrunc + 1;
Chris@534 211 }
Chris@534 212
Chris@534 213 Column p = expandAndRetrieve(index - tdist);
Chris@534 214 int h = m_yBinCount;
Chris@534 215
Chris@1154 216 if (int(p.size()) == h && tdist <= maxdist) {
Chris@534 217
Chris@534 218 int bcount = 0, tcount = 0;
Chris@534 219 if (!known || !top) {
Chris@535 220 // count how many identical values there are at the bottom
Chris@534 221 for (int i = 0; i < h; ++i) {
Chris@534 222 if (values.at(i) == p.at(i)) ++bcount;
Chris@534 223 else break;
Chris@534 224 }
Chris@534 225 }
Chris@534 226 if (!known || top) {
Chris@535 227 // count how many identical values there are at the top
Chris@534 228 for (int i = h; i > 0; --i) {
Chris@534 229 if (values.at(i-1) == p.at(i-1)) ++tcount;
Chris@534 230 else break;
Chris@534 231 }
Chris@534 232 }
Chris@534 233 if (!known) top = (tcount > bcount);
Chris@534 234
Chris@535 235 int limit = h / 4; // don't bother unless we have at least this many
Chris@534 236 if ((top ? tcount : bcount) > limit) {
Chris@534 237
Chris@534 238 if (!top) {
Chris@535 239 // create a new column with h - bcount values from bcount up
Chris@534 240 Column tcol(h - bcount);
Chris@535 241 // given += values.size();
Chris@535 242 // stored += h - bcount;
Chris@534 243 for (int i = bcount; i < h; ++i) {
Chris@534 244 tcol[i - bcount] = values.at(i);
Chris@534 245 }
Chris@534 246 m_data[index] = tcol;
Chris@1038 247 m_trunc[index] = (signed char)(-tdist);
Chris@534 248 return;
Chris@534 249 } else {
Chris@535 250 // create a new column with h - tcount values from 0 up
Chris@534 251 Column tcol(h - tcount);
Chris@535 252 // given += values.size();
Chris@535 253 // stored += h - tcount;
Chris@534 254 for (int i = 0; i < h - tcount; ++i) {
Chris@534 255 tcol[i] = values.at(i);
Chris@534 256 }
Chris@534 257 m_data[index] = tcol;
Chris@1038 258 m_trunc[index] = (signed char)(tdist);
Chris@534 259 return;
Chris@534 260 }
Chris@534 261 }
Chris@534 262 }
Chris@534 263
Chris@535 264 // given += values.size();
Chris@535 265 // stored += values.size();
Chris@843 266 // cout << "given: " << given << ", stored: " << stored << " ("
Chris@843 267 // << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
Chris@534 268
Chris@535 269 // default case if nothing wacky worked out
Chris@534 270 m_data[index] = values;
Chris@534 271 return;
Chris@534 272 }
Chris@534 273
Chris@534 274 EditableDenseThreeDimensionalModel::Column
Chris@1252 275 EditableDenseThreeDimensionalModel::rightHeight(const Column &c) const
Chris@1252 276 {
Chris@1252 277 if (int(c.size()) == m_yBinCount) return c;
Chris@1252 278 else {
Chris@1252 279 Column cc(c);
Chris@1252 280 cc.resize(m_yBinCount, 0.0);
Chris@1252 281 return cc;
Chris@1252 282 }
Chris@1252 283 }
Chris@1252 284
Chris@1252 285 EditableDenseThreeDimensionalModel::Column
Chris@929 286 EditableDenseThreeDimensionalModel::expandAndRetrieve(int index) const
Chris@534 287 {
Chris@535 288 // See comment above m_trunc declaration in header
Chris@535 289
Chris@941 290 assert(index >= 0 && index < int(m_data.size()));
Chris@534 291 Column c = m_data.at(index);
Chris@534 292 if (index == 0) {
Chris@1252 293 return rightHeight(c);
Chris@534 294 }
Chris@534 295 int trunc = (int)m_trunc[index];
Chris@534 296 if (trunc == 0) {
Chris@1252 297 return rightHeight(c);
Chris@534 298 }
Chris@534 299 bool top = true;
Chris@534 300 int tdist = trunc;
Chris@534 301 if (trunc < 0) { top = false; tdist = -trunc; }
Chris@534 302 Column p = expandAndRetrieve(index - tdist);
Chris@1154 303 int psize = int(p.size()), csize = int(c.size());
Chris@941 304 if (psize != m_yBinCount) {
Chris@843 305 cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl;
Chris@534 306 }
Chris@534 307 if (top) {
Chris@537 308 for (int i = csize; i < psize; ++i) {
Chris@534 309 c.push_back(p.at(i));
Chris@534 310 }
Chris@534 311 } else {
Chris@593 312 Column cc(psize);
Chris@593 313 for (int i = 0; i < psize - csize; ++i) {
Chris@593 314 cc[i] = p.at(i);
Chris@534 315 }
Chris@593 316 for (int i = 0; i < csize; ++i) {
Chris@593 317 cc[i + (psize - csize)] = c.at(i);
Chris@593 318 }
Chris@593 319 return cc;
Chris@534 320 }
Chris@534 321 return c;
Chris@152 322 }
Chris@152 323
Chris@152 324 void
Chris@929 325 EditableDenseThreeDimensionalModel::setColumn(int index,
Chris@182 326 const Column &values)
Chris@152 327 {
Chris@536 328 QWriteLocker locker(&m_lock);
Chris@152 329
Chris@1154 330 while (index >= int(m_data.size())) {
Chris@1429 331 m_data.push_back(Column());
Chris@534 332 m_trunc.push_back(0);
Chris@152 333 }
Chris@152 334
Chris@152 335 bool allChange = false;
Chris@152 336
Chris@1154 337 for (int i = 0; in_range_for(values, i); ++i) {
Chris@256 338 float value = values[i];
Chris@606 339 if (ISNAN(value) || ISINF(value)) {
Chris@256 340 continue;
Chris@256 341 }
Chris@1429 342 if (!m_haveExtents || value < m_minimum) {
Chris@1429 343 m_minimum = value;
Chris@1429 344 allChange = true;
Chris@1429 345 }
Chris@1429 346 if (!m_haveExtents || value > m_maximum) {
Chris@1429 347 m_maximum = value;
Chris@1429 348 allChange = true;
Chris@1429 349 }
Chris@256 350 m_haveExtents = true;
Chris@152 351 }
Chris@152 352
Chris@534 353 truncateAndStore(index, values);
Chris@534 354
Chris@593 355 // assert(values == expandAndRetrieve(index));
Chris@152 356
Chris@1110 357 sv_frame_t windowStart = index;
Chris@182 358 windowStart *= m_resolution;
Chris@182 359
Chris@152 360 if (m_notifyOnAdd) {
Chris@1429 361 if (allChange) {
Chris@1752 362 emit modelChanged(getId());
Chris@1429 363 } else {
Chris@1752 364 emit modelChangedWithin(getId(),
Chris@1752 365 windowStart, windowStart + m_resolution);
Chris@1429 366 }
Chris@152 367 } else {
Chris@1429 368 if (allChange) {
Chris@1429 369 m_sinceLastNotifyMin = -1;
Chris@1429 370 m_sinceLastNotifyMax = -1;
Chris@1752 371 emit modelChanged(getId());
Chris@1429 372 } else {
Chris@1429 373 if (m_sinceLastNotifyMin == -1 ||
Chris@1429 374 windowStart < m_sinceLastNotifyMin) {
Chris@1429 375 m_sinceLastNotifyMin = windowStart;
Chris@1429 376 }
Chris@1429 377 if (m_sinceLastNotifyMax == -1 ||
Chris@1429 378 windowStart > m_sinceLastNotifyMax) {
Chris@1429 379 m_sinceLastNotifyMax = windowStart;
Chris@1429 380 }
Chris@1429 381 }
Chris@152 382 }
Chris@152 383 }
Chris@152 384
Chris@152 385 QString
Chris@929 386 EditableDenseThreeDimensionalModel::getBinName(int n) const
Chris@152 387 {
Chris@939 388 if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
Chris@152 389 else return "";
Chris@152 390 }
Chris@152 391
Chris@152 392 void
Chris@929 393 EditableDenseThreeDimensionalModel::setBinName(int n, QString name)
Chris@152 394 {
Chris@929 395 while ((int)m_binNames.size() <= n) m_binNames.push_back("");
Chris@152 396 m_binNames[n] = name;
Chris@1752 397 emit modelChanged(getId());
Chris@152 398 }
Chris@152 399
Chris@152 400 void
Chris@152 401 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
Chris@152 402 {
Chris@152 403 m_binNames = names;
Chris@1752 404 emit modelChanged(getId());
Chris@152 405 }
Chris@152 406
Chris@478 407 bool
Chris@886 408 EditableDenseThreeDimensionalModel::hasBinValues() const
Chris@886 409 {
Chris@886 410 return !m_binValues.empty();
Chris@886 411 }
Chris@886 412
Chris@886 413 float
Chris@929 414 EditableDenseThreeDimensionalModel::getBinValue(int n) const
Chris@886 415 {
Chris@929 416 if (n < (int)m_binValues.size()) return m_binValues[n];
Chris@886 417 else return 0.f;
Chris@886 418 }
Chris@886 419
Chris@886 420 void
Chris@886 421 EditableDenseThreeDimensionalModel::setBinValues(std::vector<float> values)
Chris@886 422 {
Chris@886 423 m_binValues = values;
Chris@886 424 }
Chris@886 425
Chris@886 426 QString
Chris@886 427 EditableDenseThreeDimensionalModel::getBinValueUnit() const
Chris@886 428 {
Chris@886 429 return m_binValueUnit;
Chris@886 430 }
Chris@886 431
Chris@886 432 void
Chris@886 433 EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit)
Chris@886 434 {
Chris@886 435 m_binValueUnit = unit;
Chris@886 436 }
Chris@886 437
Chris@886 438 bool
Chris@478 439 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
Chris@478 440 {
Chris@536 441 QReadLocker locker(&m_lock);
Chris@534 442
Chris@1044 443 vector<double> sample;
Chris@1044 444 vector<int> n;
Chris@478 445
Chris@478 446 for (int i = 0; i < 10; ++i) {
Chris@929 447 int index = i * 10;
Chris@1154 448 if (in_range_for(m_data, index)) {
Chris@533 449 const Column &c = m_data.at(index);
Chris@1154 450 while (c.size() > sample.size()) {
Chris@1044 451 sample.push_back(0.0);
Chris@478 452 n.push_back(0);
Chris@478 453 }
Chris@1154 454 for (int j = 0; in_range_for(c, j); ++j) {
Chris@533 455 sample[j] += c.at(j);
Chris@478 456 ++n[j];
Chris@478 457 }
Chris@478 458 }
Chris@478 459 }
Chris@478 460
Chris@478 461 if (sample.empty()) return false;
Chris@1044 462 for (decltype(sample)::size_type j = 0; j < sample.size(); ++j) {
Chris@1044 463 if (n[j]) sample[j] /= n[j];
Chris@478 464 }
Chris@478 465
Chris@1392 466 return LogRange::shouldUseLogScale(sample);
Chris@478 467 }
Chris@478 468
Chris@152 469 void
Chris@333 470 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
Chris@152 471 {
Chris@152 472 if (m_completion != completion) {
Chris@1429 473 m_completion = completion;
Chris@152 474
Chris@1429 475 if (completion == 100) {
Chris@152 476
Chris@1429 477 m_notifyOnAdd = true; // henceforth
Chris@1752 478 emit modelChanged(getId());
Chris@152 479
Chris@1429 480 } else if (!m_notifyOnAdd) {
Chris@152 481
Chris@1429 482 if (update &&
Chris@333 483 m_sinceLastNotifyMin >= 0 &&
Chris@1429 484 m_sinceLastNotifyMax >= 0) {
Chris@1752 485 emit modelChangedWithin(getId(),
Chris@1752 486 m_sinceLastNotifyMin,
Chris@931 487 m_sinceLastNotifyMax + m_resolution);
Chris@1429 488 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@1429 489 } else {
Chris@1752 490 emit completionChanged(getId());
Chris@1429 491 }
Chris@1429 492 } else {
Chris@1752 493 emit completionChanged(getId());
Chris@1429 494 }
Chris@152 495 }
Chris@152 496 }
Chris@152 497
Chris@1701 498 int
Chris@1701 499 EditableDenseThreeDimensionalModel::getCompletion() const
Chris@1701 500 {
Chris@1701 501 return m_completion;
Chris@1701 502 }
Chris@1701 503
Chris@318 504 QString
Chris@1679 505 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter,
Chris@1679 506 DataExportOptions,
Chris@1679 507 sv_frame_t startFrame,
Chris@1679 508 sv_frame_t duration) const
Chris@838 509 {
Chris@838 510 QReadLocker locker(&m_lock);
Chris@838 511 QString s;
Chris@1154 512 for (int i = 0; in_range_for(m_data, i); ++i) {
Chris@1038 513 sv_frame_t fr = m_startFrame + i * m_resolution;
Chris@1679 514 if (fr >= startFrame && fr < startFrame + duration) {
Chris@838 515 QStringList list;
Chris@1154 516 for (int j = 0; in_range_for(m_data.at(i), j); ++j) {
Chris@838 517 list << QString("%1").arg(m_data.at(i).at(j));
Chris@838 518 }
Chris@838 519 s += list.join(delimiter) + "\n";
Chris@838 520 }
Chris@838 521 }
Chris@838 522 return s;
Chris@838 523 }
Chris@838 524
Chris@152 525 void
Chris@152 526 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
Chris@314 527 QString indent,
Chris@314 528 QString extraAttributes) const
Chris@152 529 {
Chris@536 530 QReadLocker locker(&m_lock);
Chris@534 531
Chris@1677 532 // For historical reasons we read and write "resolution" as "windowSize".
Chris@1677 533
Chris@1677 534 // Our dataset doesn't have its own export ID, we just use
Chris@1677 535 // ours. Actually any model could do that, since datasets aren't
Chris@1677 536 // in the same id-space as models when re-read
Chris@152 537
Chris@690 538 SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
Chris@318 539
Chris@314 540 Model::toXml
Chris@1429 541 (out, indent,
Chris@611 542 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
Chris@1429 543 .arg(m_resolution)
Chris@1429 544 .arg(m_yBinCount)
Chris@1429 545 .arg(m_minimum)
Chris@1429 546 .arg(m_maximum)
Chris@1677 547 .arg(getExportId())
Chris@611 548 .arg(m_startFrame)
Chris@1429 549 .arg(extraAttributes));
Chris@152 550
Chris@152 551 out << indent;
Chris@152 552 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
Chris@1677 553 .arg(getExportId());
Chris@152 554
Chris@929 555 for (int i = 0; i < (int)m_binNames.size(); ++i) {
Chris@1429 556 if (m_binNames[i] != "") {
Chris@1429 557 out << indent + " ";
Chris@1429 558 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
Chris@1429 559 .arg(i).arg(m_binNames[i]);
Chris@1429 560 }
Chris@152 561 }
Chris@152 562
Chris@929 563 for (int i = 0; i < (int)m_data.size(); ++i) {
Chris@1429 564 out << indent + " ";
Chris@1429 565 out << QString("<row n=\"%1\">").arg(i);
Chris@1429 566 for (int j = 0; j < (int)m_data.at(i).size(); ++j) {
Chris@1429 567 if (j > 0) out << " ";
Chris@1429 568 out << m_data.at(i).at(j);
Chris@1429 569 }
Chris@1429 570 out << QString("</row>\n");
Chris@318 571 out.flush();
Chris@152 572 }
Chris@152 573
Chris@152 574 out << indent + "</dataset>\n";
Chris@152 575 }
Chris@152 576
Chris@152 577