Chris@133: Chris@133: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@133: Chris@133: /* Chris@133: Sonic Visualiser Chris@133: An audio file viewer and annotation editor. Chris@133: Centre for Digital Music, Queen Mary, University of London. Chris@195: This file copyright 2006-2007 QMUL. Chris@133: Chris@133: This program is free software; you can redistribute it and/or Chris@133: modify it under the terms of the GNU General Public License as Chris@133: published by the Free Software Foundation; either version 2 of the Chris@133: License, or (at your option) any later version. See the file Chris@133: COPYING included with this distribution for more information. Chris@133: */ Chris@133: Chris@193: #include "SliceLayer.h" Chris@133: Chris@133: #include "view/View.h" Chris@153: #include "base/AudioLevel.h" Chris@167: #include "base/RangeMapper.h" Chris@195: #include "base/RealTime.h" Chris@195: Chris@197: #include "ColourMapper.h" Chris@195: #include "PaintAssistant.h" Chris@133: Chris@133: #include Chris@133: #include Chris@133: Chris@193: SliceLayer::SliceLayer() : Chris@193: m_sliceableModel(0), Chris@153: m_colour(Qt::darkBlue), Chris@197: m_colourMap(0), Chris@153: m_energyScale(dBScale), Chris@198: m_samplingMode(SampleMean), Chris@195: m_plotStyle(PlotSteps), Chris@193: m_binScale(LinearBins), Chris@153: m_normalize(false), Chris@194: m_bias(false), Chris@198: m_gain(1.0), Chris@198: m_currentf0(0), Chris@198: m_currentf1(0) Chris@133: { Chris@133: } Chris@133: Chris@193: SliceLayer::~SliceLayer() Chris@133: { Chris@193: Chris@133: } Chris@133: Chris@133: void Chris@193: SliceLayer::setSliceableModel(const Model *model) Chris@133: { Chris@193: const DenseThreeDimensionalModel *sliceable = Chris@193: dynamic_cast(model); Chris@193: Chris@193: if (model && !sliceable) { Chris@193: std::cerr << "WARNING: SliceLayer::setSliceableModel(" << model Chris@193: << "): model is not a DenseThreeDimensionalModel" << std::endl; Chris@193: } Chris@193: Chris@193: if (m_sliceableModel == sliceable) return; Chris@193: Chris@193: m_sliceableModel = sliceable; Chris@193: Chris@193: connect(m_sliceableModel, SIGNAL(modelChanged()), Chris@193: this, SIGNAL(modelChanged())); Chris@193: Chris@193: connect(m_sliceableModel, SIGNAL(modelChanged(size_t, size_t)), Chris@193: this, SIGNAL(modelChanged(size_t, size_t))); Chris@193: Chris@193: connect(m_sliceableModel, SIGNAL(completionChanged()), Chris@193: this, SIGNAL(modelCompletionChanged())); Chris@193: Chris@193: emit modelReplaced(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement) Chris@153: { Chris@193: std::cerr << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << std::endl; Chris@153: Chris@193: if (orig == m_sliceableModel) { Chris@193: setSliceableModel Chris@193: (dynamic_cast(replacement)); Chris@153: } Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::modelAboutToBeDeleted(Model *m) Chris@153: { Chris@193: std::cerr << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << std::endl; Chris@153: Chris@193: if (m == m_sliceableModel) { Chris@193: setSliceableModel(0); Chris@153: } Chris@133: } Chris@133: Chris@198: QString Chris@198: SliceLayer::getFeatureDescription(View *v, QPoint &p) const Chris@198: { Chris@199: int minbin, maxbin, range; Chris@199: return getFeatureDescription(v, p, true, minbin, maxbin, range); Chris@199: } Chris@199: Chris@199: QString Chris@199: SliceLayer::getFeatureDescription(View *v, QPoint &p, Chris@199: bool includeBinDescription, Chris@199: int &minbin, int &maxbin, int &range) const Chris@199: { Chris@199: minbin = 0; Chris@199: maxbin = 0; Chris@198: if (!m_sliceableModel) return ""; Chris@198: Chris@198: int xorigin = m_xorigins[v]; Chris@198: int w = v->width() - xorigin - 1; Chris@198: Chris@198: int mh = m_sliceableModel->getHeight(); Chris@199: minbin = getBinForX(p.x() - xorigin, mh, w); Chris@199: maxbin = getBinForX(p.x() - xorigin + 1, mh, w); Chris@199: Chris@199: if (minbin >= mh) minbin = mh - 1; Chris@199: if (maxbin >= mh) maxbin = mh - 1; Chris@199: if (minbin < 0) minbin = 0; Chris@199: if (maxbin < 0) maxbin = 0; Chris@198: Chris@198: int sampleRate = m_sliceableModel->getSampleRate(); Chris@198: Chris@198: size_t f0 = m_currentf0; Chris@198: size_t f1 = m_currentf1; Chris@198: Chris@198: RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate); Chris@198: RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate); Chris@198: Chris@199: range = f1 - f0 + 1; Chris@198: Chris@199: if (includeBinDescription) { Chris@198: Chris@199: float minvalue = 0.f; Chris@199: if (minbin < m_values.size()) minvalue = m_values[minbin]; Chris@198: Chris@199: float maxvalue = minvalue; Chris@199: if (maxbin < m_values.size()) maxvalue = m_values[maxbin]; Chris@199: Chris@199: if (minvalue > maxvalue) std::swap(minvalue, maxvalue); Chris@199: Chris@199: QString binstr; Chris@199: if (maxbin != minbin) { Chris@199: binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); Chris@199: } else { Chris@199: binstr = QString("%1").arg(minbin+1); Chris@199: } Chris@199: Chris@199: QString valuestr; Chris@199: if (maxvalue != minvalue) { Chris@199: valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue); Chris@199: } else { Chris@199: valuestr = QString("%1").arg(minvalue); Chris@199: } Chris@199: Chris@199: QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples\nBin:\t%4\n%5 value:\t%6") Chris@199: .arg(QString::fromStdString(rt0.toText(true))) Chris@199: .arg(QString::fromStdString(rt1.toText(true))) Chris@199: .arg(range) Chris@199: .arg(binstr) Chris@199: .arg(m_samplingMode == NearestSample ? tr("First") : Chris@199: m_samplingMode == SampleMean ? tr("Mean") : tr("Peak")) Chris@199: .arg(valuestr); Chris@199: Chris@199: return description; Chris@199: Chris@199: } else { Chris@199: Chris@199: QString description = tr("Time:\t%1 - %2\nRange:\t%3 samples") Chris@199: .arg(QString::fromStdString(rt0.toText(true))) Chris@199: .arg(QString::fromStdString(rt1.toText(true))) Chris@199: .arg(range); Chris@199: Chris@199: return description; Chris@199: } Chris@198: } Chris@198: Chris@198: float Chris@198: SliceLayer::getXForBin(int bin, int count, float w) const Chris@198: { Chris@198: float x = 0; Chris@198: Chris@198: switch (m_binScale) { Chris@198: Chris@198: case LinearBins: Chris@198: x = (float(w) * bin) / count; Chris@198: break; Chris@198: Chris@198: case LogBins: Chris@198: x = (float(w) * log10f(bin + 1)) / log10f(count + 1); Chris@198: break; Chris@198: Chris@198: case InvertedLogBins: Chris@198: x = w - (float(w) * log10f(count - bin - 1)) / log10f(count); Chris@198: break; Chris@198: } Chris@198: Chris@198: return x; Chris@198: } Chris@198: Chris@198: int Chris@198: SliceLayer::getBinForX(float x, int count, float w) const Chris@198: { Chris@198: int bin = 0; Chris@198: Chris@198: switch (m_binScale) { Chris@198: Chris@198: case LinearBins: Chris@198: bin = int((x * count) / w + 0.0001); Chris@198: break; Chris@198: Chris@198: case LogBins: Chris@198: bin = int(powf(10.f, (x * log10f(count + 1)) / w) - 1 + 0.0001); Chris@198: break; Chris@198: Chris@198: case InvertedLogBins: Chris@198: bin = count + 1 - int(powf(10.f, (log10f(count) * (w - x)) / float(w)) + 0.0001); Chris@198: break; Chris@198: } Chris@198: Chris@198: return bin; Chris@198: } Chris@198: Chris@133: void Chris@193: SliceLayer::paint(View *v, QPainter &paint, QRect rect) const Chris@133: { Chris@193: if (!m_sliceableModel) return; Chris@133: Chris@195: paint.save(); Chris@195: paint.setRenderHint(QPainter::Antialiasing, false); Chris@195: Chris@195: if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) { Chris@195: if (!m_scalePoints.empty()) { Chris@195: paint.setPen(QColor(240, 240, 240)); //!!! and dark background? Chris@195: for (size_t i = 0; i < m_scalePoints.size(); ++i) { Chris@195: paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]); Chris@195: } Chris@195: } Chris@195: } Chris@195: Chris@195: paint.setPen(m_colour); Chris@195: Chris@195: // int w = (v->width() * 2) / 3; Chris@195: int xorigin = getVerticalScaleWidth(v, paint) + 1; //!!! (v->width() / 2) - (w / 2); Chris@195: int w = v->width() - xorigin - 1; Chris@198: Chris@198: m_xorigins[v] = xorigin; // for use in getFeatureDescription Chris@133: Chris@195: int yorigin = v->height() - 20 - paint.fontMetrics().height() - 7; Chris@195: int h = yorigin - paint.fontMetrics().height() - 8; Chris@195: if (h < 0) return; Chris@133: Chris@195: // int h = (v->height() * 3) / 4; Chris@195: // int yorigin = (v->height() / 2) + (h / 2); Chris@133: Chris@133: QPainterPath path; Chris@133: float thresh = -80.f; Chris@133: Chris@193: int mh = m_sliceableModel->getHeight(); Chris@133: Chris@193: int divisor = 0; Chris@193: Chris@198: m_values.clear(); Chris@193: for (size_t bin = 0; bin < mh; ++bin) { Chris@198: m_values.push_back(0.f); Chris@193: } Chris@193: Chris@193: size_t f0 = v->getCentreFrame(); Chris@193: int f0x = v->getXForFrame(f0); Chris@195: f0 = v->getFrameForX(f0x); Chris@193: size_t f1 = v->getFrameForX(f0x + 1); Chris@195: if (f1 > f0) --f1; Chris@193: Chris@193: size_t col0 = f0 / m_sliceableModel->getResolution(); Chris@193: size_t col1 = col0; Chris@193: if (m_samplingMode != NearestSample) { Chris@193: col1 = f1 / m_sliceableModel->getResolution(); Chris@193: } Chris@195: f0 = col0 * m_sliceableModel->getResolution(); Chris@195: f1 = (col1 + 1) * m_sliceableModel->getResolution() - 1; Chris@193: Chris@198: m_currentf0 = f0; Chris@198: m_currentf1 = f1; Chris@198: Chris@195: for (size_t col = col0; col <= col1; ++col) { Chris@193: for (size_t bin = 0; bin < mh; ++bin) { Chris@193: float value = m_sliceableModel->getValueAt(col, bin); Chris@193: if (m_bias) value *= bin + 1; Chris@193: if (m_samplingMode == SamplePeak) { Chris@198: if (value > m_values[bin]) m_values[bin] = value; Chris@193: } else { Chris@198: m_values[bin] += value; Chris@193: } Chris@153: } Chris@193: ++divisor; Chris@193: } Chris@193: Chris@193: float max = 0.f; Chris@193: for (size_t bin = 0; bin < mh; ++bin) { Chris@198: if (m_samplingMode == SampleMean) m_values[bin] /= divisor; Chris@198: if (m_values[bin] > max) max = m_values[bin]; Chris@193: } Chris@193: if (max != 0.f && m_normalize) { Chris@193: for (size_t bin = 0; bin < mh; ++bin) { Chris@198: m_values[bin] /= max; Chris@193: } Chris@193: } Chris@193: Chris@193: float py = 0; Chris@193: float nx = xorigin; Chris@193: Chris@197: ColourMapper mapper(m_colourMap, 0, 1); Chris@197: Chris@193: for (size_t bin = 0; bin < mh; ++bin) { Chris@193: Chris@198: float x = nx; Chris@198: nx = xorigin + getXForBin(bin + 1, mh, w); Chris@193: Chris@198: float value = m_values[bin]; Chris@193: Chris@193: value *= m_gain; Chris@197: float norm = 0.f; Chris@153: float y = 0.f; Chris@153: Chris@153: switch (m_energyScale) { Chris@153: Chris@153: case dBScale: Chris@153: { Chris@153: float db = thresh; Chris@193: if (value > 0.f) db = 10.f * log10f(value); Chris@153: if (db < thresh) db = thresh; Chris@197: norm = (db - thresh) / -thresh; Chris@197: y = yorigin - (float(h) * norm); Chris@153: break; Chris@153: } Chris@153: Chris@153: case MeterScale: Chris@197: y = AudioLevel::multiplier_to_preview(value, h); Chris@197: norm = float(y) / float(h); Chris@197: y = yorigin - y; Chris@153: break; Chris@153: Chris@153: default: Chris@197: norm = value; Chris@193: y = yorigin - (float(h) * value); Chris@153: break; Chris@153: } Chris@133: Chris@193: if (m_plotStyle == PlotLines) { Chris@193: Chris@193: if (bin == 0) { Chris@193: path.moveTo(x, y); Chris@193: } else { Chris@193: path.lineTo(x, y); Chris@193: } Chris@193: Chris@193: } else if (m_plotStyle == PlotSteps) { Chris@193: Chris@193: if (bin == 0) { Chris@193: path.moveTo(x, y); Chris@193: } else { Chris@193: path.lineTo(x, y); Chris@193: } Chris@193: path.lineTo(nx, y); Chris@193: Chris@193: } else if (m_plotStyle == PlotBlocks) { Chris@193: Chris@193: path.moveTo(x, yorigin); Chris@133: path.lineTo(x, y); Chris@193: path.lineTo(nx, y); Chris@193: path.lineTo(nx, yorigin); Chris@193: path.lineTo(x, yorigin); Chris@197: Chris@197: } else if (m_plotStyle == PlotFilledBlocks) { Chris@197: Chris@197: paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm)); Chris@133: } Chris@193: Chris@193: py = y; Chris@133: } Chris@133: Chris@197: if (m_plotStyle != PlotFilledBlocks) { Chris@197: paint.drawPath(path); Chris@197: } Chris@133: paint.restore(); Chris@198: /* Chris@197: QPoint discard; Chris@197: Chris@197: if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() && Chris@197: v->shouldIlluminateLocalFeatures(this, discard)) { Chris@195: Chris@195: int sampleRate = m_sliceableModel->getSampleRate(); Chris@195: Chris@195: QString startText = QString("%1 / %2") Chris@195: .arg(QString::fromStdString Chris@195: (RealTime::frame2RealTime Chris@195: (f0, sampleRate).toText(true))) Chris@195: .arg(f0); Chris@195: Chris@195: QString endText = QString(" %1 / %2") Chris@195: .arg(QString::fromStdString Chris@195: (RealTime::frame2RealTime Chris@195: (f1, sampleRate).toText(true))) Chris@195: .arg(f1); Chris@195: Chris@195: QString durationText = QString("(%1 / %2) ") Chris@195: .arg(QString::fromStdString Chris@195: (RealTime::frame2RealTime Chris@195: (f1 - f0 + 1, sampleRate).toText(true))) Chris@195: .arg(f1 - f0 + 1); Chris@195: Chris@195: v->drawVisibleText Chris@195: (paint, xorigin + 5, Chris@195: paint.fontMetrics().ascent() + 5, Chris@195: startText, View::OutlinedText); Chris@195: Chris@195: v->drawVisibleText Chris@195: (paint, xorigin + 5, Chris@195: paint.fontMetrics().ascent() + paint.fontMetrics().height() + 10, Chris@195: endText, View::OutlinedText); Chris@195: Chris@195: v->drawVisibleText Chris@195: (paint, xorigin + 5, Chris@195: paint.fontMetrics().ascent() + 2*paint.fontMetrics().height() + 15, Chris@195: durationText, View::OutlinedText); Chris@195: } Chris@195: */ Chris@195: } Chris@195: Chris@195: int Chris@195: SliceLayer::getVerticalScaleWidth(View *v, QPainter &paint) const Chris@195: { Chris@195: if (m_energyScale == LinearScale) { Chris@195: return paint.fontMetrics().width("0.0") + 13; Chris@195: } else { Chris@195: return std::max(paint.fontMetrics().width(tr("0dB")), Chris@195: paint.fontMetrics().width(tr("-Inf"))) + 13; Chris@195: } Chris@195: } Chris@195: Chris@195: void Chris@195: SliceLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const Chris@195: { Chris@195: float thresh = 0; Chris@195: if (m_energyScale != LinearScale) { Chris@195: thresh = AudioLevel::dB_to_multiplier(-80); //!!! thresh Chris@195: } Chris@195: Chris@195: // int h = (rect.height() * 3) / 4; Chris@195: // int y = (rect.height() / 2) - (h / 2); Chris@195: Chris@195: int yorigin = v->height() - 20 - paint.fontMetrics().height() - 6; Chris@195: int h = yorigin - paint.fontMetrics().height() - 8; Chris@195: if (h < 0) return; Chris@195: Chris@195: QRect actual(rect.x(), rect.y() + yorigin - h, rect.width(), h); Chris@195: Chris@195: PaintAssistant::paintVerticalLevelScale Chris@195: (paint, actual, thresh, 1.0 / m_gain, Chris@195: PaintAssistant::Scale(m_energyScale), Chris@195: const_cast *>(&m_scalePoints)); Chris@133: } Chris@133: Chris@153: Layer::PropertyList Chris@193: SliceLayer::getProperties() const Chris@153: { Chris@153: PropertyList list; Chris@153: list.push_back("Colour"); Chris@193: list.push_back("Plot Type"); Chris@198: // list.push_back("Sampling Mode"); Chris@153: list.push_back("Scale"); Chris@153: list.push_back("Normalize"); Chris@153: list.push_back("Gain"); Chris@193: list.push_back("Bin Scale"); Chris@153: Chris@153: return list; Chris@153: } Chris@153: Chris@153: QString Chris@193: SliceLayer::getPropertyLabel(const PropertyName &name) const Chris@153: { Chris@153: if (name == "Colour") return tr("Colour"); Chris@193: if (name == "Plot Type") return tr("Plot Type"); Chris@153: if (name == "Energy Scale") return tr("Scale"); Chris@153: if (name == "Normalize") return tr("Normalize"); Chris@153: if (name == "Gain") return tr("Gain"); Chris@193: if (name == "Sampling Mode") return tr("Sampling Mode"); Chris@193: if (name == "Bin Scale") return tr("Plot X Scale"); Chris@153: return ""; Chris@153: } Chris@153: Chris@153: Layer::PropertyType Chris@193: SliceLayer::getPropertyType(const PropertyName &name) const Chris@153: { Chris@153: if (name == "Gain") return RangeProperty; Chris@153: if (name == "Normalize") return ToggleProperty; Chris@153: return ValueProperty; Chris@153: } Chris@153: Chris@153: QString Chris@193: SliceLayer::getPropertyGroupName(const PropertyName &name) const Chris@153: { Chris@153: if (name == "Scale" || Chris@153: name == "Normalize" || Chris@193: name == "Sampling Mode" || Chris@193: name == "Gain") return tr("Scale"); Chris@193: if (name == "Plot Type" || Chris@193: name == "Bin Scale") return tr("Plot Type"); Chris@153: return QString(); Chris@153: } Chris@153: Chris@153: int Chris@193: SliceLayer::getPropertyRangeAndValue(const PropertyName &name, Chris@153: int *min, int *max) const Chris@153: { Chris@153: int deft = 0; Chris@153: Chris@153: int garbage0, garbage1; Chris@153: if (!min) min = &garbage0; Chris@153: if (!max) max = &garbage1; Chris@153: Chris@153: if (name == "Gain") { Chris@153: Chris@153: *min = -50; Chris@153: *max = 50; Chris@153: Chris@193: std::cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << std::endl; Chris@193: Chris@153: deft = lrint(log10(m_gain) * 20.0); Chris@153: if (deft < *min) deft = *min; Chris@153: if (deft > *max) deft = *max; Chris@153: Chris@153: } else if (name == "Normalize") { Chris@153: Chris@153: deft = (m_normalize ? 1 : 0); Chris@153: Chris@153: } else if (name == "Colour") { Chris@153: Chris@197: if (m_plotStyle == PlotFilledBlocks) { Chris@197: Chris@197: *min = 0; Chris@197: *max = ColourMapper::getColourMapCount() - 1; Chris@153: Chris@197: deft = m_colourMap; Chris@197: Chris@197: } else { Chris@197: Chris@197: *min = 0; Chris@197: *max = 5; Chris@197: Chris@197: if (m_colour == Qt::black) deft = 0; Chris@197: else if (m_colour == Qt::darkRed) deft = 1; Chris@197: else if (m_colour == Qt::darkBlue) deft = 2; Chris@197: else if (m_colour == Qt::darkGreen) deft = 3; Chris@197: else if (m_colour == QColor(200, 50, 255)) deft = 4; Chris@197: else if (m_colour == QColor(255, 150, 50)) deft = 5; Chris@197: } Chris@153: Chris@153: } else if (name == "Scale") { Chris@153: Chris@153: *min = 0; Chris@153: *max = 2; Chris@153: Chris@153: deft = (int)m_energyScale; Chris@153: Chris@193: } else if (name == "Sampling Mode") { Chris@153: Chris@153: *min = 0; Chris@193: *max = 2; Chris@193: Chris@193: deft = (int)m_samplingMode; Chris@153: Chris@193: } else if (name == "Plot Type") { Chris@193: Chris@193: *min = 0; Chris@197: *max = 3; Chris@193: Chris@193: deft = (int)m_plotStyle; Chris@193: Chris@193: } else if (name == "Bin Scale") { Chris@193: Chris@193: *min = 0; Chris@198: *max = 2; Chris@198: // *max = 1; // I don't think we really do want to offer inverted log Chris@193: Chris@193: deft = (int)m_binScale; Chris@193: Chris@153: } else { Chris@153: deft = Layer::getPropertyRangeAndValue(name, min, max); Chris@153: } Chris@153: Chris@153: return deft; Chris@153: } Chris@153: Chris@153: QString Chris@193: SliceLayer::getPropertyValueLabel(const PropertyName &name, Chris@153: int value) const Chris@153: { Chris@153: if (name == "Colour") { Chris@197: if (m_plotStyle == PlotFilledBlocks) { Chris@197: return ColourMapper::getColourMapName(value); Chris@197: } else { Chris@197: switch (value) { Chris@197: default: Chris@197: case 0: return tr("Black"); Chris@197: case 1: return tr("Red"); Chris@197: case 2: return tr("Blue"); Chris@197: case 3: return tr("Green"); Chris@197: case 4: return tr("Purple"); Chris@197: case 5: return tr("Orange"); Chris@197: } Chris@153: } Chris@153: } Chris@153: if (name == "Scale") { Chris@153: switch (value) { Chris@153: default: Chris@153: case 0: return tr("Linear"); Chris@153: case 1: return tr("Meter"); Chris@153: case 2: return tr("dB"); Chris@153: } Chris@153: } Chris@193: if (name == "Sampling Mode") { Chris@153: switch (value) { Chris@153: default: Chris@193: case 0: return tr("Any"); Chris@193: case 1: return tr("Mean"); Chris@193: case 2: return tr("Peak"); Chris@193: } Chris@193: } Chris@193: if (name == "Plot Type") { Chris@193: switch (value) { Chris@193: default: Chris@193: case 0: return tr("Lines"); Chris@193: case 1: return tr("Steps"); Chris@193: case 2: return tr("Blocks"); Chris@197: case 3: return tr("Colours"); Chris@193: } Chris@193: } Chris@193: if (name == "Bin Scale") { Chris@193: switch (value) { Chris@193: default: Chris@198: case 0: return tr("Linear Bins"); Chris@198: case 1: return tr("Log Bins"); Chris@198: case 2: return tr("Rev Log Bins"); Chris@153: } Chris@153: } Chris@153: return tr(""); Chris@153: } Chris@153: Chris@167: RangeMapper * Chris@193: SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const Chris@167: { Chris@167: if (name == "Gain") { Chris@167: return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); Chris@167: } Chris@167: return 0; Chris@167: } Chris@167: Chris@133: void Chris@193: SliceLayer::setProperty(const PropertyName &name, int value) Chris@133: { Chris@153: if (name == "Gain") { Chris@153: setGain(pow(10, float(value)/20.0)); Chris@153: } else if (name == "Colour") { Chris@197: if (m_plotStyle == PlotFilledBlocks) { Chris@197: setFillColourMap(value); Chris@197: } else { Chris@197: switch (value) { Chris@197: default: Chris@197: case 0: setBaseColour(Qt::black); break; Chris@197: case 1: setBaseColour(Qt::darkRed); break; Chris@197: case 2: setBaseColour(Qt::darkBlue); break; Chris@197: case 3: setBaseColour(Qt::darkGreen); break; Chris@197: case 4: setBaseColour(QColor(200, 50, 255)); break; Chris@197: case 5: setBaseColour(QColor(255, 150, 50)); break; Chris@197: } Chris@153: } Chris@153: } else if (name == "Scale") { Chris@153: switch (value) { Chris@153: default: Chris@153: case 0: setEnergyScale(LinearScale); break; Chris@153: case 1: setEnergyScale(MeterScale); break; Chris@153: case 2: setEnergyScale(dBScale); break; Chris@153: } Chris@193: } else if (name == "Plot Type") { Chris@193: setPlotStyle(PlotStyle(value)); Chris@193: } else if (name == "Sampling Mode") { Chris@193: switch (value) { Chris@193: default: Chris@193: case 0: setSamplingMode(NearestSample); break; Chris@193: case 1: setSamplingMode(SampleMean); break; Chris@193: case 2: setSamplingMode(SamplePeak); break; Chris@193: } Chris@193: } else if (name == "Bin Scale") { Chris@193: switch (value) { Chris@193: default: Chris@193: case 0: setBinScale(LinearBins); break; Chris@193: case 1: setBinScale(LogBins); break; Chris@193: case 2: setBinScale(InvertedLogBins); break; Chris@193: } Chris@153: } else if (name == "Normalize") { Chris@153: setNormalize(value ? true : false); Chris@153: } Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setBaseColour(QColor colour) Chris@153: { Chris@153: if (m_colour == colour) return; Chris@153: m_colour = colour; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@197: SliceLayer::setFillColourMap(int map) Chris@197: { Chris@197: if (m_colourMap == map) return; Chris@197: m_colourMap = map; Chris@197: emit layerParametersChanged(); Chris@197: } Chris@197: Chris@197: void Chris@193: SliceLayer::setEnergyScale(EnergyScale scale) Chris@153: { Chris@153: if (m_energyScale == scale) return; Chris@153: m_energyScale = scale; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setSamplingMode(SamplingMode mode) Chris@153: { Chris@193: if (m_samplingMode == mode) return; Chris@193: m_samplingMode = mode; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setPlotStyle(PlotStyle style) Chris@153: { Chris@193: if (m_plotStyle == style) return; Chris@197: bool colourTypeChanged = (style == PlotFilledBlocks || Chris@197: m_plotStyle == PlotFilledBlocks); Chris@193: m_plotStyle = style; Chris@197: if (colourTypeChanged) { Chris@197: emit layerParameterRangesChanged(); Chris@197: } Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setBinScale(BinScale scale) Chris@153: { Chris@193: if (m_binScale == scale) return; Chris@193: m_binScale = scale; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setNormalize(bool n) Chris@153: { Chris@153: if (m_normalize == n) return; Chris@153: m_normalize = n; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setGain(float gain) Chris@153: { Chris@153: if (m_gain == gain) return; Chris@153: m_gain = gain; Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: QString Chris@193: SliceLayer::toXmlString(QString indent, QString extraAttributes) const Chris@153: { Chris@153: QString s; Chris@153: Chris@153: s += QString("colour=\"%1\" " Chris@197: "colourScheme=\"%2\" " Chris@197: "energyScale=\"%3\" " Chris@197: "samplingMode=\"%4\" " Chris@197: "gain=\"%5\" " Chris@197: "normalize=\"%6\"") Chris@153: .arg(encodeColour(m_colour)) Chris@197: .arg(m_colourMap) Chris@153: .arg(m_energyScale) Chris@193: .arg(m_samplingMode) Chris@153: .arg(m_gain) Chris@153: .arg(m_normalize ? "true" : "false"); Chris@153: Chris@153: return Layer::toXmlString(indent, extraAttributes + " " + s); Chris@153: } Chris@153: Chris@153: void Chris@193: SliceLayer::setProperties(const QXmlAttributes &attributes) Chris@153: { Chris@153: bool ok = false; Chris@153: Chris@153: QString colourSpec = attributes.value("colour"); Chris@153: if (colourSpec != "") { Chris@153: QColor colour(colourSpec); Chris@153: if (colour.isValid()) { Chris@153: setBaseColour(QColor(colourSpec)); Chris@153: } Chris@153: } Chris@153: Chris@153: EnergyScale scale = (EnergyScale) Chris@153: attributes.value("energyScale").toInt(&ok); Chris@153: if (ok) setEnergyScale(scale); Chris@153: Chris@193: SamplingMode mode = (SamplingMode) Chris@193: attributes.value("samplingMode").toInt(&ok); Chris@193: if (ok) setSamplingMode(mode); Chris@153: Chris@197: int colourMap = attributes.value("colourScheme").toInt(&ok); Chris@197: if (ok) setFillColourMap(colourMap); Chris@197: Chris@153: float gain = attributes.value("gain").toFloat(&ok); Chris@153: if (ok) setGain(gain); Chris@153: Chris@153: bool normalize = (attributes.value("normalize").trimmed() == "true"); Chris@153: setNormalize(normalize); Chris@133: } Chris@133: Chris@133: bool Chris@193: SliceLayer::getValueExtents(float &min, float &max, bool &logarithmic, Chris@133: QString &units) const Chris@133: { Chris@133: return false; Chris@133: } Chris@133: