Mercurial > hg > svgui
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 |