annotate layer/SliceLayer.cpp @ 1386:fc3d89f88690 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:34:34 +0000
parents 413e09f303ba
children bca9870301b7
rev   line source
Chris@133 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@133 2
Chris@133 3 /*
Chris@133 4 Sonic Visualiser
Chris@133 5 An audio file viewer and annotation editor.
Chris@133 6 Centre for Digital Music, Queen Mary, University of London.
Chris@195 7 This file copyright 2006-2007 QMUL.
Chris@133 8
Chris@133 9 This program is free software; you can redistribute it and/or
Chris@133 10 modify it under the terms of the GNU General Public License as
Chris@133 11 published by the Free Software Foundation; either version 2 of the
Chris@133 12 License, or (at your option) any later version. See the file
Chris@133 13 COPYING included with this distribution for more information.
Chris@133 14 */
Chris@133 15
Chris@193 16 #include "SliceLayer.h"
Chris@133 17
Chris@133 18 #include "view/View.h"
Chris@153 19 #include "base/AudioLevel.h"
Chris@167 20 #include "base/RangeMapper.h"
Chris@195 21 #include "base/RealTime.h"
Chris@376 22 #include "ColourMapper.h"
Chris@376 23 #include "ColourDatabase.h"
Chris@195 24
Chris@195 25 #include "PaintAssistant.h"
Chris@133 26
Chris@1383 27 #include "base/Profiler.h"
Chris@1383 28
Chris@133 29 #include <QPainter>
Chris@133 30 #include <QPainterPath>
Chris@316 31 #include <QTextStream>
Chris@316 32
Chris@133 33
Chris@193 34 SliceLayer::SliceLayer() :
Chris@193 35 m_sliceableModel(0),
Chris@1281 36 m_colourMap(int(ColourMapper::Ice)),
Chris@1362 37 m_colourInverted(false),
Chris@153 38 m_energyScale(dBScale),
Chris@198 39 m_samplingMode(SampleMean),
Chris@1233 40 m_plotStyle(PlotLines),
Chris@193 41 m_binScale(LinearBins),
Chris@153 42 m_normalize(false),
Chris@284 43 m_threshold(0.0),
Chris@284 44 m_initialThreshold(0.0),
Chris@198 45 m_gain(1.0),
Chris@1238 46 m_minbin(0),
Chris@1238 47 m_maxbin(0),
Chris@198 48 m_currentf0(0),
Chris@198 49 m_currentf1(0)
Chris@133 50 {
Chris@133 51 }
Chris@133 52
Chris@193 53 SliceLayer::~SliceLayer()
Chris@133 54 {
Chris@193 55
Chris@133 56 }
Chris@133 57
Chris@133 58 void
Chris@193 59 SliceLayer::setSliceableModel(const Model *model)
Chris@133 60 {
Chris@193 61 const DenseThreeDimensionalModel *sliceable =
Chris@193 62 dynamic_cast<const DenseThreeDimensionalModel *>(model);
Chris@193 63
Chris@193 64 if (model && !sliceable) {
Chris@682 65 cerr << "WARNING: SliceLayer::setSliceableModel(" << model
Chris@682 66 << "): model is not a DenseThreeDimensionalModel" << endl;
Chris@193 67 }
Chris@193 68
Chris@193 69 if (m_sliceableModel == sliceable) return;
Chris@193 70
Chris@193 71 m_sliceableModel = sliceable;
Chris@193 72
Chris@1255 73 if (!m_sliceableModel) return;
Chris@1255 74
Chris@320 75 connectSignals(m_sliceableModel);
Chris@193 76
Chris@1238 77 m_minbin = 0;
Chris@1238 78 m_maxbin = m_sliceableModel->getHeight();
Chris@1238 79
Chris@193 80 emit modelReplaced();
Chris@1238 81 emit layerParametersChanged();
Chris@153 82 }
Chris@153 83
Chris@153 84 void
Chris@193 85 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement)
Chris@153 86 {
Chris@587 87 SVDEBUG << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << endl;
Chris@153 88
Chris@193 89 if (orig == m_sliceableModel) {
Chris@193 90 setSliceableModel
Chris@193 91 (dynamic_cast<const DenseThreeDimensionalModel *>(replacement));
Chris@153 92 }
Chris@153 93 }
Chris@153 94
Chris@153 95 void
Chris@193 96 SliceLayer::modelAboutToBeDeleted(Model *m)
Chris@153 97 {
Chris@587 98 SVDEBUG << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << endl;
Chris@153 99
Chris@193 100 if (m == m_sliceableModel) {
Chris@193 101 setSliceableModel(0);
Chris@153 102 }
Chris@133 103 }
Chris@133 104
Chris@198 105 QString
Chris@918 106 SliceLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const
Chris@198 107 {
Chris@199 108 int minbin, maxbin, range;
Chris@805 109 return getFeatureDescriptionAux(v, p, true, minbin, maxbin, range);
Chris@199 110 }
Chris@199 111
Chris@199 112 QString
Chris@918 113 SliceLayer::getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &p,
Chris@805 114 bool includeBinDescription,
Chris@805 115 int &minbin, int &maxbin, int &range) const
Chris@199 116 {
Chris@199 117 minbin = 0;
Chris@199 118 maxbin = 0;
Chris@198 119 if (!m_sliceableModel) return "";
Chris@198 120
Chris@1256 121 minbin = int(getBinForX(v, p.x()));
Chris@1256 122 maxbin = int(getBinForX(v, p.x() + 1));
Chris@1238 123
Chris@198 124 int mh = m_sliceableModel->getHeight();
Chris@199 125 if (minbin >= mh) minbin = mh - 1;
Chris@199 126 if (maxbin >= mh) maxbin = mh - 1;
Chris@199 127 if (minbin < 0) minbin = 0;
Chris@199 128 if (maxbin < 0) maxbin = 0;
Chris@198 129
Chris@906 130 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate();
Chris@198 131
Chris@906 132 sv_frame_t f0 = m_currentf0;
Chris@906 133 sv_frame_t f1 = m_currentf1;
Chris@198 134
Chris@198 135 RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate);
Chris@198 136 RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate);
Chris@198 137
Chris@906 138 range = int(f1 - f0 + 1);
Chris@198 139
Chris@280 140 QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str());
Chris@280 141
Chris@199 142 if (includeBinDescription) {
Chris@198 143
Chris@1238 144 int i0 = minbin - m_minbin;
Chris@1238 145 int i1 = maxbin - m_minbin;
Chris@1238 146
Chris@906 147 float minvalue = 0.0;
Chris@1238 148 if (in_range_for(m_values, i0)) minvalue = m_values[i0];
Chris@198 149
Chris@199 150 float maxvalue = minvalue;
Chris@1238 151 if (in_range_for(m_values, i1)) maxvalue = m_values[i1];
Chris@1238 152
Chris@199 153 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
Chris@199 154
Chris@199 155 QString binstr;
Chris@199 156 if (maxbin != minbin) {
Chris@199 157 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
Chris@199 158 } else {
Chris@199 159 binstr = QString("%1").arg(minbin+1);
Chris@199 160 }
Chris@199 161
Chris@199 162 QString valuestr;
Chris@199 163 if (maxvalue != minvalue) {
Chris@199 164 valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue);
Chris@199 165 } else {
Chris@199 166 valuestr = QString("%1").arg(minvalue);
Chris@199 167 }
Chris@199 168
Chris@280 169 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)\nBin:\t%5\n%6 value:\t%7")
Chris@199 170 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 171 .arg(QString::fromStdString(rt1.toText(true)))
Chris@199 172 .arg(range)
Chris@280 173 .arg(rtrangestr)
Chris@199 174 .arg(binstr)
Chris@199 175 .arg(m_samplingMode == NearestSample ? tr("First") :
Chris@199 176 m_samplingMode == SampleMean ? tr("Mean") : tr("Peak"))
Chris@199 177 .arg(valuestr);
Chris@199 178
Chris@199 179 return description;
Chris@199 180
Chris@199 181 } else {
Chris@199 182
Chris@280 183 QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples (%4)")
Chris@199 184 .arg(QString::fromStdString(rt0.toText(true)))
Chris@199 185 .arg(QString::fromStdString(rt1.toText(true)))
Chris@280 186 .arg(range)
Chris@280 187 .arg(rtrangestr);
Chris@199 188
Chris@199 189 return description;
Chris@199 190 }
Chris@198 191 }
Chris@198 192
Chris@906 193 double
Chris@1238 194 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const
Chris@198 195 {
Chris@1386 196 return getXForScalePoint(v, bin, m_minbin, m_maxbin);
Chris@1386 197 }
Chris@1386 198
Chris@1386 199 double
Chris@1386 200 SliceLayer::getXForScalePoint(const LayerGeometryProvider *v,
Chris@1386 201 double p, double pmin, double pmax) const
Chris@1386 202 {
Chris@906 203 double x = 0;
Chris@198 204
Chris@1386 205 p -= pmin;
Chris@1386 206 if (p < 0) p = 0;
Chris@1238 207
Chris@1386 208 double extent = pmax - pmin;
Chris@1386 209 if (extent < 0) extent = 0;
Chris@1238 210
Chris@1238 211 int pw = v->getPaintWidth();
Chris@1238 212 int origin = m_xorigins[v->getId()];
Chris@1238 213 int w = pw - origin;
Chris@1238 214 if (w < 1) w = 1;
Chris@1238 215
Chris@198 216 switch (m_binScale) {
Chris@198 217
Chris@198 218 case LinearBins:
Chris@1386 219 x = (w * p) / extent;
Chris@198 220 break;
Chris@198 221
Chris@198 222 case LogBins:
Chris@1281 223 // The 0.8 here is an awkward compromise. Our x-coord is
Chris@1281 224 // proportional to log of bin number, with the x-coord "of a
Chris@1281 225 // bin" being that of the left edge of the bin range. We can't
Chris@1281 226 // start counting bins from 0, as that would give us x = -Inf
Chris@1281 227 // and hide the first bin entirely. But if we start from 1, we
Chris@1281 228 // are giving a lot of space to the first bin, which in most
Chris@1281 229 // display modes won't be used because the "point" location
Chris@1281 230 // for that bin is in the middle of it. Yet in some modes
Chris@1281 231 // we'll still want it. A compromise is to count our first bin
Chris@1281 232 // as "a bit less than 1", so that most of it is visible but a
Chris@1281 233 // bit is tactfully cropped at the left edge so it doesn't
Chris@1281 234 // take up so much space.
Chris@1386 235 x = (w * log10(p + 0.8)) / log10(extent + 0.8);
Chris@198 236 break;
Chris@198 237
Chris@198 238 case InvertedLogBins:
Chris@1386 239 x = w - (w * log10(extent - p - 1)) / log10(extent);
Chris@198 240 break;
Chris@198 241 }
Chris@1238 242
Chris@1238 243 return x + origin;
Chris@198 244 }
Chris@198 245
Chris@1238 246 double
Chris@1238 247 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const
Chris@198 248 {
Chris@1386 249 return getScalePointForX(v, x, m_minbin, m_maxbin);
Chris@1386 250 }
Chris@198 251
Chris@1386 252 double
Chris@1386 253 SliceLayer::getScalePointForX(const LayerGeometryProvider *v,
Chris@1386 254 double x, double pmin, double pmax) const
Chris@1386 255 {
Chris@1386 256 double p = 0;
Chris@1386 257
Chris@1386 258 double extent = pmax - pmin;
Chris@1386 259 if (extent < 0) extent = 0;
Chris@1238 260
Chris@1238 261 int pw = v->getPaintWidth();
Chris@1238 262 int origin = m_xorigins[v->getId()];
Chris@1238 263
Chris@1238 264 int w = pw - origin;
Chris@1238 265 if (w < 1) w = 1;
Chris@1238 266
Chris@1238 267 x = x - origin;
Chris@1238 268 if (x < 0) x = 0;
Chris@1256 269
Chris@1256 270 double eps = 1e-10;
Chris@1238 271
Chris@198 272 switch (m_binScale) {
Chris@198 273
Chris@198 274 case LinearBins:
Chris@1386 275 p = (x * extent) / w + eps;
Chris@198 276 break;
Chris@198 277
Chris@198 278 case LogBins:
Chris@1386 279 // See comment in getXForScalePoint
Chris@1386 280 p = pow(10.0, (x * log10(extent + 0.8)) / w) - 0.8 + eps;
Chris@198 281 break;
Chris@198 282
Chris@198 283 case InvertedLogBins:
Chris@1386 284 p = extent + 1 - pow(10.0, (log10(extent) * (w - x)) / double(w)) + eps;
Chris@198 285 break;
Chris@198 286 }
Chris@198 287
Chris@1386 288 return p + pmin;
Chris@198 289 }
Chris@198 290
Chris@906 291 double
Chris@1238 292 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const
Chris@274 293 {
Chris@906 294 norm = 0.0;
Chris@274 295
Chris@1238 296 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0;
Chris@274 297
Chris@274 298 value *= m_gain;
Chris@274 299
Chris@1238 300 int yorigin = m_yorigins[v->getId()];
Chris@1238 301 int h = m_heights[v->getId()];
Chris@906 302 double thresh = getThresholdDb();
Chris@274 303
Chris@906 304 double y = 0.0;
Chris@274 305
Chris@274 306 if (h <= 0) return y;
Chris@274 307
Chris@274 308 switch (m_energyScale) {
Chris@274 309
Chris@274 310 case dBScale:
Chris@274 311 {
Chris@906 312 double db = thresh;
Chris@906 313 if (value > 0.0) db = 10.0 * log10(fabs(value));
Chris@274 314 if (db < thresh) db = thresh;
Chris@274 315 norm = (db - thresh) / -thresh;
Chris@906 316 y = yorigin - (double(h) * norm);
Chris@274 317 break;
Chris@274 318 }
Chris@274 319
Chris@274 320 case MeterScale:
Chris@274 321 y = AudioLevel::multiplier_to_preview(value, h);
Chris@906 322 norm = double(y) / double(h);
Chris@274 323 y = yorigin - y;
Chris@274 324 break;
Chris@274 325
Chris@538 326 case AbsoluteScale:
Chris@906 327 value = fabs(value);
Chris@1264 328 #if (__GNUC__ >= 7)
Chris@1263 329 __attribute__ ((fallthrough));
Chris@1263 330 #endif
Chris@538 331
Chris@805 332 case LinearScale:
Chris@274 333 default:
Chris@538 334 norm = (value - m_threshold);
Chris@284 335 if (norm < 0) norm = 0;
Chris@906 336 y = yorigin - (double(h) * norm);
Chris@274 337 break;
Chris@274 338 }
Chris@274 339
Chris@274 340 return y;
Chris@274 341 }
Chris@274 342
Chris@906 343 double
Chris@1238 344 SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const
Chris@274 345 {
Chris@906 346 double value = 0.0;
Chris@274 347
Chris@1238 348 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value;
Chris@274 349
Chris@1238 350 int yorigin = m_yorigins[v->getId()];
Chris@1238 351 int h = m_heights[v->getId()];
Chris@906 352 double thresh = getThresholdDb();
Chris@274 353
Chris@274 354 if (h <= 0) return value;
Chris@274 355
Chris@274 356 y = yorigin - y;
Chris@274 357
Chris@274 358 switch (m_energyScale) {
Chris@274 359
Chris@274 360 case dBScale:
Chris@274 361 {
Chris@906 362 double db = ((y / h) * -thresh) + thresh;
Chris@906 363 value = pow(10.0, db/10.0);
Chris@274 364 break;
Chris@274 365 }
Chris@274 366
Chris@274 367 case MeterScale:
Chris@906 368 value = AudioLevel::preview_to_multiplier(int(lrint(y)), h);
Chris@274 369 break;
Chris@805 370
Chris@805 371 case LinearScale:
Chris@805 372 case AbsoluteScale:
Chris@274 373 default:
Chris@284 374 value = y / h + m_threshold;
Chris@274 375 }
Chris@274 376
Chris@274 377 return value / m_gain;
Chris@274 378 }
Chris@274 379
Chris@133 380 void
Chris@916 381 SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@133 382 {
Chris@1383 383 if (!m_sliceableModel ||
Chris@1383 384 !m_sliceableModel->isOK() ||
Chris@254 385 !m_sliceableModel->isReady()) return;
Chris@133 386
Chris@1383 387 Profiler profiler("SliceLayer::paint()");
Chris@1383 388
Chris@195 389 paint.save();
Chris@1383 390 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@538 391 paint.setBrush(Qt::NoBrush);
Chris@195 392
Chris@195 393 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) {
Chris@195 394 if (!m_scalePoints.empty()) {
Chris@195 395 paint.setPen(QColor(240, 240, 240)); //!!! and dark background?
Chris@1284 396 int ratio = int(round(double(v->getPaintHeight()) /
Chris@1284 397 m_scalePaintHeight));
Chris@805 398 for (int i = 0; i < (int)m_scalePoints.size(); ++i) {
Chris@1284 399 paint.drawLine(0, m_scalePoints[i] * ratio,
Chris@1284 400 rect.width(), m_scalePoints[i] * ratio);
Chris@195 401 }
Chris@195 402 }
Chris@195 403 }
Chris@195 404
Chris@1233 405 if (m_plotStyle == PlotBlocks) {
Chris@1233 406 // Must use actual zero-width pen, too slow otherwise
Chris@1233 407 paint.setPen(QPen(getBaseQColor(), 0));
Chris@1233 408 } else {
Chris@1233 409 paint.setPen(PaintAssistant::scalePen(getBaseQColor()));
Chris@1233 410 }
Chris@195 411
Chris@607 412 int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
Chris@1238 413 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription
Chris@133 414
Chris@1383 415 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
Chris@1383 416 paint.fontMetrics().height();
Chris@195 417 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@133 418
Chris@1238 419 m_yorigins[v->getId()] = yorigin; // for getYForValue etc
Chris@1238 420 m_heights[v->getId()] = h;
Chris@274 421
Chris@274 422 if (h <= 0) return;
Chris@274 423
Chris@133 424 QPainterPath path;
Chris@133 425
Chris@805 426 int mh = m_sliceableModel->getHeight();
Chris@1238 427 int bin0 = 0;
Chris@133 428
Chris@1238 429 if (m_maxbin > m_minbin) {
Chris@1238 430 mh = m_maxbin - m_minbin;
Chris@1238 431 bin0 = m_minbin;
Chris@1238 432 }
Chris@1238 433
Chris@193 434 int divisor = 0;
Chris@193 435
Chris@198 436 m_values.clear();
Chris@805 437 for (int bin = 0; bin < mh; ++bin) {
Chris@906 438 m_values.push_back(0.0);
Chris@193 439 }
Chris@193 440
Chris@906 441 sv_frame_t f0 = v->getCentreFrame();
Chris@193 442 int f0x = v->getXForFrame(f0);
Chris@195 443 f0 = v->getFrameForX(f0x);
Chris@906 444 sv_frame_t f1 = v->getFrameForX(f0x + 1);
Chris@195 445 if (f1 > f0) --f1;
Chris@193 446
Chris@682 447 // cerr << "centre frame " << v->getCentreFrame() << ", x " << f0x << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@274 448
Chris@805 449 int res = m_sliceableModel->getResolution();
Chris@906 450 int col0 = int(f0 / res);
Chris@805 451 int col1 = col0;
Chris@906 452 if (m_samplingMode != NearestSample) col1 = int(f1 / res);
Chris@274 453 f0 = col0 * res;
Chris@274 454 f1 = (col1 + 1) * res - 1;
Chris@274 455
Chris@682 456 // cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << endl;
Chris@1383 457 // cerr << "mh = " << mh << endl;
Chris@193 458
Chris@198 459 m_currentf0 = f0;
Chris@198 460 m_currentf1 = f1;
Chris@198 461
Chris@254 462 BiasCurve curve;
Chris@254 463 getBiasCurve(curve);
Chris@906 464 int cs = int(curve.size());
Chris@254 465
Chris@805 466 for (int col = col0; col <= col1; ++col) {
Chris@1383 467 DenseThreeDimensionalModel::Column column =
Chris@1383 468 m_sliceableModel->getColumn(col);
Chris@805 469 for (int bin = 0; bin < mh; ++bin) {
Chris@1383 470 float value = column[bin0 + bin];
Chris@254 471 if (bin < cs) value *= curve[bin];
Chris@193 472 if (m_samplingMode == SamplePeak) {
Chris@198 473 if (value > m_values[bin]) m_values[bin] = value;
Chris@193 474 } else {
Chris@198 475 m_values[bin] += value;
Chris@193 476 }
Chris@153 477 }
Chris@193 478 ++divisor;
Chris@193 479 }
Chris@193 480
Chris@906 481 float max = 0.0;
Chris@805 482 for (int bin = 0; bin < mh; ++bin) {
Chris@847 483 if (m_samplingMode == SampleMean && divisor > 0) {
Chris@906 484 m_values[bin] /= float(divisor);
Chris@847 485 }
Chris@198 486 if (m_values[bin] > max) max = m_values[bin];
Chris@193 487 }
Chris@906 488 if (max != 0.0 && m_normalize) {
Chris@805 489 for (int bin = 0; bin < mh; ++bin) {
Chris@198 490 m_values[bin] /= max;
Chris@193 491 }
Chris@193 492 }
Chris@193 493
Chris@1238 494 double nx = getXForBin(v, bin0);
Chris@193 495
Chris@1362 496 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1);
Chris@197 497
Chris@1383 498 double ytop = 0, ybottom = 0;
Chris@1383 499 bool firstBinOfPixel = true;
Chris@1384 500
Chris@1384 501 QColor prevColour = v->getBackground();
Chris@1384 502 double prevPx = 0;
Chris@1384 503 double prevYtop = 0;
Chris@1383 504
Chris@805 505 for (int bin = 0; bin < mh; ++bin) {
Chris@193 506
Chris@906 507 double x = nx;
Chris@1238 508 nx = getXForBin(v, bin + bin0 + 1);
Chris@193 509
Chris@906 510 double value = m_values[bin];
Chris@906 511 double norm = 0.0;
Chris@1238 512 double y = getYForValue(v, value, norm);
Chris@133 513
Chris@1383 514 if (y < ytop || firstBinOfPixel) {
Chris@1383 515 ytop = y;
Chris@1383 516 }
Chris@1383 517 if (y > ybottom || firstBinOfPixel) {
Chris@1383 518 ybottom = y;
Chris@1383 519 }
Chris@193 520
Chris@1383 521 if (int(nx) != int(x) || bin+1 == mh) {
Chris@1383 522
Chris@1383 523 if (m_plotStyle == PlotLines) {
Chris@1383 524
Chris@1384 525 double px = (x + nx) / 2;
Chris@1383 526
Chris@1383 527 if (bin == 0) {
Chris@1383 528 path.moveTo(px, y);
Chris@1383 529 } else {
Chris@1383 530 if (ytop != ybottom) {
Chris@1383 531 path.lineTo(px, ybottom);
Chris@1383 532 path.lineTo(px, ytop);
Chris@1383 533 path.moveTo(px, ybottom);
Chris@1383 534 } else {
Chris@1383 535 path.lineTo(px, ytop);
Chris@1383 536 }
Chris@1383 537 }
Chris@1383 538
Chris@1383 539 } else if (m_plotStyle == PlotSteps) {
Chris@1383 540
Chris@1383 541 if (bin == 0) {
Chris@1383 542 path.moveTo(x, y);
Chris@1383 543 } else {
Chris@1383 544 path.lineTo(x, ytop);
Chris@1383 545 }
Chris@1383 546 path.lineTo(nx, ytop);
Chris@1383 547
Chris@1383 548 } else if (m_plotStyle == PlotBlocks) {
Chris@1383 549
Chris@1384 550 // work in pixel coords here, as we don't want the
Chris@1384 551 // vertical edges to be antialiased
Chris@1384 552
Chris@1384 553 path.moveTo(QPoint(int(x), int(yorigin)));
Chris@1384 554 path.lineTo(QPoint(int(x), int(ytop)));
Chris@1384 555 path.lineTo(QPoint(int(nx), int(ytop)));
Chris@1384 556 path.lineTo(QPoint(int(nx), int(yorigin)));
Chris@1384 557 path.lineTo(QPoint(int(x), int(yorigin)));
Chris@1383 558
Chris@1383 559 } else if (m_plotStyle == PlotFilledBlocks) {
Chris@1383 560
Chris@1384 561 QColor c = mapper.map(norm);
Chris@1384 562 paint.setPen(Qt::NoPen);
Chris@1384 563
Chris@1384 564 // work in pixel coords here, as we don't want the
Chris@1384 565 // vertical edges to be antialiased
Chris@1384 566
Chris@1384 567 if (nx > x + 1) {
Chris@1384 568
Chris@1384 569 double px = (x + nx) / 2;
Chris@1384 570
Chris@1384 571 QVector<QPoint> pp;
Chris@1384 572
Chris@1384 573 if (bin > 0) {
Chris@1384 574 paint.setBrush(prevColour);
Chris@1384 575 pp.clear();
Chris@1384 576 pp << QPoint(int(prevPx), int(yorigin));
Chris@1384 577 pp << QPoint(int(prevPx), int(prevYtop));
Chris@1384 578 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 579 int((ytop + prevYtop) / 2));
Chris@1384 580 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 581 int(yorigin));
Chris@1384 582 paint.drawConvexPolygon(QPolygon(pp));
Chris@1384 583
Chris@1384 584 paint.setBrush(c);
Chris@1384 585 pp.clear();
Chris@1384 586 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 587 int(yorigin));
Chris@1384 588 pp << QPoint(int((px + prevPx) / 2),
Chris@1384 589 int((ytop + prevYtop) / 2));
Chris@1384 590 pp << QPoint(int(px), int(ytop));
Chris@1384 591 pp << QPoint(int(px), int(yorigin));
Chris@1384 592 paint.drawConvexPolygon(QPolygon(pp));
Chris@1384 593 }
Chris@1384 594
Chris@1384 595 prevPx = px;
Chris@1384 596 prevColour = c;
Chris@1384 597 prevYtop = ytop;
Chris@1384 598
Chris@1384 599 } else {
Chris@1384 600
Chris@1384 601 paint.fillRect(QRect(int(x), int(ytop),
Chris@1384 602 int(nx) - int(x),
Chris@1384 603 int(yorigin) - int(ytop)),
Chris@1384 604 c);
Chris@1384 605 }
Chris@193 606 }
Chris@193 607
Chris@1383 608 firstBinOfPixel = true;
Chris@193 609
Chris@1383 610 } else {
Chris@1383 611 firstBinOfPixel = false;
Chris@133 612 }
Chris@133 613 }
Chris@133 614
Chris@197 615 if (m_plotStyle != PlotFilledBlocks) {
Chris@197 616 paint.drawPath(path);
Chris@197 617 }
Chris@133 618 paint.restore();
Chris@195 619 }
Chris@195 620
Chris@195 621 int
Chris@918 622 SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@195 623 {
Chris@1238 624 int width;
Chris@538 625 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) {
Chris@1266 626 width = std::max(paint.fontMetrics().width("0.0") + 13,
Chris@1238 627 paint.fontMetrics().width("x10-10"));
Chris@195 628 } else {
Chris@1238 629 width = std::max(paint.fontMetrics().width(tr("0dB")),
Chris@1238 630 paint.fontMetrics().width(tr("-Inf"))) + 13;
Chris@195 631 }
Chris@1238 632 return width;
Chris@195 633 }
Chris@195 634
Chris@195 635 void
Chris@918 636 SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@195 637 {
Chris@906 638 double thresh = m_threshold;
Chris@538 639 if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) {
Chris@284 640 thresh = AudioLevel::dB_to_multiplier(getThresholdDb());
Chris@195 641 }
Chris@195 642
Chris@195 643 // int h = (rect.height() * 3) / 4;
Chris@195 644 // int y = (rect.height() / 2) - (h / 2);
Chris@195 645
Chris@1383 646 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
Chris@1383 647 paint.fontMetrics().height();
Chris@195 648 int h = yorigin - paint.fontMetrics().height() - 8;
Chris@195 649 if (h < 0) return;
Chris@195 650
Chris@195 651 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
Chris@195 652
Chris@220 653 int mult = 1;
Chris@220 654
Chris@195 655 PaintAssistant::paintVerticalLevelScale
Chris@195 656 (paint, actual, thresh, 1.0 / m_gain,
Chris@195 657 PaintAssistant::Scale(m_energyScale),
Chris@220 658 mult,
Chris@195 659 const_cast<std::vector<int> *>(&m_scalePoints));
Chris@220 660
Chris@1284 661 // Ugly hack (but then everything about this scale drawing is a
Chris@1284 662 // bit ugly). In pixel-doubling hi-dpi scenarios, the scale is
Chris@1284 663 // painted at pixel-doubled resolution but we do explicit
Chris@1284 664 // pixel-doubling ourselves when painting the layer content. We
Chris@1284 665 // make a note of this here so that we can compare with the
Chris@1284 666 // equivalent dimension in the paint method when deciding where to
Chris@1284 667 // place scale continuation lines.
Chris@1284 668 m_scalePaintHeight = v->getPaintHeight();
Chris@1284 669
Chris@220 670 if (mult != 1 && mult != 0) {
Chris@906 671 int log = int(lrint(log10(mult)));
Chris@220 672 QString a = tr("x10");
Chris@220 673 QString b = QString("%1").arg(-log);
Chris@220 674 paint.drawText(3, 8 + paint.fontMetrics().ascent(), a);
Chris@220 675 paint.drawText(3 + paint.fontMetrics().width(a),
Chris@220 676 3 + paint.fontMetrics().ascent(), b);
Chris@220 677 }
Chris@133 678 }
Chris@133 679
Chris@1281 680 bool
Chris@1281 681 SliceLayer::hasLightBackground() const
Chris@1281 682 {
Chris@1281 683 if (usesSolidColour()) {
Chris@1362 684 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1);
Chris@1281 685 return mapper.hasLightBackground();
Chris@1281 686 } else {
Chris@1281 687 return SingleColourLayer::hasLightBackground();
Chris@1281 688 }
Chris@1281 689 }
Chris@1281 690
Chris@153 691 Layer::PropertyList
Chris@193 692 SliceLayer::getProperties() const
Chris@153 693 {
Chris@287 694 PropertyList list = SingleColourLayer::getProperties();
Chris@538 695 list.push_back("Bin Scale");
Chris@193 696 list.push_back("Plot Type");
Chris@153 697 list.push_back("Scale");
Chris@153 698 list.push_back("Normalize");
Chris@284 699 list.push_back("Threshold");
Chris@153 700 list.push_back("Gain");
Chris@153 701
Chris@153 702 return list;
Chris@153 703 }
Chris@153 704
Chris@153 705 QString
Chris@193 706 SliceLayer::getPropertyLabel(const PropertyName &name) const
Chris@153 707 {
Chris@193 708 if (name == "Plot Type") return tr("Plot Type");
Chris@290 709 if (name == "Scale") return tr("Scale");
Chris@153 710 if (name == "Normalize") return tr("Normalize");
Chris@284 711 if (name == "Threshold") return tr("Threshold");
Chris@153 712 if (name == "Gain") return tr("Gain");
Chris@193 713 if (name == "Sampling Mode") return tr("Sampling Mode");
Chris@538 714 if (name == "Bin Scale") return tr("Bin Scale");
Chris@287 715 return SingleColourLayer::getPropertyLabel(name);
Chris@153 716 }
Chris@153 717
Chris@335 718 QString
Chris@335 719 SliceLayer::getPropertyIconName(const PropertyName &name) const
Chris@335 720 {
Chris@335 721 if (name == "Normalize") return "normalise";
Chris@335 722 return "";
Chris@335 723 }
Chris@335 724
Chris@153 725 Layer::PropertyType
Chris@193 726 SliceLayer::getPropertyType(const PropertyName &name) const
Chris@153 727 {
Chris@153 728 if (name == "Gain") return RangeProperty;
Chris@153 729 if (name == "Normalize") return ToggleProperty;
Chris@284 730 if (name == "Threshold") return RangeProperty;
Chris@287 731 if (name == "Plot Type") return ValueProperty;
Chris@290 732 if (name == "Scale") return ValueProperty;
Chris@287 733 if (name == "Sampling Mode") return ValueProperty;
Chris@287 734 if (name == "Bin Scale") return ValueProperty;
Chris@1281 735 if (name == "Colour" && usesSolidColour()) return ColourMapProperty;
Chris@287 736 return SingleColourLayer::getPropertyType(name);
Chris@153 737 }
Chris@153 738
Chris@153 739 QString
Chris@193 740 SliceLayer::getPropertyGroupName(const PropertyName &name) const
Chris@153 741 {
Chris@153 742 if (name == "Scale" ||
Chris@153 743 name == "Normalize" ||
Chris@193 744 name == "Sampling Mode" ||
Chris@284 745 name == "Threshold" ||
Chris@193 746 name == "Gain") return tr("Scale");
Chris@193 747 if (name == "Plot Type" ||
Chris@538 748 name == "Bin Scale") return tr("Bins");
Chris@287 749 return SingleColourLayer::getPropertyGroupName(name);
Chris@153 750 }
Chris@153 751
Chris@153 752 int
Chris@193 753 SliceLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 754 int *min, int *max, int *deflt) const
Chris@153 755 {
Chris@216 756 int val = 0;
Chris@153 757
Chris@216 758 int garbage0, garbage1, garbage2;
Chris@153 759 if (!min) min = &garbage0;
Chris@153 760 if (!max) max = &garbage1;
Chris@248 761 if (!deflt) deflt = &garbage2;
Chris@153 762
Chris@153 763 if (name == "Gain") {
Chris@153 764
Chris@1266 765 *min = -50;
Chris@1266 766 *max = 50;
Chris@216 767 *deflt = 0;
Chris@153 768
Chris@1238 769 // cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl;
Chris@193 770
Chris@1266 771 val = int(lrint(log10(m_gain) * 20.0));
Chris@1266 772 if (val < *min) val = *min;
Chris@1266 773 if (val > *max) val = *max;
Chris@153 774
Chris@284 775 } else if (name == "Threshold") {
Chris@284 776
Chris@1266 777 *min = -80;
Chris@1266 778 *max = 0;
Chris@284 779
Chris@906 780 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
Chris@1266 781 if (*deflt < *min) *deflt = *min;
Chris@1266 782 if (*deflt > *max) *deflt = *max;
Chris@284 783
Chris@1266 784 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold)));
Chris@1266 785 if (val < *min) val = *min;
Chris@1266 786 if (val > *max) val = *max;
Chris@284 787
Chris@153 788 } else if (name == "Normalize") {
Chris@1266 789
Chris@1266 790 val = (m_normalize ? 1 : 0);
Chris@216 791 *deflt = 0;
Chris@153 792
Chris@1281 793 } else if (name == "Colour" && usesSolidColour()) {
Chris@197 794
Chris@287 795 *min = 0;
Chris@287 796 *max = ColourMapper::getColourMapCount() - 1;
Chris@1281 797 *deflt = int(ColourMapper::Ice);
Chris@287 798
Chris@287 799 val = m_colourMap;
Chris@153 800
Chris@153 801 } else if (name == "Scale") {
Chris@153 802
Chris@1266 803 *min = 0;
Chris@1266 804 *max = 3;
Chris@216 805 *deflt = (int)dBScale;
Chris@153 806
Chris@1266 807 val = (int)m_energyScale;
Chris@153 808
Chris@193 809 } else if (name == "Sampling Mode") {
Chris@153 810
Chris@1266 811 *min = 0;
Chris@1266 812 *max = 2;
Chris@216 813 *deflt = (int)SampleMean;
Chris@193 814
Chris@1266 815 val = (int)m_samplingMode;
Chris@153 816
Chris@193 817 } else if (name == "Plot Type") {
Chris@193 818
Chris@193 819 *min = 0;
Chris@197 820 *max = 3;
Chris@216 821 *deflt = (int)PlotSteps;
Chris@193 822
Chris@216 823 val = (int)m_plotStyle;
Chris@193 824
Chris@193 825 } else if (name == "Bin Scale") {
Chris@193 826
Chris@193 827 *min = 0;
Chris@198 828 *max = 2;
Chris@216 829 *deflt = (int)LinearBins;
Chris@198 830 // *max = 1; // I don't think we really do want to offer inverted log
Chris@193 831
Chris@216 832 val = (int)m_binScale;
Chris@193 833
Chris@153 834 } else {
Chris@1266 835 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@153 836 }
Chris@153 837
Chris@216 838 return val;
Chris@153 839 }
Chris@153 840
Chris@153 841 QString
Chris@193 842 SliceLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1281 843 int value) const
Chris@153 844 {
Chris@1281 845 if (name == "Colour" && usesSolidColour()) {
Chris@1362 846 return ColourMapper::getColourMapLabel(value);
Chris@153 847 }
Chris@153 848 if (name == "Scale") {
Chris@1266 849 switch (value) {
Chris@1266 850 default:
Chris@1266 851 case 0: return tr("Linear");
Chris@1266 852 case 1: return tr("Meter");
Chris@1266 853 case 2: return tr("Log");
Chris@1266 854 case 3: return tr("Absolute");
Chris@1266 855 }
Chris@153 856 }
Chris@193 857 if (name == "Sampling Mode") {
Chris@1266 858 switch (value) {
Chris@1266 859 default:
Chris@1266 860 case 0: return tr("Any");
Chris@1266 861 case 1: return tr("Mean");
Chris@1266 862 case 2: return tr("Peak");
Chris@1266 863 }
Chris@193 864 }
Chris@193 865 if (name == "Plot Type") {
Chris@1266 866 switch (value) {
Chris@1266 867 default:
Chris@1266 868 case 0: return tr("Lines");
Chris@1266 869 case 1: return tr("Steps");
Chris@1266 870 case 2: return tr("Blocks");
Chris@1266 871 case 3: return tr("Colours");
Chris@1266 872 }
Chris@193 873 }
Chris@193 874 if (name == "Bin Scale") {
Chris@1266 875 switch (value) {
Chris@1266 876 default:
Chris@1266 877 case 0: return tr("Linear");
Chris@1266 878 case 1: return tr("Log");
Chris@1266 879 case 2: return tr("Rev Log");
Chris@1266 880 }
Chris@153 881 }
Chris@287 882 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@153 883 }
Chris@153 884
Chris@167 885 RangeMapper *
Chris@193 886 SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@167 887 {
Chris@167 888 if (name == "Gain") {
Chris@167 889 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@167 890 }
Chris@284 891 if (name == "Threshold") {
Chris@284 892 return new LinearRangeMapper(-80, 0, -80, 0, tr("dB"));
Chris@284 893 }
Chris@287 894 return SingleColourLayer::getNewPropertyRangeMapper(name);
Chris@167 895 }
Chris@167 896
Chris@133 897 void
Chris@193 898 SliceLayer::setProperty(const PropertyName &name, int value)
Chris@133 899 {
Chris@153 900 if (name == "Gain") {
Chris@1266 901 setGain(powf(10, float(value)/20.0f));
Chris@284 902 } else if (name == "Threshold") {
Chris@1266 903 if (value == -80) setThreshold(0.0f);
Chris@1266 904 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
Chris@1281 905 } else if (name == "Colour" && usesSolidColour()) {
Chris@287 906 setFillColourMap(value);
Chris@153 907 } else if (name == "Scale") {
Chris@1266 908 switch (value) {
Chris@1266 909 default:
Chris@1266 910 case 0: setEnergyScale(LinearScale); break;
Chris@1266 911 case 1: setEnergyScale(MeterScale); break;
Chris@1266 912 case 2: setEnergyScale(dBScale); break;
Chris@1266 913 case 3: setEnergyScale(AbsoluteScale); break;
Chris@1266 914 }
Chris@193 915 } else if (name == "Plot Type") {
Chris@1266 916 setPlotStyle(PlotStyle(value));
Chris@193 917 } else if (name == "Sampling Mode") {
Chris@1266 918 switch (value) {
Chris@1266 919 default:
Chris@1266 920 case 0: setSamplingMode(NearestSample); break;
Chris@1266 921 case 1: setSamplingMode(SampleMean); break;
Chris@1266 922 case 2: setSamplingMode(SamplePeak); break;
Chris@1266 923 }
Chris@193 924 } else if (name == "Bin Scale") {
Chris@1266 925 switch (value) {
Chris@1266 926 default:
Chris@1266 927 case 0: setBinScale(LinearBins); break;
Chris@1266 928 case 1: setBinScale(LogBins); break;
Chris@1266 929 case 2: setBinScale(InvertedLogBins); break;
Chris@1266 930 }
Chris@153 931 } else if (name == "Normalize") {
Chris@1266 932 setNormalize(value ? true : false);
Chris@287 933 } else {
Chris@287 934 SingleColourLayer::setProperty(name, value);
Chris@153 935 }
Chris@153 936 }
Chris@153 937
Chris@153 938 void
Chris@197 939 SliceLayer::setFillColourMap(int map)
Chris@197 940 {
Chris@197 941 if (m_colourMap == map) return;
Chris@197 942 m_colourMap = map;
Chris@197 943 emit layerParametersChanged();
Chris@197 944 }
Chris@197 945
Chris@197 946 void
Chris@193 947 SliceLayer::setEnergyScale(EnergyScale scale)
Chris@153 948 {
Chris@153 949 if (m_energyScale == scale) return;
Chris@153 950 m_energyScale = scale;
Chris@153 951 emit layerParametersChanged();
Chris@153 952 }
Chris@153 953
Chris@153 954 void
Chris@193 955 SliceLayer::setSamplingMode(SamplingMode mode)
Chris@153 956 {
Chris@193 957 if (m_samplingMode == mode) return;
Chris@193 958 m_samplingMode = mode;
Chris@153 959 emit layerParametersChanged();
Chris@153 960 }
Chris@153 961
Chris@153 962 void
Chris@193 963 SliceLayer::setPlotStyle(PlotStyle style)
Chris@153 964 {
Chris@193 965 if (m_plotStyle == style) return;
Chris@197 966 bool colourTypeChanged = (style == PlotFilledBlocks ||
Chris@197 967 m_plotStyle == PlotFilledBlocks);
Chris@193 968 m_plotStyle = style;
Chris@197 969 if (colourTypeChanged) {
Chris@197 970 emit layerParameterRangesChanged();
Chris@197 971 }
Chris@153 972 emit layerParametersChanged();
Chris@153 973 }
Chris@153 974
Chris@153 975 void
Chris@193 976 SliceLayer::setBinScale(BinScale scale)
Chris@153 977 {
Chris@193 978 if (m_binScale == scale) return;
Chris@193 979 m_binScale = scale;
Chris@153 980 emit layerParametersChanged();
Chris@153 981 }
Chris@153 982
Chris@153 983 void
Chris@193 984 SliceLayer::setNormalize(bool n)
Chris@153 985 {
Chris@153 986 if (m_normalize == n) return;
Chris@153 987 m_normalize = n;
Chris@153 988 emit layerParametersChanged();
Chris@153 989 }
Chris@153 990
Chris@153 991 void
Chris@284 992 SliceLayer::setThreshold(float thresh)
Chris@284 993 {
Chris@284 994 if (m_threshold == thresh) return;
Chris@284 995 m_threshold = thresh;
Chris@284 996 emit layerParametersChanged();
Chris@284 997 }
Chris@284 998
Chris@284 999 void
Chris@193 1000 SliceLayer::setGain(float gain)
Chris@153 1001 {
Chris@153 1002 if (m_gain == gain) return;
Chris@153 1003 m_gain = gain;
Chris@153 1004 emit layerParametersChanged();
Chris@153 1005 }
Chris@153 1006
Chris@284 1007 float
Chris@284 1008 SliceLayer::getThresholdDb() const
Chris@284 1009 {
Chris@284 1010 if (m_threshold == 0.0) return -80.f;
Chris@906 1011 float db = float(AudioLevel::multiplier_to_dB(m_threshold));
Chris@284 1012 return db;
Chris@284 1013 }
Chris@284 1014
Chris@287 1015 int
Chris@287 1016 SliceLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1017 {
Chris@287 1018 impose = false;
Chris@287 1019 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1020 (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@287 1021 }
Chris@287 1022
Chris@316 1023 void
Chris@316 1024 SliceLayer::toXml(QTextStream &stream,
Chris@316 1025 QString indent, QString extraAttributes) const
Chris@153 1026 {
Chris@153 1027 QString s;
Chris@153 1028
Chris@1362 1029 s += QString("energyScale=\"%1\" "
Chris@1362 1030 "samplingMode=\"%2\" "
Chris@1362 1031 "plotStyle=\"%3\" "
Chris@1362 1032 "binScale=\"%4\" "
Chris@1362 1033 "gain=\"%5\" "
Chris@1362 1034 "threshold=\"%6\" "
Chris@1362 1035 "normalize=\"%7\" %8 ")
Chris@1266 1036 .arg(m_energyScale)
Chris@193 1037 .arg(m_samplingMode)
Chris@598 1038 .arg(m_plotStyle)
Chris@598 1039 .arg(m_binScale)
Chris@153 1040 .arg(m_gain)
Chris@598 1041 .arg(m_threshold)
Chris@1238 1042 .arg(m_normalize ? "true" : "false")
Chris@1238 1043 .arg(QString("minbin=\"%1\" "
Chris@1238 1044 "maxbin=\"%2\"")
Chris@1238 1045 .arg(m_minbin)
Chris@1238 1046 .arg(m_maxbin));
Chris@153 1047
Chris@1362 1048 // New-style colour map attribute, by string id rather than by
Chris@1362 1049 // number
Chris@1362 1050
Chris@1362 1051 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1052 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1053
Chris@1362 1054 // Old-style colour map attribute
Chris@1362 1055
Chris@1362 1056 s += QString("colourScheme=\"%1\" ")
Chris@1362 1057 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1058
Chris@316 1059 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@153 1060 }
Chris@153 1061
Chris@153 1062 void
Chris@193 1063 SliceLayer::setProperties(const QXmlAttributes &attributes)
Chris@153 1064 {
Chris@153 1065 bool ok = false;
Chris@153 1066
Chris@287 1067 SingleColourLayer::setProperties(attributes);
Chris@153 1068
Chris@153 1069 EnergyScale scale = (EnergyScale)
Chris@1266 1070 attributes.value("energyScale").toInt(&ok);
Chris@153 1071 if (ok) setEnergyScale(scale);
Chris@153 1072
Chris@193 1073 SamplingMode mode = (SamplingMode)
Chris@1266 1074 attributes.value("samplingMode").toInt(&ok);
Chris@193 1075 if (ok) setSamplingMode(mode);
Chris@153 1076
Chris@1362 1077 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1078 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1079 if (colourMap >= 0) {
Chris@1362 1080 setFillColourMap(colourMap);
Chris@1362 1081 } else {
Chris@1362 1082 colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@1362 1083 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1084 setFillColourMap(colourMap);
Chris@1362 1085 }
Chris@1362 1086 }
Chris@197 1087
Chris@598 1088 PlotStyle s = (PlotStyle)
Chris@1266 1089 attributes.value("plotStyle").toInt(&ok);
Chris@598 1090 if (ok) setPlotStyle(s);
Chris@598 1091
Chris@598 1092 BinScale b = (BinScale)
Chris@1266 1093 attributes.value("binScale").toInt(&ok);
Chris@598 1094 if (ok) setBinScale(b);
Chris@598 1095
Chris@153 1096 float gain = attributes.value("gain").toFloat(&ok);
Chris@153 1097 if (ok) setGain(gain);
Chris@153 1098
Chris@598 1099 float threshold = attributes.value("threshold").toFloat(&ok);
Chris@598 1100 if (ok) setThreshold(threshold);
Chris@598 1101
Chris@153 1102 bool normalize = (attributes.value("normalize").trimmed() == "true");
Chris@153 1103 setNormalize(normalize);
Chris@1238 1104
Chris@1238 1105 bool alsoOk = false;
Chris@1238 1106
Chris@1238 1107 float min = attributes.value("minbin").toFloat(&ok);
Chris@1238 1108 float max = attributes.value("maxbin").toFloat(&alsoOk);
Chris@1238 1109 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@133 1110 }
Chris@133 1111
Chris@133 1112 bool
Chris@1238 1113 SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic,
Chris@1238 1114 QString &unit) const
Chris@133 1115 {
Chris@1238 1116 if (!m_sliceableModel) return false;
Chris@1238 1117
Chris@1238 1118 min = 0;
Chris@1238 1119 max = double(m_sliceableModel->getHeight());
Chris@1238 1120
Chris@1238 1121 logarithmic = (m_binScale == BinScale::LogBins);
Chris@1238 1122 unit = "";
Chris@1238 1123
Chris@1238 1124 return true;
Chris@133 1125 }
Chris@133 1126
Chris@1238 1127 bool
Chris@1238 1128 SliceLayer::getDisplayExtents(double &min, double &max) const
Chris@1238 1129 {
Chris@1238 1130 if (!m_sliceableModel) return false;
Chris@1238 1131
Chris@1238 1132 double hmax = double(m_sliceableModel->getHeight());
Chris@1238 1133
Chris@1238 1134 min = m_minbin;
Chris@1238 1135 max = m_maxbin;
Chris@1238 1136 if (max <= min) {
Chris@1238 1137 min = 0;
Chris@1238 1138 max = hmax;
Chris@1238 1139 }
Chris@1238 1140 if (min < 0) min = 0;
Chris@1238 1141 if (max > hmax) max = hmax;
Chris@1238 1142
Chris@1238 1143 return true;
Chris@1238 1144 }
Chris@1238 1145
Chris@1238 1146 bool
Chris@1238 1147 SliceLayer::setDisplayExtents(double min, double max)
Chris@1238 1148 {
Chris@1238 1149 if (!m_sliceableModel) return false;
Chris@1238 1150
Chris@1238 1151 m_minbin = int(lrint(min));
Chris@1238 1152 m_maxbin = int(lrint(max));
Chris@1238 1153
Chris@1238 1154 emit layerParametersChanged();
Chris@1238 1155 return true;
Chris@1238 1156 }
Chris@1238 1157
Chris@1238 1158 int
Chris@1238 1159 SliceLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@1238 1160 {
Chris@1238 1161 if (!m_sliceableModel) return 0;
Chris@1238 1162
Chris@1238 1163 defaultStep = 0;
Chris@1238 1164 int h = m_sliceableModel->getHeight();
Chris@1238 1165 return h;
Chris@1238 1166 }
Chris@1238 1167
Chris@1238 1168 int
Chris@1238 1169 SliceLayer::getCurrentVerticalZoomStep() const
Chris@1238 1170 {
Chris@1238 1171 if (!m_sliceableModel) return 0;
Chris@1238 1172
Chris@1238 1173 double min, max;
Chris@1238 1174 getDisplayExtents(min, max);
Chris@1238 1175 return m_sliceableModel->getHeight() - int(lrint(max - min));
Chris@1238 1176 }
Chris@1238 1177
Chris@1238 1178 void
Chris@1238 1179 SliceLayer::setVerticalZoomStep(int step)
Chris@1238 1180 {
Chris@1238 1181 if (!m_sliceableModel) return;
Chris@1238 1182
Chris@1238 1183 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): before: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1184
Chris@1238 1185 int dist = m_sliceableModel->getHeight() - step;
Chris@1238 1186 if (dist < 1) dist = 1;
Chris@1238 1187 double centre = m_minbin + (m_maxbin - m_minbin) / 2.0;
Chris@1238 1188 m_minbin = int(lrint(centre - dist/2.0));
Chris@1238 1189 if (m_minbin < 0) m_minbin = 0;
Chris@1238 1190 m_maxbin = m_minbin + dist;
Chris@1238 1191 if (m_maxbin > m_sliceableModel->getHeight()) m_maxbin = m_sliceableModel->getHeight();
Chris@1238 1192
Chris@1238 1193 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): after: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl;
Chris@1238 1194
Chris@1238 1195 emit layerParametersChanged();
Chris@1238 1196 }
Chris@1238 1197
Chris@1238 1198 RangeMapper *
Chris@1238 1199 SliceLayer::getNewVerticalZoomRangeMapper() const
Chris@1238 1200 {
Chris@1238 1201 if (!m_sliceableModel) return 0;
Chris@1238 1202
Chris@1238 1203 return new LinearRangeMapper(0, m_sliceableModel->getHeight(),
Chris@1238 1204 0, m_sliceableModel->getHeight(), "");
Chris@1238 1205 }