19 #include "base/AudioLevel.h" 20 #include "base/RangeMapper.h" 21 #include "base/RealTime.h" 27 #include "base/Profiler.h" 30 #include <QPainterPath> 31 #include <QTextStream> 35 m_binAlignment(BinsSpanScalePoints),
37 m_colourInverted(false),
38 m_energyScale(dBScale),
39 m_samplingMode(SampleMean),
40 m_plotStyle(PlotLines),
41 m_binScale(LinearBins),
44 m_initialThreshold(0.0),
61 auto newModel = ModelById::getAs<DenseThreeDimensionalModel>(modelId);
63 if (!modelId.isNone() && !newModel) {
64 throw std::logic_error(
"Not a DenseThreeDimensionalModel");
86 SVDEBUG <<
"SliceLayer::sliceableModelReplaced(" << orig <<
", " << replacement <<
")" << endl;
96 int minbin, maxbin, range;
102 bool includeBinDescription,
103 int &minbin,
int &maxbin,
int &range)
const 108 auto sliceableModel =
110 if (!sliceableModel)
return "";
120 int mh = sliceableModel->getHeight();
121 if (minbin >= mh) minbin = mh - 1;
122 if (maxbin >= mh) maxbin = mh - 1;
123 if (minbin < 0) minbin = 0;
124 if (maxbin < 0) maxbin = 0;
126 sv_samplerate_t sampleRate = sliceableModel->getSampleRate();
131 RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate);
132 RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate);
134 range = int(f1 - f0 + 1);
136 QString rtrangestr = QString(
"%1 s").arg((rt1 - rt0).toText().c_str());
138 if (includeBinDescription) {
143 float minvalue = 0.0;
146 float maxvalue = minvalue;
149 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
152 if (maxbin != minbin) {
153 binstr = tr(
"%1 - %2").arg(minbin+1).arg(maxbin+1);
155 binstr = QString(
"%1").arg(minbin+1);
159 if (maxvalue != minvalue) {
160 valuestr = tr(
"%1 - %2").arg(minvalue).arg(maxvalue);
162 valuestr = QString(
"%1").arg(minvalue);
165 QString description = tr(
"Time:\t%1 - %2\nRange:\t%3 samples (%4)\nBin:\t%5\n%6 value:\t%7")
166 .arg(QString::fromStdString(rt0.toText(
true)))
167 .arg(QString::fromStdString(rt1.toText(
true)))
179 QString description = tr(
"Time:\t%1 - %2\nRange:\t%3 samples (%4)")
180 .arg(QString::fromStdString(rt0.toText(
true)))
181 .arg(QString::fromStdString(rt1.toText(
true)))
197 double p,
double pmin,
double pmax)
const 210 if (p < pmin) p = pmin;
211 if (p > pmax) p = pmax;
214 x = (w * (p - pmin)) / (pmax - pmin);
234 const double origin = 0.8;
240 double reqdshift = 0.0;
241 if (pmin < 0) reqdshift = -pmin;
243 double pminlog = log10(pmin + reqdshift + origin);
244 double pmaxlog = log10(pmax + reqdshift + origin);
245 double plog = log10(p + reqdshift + origin);
246 x = (w * (plog - pminlog)) / (pmaxlog - pminlog);
272 double x,
double pmin,
double pmax)
const 292 p = pmin + eps + (x * (pmax - pmin)) / w;
301 const double origin = 0.8;
302 double reqdshift = 0.0;
303 if (pmin < 0) reqdshift = -pmin;
305 double pminlog = log10(pmin + reqdshift + origin);
306 double pmaxlog = log10(pmax + reqdshift + origin);
308 double plog = pminlog + eps + (x * (pmaxlog - pminlog)) / w;
309 p = pow(10.0, plog) - reqdshift - origin;
334 if (h <= 0)
return y;
341 if (value > 0.0) db = 10.0 * log10(fabs(value));
342 if (db < thresh) db = thresh;
343 norm = (db - thresh) / -thresh;
344 y = yorigin - (double(h) * norm);
349 y = AudioLevel::multiplier_to_preview(value, h);
350 norm = double(y) / double(h);
357 __attribute__ ((fallthrough));
363 if (norm < 0) norm = 0;
364 y = yorigin - (double(h) * norm);
382 if (h <= 0)
return value;
390 double db = ((y / h) * -thresh) + thresh;
391 value = pow(10.0, db/10.0);
396 value = AudioLevel::preview_to_multiplier(
int(lrint(y)), h);
411 auto sliceableModel =
413 if (!sliceableModel ||
414 !sliceableModel->isOK() ||
415 !sliceableModel->isReady())
return;
417 Profiler profiler(
"SliceLayer::paint()");
420 paint.setRenderHint(QPainter::Antialiasing,
true);
421 paint.setBrush(Qt::NoBrush);
425 paint.setPen(QColor(240, 240, 240));
435 int mh = sliceableModel->getHeight();
461 paint.fontMetrics().height();
462 int h = yorigin - paint.fontMetrics().height() - 8;
474 for (
int bin = 0; bin < mh; ++bin) {
486 int res = sliceableModel->getResolution();
487 int col0 = int(f0 / res);
491 f1 = (col1 + 1) * res - 1;
501 int cs = int(curve.size());
503 for (
int col = col0; col <= col1; ++col) {
504 DenseThreeDimensionalModel::Column column =
505 sliceableModel->getColumn(col);
506 for (
int bin = 0; bin < mh; ++bin) {
507 float value = column[bin0 + bin];
508 if (bin < cs) value *= curve[bin];
519 for (
int bin = 0; bin < mh; ++bin) {
526 for (
int bin = 0; bin < mh; ++bin) {
533 double ytop = 0, ybottom = 0;
534 bool firstBinOfPixel =
true;
539 double xleft = -1, xmiddle = -1, xright = -1;
540 double prevXmiddle = 0;
542 for (
int bin = 0; bin < mh; ++bin) {
545 if (xright >= 0) xleft = xright;
550 if (xright >= 0) xleft = xright;
560 if (y < ytop || firstBinOfPixel) {
563 if (y > ybottom || firstBinOfPixel) {
567 if (
int(xright) !=
int(xleft) || bin+1 == mh) {
572 path.moveTo(xmiddle, y);
574 if (ytop != ybottom) {
575 path.lineTo(xmiddle, ybottom);
576 path.lineTo(xmiddle, ytop);
577 path.moveTo(xmiddle, ybottom);
579 path.lineTo(xmiddle, ytop);
586 path.moveTo(xleft, y);
588 path.lineTo(xleft, ytop);
590 path.lineTo(xright, ytop);
597 path.moveTo(QPoint(
int(xleft),
int(yorigin)));
598 path.lineTo(QPoint(
int(xleft),
int(ytop)));
599 path.lineTo(QPoint(
int(xright),
int(ytop)));
600 path.lineTo(QPoint(
int(xright),
int(yorigin)));
601 path.lineTo(QPoint(
int(xleft),
int(yorigin)));
605 QColor c = mapper.
map(norm);
606 paint.setPen(Qt::NoPen);
611 if (xright > xleft + 1) {
616 paint.setBrush(prevColour);
618 pp << QPoint(
int(prevXmiddle),
int(yorigin));
619 pp << QPoint(
int(prevXmiddle),
int(prevYtop));
620 pp << QPoint(
int((xmiddle + prevXmiddle) / 2),
621 int((ytop + prevYtop) / 2));
622 pp << QPoint(
int((xmiddle + prevXmiddle) / 2),
624 paint.drawConvexPolygon(QPolygon(pp));
628 pp << QPoint(
int((xmiddle + prevXmiddle) / 2),
630 pp << QPoint(
int((xmiddle + prevXmiddle) / 2),
631 int((ytop + prevYtop) / 2));
632 pp << QPoint(
int(xmiddle),
int(ytop));
633 pp << QPoint(
int(xmiddle),
int(yorigin));
634 paint.drawConvexPolygon(QPolygon(pp));
642 paint.fillRect(QRect(
int(xleft),
int(ytop),
643 int(xright) -
int(xleft),
644 int(yorigin) -
int(ytop)),
648 prevXmiddle = xmiddle;
651 firstBinOfPixel =
true;
654 firstBinOfPixel =
false;
659 paint.drawPath(path);
670 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 674 width = std::max(paint.fontMetrics().width(
"0.0") + 13,
675 paint.fontMetrics().width(
"x10-10"));
677 width = std::max(paint.fontMetrics().width(tr(
"0dB")),
678 paint.fontMetrics().width(tr(
"-Inf"))) + 13;
695 paint.fontMetrics().height();
696 int h = yorigin - paint.fontMetrics().height() - 8;
699 QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h);
704 (paint, actual, thresh, 1.0 /
m_gain,
718 if (mult != 1 && mult != 0) {
719 int log = int(lrint(log10(mult)));
720 QString a = tr(
"x10");
721 QString b = QString(
"%1").arg(-log);
722 paint.drawText(3, 8 + paint.fontMetrics().ascent(), a);
723 paint.drawText(3 + paint.fontMetrics().width(a),
724 3 + paint.fontMetrics().ascent(), b);
743 list.push_back(
"Bin Scale");
744 list.push_back(
"Plot Type");
745 list.push_back(
"Scale");
746 list.push_back(
"Normalize");
747 list.push_back(
"Threshold");
748 list.push_back(
"Gain");
756 if (name ==
"Plot Type")
return tr(
"Plot Type");
757 if (name ==
"Scale")
return tr(
"Scale");
758 if (name ==
"Normalize")
return tr(
"Normalize");
759 if (name ==
"Threshold")
return tr(
"Threshold");
760 if (name ==
"Gain")
return tr(
"Gain");
761 if (name ==
"Sampling Mode")
return tr(
"Sampling Mode");
762 if (name ==
"Bin Scale")
return tr(
"Bin Scale");
769 if (name ==
"Normalize")
return "normalise";
776 if (name ==
"Gain")
return RangeProperty;
777 if (name ==
"Normalize")
return ToggleProperty;
778 if (name ==
"Threshold")
return RangeProperty;
779 if (name ==
"Plot Type")
return ValueProperty;
780 if (name ==
"Scale")
return ValueProperty;
781 if (name ==
"Sampling Mode")
return ValueProperty;
782 if (name ==
"Bin Scale")
return ValueProperty;
790 if (name ==
"Scale" ||
791 name ==
"Normalize" ||
792 name ==
"Sampling Mode" ||
793 name ==
"Threshold" ||
794 name ==
"Gain")
return tr(
"Scale");
795 if (name ==
"Plot Type" ||
796 name ==
"Bin Scale")
return tr(
"Bins");
802 int *min,
int *max,
int *deflt)
const 806 int garbage0, garbage1, garbage2;
807 if (!min) min = &garbage0;
808 if (!max) max = &garbage1;
809 if (!deflt) deflt = &garbage2;
811 if (name ==
"Gain") {
819 val = int(lrint(log10(
m_gain) * 20.0));
820 if (val < *min) val = *min;
821 if (val > *max) val = *max;
823 }
else if (name ==
"Threshold") {
829 if (*deflt < *min) *deflt = *min;
830 if (*deflt > *max) *deflt = *max;
832 val = int(lrint(AudioLevel::multiplier_to_dB(
m_threshold)));
833 if (val < *min) val = *min;
834 if (val > *max) val = *max;
836 }
else if (name ==
"Normalize") {
849 }
else if (name ==
"Scale") {
857 }
else if (name ==
"Sampling Mode") {
865 }
else if (name ==
"Plot Type") {
873 }
else if (name ==
"Bin Scale") {
896 if (name ==
"Scale") {
899 case 0:
return tr(
"Linear");
900 case 1:
return tr(
"Meter");
901 case 2:
return tr(
"Log");
902 case 3:
return tr(
"Absolute");
905 if (name ==
"Sampling Mode") {
908 case 0:
return tr(
"Any");
909 case 1:
return tr(
"Mean");
910 case 2:
return tr(
"Peak");
913 if (name ==
"Plot Type") {
916 case 0:
return tr(
"Lines");
917 case 1:
return tr(
"Steps");
918 case 2:
return tr(
"Blocks");
919 case 3:
return tr(
"Colours");
922 if (name ==
"Bin Scale") {
925 case 0:
return tr(
"Linear");
926 case 1:
return tr(
"Log");
927 case 2:
return tr(
"Rev Log");
936 if (name ==
"Gain") {
937 return new LinearRangeMapper(-50, 50, -25, 25, tr(
"dB"));
939 if (name ==
"Threshold") {
940 return new LinearRangeMapper(-80, 0, -80, 0, tr(
"dB"));
948 if (name ==
"Gain") {
949 setGain(powf(10,
float(value)/20.0f));
950 }
else if (name ==
"Threshold") {
952 else setThreshold(
float(AudioLevel::dB_to_multiplier(value)));
955 }
else if (name ==
"Scale") {
963 }
else if (name ==
"Plot Type") {
965 }
else if (name ==
"Sampling Mode") {
972 }
else if (name ==
"Bin Scale") {
979 }
else if (name ==
"Normalize") {
1017 if (colourTypeChanged) {
1050 if (
m_gain == gain)
return;
1059 float db = float(AudioLevel::multiplier_to_dB(
m_threshold));
1068 (QString(darkbg ?
"Bright Blue" :
"Blue"));
1073 QString indent, QString extraAttributes)
const 1077 s += QString(
"energyScale=\"%1\" " 1078 "samplingMode=\"%2\" " 1083 "normalize=\"%7\" %8 ")
1091 .arg(QString(
"minbin=\"%1\" " 1099 s += QString(
"fillColourMap=\"%1\" ")
1104 s += QString(
"colourScheme=\"%1\" ")
1118 attributes.value(
"energyScale").toInt(&ok);
1122 attributes.value(
"samplingMode").toInt(&ok);
1125 QString colourMapId = attributes.value(
"fillColourMap");
1127 if (colourMap >= 0) {
1130 colourMap = attributes.value(
"colourScheme").toInt(&ok);
1137 attributes.value(
"plotStyle").toInt(&ok);
1141 attributes.value(
"binScale").toInt(&ok);
1144 float gain = attributes.value(
"gain").toFloat(&ok);
1147 float threshold = attributes.value(
"threshold").toFloat(&ok);
1150 bool normalize = (attributes.value(
"normalize").trimmed() ==
"true");
1153 bool alsoOk =
false;
1155 float min = attributes.value(
"minbin").toFloat(&ok);
1156 float max = attributes.value(
"maxbin").toFloat(&alsoOk);
1162 QString &unit)
const 1164 auto sliceableModel =
1166 if (!sliceableModel)
return false;
1169 max = double(sliceableModel->getHeight());
1171 logarithmic = (
m_binScale == BinScale::LogBins);
1180 auto sliceableModel =
1182 if (!sliceableModel)
return false;
1184 double hmax = double(sliceableModel->getHeight());
1192 if (min < 0) min = 0;
1193 if (max > hmax) max = hmax;
1201 auto sliceableModel =
1203 if (!sliceableModel)
return false;
1214 if (
m_minbin > sliceableModel->getHeight()) {
1215 m_minbin = sliceableModel->getHeight();
1217 if (
m_maxbin > sliceableModel->getHeight()) {
1218 m_maxbin = sliceableModel->getHeight();
1231 auto sliceableModel =
1233 if (!sliceableModel)
return 0;
1236 int h = sliceableModel->getHeight();
1243 auto sliceableModel =
1245 if (!sliceableModel)
return 0;
1249 return sliceableModel->getHeight() - int(lrint(max - min));
1255 auto sliceableModel =
1257 if (!sliceableModel)
return;
1261 int dist = sliceableModel->getHeight() - step;
1262 if (dist < 1) dist = 1;
1264 int minbin = int(lrint(centre - dist/2.0));
1265 int maxbin = minbin + dist;
1272 auto sliceableModel =
1274 if (!sliceableModel)
return nullptr;
1276 return new LinearRangeMapper(0, sliceableModel->getHeight(),
1277 0, sliceableModel->getHeight(),
"");
1284 double bin1 =
getBinForX(v, rect.x() + rect.width());
1288 SVDEBUG <<
"SliceLayer::zoomToRegion: zooming to bin range " 1289 << bin0 <<
" -> " << bin1 << endl;
EnergyScale m_energyScale
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
void setFillColourMap(int)
virtual QColor getBackground() const =0
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
PropertyList getProperties() const override
bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const override
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
bool setDisplayExtents(double min, double max) override
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
virtual double getValueForY(const LayerGeometryProvider *v, double y) const
bool hasLightBackground() const override
Return true if the layer currently has a dark colour on a light background, false if it has a light c...
void setSliceableModel(ModelId model)
void setVerticalZoomStep(int) override
Set the vertical zoom step.
PropertyType getPropertyType(const PropertyName &) const override
void setSamplingMode(SamplingMode)
PropertyList getProperties() const override
std::map< int, int > m_heights
QString getPropertyIconName(const PropertyName &) const override
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
static int getColourMapCount()
Return the number of known colour maps.
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
QString getPropertyValueLabel(const PropertyName &, int value) const override
virtual QColor getBaseQColor() const
std::vector< int > m_scalePoints
void layerParameterRangesChanged()
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
virtual float getThresholdDb() const
static int getBackwardCompatibilityColourMap(int n)
Older versions of colour-handling code save and reload colour maps by numerical index and can't prope...
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
static QString getColourMapLabel(int n)
Return a human-readable label for the colour map with the given index.
RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const override
static void paintVerticalLevelScale(QPainter &p, QRect rect, double minVal, double maxVal, Scale scale, int &multRtn, std::vector< int > *markCoordRtns=0)
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
bool hasLightBackground() const
Return true if the colour map is intended to be placed over a light background, false otherwise...
virtual int getHorizontalScaleHeight(LayerGeometryProvider *, QPainter &) const
QString getPropertyGroupName(const PropertyName &) const override
void setNormalize(bool n)
static int getColourMapById(QString id)
Return the index for the colour map with the given machine-readable id string, or -1 if the id is not...
void layerParametersChanged()
std::vector< float > m_values
double getXForScalePoint(const LayerGeometryProvider *, double p, double pmin, double pmax) const
Convert a point such as a bin number into x-coord, given max & min.
virtual double getYForValue(const LayerGeometryProvider *v, double value, double &norm) const
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
QColor map(double value) const
Map the given value to a colour.
A class for mapping intensity values onto various colour maps.
virtual double getBinForX(const LayerGeometryProvider *, double x) const
Convert an x-coord into (possibly non-integral) bin. May be overridden.
void connectSignals(ModelId)
virtual int getPaintHeight() const
void setPlotStyle(PlotStyle style)
QString getPropertyLabel(const PropertyName &) const override
PropertyType getPropertyType(const PropertyName &) const override
RangeMapper * getNewVerticalZoomRangeMapper() const override
Create and return a range mapper for vertical zoom step values.
void setBinScale(BinScale scale)
double getScalePointForX(const LayerGeometryProvider *, double x, double pmin, double pmax) const
Convert an x-coord into a point such as a bin number, given max & min.
virtual QPen scalePen(QPen pen) const =0
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
virtual void zoomToRegion(const LayerGeometryProvider *, QRect) override
Update the X and Y axis scales, where appropriate, to focus on the given rectangular region...
std::vector< float > BiasCurve
virtual ViewManager * getViewManager() const =0
void setEnergyScale(EnergyScale)
BinAlignment m_binAlignment
int getDefaultColourHint(bool dark, bool &impose) override
bool shouldShowScaleGuides() const
void setProperty(const PropertyName &, int value) override
QString getPropertyValueLabel(const PropertyName &, int value) const override
SamplingMode m_samplingMode
virtual int getId() const =0
Retrieve the id of this object.
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
bool hasLightBackground() const override
Return true if the layer currently has a dark colour on a light background, false if it has a light c...
static QString getColourMapId(int n)
Return a machine-readable id string for the colour map with the given index.
void setProperties(const QXmlAttributes &) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
virtual QString getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &, bool includeBinDescription, int &minbin, int &maxbin, int &range) const
virtual double getXForBin(const LayerGeometryProvider *, double bin) const
Convert a (possibly non-integral) bin into x-coord. May be overridden.
virtual sv_frame_t getCentreFrame() const =0
Return the centre frame of the visible widget.
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const override
virtual void getBiasCurve(BiasCurve &) const
std::map< int, int > m_xorigins
void setProperty(const PropertyName &, int value) override
QString getPropertyGroupName(const PropertyName &) const override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
virtual int getPaintWidth() const
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
bool usesSolidColour() const
QString getPropertyLabel(const PropertyName &) const override
static ColourDatabase * getInstance()
void sliceableModelReplaced(ModelId, ModelId)
std::map< int, int > m_yorigins