comparison layer/SliceLayer.cpp @ 1395:32bbb86094c3

Merge from branch spectrogramparam
author Chris Cannam
date Wed, 14 Nov 2018 14:23:17 +0000
parents 4a36f6130056
children ba1f0234efa7
comparison
equal deleted inserted replaced
1380:78eecb19e688 1395:32bbb86094c3
21 #include "base/RealTime.h" 21 #include "base/RealTime.h"
22 #include "ColourMapper.h" 22 #include "ColourMapper.h"
23 #include "ColourDatabase.h" 23 #include "ColourDatabase.h"
24 24
25 #include "PaintAssistant.h" 25 #include "PaintAssistant.h"
26
27 #include "base/Profiler.h"
26 28
27 #include <QPainter> 29 #include <QPainter>
28 #include <QPainterPath> 30 #include <QPainterPath>
29 #include <QTextStream> 31 #include <QTextStream>
30 32
70 72
71 if (!m_sliceableModel) return; 73 if (!m_sliceableModel) return;
72 74
73 connectSignals(m_sliceableModel); 75 connectSignals(m_sliceableModel);
74 76
75 m_minbin = 0; 77 if (m_minbin == 0 && m_maxbin == 0) {
76 m_maxbin = m_sliceableModel->getHeight(); 78 m_minbin = 0;
79 m_maxbin = m_sliceableModel->getHeight();
80 }
77 81
78 emit modelReplaced(); 82 emit modelReplaced();
79 emit layerParametersChanged(); 83 emit layerParametersChanged();
80 } 84 }
81 85
189 } 193 }
190 194
191 double 195 double
192 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const 196 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const
193 { 197 {
198 return getXForScalePoint(v, bin, m_minbin, m_maxbin);
199 }
200
201 double
202 SliceLayer::getXForScalePoint(const LayerGeometryProvider *v,
203 double p, double pmin, double pmax) const
204 {
194 double x = 0; 205 double x = 0;
195
196 bin -= m_minbin;
197 if (bin < 0) bin = 0;
198
199 double count = m_maxbin - m_minbin;
200 if (count < 0) count = 0;
201 206
202 int pw = v->getPaintWidth(); 207 int pw = v->getPaintWidth();
203 int origin = m_xorigins[v->getId()]; 208 int origin = m_xorigins[v->getId()];
204 int w = pw - origin; 209 int w = pw - origin;
205 if (w < 1) w = 1; 210 if (w < 1) w = 1;
206 211
207 switch (m_binScale) { 212 if (p < pmin) p = pmin;
208 213 if (p > pmax) p = pmax;
209 case LinearBins: 214
210 x = (w * bin) / count; 215 if (m_binScale == LinearBins) {
211 break; 216 x = (w * (p - pmin)) / (pmax - pmin);
212 217 } else {
213 case LogBins: 218
219 if (m_binScale == InvertedLogBins) {
220 // stoopid
221 p = pmax - p;
222 }
223
214 // The 0.8 here is an awkward compromise. Our x-coord is 224 // The 0.8 here is an awkward compromise. Our x-coord is
215 // proportional to log of bin number, with the x-coord "of a 225 // proportional to log of bin number, with the x-coord "of a
216 // bin" being that of the left edge of the bin range. We can't 226 // bin" being that of the left edge of the bin range. We can't
217 // start counting bins from 0, as that would give us x = -Inf 227 // start counting bins from 0, as that would give us x = -Inf
218 // and hide the first bin entirely. But if we start from 1, we 228 // and hide the first bin entirely. But if we start from 1, we
221 // for that bin is in the middle of it. Yet in some modes 231 // for that bin is in the middle of it. Yet in some modes
222 // we'll still want it. A compromise is to count our first bin 232 // we'll still want it. A compromise is to count our first bin
223 // as "a bit less than 1", so that most of it is visible but a 233 // as "a bit less than 1", so that most of it is visible but a
224 // bit is tactfully cropped at the left edge so it doesn't 234 // bit is tactfully cropped at the left edge so it doesn't
225 // take up so much space. 235 // take up so much space.
226 x = (w * log10(bin + 0.8)) / log10(count + 0.8); 236 const double origin = 0.8;
227 break; 237
228 238 // sometimes we are called with a pmin/pmax range that begins
229 case InvertedLogBins: 239 // before 0: in that situation, we shift everything along by
230 x = w - (w * log10(count - bin - 1)) / log10(count); 240 // the difference between 0 and pmin before doing any other
231 break; 241 // calculations
242 double reqdshift = 0.0;
243 if (pmin < 0) reqdshift = -pmin;
244
245 double pminlog = log10(pmin + reqdshift + origin);
246 double pmaxlog = log10(pmax + reqdshift + origin);
247 double plog = log10(p + reqdshift + origin);
248 x = (w * (plog - pminlog)) / (pmaxlog - pminlog);
249
250 /*
251 cerr << "getXForScalePoint(" << p << "): pmin = " << pmin
252 << ", pmax = " << pmax << ", w = " << w
253 << ", reqdshift = " << reqdshift
254 << ", pminlog = " << pminlog << ", pmaxlog = " << pmaxlog
255 << ", plog = " << plog
256 << " -> x = " << x << endl;
257 */
258
259 if (m_binScale == InvertedLogBins) {
260 // still stoopid
261 x = w - x;
262 }
232 } 263 }
233 264
234 return x + origin; 265 return x + origin;
235 } 266 }
236 267
237 double 268 double
238 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const 269 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const
239 { 270 {
240 double bin = 0; 271 return getScalePointForX(v, x, m_minbin, m_maxbin);
241 272 }
242 double count = m_maxbin - m_minbin; 273
243 if (count < 0) count = 0; 274 double
275 SliceLayer::getScalePointForX(const LayerGeometryProvider *v,
276 double x, double pmin, double pmax) const
277 {
278 double p = 0;
244 279
245 int pw = v->getPaintWidth(); 280 int pw = v->getPaintWidth();
246 int origin = m_xorigins[v->getId()]; 281 int origin = m_xorigins[v->getId()];
247 282
248 int w = pw - origin; 283 int w = pw - origin;
250 285
251 x = x - origin; 286 x = x - origin;
252 if (x < 0) x = 0; 287 if (x < 0) x = 0;
253 288
254 double eps = 1e-10; 289 double eps = 1e-10;
255 290
256 switch (m_binScale) { 291 if (m_binScale == LinearBins) {
257 292 p = pmin + eps + (x * (pmax - pmin)) / w;
258 case LinearBins: 293 } else {
259 bin = (x * count) / w + eps; 294
260 break; 295 if (m_binScale == InvertedLogBins) {
261 296 x = w - x;
262 case LogBins: 297 }
263 // See comment in getXForBin 298
264 bin = pow(10.0, (x * log10(count + 0.8)) / w) - 0.8 + eps; 299 // See comments in getXForScalePoint
265 break; 300
266 301 const double origin = 0.8;
267 case InvertedLogBins: 302 double reqdshift = 0.0;
268 bin = count + 1 - pow(10.0, (log10(count) * (w - x)) / double(w)) + eps; 303 if (pmin < 0) reqdshift = -pmin;
269 break; 304
270 } 305 double pminlog = log10(pmin + reqdshift + origin);
271 306 double pmaxlog = log10(pmax + reqdshift + origin);
272 return bin + m_minbin; 307
308 double plog = pminlog + eps + (x * (pmaxlog - pminlog)) / w;
309 p = pow(10.0, plog) - reqdshift - origin;
310
311 if (m_binScale == InvertedLogBins) {
312 p = pmax - p;
313 }
314 }
315
316 return p;
273 } 317 }
274 318
275 double 319 double
276 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const 320 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const
277 { 321 {
362 } 406 }
363 407
364 void 408 void
365 SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const 409 SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
366 { 410 {
367 if (!m_sliceableModel || !m_sliceableModel->isOK() || 411 if (!m_sliceableModel ||
412 !m_sliceableModel->isOK() ||
368 !m_sliceableModel->isReady()) return; 413 !m_sliceableModel->isReady()) return;
369 414
415 Profiler profiler("SliceLayer::paint()");
416
370 paint.save(); 417 paint.save();
371 paint.setRenderHint(QPainter::Antialiasing, false); 418 paint.setRenderHint(QPainter::Antialiasing, true);
372 paint.setBrush(Qt::NoBrush); 419 paint.setBrush(Qt::NoBrush);
373 420
374 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) { 421 if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) {
375 if (!m_scalePoints.empty()) { 422 if (!m_scalePoints.empty()) {
376 paint.setPen(QColor(240, 240, 240)); //!!! and dark background? 423 paint.setPen(QColor(240, 240, 240)); //!!! and dark background?
381 rect.width(), m_scalePoints[i] * ratio); 428 rect.width(), m_scalePoints[i] * ratio);
382 } 429 }
383 } 430 }
384 } 431 }
385 432
433 int mh = m_sliceableModel->getHeight();
434 int bin0 = 0;
435 if (m_maxbin > m_minbin) {
436 mh = m_maxbin - m_minbin;
437 bin0 = m_minbin;
438 }
439
386 if (m_plotStyle == PlotBlocks) { 440 if (m_plotStyle == PlotBlocks) {
387 // Must use actual zero-width pen, too slow otherwise 441 // Must use actual zero-width pen, too slow otherwise
388 paint.setPen(QPen(getBaseQColor(), 0)); 442 paint.setPen(QPen(getBaseQColor(), 0));
389 } else { 443 } else {
390 paint.setPen(PaintAssistant::scalePen(getBaseQColor())); 444 // Similarly, if there are very many bins here, we use a
445 // thinner pen
446 QPen pen(getBaseQColor(), 1);
447 if (mh < 10000) {
448 pen = PaintAssistant::scalePen(pen);
449 }
450 paint.setPen(pen);
391 } 451 }
392 452
393 int xorigin = getVerticalScaleWidth(v, true, paint) + 1; 453 int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
394 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription 454 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription
395 455
396 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; 456 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
457 paint.fontMetrics().height();
397 int h = yorigin - paint.fontMetrics().height() - 8; 458 int h = yorigin - paint.fontMetrics().height() - 8;
398 459
399 m_yorigins[v->getId()] = yorigin; // for getYForValue etc 460 m_yorigins[v->getId()] = yorigin; // for getYForValue etc
400 m_heights[v->getId()] = h; 461 m_heights[v->getId()] = h;
401 462
402 if (h <= 0) return; 463 if (h <= 0) return;
403 464
404 QPainterPath path; 465 QPainterPath path;
405
406 int mh = m_sliceableModel->getHeight();
407 int bin0 = 0;
408
409 if (m_maxbin > m_minbin) {
410 mh = m_maxbin - m_minbin;
411 bin0 = m_minbin;
412 }
413 466
414 int divisor = 0; 467 int divisor = 0;
415 468
416 m_values.clear(); 469 m_values.clear();
417 for (int bin = 0; bin < mh; ++bin) { 470 for (int bin = 0; bin < mh; ++bin) {
432 if (m_samplingMode != NearestSample) col1 = int(f1 / res); 485 if (m_samplingMode != NearestSample) col1 = int(f1 / res);
433 f0 = col0 * res; 486 f0 = col0 * res;
434 f1 = (col1 + 1) * res - 1; 487 f1 = (col1 + 1) * res - 1;
435 488
436 // cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << endl; 489 // cerr << "resolution " << res << ", col0 " << col0 << ", col1 " << col1 << ", f0 " << f0 << ", f1 " << f1 << endl;
490 // cerr << "mh = " << mh << endl;
437 491
438 m_currentf0 = f0; 492 m_currentf0 = f0;
439 m_currentf1 = f1; 493 m_currentf1 = f1;
440 494
441 BiasCurve curve; 495 BiasCurve curve;
442 getBiasCurve(curve); 496 getBiasCurve(curve);
443 int cs = int(curve.size()); 497 int cs = int(curve.size());
444 498
445 for (int col = col0; col <= col1; ++col) { 499 for (int col = col0; col <= col1; ++col) {
500 DenseThreeDimensionalModel::Column column =
501 m_sliceableModel->getColumn(col);
446 for (int bin = 0; bin < mh; ++bin) { 502 for (int bin = 0; bin < mh; ++bin) {
447 float value = m_sliceableModel->getValueAt(col, bin0 + bin); 503 float value = column[bin0 + bin];
448 if (bin < cs) value *= curve[bin]; 504 if (bin < cs) value *= curve[bin];
449 if (m_samplingMode == SamplePeak) { 505 if (m_samplingMode == SamplePeak) {
450 if (value > m_values[bin]) m_values[bin] = value; 506 if (value > m_values[bin]) m_values[bin] = value;
451 } else { 507 } else {
452 m_values[bin] += value; 508 m_values[bin] += value;
470 526
471 double nx = getXForBin(v, bin0); 527 double nx = getXForBin(v, bin0);
472 528
473 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1); 529 ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1);
474 530
531 double ytop = 0, ybottom = 0;
532 bool firstBinOfPixel = true;
533
534 QColor prevColour = v->getBackground();
535 double prevPx = 0;
536 double prevYtop = 0;
537
475 for (int bin = 0; bin < mh; ++bin) { 538 for (int bin = 0; bin < mh; ++bin) {
476 539
477 double x = nx; 540 double x = nx;
478 nx = getXForBin(v, bin + bin0 + 1); 541 nx = getXForBin(v, bin + bin0 + 1);
479 542
480 double value = m_values[bin]; 543 double value = m_values[bin];
481 double norm = 0.0; 544 double norm = 0.0;
482 double y = getYForValue(v, value, norm); 545 double y = getYForValue(v, value, norm);
483 546
484 if (m_plotStyle == PlotLines) { 547 if (y < ytop || firstBinOfPixel) {
485 548 ytop = y;
486 if (bin == 0) { 549 }
487 path.moveTo((x + nx) / 2, y); 550 if (y > ybottom || firstBinOfPixel) {
488 } else { 551 ybottom = y;
489 path.lineTo((x + nx) / 2, y); 552 }
553
554 if (int(nx) != int(x) || bin+1 == mh) {
555
556 if (m_plotStyle == PlotLines) {
557
558 double px = (x + nx) / 2;
559
560 if (bin == 0) {
561 path.moveTo(px, y);
562 } else {
563 if (ytop != ybottom) {
564 path.lineTo(px, ybottom);
565 path.lineTo(px, ytop);
566 path.moveTo(px, ybottom);
567 } else {
568 path.lineTo(px, ytop);
569 }
570 }
571
572 } else if (m_plotStyle == PlotSteps) {
573
574 if (bin == 0) {
575 path.moveTo(x, y);
576 } else {
577 path.lineTo(x, ytop);
578 }
579 path.lineTo(nx, ytop);
580
581 } else if (m_plotStyle == PlotBlocks) {
582
583 // work in pixel coords here, as we don't want the
584 // vertical edges to be antialiased
585
586 path.moveTo(QPoint(int(x), int(yorigin)));
587 path.lineTo(QPoint(int(x), int(ytop)));
588 path.lineTo(QPoint(int(nx), int(ytop)));
589 path.lineTo(QPoint(int(nx), int(yorigin)));
590 path.lineTo(QPoint(int(x), int(yorigin)));
591
592 } else if (m_plotStyle == PlotFilledBlocks) {
593
594 QColor c = mapper.map(norm);
595 paint.setPen(Qt::NoPen);
596
597 // work in pixel coords here, as we don't want the
598 // vertical edges to be antialiased
599
600 if (nx > x + 1) {
601
602 double px = (x + nx) / 2;
603
604 QVector<QPoint> pp;
605
606 if (bin > 0) {
607 paint.setBrush(prevColour);
608 pp.clear();
609 pp << QPoint(int(prevPx), int(yorigin));
610 pp << QPoint(int(prevPx), int(prevYtop));
611 pp << QPoint(int((px + prevPx) / 2),
612 int((ytop + prevYtop) / 2));
613 pp << QPoint(int((px + prevPx) / 2),
614 int(yorigin));
615 paint.drawConvexPolygon(QPolygon(pp));
616
617 paint.setBrush(c);
618 pp.clear();
619 pp << QPoint(int((px + prevPx) / 2),
620 int(yorigin));
621 pp << QPoint(int((px + prevPx) / 2),
622 int((ytop + prevYtop) / 2));
623 pp << QPoint(int(px), int(ytop));
624 pp << QPoint(int(px), int(yorigin));
625 paint.drawConvexPolygon(QPolygon(pp));
626 }
627
628 prevPx = px;
629 prevColour = c;
630 prevYtop = ytop;
631
632 } else {
633
634 paint.fillRect(QRect(int(x), int(ytop),
635 int(nx) - int(x),
636 int(yorigin) - int(ytop)),
637 c);
638 }
490 } 639 }
491 640
492 } else if (m_plotStyle == PlotSteps) { 641 firstBinOfPixel = true;
493 642
494 if (bin == 0) { 643 } else {
495 path.moveTo(x, y); 644 firstBinOfPixel = false;
496 } else { 645 }
497 path.lineTo(x, y);
498 }
499 path.lineTo(nx, y);
500
501 } else if (m_plotStyle == PlotBlocks) {
502
503 path.moveTo(x, yorigin);
504 path.lineTo(x, y);
505 path.lineTo(nx, y);
506 path.lineTo(nx, yorigin);
507 path.lineTo(x, yorigin);
508
509 } else if (m_plotStyle == PlotFilledBlocks) {
510
511 paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm));
512 }
513
514 } 646 }
515 647
516 if (m_plotStyle != PlotFilledBlocks) { 648 if (m_plotStyle != PlotFilledBlocks) {
517 paint.drawPath(path); 649 paint.drawPath(path);
518 } 650 }
542 } 674 }
543 675
544 // int h = (rect.height() * 3) / 4; 676 // int h = (rect.height() * 3) / 4;
545 // int y = (rect.height() / 2) - (h / 2); 677 // int y = (rect.height() / 2) - (h / 2);
546 678
547 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 6; 679 int yorigin = v->getPaintHeight() - getHorizontalScaleHeight(v, paint) -
680 paint.fontMetrics().height();
548 int h = yorigin - paint.fontMetrics().height() - 8; 681 int h = yorigin - paint.fontMetrics().height() - 8;
549 if (h < 0) return; 682 if (h < 0) return;
550 683
551 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h); 684 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
552 685
1101 if (!m_sliceableModel) return 0; 1234 if (!m_sliceableModel) return 0;
1102 1235
1103 return new LinearRangeMapper(0, m_sliceableModel->getHeight(), 1236 return new LinearRangeMapper(0, m_sliceableModel->getHeight(),
1104 0, m_sliceableModel->getHeight(), ""); 1237 0, m_sliceableModel->getHeight(), "");
1105 } 1238 }
1239
1240 void
1241 SliceLayer::zoomToRegion(const LayerGeometryProvider *v, QRect rect)
1242 {
1243 double bin0 = getBinForX(v, rect.x());
1244 double bin1 = getBinForX(v, rect.x() + rect.width());
1245
1246 // ignore y for now...
1247
1248 SVDEBUG << "SliceLayer::zoomToRegion: zooming to bin range "
1249 << bin0 << " -> " << bin1 << endl;
1250
1251 setDisplayExtents(floor(bin0), ceil(bin1));
1252 }
1253