annotate layer/SliceLayer.cpp @ 1330:c1f719094c25 zoom

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