annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 0009b2b066e0
children cc27f35aa75c
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@607 30 #include "system/System.h"
Chris@607 31
Chris@929 32 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(int sampleRate,
Chris@929 33 int resolution,
Chris@929 34 int yBinCount,
Chris@535 35 CompressionType compression,
Chris@152 36 bool notifyOnAdd) :
Chris@611 37 m_startFrame(0),
Chris@152 38 m_sampleRate(sampleRate),
Chris@152 39 m_resolution(resolution),
Chris@152 40 m_yBinCount(yBinCount),
Chris@535 41 m_compression(compression),
Chris@152 42 m_minimum(0.0),
Chris@152 43 m_maximum(0.0),
Chris@256 44 m_haveExtents(false),
Chris@152 45 m_notifyOnAdd(notifyOnAdd),
Chris@152 46 m_sinceLastNotifyMin(-1),
Chris@152 47 m_sinceLastNotifyMax(-1),
Chris@152 48 m_completion(100)
Chris@152 49 {
Chris@152 50 }
Chris@152 51
Chris@152 52 bool
Chris@152 53 EditableDenseThreeDimensionalModel::isOK() const
Chris@152 54 {
Chris@152 55 return true;
Chris@152 56 }
Chris@152 57
Chris@929 58 int
Chris@152 59 EditableDenseThreeDimensionalModel::getSampleRate() const
Chris@152 60 {
Chris@152 61 return m_sampleRate;
Chris@152 62 }
Chris@152 63
Chris@929 64 int
Chris@152 65 EditableDenseThreeDimensionalModel::getStartFrame() const
Chris@152 66 {
Chris@611 67 return m_startFrame;
Chris@611 68 }
Chris@611 69
Chris@611 70 void
Chris@929 71 EditableDenseThreeDimensionalModel::setStartFrame(int f)
Chris@611 72 {
Chris@611 73 m_startFrame = f;
Chris@152 74 }
Chris@152 75
Chris@929 76 int
Chris@152 77 EditableDenseThreeDimensionalModel::getEndFrame() const
Chris@152 78 {
Chris@152 79 return m_resolution * m_data.size() + (m_resolution - 1);
Chris@152 80 }
Chris@152 81
Chris@152 82 Model *
Chris@152 83 EditableDenseThreeDimensionalModel::clone() const
Chris@152 84 {
Chris@536 85 QReadLocker locker(&m_lock);
Chris@534 86
Chris@152 87 EditableDenseThreeDimensionalModel *model =
Chris@152 88 new EditableDenseThreeDimensionalModel
Chris@535 89 (m_sampleRate, m_resolution, m_yBinCount, m_compression);
Chris@152 90
Chris@152 91 model->m_minimum = m_minimum;
Chris@152 92 model->m_maximum = m_maximum;
Chris@256 93 model->m_haveExtents = m_haveExtents;
Chris@152 94
Chris@929 95 for (int i = 0; i < m_data.size(); ++i) {
Chris@533 96 model->setColumn(i, m_data.at(i));
Chris@152 97 }
Chris@152 98
Chris@152 99 return model;
Chris@152 100 }
Chris@152 101
Chris@929 102 int
Chris@152 103 EditableDenseThreeDimensionalModel::getResolution() const
Chris@152 104 {
Chris@152 105 return m_resolution;
Chris@152 106 }
Chris@152 107
Chris@152 108 void
Chris@929 109 EditableDenseThreeDimensionalModel::setResolution(int sz)
Chris@152 110 {
Chris@152 111 m_resolution = sz;
Chris@152 112 }
Chris@152 113
Chris@929 114 int
Chris@182 115 EditableDenseThreeDimensionalModel::getWidth() const
Chris@182 116 {
Chris@182 117 return m_data.size();
Chris@182 118 }
Chris@182 119
Chris@929 120 int
Chris@182 121 EditableDenseThreeDimensionalModel::getHeight() const
Chris@152 122 {
Chris@152 123 return m_yBinCount;
Chris@152 124 }
Chris@152 125
Chris@152 126 void
Chris@929 127 EditableDenseThreeDimensionalModel::setHeight(int sz)
Chris@152 128 {
Chris@152 129 m_yBinCount = sz;
Chris@152 130 }
Chris@152 131
Chris@152 132 float
Chris@152 133 EditableDenseThreeDimensionalModel::getMinimumLevel() const
Chris@152 134 {
Chris@152 135 return m_minimum;
Chris@152 136 }
Chris@152 137
Chris@152 138 void
Chris@152 139 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
Chris@152 140 {
Chris@152 141 m_minimum = level;
Chris@152 142 }
Chris@152 143
Chris@152 144 float
Chris@152 145 EditableDenseThreeDimensionalModel::getMaximumLevel() const
Chris@152 146 {
Chris@152 147 return m_maximum;
Chris@152 148 }
Chris@152 149
Chris@152 150 void
Chris@152 151 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
Chris@152 152 {
Chris@152 153 m_maximum = level;
Chris@152 154 }
Chris@152 155
Chris@533 156 EditableDenseThreeDimensionalModel::Column
Chris@929 157 EditableDenseThreeDimensionalModel::getColumn(int index) const
Chris@152 158 {
Chris@536 159 QReadLocker locker(&m_lock);
Chris@937 160 if (index < 0 || index >= m_data.size()) return Column();
Chris@534 161 return expandAndRetrieve(index);
Chris@152 162 }
Chris@152 163
Chris@152 164 float
Chris@929 165 EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const
Chris@152 166 {
Chris@534 167 Column c = getColumn(index);
Chris@929 168 if (int(n) < c.size()) return c.at(n);
Chris@534 169 return m_minimum;
Chris@534 170 }
Chris@152 171
Chris@535 172 //static int given = 0, stored = 0;
Chris@534 173
Chris@534 174 void
Chris@929 175 EditableDenseThreeDimensionalModel::truncateAndStore(int index,
Chris@534 176 const Column &values)
Chris@534 177 {
Chris@929 178 assert(int(index) < m_data.size());
Chris@534 179
Chris@843 180 //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
Chris@534 181
Chris@535 182 // The default case is to store the entire column at m_data[index]
Chris@535 183 // and place 0 at m_trunc[index] to indicate that it has not been
Chris@535 184 // truncated. We only do clever stuff if one of the clever-stuff
Chris@535 185 // tests works out.
Chris@535 186
Chris@534 187 m_trunc[index] = 0;
Chris@535 188 if (index == 0 ||
Chris@535 189 m_compression == NoCompression ||
Chris@929 190 values.size() != int(m_yBinCount)) {
Chris@535 191 // given += values.size();
Chris@535 192 // stored += values.size();
Chris@534 193 m_data[index] = values;
Chris@534 194 return;
Chris@152 195 }
Chris@152 196
Chris@535 197 // Maximum distance between a column and the one we refer to as
Chris@535 198 // the source of its truncated values. Limited by having to fit
Chris@535 199 // in a signed char, but in any case small values are usually
Chris@535 200 // better
Chris@535 201 static int maxdist = 6;
Chris@534 202
Chris@535 203 bool known = false; // do we know whether to truncate at top or bottom?
Chris@535 204 bool top = false; // if we do know, will we truncate at top?
Chris@534 205
Chris@535 206 // If the previous column is not truncated, then it is the only
Chris@535 207 // candidate for comparison. If it is truncated, then the column
Chris@535 208 // that it refers to is the only candidate. Either way, we only
Chris@535 209 // have one possible column to compare against here, and we are
Chris@535 210 // being careful to ensure it is not a truncated one (to avoid
Chris@535 211 // doing more work recursively when uncompressing).
Chris@534 212 int tdist = 1;
Chris@534 213 int ptrunc = m_trunc[index-1];
Chris@534 214 if (ptrunc < 0) {
Chris@534 215 top = false;
Chris@534 216 known = true;
Chris@534 217 tdist = -ptrunc + 1;
Chris@534 218 } else if (ptrunc > 0) {
Chris@534 219 top = true;
Chris@534 220 known = true;
Chris@534 221 tdist = ptrunc + 1;
Chris@534 222 }
Chris@534 223
Chris@534 224 Column p = expandAndRetrieve(index - tdist);
Chris@534 225 int h = m_yBinCount;
Chris@534 226
Chris@534 227 if (p.size() == h && tdist <= maxdist) {
Chris@534 228
Chris@534 229 int bcount = 0, tcount = 0;
Chris@534 230 if (!known || !top) {
Chris@535 231 // count how many identical values there are at the bottom
Chris@534 232 for (int i = 0; i < h; ++i) {
Chris@534 233 if (values.at(i) == p.at(i)) ++bcount;
Chris@534 234 else break;
Chris@534 235 }
Chris@534 236 }
Chris@534 237 if (!known || top) {
Chris@535 238 // count how many identical values there are at the top
Chris@534 239 for (int i = h; i > 0; --i) {
Chris@534 240 if (values.at(i-1) == p.at(i-1)) ++tcount;
Chris@534 241 else break;
Chris@534 242 }
Chris@534 243 }
Chris@534 244 if (!known) top = (tcount > bcount);
Chris@534 245
Chris@535 246 int limit = h / 4; // don't bother unless we have at least this many
Chris@534 247 if ((top ? tcount : bcount) > limit) {
Chris@534 248
Chris@534 249 if (!top) {
Chris@535 250 // create a new column with h - bcount values from bcount up
Chris@534 251 Column tcol(h - bcount);
Chris@535 252 // given += values.size();
Chris@535 253 // stored += h - bcount;
Chris@534 254 for (int i = bcount; i < h; ++i) {
Chris@534 255 tcol[i - bcount] = values.at(i);
Chris@534 256 }
Chris@534 257 m_data[index] = tcol;
Chris@534 258 m_trunc[index] = -tdist;
Chris@534 259 return;
Chris@534 260 } else {
Chris@535 261 // create a new column with h - tcount values from 0 up
Chris@534 262 Column tcol(h - tcount);
Chris@535 263 // given += values.size();
Chris@535 264 // stored += h - tcount;
Chris@534 265 for (int i = 0; i < h - tcount; ++i) {
Chris@534 266 tcol[i] = values.at(i);
Chris@534 267 }
Chris@534 268 m_data[index] = tcol;
Chris@534 269 m_trunc[index] = tdist;
Chris@534 270 return;
Chris@534 271 }
Chris@534 272 }
Chris@534 273 }
Chris@534 274
Chris@535 275 // given += values.size();
Chris@535 276 // stored += values.size();
Chris@843 277 // cout << "given: " << given << ", stored: " << stored << " ("
Chris@843 278 // << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
Chris@534 279
Chris@535 280 // default case if nothing wacky worked out
Chris@534 281 m_data[index] = values;
Chris@534 282 return;
Chris@534 283 }
Chris@534 284
Chris@534 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@534 293 return c;
Chris@534 294 }
Chris@534 295 int trunc = (int)m_trunc[index];
Chris@534 296 if (trunc == 0) {
Chris@534 297 return 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@537 303 int psize = p.size(), csize = 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 // push_front is very slow on QVector -- but not enough to
Chris@593 313 // make it desirable to choose a different container, since
Chris@593 314 // QVector has all the other advantages for us. easier to
Chris@593 315 // write the whole array out to a new vector
Chris@593 316 Column cc(psize);
Chris@593 317 for (int i = 0; i < psize - csize; ++i) {
Chris@593 318 cc[i] = p.at(i);
Chris@534 319 }
Chris@593 320 for (int i = 0; i < csize; ++i) {
Chris@593 321 cc[i + (psize - csize)] = c.at(i);
Chris@593 322 }
Chris@593 323 return cc;
Chris@534 324 }
Chris@534 325 return c;
Chris@152 326 }
Chris@152 327
Chris@152 328 void
Chris@929 329 EditableDenseThreeDimensionalModel::setColumn(int index,
Chris@182 330 const Column &values)
Chris@152 331 {
Chris@536 332 QWriteLocker locker(&m_lock);
Chris@152 333
Chris@929 334 while (int(index) >= m_data.size()) {
Chris@182 335 m_data.push_back(Column());
Chris@534 336 m_trunc.push_back(0);
Chris@152 337 }
Chris@152 338
Chris@152 339 bool allChange = false;
Chris@152 340
Chris@534 341 // if (values.size() > m_yBinCount) m_yBinCount = values.size();
Chris@439 342
Chris@929 343 for (int i = 0; i < values.size(); ++i) {
Chris@256 344 float value = values[i];
Chris@606 345 if (ISNAN(value) || ISINF(value)) {
Chris@256 346 continue;
Chris@256 347 }
Chris@256 348 if (!m_haveExtents || value < m_minimum) {
Chris@256 349 m_minimum = value;
Chris@152 350 allChange = true;
Chris@152 351 }
Chris@256 352 if (!m_haveExtents || value > m_maximum) {
Chris@256 353 m_maximum = value;
Chris@152 354 allChange = true;
Chris@152 355 }
Chris@256 356 m_haveExtents = true;
Chris@152 357 }
Chris@152 358
Chris@534 359 truncateAndStore(index, values);
Chris@534 360
Chris@593 361 // assert(values == expandAndRetrieve(index));
Chris@152 362
Chris@182 363 long windowStart = index;
Chris@182 364 windowStart *= m_resolution;
Chris@182 365
Chris@152 366 if (m_notifyOnAdd) {
Chris@152 367 if (allChange) {
Chris@152 368 emit modelChanged();
Chris@152 369 } else {
Chris@931 370 emit modelChangedWithin(windowStart, windowStart + m_resolution);
Chris@152 371 }
Chris@152 372 } else {
Chris@152 373 if (allChange) {
Chris@152 374 m_sinceLastNotifyMin = -1;
Chris@152 375 m_sinceLastNotifyMax = -1;
Chris@152 376 emit modelChanged();
Chris@152 377 } else {
Chris@152 378 if (m_sinceLastNotifyMin == -1 ||
Chris@152 379 windowStart < m_sinceLastNotifyMin) {
Chris@152 380 m_sinceLastNotifyMin = windowStart;
Chris@152 381 }
Chris@152 382 if (m_sinceLastNotifyMax == -1 ||
Chris@152 383 windowStart > m_sinceLastNotifyMax) {
Chris@152 384 m_sinceLastNotifyMax = windowStart;
Chris@152 385 }
Chris@152 386 }
Chris@152 387 }
Chris@152 388 }
Chris@152 389
Chris@152 390 QString
Chris@929 391 EditableDenseThreeDimensionalModel::getBinName(int n) const
Chris@152 392 {
Chris@939 393 if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
Chris@152 394 else return "";
Chris@152 395 }
Chris@152 396
Chris@152 397 void
Chris@929 398 EditableDenseThreeDimensionalModel::setBinName(int n, QString name)
Chris@152 399 {
Chris@929 400 while ((int)m_binNames.size() <= n) m_binNames.push_back("");
Chris@152 401 m_binNames[n] = name;
Chris@152 402 emit modelChanged();
Chris@152 403 }
Chris@152 404
Chris@152 405 void
Chris@152 406 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
Chris@152 407 {
Chris@152 408 m_binNames = names;
Chris@152 409 emit modelChanged();
Chris@152 410 }
Chris@152 411
Chris@478 412 bool
Chris@886 413 EditableDenseThreeDimensionalModel::hasBinValues() const
Chris@886 414 {
Chris@886 415 return !m_binValues.empty();
Chris@886 416 }
Chris@886 417
Chris@886 418 float
Chris@929 419 EditableDenseThreeDimensionalModel::getBinValue(int n) const
Chris@886 420 {
Chris@929 421 if (n < (int)m_binValues.size()) return m_binValues[n];
Chris@886 422 else return 0.f;
Chris@886 423 }
Chris@886 424
Chris@886 425 void
Chris@886 426 EditableDenseThreeDimensionalModel::setBinValues(std::vector<float> values)
Chris@886 427 {
Chris@886 428 m_binValues = values;
Chris@886 429 }
Chris@886 430
Chris@886 431 QString
Chris@886 432 EditableDenseThreeDimensionalModel::getBinValueUnit() const
Chris@886 433 {
Chris@886 434 return m_binValueUnit;
Chris@886 435 }
Chris@886 436
Chris@886 437 void
Chris@886 438 EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit)
Chris@886 439 {
Chris@886 440 m_binValueUnit = unit;
Chris@886 441 }
Chris@886 442
Chris@886 443 bool
Chris@478 444 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
Chris@478 445 {
Chris@536 446 QReadLocker locker(&m_lock);
Chris@534 447
Chris@533 448 QVector<float> sample;
Chris@533 449 QVector<int> n;
Chris@478 450
Chris@478 451 for (int i = 0; i < 10; ++i) {
Chris@929 452 int index = i * 10;
Chris@478 453 if (index < m_data.size()) {
Chris@533 454 const Column &c = m_data.at(index);
Chris@478 455 while (c.size() > sample.size()) {
Chris@478 456 sample.push_back(0.f);
Chris@478 457 n.push_back(0);
Chris@478 458 }
Chris@478 459 for (int j = 0; j < c.size(); ++j) {
Chris@533 460 sample[j] += c.at(j);
Chris@478 461 ++n[j];
Chris@478 462 }
Chris@478 463 }
Chris@478 464 }
Chris@478 465
Chris@478 466 if (sample.empty()) return false;
Chris@478 467 for (int j = 0; j < sample.size(); ++j) {
Chris@478 468 if (n[j]) sample[j] /= n[j];
Chris@478 469 }
Chris@478 470
Chris@533 471 return LogRange::useLogScale(sample.toStdVector());
Chris@478 472 }
Chris@478 473
Chris@152 474 void
Chris@333 475 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
Chris@152 476 {
Chris@152 477 if (m_completion != completion) {
Chris@152 478 m_completion = completion;
Chris@152 479
Chris@152 480 if (completion == 100) {
Chris@152 481
Chris@152 482 m_notifyOnAdd = true; // henceforth
Chris@152 483 emit modelChanged();
Chris@152 484
Chris@152 485 } else if (!m_notifyOnAdd) {
Chris@152 486
Chris@333 487 if (update &&
Chris@333 488 m_sinceLastNotifyMin >= 0 &&
Chris@152 489 m_sinceLastNotifyMax >= 0) {
Chris@931 490 emit modelChangedWithin(m_sinceLastNotifyMin,
Chris@931 491 m_sinceLastNotifyMax + m_resolution);
Chris@152 492 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@152 493 } else {
Chris@152 494 emit completionChanged();
Chris@152 495 }
Chris@152 496 } else {
Chris@152 497 emit completionChanged();
Chris@152 498 }
Chris@152 499 }
Chris@152 500 }
Chris@152 501
Chris@318 502 QString
Chris@318 503 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const
Chris@318 504 {
Chris@536 505 QReadLocker locker(&m_lock);
Chris@318 506 QString s;
Chris@929 507 for (int i = 0; i < m_data.size(); ++i) {
Chris@318 508 QStringList list;
Chris@929 509 for (int j = 0; j < m_data.at(i).size(); ++j) {
Chris@533 510 list << QString("%1").arg(m_data.at(i).at(j));
Chris@318 511 }
Chris@318 512 s += list.join(delimiter) + "\n";
Chris@318 513 }
Chris@318 514 return s;
Chris@318 515 }
Chris@318 516
Chris@838 517 QString
Chris@929 518 EditableDenseThreeDimensionalModel::toDelimitedDataStringSubset(QString delimiter, int f0, int f1) const
Chris@838 519 {
Chris@838 520 QReadLocker locker(&m_lock);
Chris@838 521 QString s;
Chris@929 522 for (int i = 0; i < m_data.size(); ++i) {
Chris@929 523 int fr = m_startFrame + i * m_resolution;
Chris@929 524 if (fr >= int(f0) && fr < int(f1)) {
Chris@838 525 QStringList list;
Chris@929 526 for (int j = 0; j < m_data.at(i).size(); ++j) {
Chris@838 527 list << QString("%1").arg(m_data.at(i).at(j));
Chris@838 528 }
Chris@838 529 s += list.join(delimiter) + "\n";
Chris@838 530 }
Chris@838 531 }
Chris@838 532 return s;
Chris@838 533 }
Chris@838 534
Chris@152 535 void
Chris@152 536 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
Chris@314 537 QString indent,
Chris@314 538 QString extraAttributes) const
Chris@152 539 {
Chris@536 540 QReadLocker locker(&m_lock);
Chris@534 541
Chris@152 542 // For historical reasons we read and write "resolution" as "windowSize"
Chris@152 543
Chris@690 544 SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
Chris@318 545
Chris@314 546 Model::toXml
Chris@314 547 (out, indent,
Chris@611 548 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
Chris@152 549 .arg(m_resolution)
Chris@152 550 .arg(m_yBinCount)
Chris@152 551 .arg(m_minimum)
Chris@152 552 .arg(m_maximum)
Chris@152 553 .arg(getObjectExportId(&m_data))
Chris@611 554 .arg(m_startFrame)
Chris@152 555 .arg(extraAttributes));
Chris@152 556
Chris@152 557 out << indent;
Chris@152 558 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
Chris@152 559 .arg(getObjectExportId(&m_data));
Chris@152 560
Chris@929 561 for (int i = 0; i < (int)m_binNames.size(); ++i) {
Chris@152 562 if (m_binNames[i] != "") {
Chris@152 563 out << indent + " ";
Chris@152 564 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
Chris@152 565 .arg(i).arg(m_binNames[i]);
Chris@152 566 }
Chris@152 567 }
Chris@152 568
Chris@929 569 for (int i = 0; i < (int)m_data.size(); ++i) {
Chris@152 570 out << indent + " ";
Chris@152 571 out << QString("<row n=\"%1\">").arg(i);
Chris@929 572 for (int j = 0; j < (int)m_data.at(i).size(); ++j) {
Chris@152 573 if (j > 0) out << " ";
Chris@533 574 out << m_data.at(i).at(j);
Chris@152 575 }
Chris@152 576 out << QString("</row>\n");
Chris@318 577 out.flush();
Chris@152 578 }
Chris@152 579
Chris@152 580 out << indent + "</dataset>\n";
Chris@152 581 }
Chris@152 582
Chris@152 583