annotate layer/SliceLayer.cpp @ 1363:bbeffb29bf09

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