annotate layer/SliceLayer.cpp @ 1403:10e768adaee5

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