annotate layer/TimeValueLayer.cpp @ 11:2d5005f2b3d9

* Rework handling of layer properties in file I/O -- we now get the individual layers to load and save them rather than doing it via generic property lists in the base class, so as to ensure we read and write meaningful values rather than generic int values requiring conversion.
author Chris Cannam
date Thu, 19 Jan 2006 12:54:38 +0000
parents 8f5b812baaee
children 01849cd277e6
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 A waveform viewer and audio annotation editor.
Chris@3 5 Chris Cannam, Queen Mary University of London, 2005-2006
Chris@0 6
Chris@0 7 This is experimental software. Not for distribution.
Chris@0 8 */
Chris@0 9
Chris@0 10 #include "TimeValueLayer.h"
Chris@0 11
Chris@0 12 #include "base/Model.h"
Chris@0 13 #include "base/RealTime.h"
Chris@0 14 #include "base/Profiler.h"
Chris@0 15 #include "base/View.h"
Chris@0 16
Chris@0 17 #include "model/SparseTimeValueModel.h"
Chris@0 18
Chris@0 19 #include <QPainter>
Chris@6 20 #include <QPainterPath>
Chris@0 21
Chris@0 22 #include <iostream>
Chris@0 23 #include <cmath>
Chris@0 24
Chris@0 25 TimeValueLayer::TimeValueLayer(View *w) :
Chris@0 26 Layer(w),
Chris@0 27 m_model(0),
Chris@0 28 m_colour(Qt::black),
Chris@6 29 m_plotStyle(PlotConnectedPoints)
Chris@0 30 {
Chris@0 31 m_view->addLayer(this);
Chris@0 32 }
Chris@0 33
Chris@0 34 void
Chris@0 35 TimeValueLayer::setModel(SparseTimeValueModel *model)
Chris@0 36 {
Chris@0 37 if (m_model == model) return;
Chris@0 38 m_model = model;
Chris@0 39
Chris@0 40 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 41 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 42 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 43
Chris@0 44 connect(m_model, SIGNAL(completionChanged()),
Chris@0 45 this, SIGNAL(modelCompletionChanged()));
Chris@0 46
Chris@0 47 std::cerr << "TimeValueLayer::setModel(" << model << ")" << std::endl;
Chris@0 48
Chris@0 49 emit modelReplaced();
Chris@0 50 }
Chris@0 51
Chris@0 52 Layer::PropertyList
Chris@0 53 TimeValueLayer::getProperties() const
Chris@0 54 {
Chris@0 55 PropertyList list;
Chris@0 56 list.push_back(tr("Colour"));
Chris@0 57 list.push_back(tr("Plot Type"));
Chris@0 58 return list;
Chris@0 59 }
Chris@0 60
Chris@0 61 Layer::PropertyType
Chris@0 62 TimeValueLayer::getPropertyType(const PropertyName &name) const
Chris@0 63 {
Chris@0 64 return ValueProperty;
Chris@0 65 }
Chris@0 66
Chris@0 67 int
Chris@0 68 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 69 int *min, int *max) const
Chris@0 70 {
Chris@0 71 //!!! factor this colour handling stuff out into a colour manager class
Chris@0 72
Chris@0 73 int deft = 0;
Chris@0 74
Chris@0 75 if (name == tr("Colour")) {
Chris@0 76
Chris@10 77 if (min) *min = 0;
Chris@10 78 if (max) *max = 5;
Chris@0 79
Chris@0 80 if (m_colour == Qt::black) deft = 0;
Chris@0 81 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 82 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 83 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 84 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 85 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 86
Chris@0 87 } else if (name == tr("Plot Type")) {
Chris@0 88
Chris@10 89 if (min) *min = 0;
Chris@10 90 if (max) *max = 4;
Chris@0 91
Chris@0 92 deft = int(m_plotStyle);
Chris@0 93
Chris@0 94 } else {
Chris@0 95
Chris@0 96 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 97 }
Chris@0 98
Chris@0 99 return deft;
Chris@0 100 }
Chris@0 101
Chris@0 102 QString
Chris@0 103 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 104 int value) const
Chris@0 105 {
Chris@0 106 if (name == tr("Colour")) {
Chris@0 107 switch (value) {
Chris@0 108 default:
Chris@0 109 case 0: return tr("Black");
Chris@0 110 case 1: return tr("Red");
Chris@0 111 case 2: return tr("Blue");
Chris@0 112 case 3: return tr("Green");
Chris@0 113 case 4: return tr("Purple");
Chris@0 114 case 5: return tr("Orange");
Chris@0 115 }
Chris@0 116 } else if (name == tr("Plot Type")) {
Chris@0 117 switch (value) {
Chris@0 118 default:
Chris@0 119 case 0: return tr("Points");
Chris@0 120 case 1: return tr("Stems");
Chris@6 121 case 2: return tr("Connected Points");
Chris@6 122 case 3: return tr("Lines");
Chris@6 123 case 4: return tr("Curve");
Chris@0 124 }
Chris@0 125 }
Chris@0 126 return tr("<unknown>");
Chris@0 127 }
Chris@0 128
Chris@0 129 void
Chris@0 130 TimeValueLayer::setProperty(const PropertyName &name, int value)
Chris@0 131 {
Chris@0 132 if (name == tr("Colour")) {
Chris@0 133 switch (value) {
Chris@0 134 default:
Chris@0 135 case 0: setBaseColour(Qt::black); break;
Chris@0 136 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 137 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 138 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 139 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 140 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 141 }
Chris@0 142 } else if (name == tr("Plot Type")) {
Chris@0 143 setPlotStyle(PlotStyle(value));
Chris@0 144 }
Chris@0 145 }
Chris@0 146
Chris@0 147 void
Chris@0 148 TimeValueLayer::setBaseColour(QColor colour)
Chris@0 149 {
Chris@0 150 if (m_colour == colour) return;
Chris@0 151 m_colour = colour;
Chris@0 152 emit layerParametersChanged();
Chris@0 153 }
Chris@0 154
Chris@0 155 void
Chris@0 156 TimeValueLayer::setPlotStyle(PlotStyle style)
Chris@0 157 {
Chris@0 158 if (m_plotStyle == style) return;
Chris@0 159 m_plotStyle = style;
Chris@0 160 emit layerParametersChanged();
Chris@0 161 }
Chris@0 162
Chris@0 163 bool
Chris@0 164 TimeValueLayer::isLayerScrollable() const
Chris@0 165 {
Chris@6 166 // We don't illuminate sections in the line or curve modes, so
Chris@6 167 // they're always scrollable
Chris@6 168
Chris@6 169 if (m_plotStyle == PlotLines ||
Chris@6 170 m_plotStyle == PlotCurve) return true;
Chris@6 171
Chris@0 172 QPoint discard;
Chris@0 173 return !m_view->shouldIlluminateLocalFeatures(this, discard);
Chris@0 174 }
Chris@0 175
Chris@0 176 QRect
Chris@0 177 TimeValueLayer::getFeatureDescriptionRect(QPainter &paint, QPoint pos) const
Chris@0 178 {
Chris@0 179 return QRect(0, 0,
Chris@0 180 std::max(100, paint.fontMetrics().width(tr("No local points"))),
Chris@0 181 70); //!!!
Chris@0 182 }
Chris@0 183
Chris@0 184 //!!! too much in common with TimeInstantLayer
Chris@0 185
Chris@0 186 SparseTimeValueModel::PointList
Chris@0 187 TimeValueLayer::getLocalPoints(int x) const
Chris@0 188 {
Chris@0 189 if (!m_model) return SparseTimeValueModel::PointList();
Chris@0 190
Chris@0 191 long startFrame = m_view->getStartFrame();
Chris@0 192 long endFrame = m_view->getEndFrame();
Chris@0 193 int zoomLevel = m_view->getZoomLevel();
Chris@0 194 long frame = startFrame + x * zoomLevel;
Chris@0 195
Chris@0 196 SparseTimeValueModel::PointList onPoints =
Chris@0 197 m_model->getPoints(frame);
Chris@0 198
Chris@0 199 if (!onPoints.empty()) {
Chris@0 200 return onPoints;
Chris@0 201 }
Chris@0 202
Chris@0 203 SparseTimeValueModel::PointList prevPoints =
Chris@0 204 m_model->getPreviousPoints(frame);
Chris@0 205 SparseTimeValueModel::PointList nextPoints =
Chris@0 206 m_model->getNextPoints(frame);
Chris@0 207
Chris@0 208 SparseTimeValueModel::PointList usePoints = prevPoints;
Chris@0 209
Chris@0 210 if (prevPoints.empty()) {
Chris@0 211 usePoints = nextPoints;
Chris@0 212 } else if (prevPoints.begin()->frame < startFrame &&
Chris@0 213 !(nextPoints.begin()->frame > endFrame)) {
Chris@0 214 usePoints = nextPoints;
Chris@0 215 } else if (nextPoints.begin()->frame - frame <
Chris@0 216 frame - prevPoints.begin()->frame) {
Chris@0 217 usePoints = nextPoints;
Chris@0 218 }
Chris@0 219
Chris@0 220 return usePoints;
Chris@0 221 }
Chris@0 222
Chris@0 223 void
Chris@0 224 TimeValueLayer::paintLocalFeatureDescription(QPainter &paint, QRect rect,
Chris@0 225 QPoint pos) const
Chris@0 226 {
Chris@0 227 //!!! bleagh
Chris@0 228
Chris@0 229 int x = pos.x();
Chris@0 230
Chris@0 231 if (!m_model || !m_model->getSampleRate()) return;
Chris@0 232
Chris@0 233 SparseTimeValueModel::PointList points = getLocalPoints(x);
Chris@0 234
Chris@0 235 QFontMetrics metrics = paint.fontMetrics();
Chris@0 236 int xbase = rect.x() + 5;
Chris@0 237 int ybase = rect.y() + 5;
Chris@0 238
Chris@0 239 if (points.empty()) {
Chris@0 240 QString label = tr("No local points");
Chris@0 241 if (!m_model->isReady()) {
Chris@0 242 label = tr("In progress");
Chris@0 243 }
Chris@0 244 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), label);
Chris@0 245 return;
Chris@0 246 }
Chris@0 247
Chris@0 248 long useFrame = points.begin()->frame;
Chris@0 249
Chris@0 250 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@0 251 QString timeText = QString("%1").arg(rt.toText(true).c_str());
Chris@0 252 QString valueText = QString("%1").arg(points.begin()->value);
Chris@0 253
Chris@0 254 int timewidth = metrics.width(timeText);
Chris@0 255 int valuewidth = metrics.width(valueText);
Chris@0 256 int labelwidth = metrics.width(points.begin()->label);
Chris@0 257
Chris@0 258 int boxheight = metrics.height() * 3 + 4;
Chris@0 259 int boxwidth = std::max(std::max(timewidth, labelwidth), valuewidth);
Chris@0 260
Chris@0 261 paint.drawRect(xbase, ybase, boxwidth + 10,
Chris@0 262 boxheight + 10 - metrics.descent() + 1);
Chris@0 263
Chris@0 264 paint.drawText(xbase + 5, ybase + 5 + metrics.ascent(), timeText);
Chris@0 265 paint.drawText(xbase + 5, ybase + 7 + metrics.ascent() + metrics.height(),
Chris@0 266 valueText);
Chris@0 267 paint.drawText(xbase + 5, ybase + 9 + metrics.ascent() + 2*metrics.height(),
Chris@0 268 points.begin()->label);
Chris@0 269 }
Chris@0 270
Chris@0 271 void
Chris@0 272 TimeValueLayer::paint(QPainter &paint, QRect rect) const
Chris@0 273 {
Chris@0 274 if (!m_model || !m_model->isOK()) return;
Chris@0 275
Chris@0 276 int sampleRate = m_model->getSampleRate();
Chris@0 277 if (!sampleRate) return;
Chris@0 278
Chris@0 279 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 280
Chris@0 281 long startFrame = m_view->getStartFrame();
Chris@0 282 int zoomLevel = m_view->getZoomLevel();
Chris@0 283
Chris@0 284 int x0 = rect.left(), x1 = rect.right();
Chris@0 285 long frame0 = startFrame + x0 * zoomLevel;
Chris@0 286 long frame1 = startFrame + x1 * zoomLevel;
Chris@0 287
Chris@0 288 SparseTimeValueModel::PointList points(m_model->getPoints
Chris@0 289 (frame0, frame1));
Chris@11 290 if (points.empty()) return;
Chris@0 291
Chris@0 292 paint.setPen(m_colour);
Chris@0 293
Chris@0 294 QColor brushColour(m_colour);
Chris@0 295 brushColour.setAlpha(80);
Chris@0 296 paint.setBrush(brushColour);
Chris@0 297
Chris@0 298 // std::cerr << "TimeValueLayer::paint: resolution is "
Chris@0 299 // << m_model->getResolution() << " frames" << std::endl;
Chris@0 300
Chris@0 301 float min = m_model->getValueMinimum();
Chris@0 302 float max = m_model->getValueMaximum();
Chris@0 303 if (max == min) max = min + 1.0;
Chris@0 304
Chris@0 305 int origin = int(nearbyint(m_view->height() -
Chris@0 306 (-min * m_view->height()) / (max - min)));
Chris@0 307
Chris@0 308 QPoint localPos;
Chris@0 309 long illuminateFrame = -1;
Chris@0 310
Chris@0 311 if (m_view->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 312 SparseTimeValueModel::PointList localPoints =
Chris@0 313 getLocalPoints(localPos.x());
Chris@0 314 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 315 }
Chris@6 316
Chris@7 317 int w = m_model->getResolution() / zoomLevel;
Chris@7 318
Chris@6 319 paint.save();
Chris@6 320
Chris@7 321 if (w > 1 &&
Chris@7 322 (m_plotStyle == PlotLines ||
Chris@7 323 m_plotStyle == PlotCurve)) {
Chris@6 324 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@6 325 }
Chris@6 326 QPainterPath path;
Chris@6 327
Chris@0 328 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@0 329 i != points.end(); ++i) {
Chris@0 330
Chris@0 331 const SparseTimeValueModel::Point &p(*i);
Chris@0 332
Chris@0 333 int x = (p.frame - startFrame) / zoomLevel;
Chris@0 334 int y = int(nearbyint(m_view->height() -
Chris@0 335 ((p.value - min) * m_view->height()) /
Chris@0 336 (max - min)));
Chris@0 337
Chris@0 338 if (w < 1) w = 1;
Chris@0 339
Chris@6 340 if (m_plotStyle == PlotLines ||
Chris@6 341 m_plotStyle == PlotCurve) {
Chris@6 342 paint.setPen(m_colour);
Chris@6 343 paint.setBrush(Qt::NoBrush);
Chris@3 344 } else {
Chris@3 345 paint.setPen(m_colour);
Chris@6 346 paint.setBrush(brushColour);
Chris@3 347 }
Chris@0 348
Chris@0 349 if (m_plotStyle == PlotStems) {
Chris@0 350 paint.setPen(brushColour);
Chris@0 351 if (y < origin - 1) {
Chris@0 352 paint.drawRect(x + w/2, y + 1, 1, origin - y);
Chris@0 353 } else if (y > origin + 1) {
Chris@0 354 paint.drawRect(x + w/2, origin, 1, y - origin - 1);
Chris@0 355 }
Chris@0 356 paint.setPen(m_colour);
Chris@0 357 }
Chris@0 358
Chris@0 359 if (illuminateFrame == p.frame) {
Chris@6 360
Chris@0 361 //!!! aside from the problem of choosing a colour, it'd be
Chris@0 362 //better to save the highlighted rects and draw them at
Chris@0 363 //the end perhaps
Chris@6 364
Chris@6 365 //!!! not equipped to illuminate the right section in line
Chris@6 366 //or curve mode
Chris@6 367
Chris@6 368 if (m_plotStyle != PlotCurve &&
Chris@6 369 m_plotStyle != PlotLines) {
Chris@6 370 paint.setPen(Qt::black);//!!!
Chris@6 371 paint.setBrush(Qt::black);//!!!
Chris@6 372 }
Chris@0 373 }
Chris@0 374
Chris@6 375 if (m_plotStyle != PlotLines &&
Chris@6 376 m_plotStyle != PlotCurve) {
Chris@3 377 paint.drawRect(x, y - 1, w, 2);
Chris@3 378 }
Chris@0 379
Chris@6 380 if (m_plotStyle == PlotConnectedPoints ||
Chris@6 381 m_plotStyle == PlotLines ||
Chris@6 382 m_plotStyle == PlotCurve) {
Chris@0 383
Chris@0 384 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@0 385 ++j;
Chris@3 386
Chris@0 387 if (j != points.end()) {
Chris@3 388
Chris@0 389 const SparseTimeValueModel::Point &q(*j);
Chris@0 390 int nx = (q.frame - startFrame) / zoomLevel;
Chris@0 391 int ny = int(nearbyint(m_view->height() -
Chris@0 392 ((q.value - min) * m_view->height()) /
Chris@0 393 (max - min)));
Chris@3 394
Chris@6 395 if (m_plotStyle == PlotConnectedPoints) {
Chris@6 396
Chris@3 397 paint.setPen(brushColour);
Chris@3 398 paint.drawLine(x + w, y, nx, ny);
Chris@6 399
Chris@6 400 } else if (m_plotStyle == PlotLines) {
Chris@6 401
Chris@6 402 paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@6 403
Chris@3 404 } else {
Chris@6 405
Chris@6 406 if (path.isEmpty()) {
Chris@6 407 path.moveTo(x + w/2, y);
Chris@6 408 }
Chris@6 409
Chris@6 410 if (nx - x > 5) {
Chris@6 411 path.cubicTo(x + w, y, nx, ny, nx + w/2, ny);
Chris@6 412 } else {
Chris@6 413 path.lineTo(nx + w/2, ny);
Chris@6 414 }
Chris@3 415 }
Chris@0 416 }
Chris@0 417 }
Chris@0 418
Chris@0 419 /// if (p.label != "") {
Chris@0 420 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@0 421 /// }
Chris@0 422 }
Chris@6 423
Chris@6 424 if (m_plotStyle == PlotCurve && !path.isEmpty()) {
Chris@6 425 paint.drawPath(path);
Chris@6 426 }
Chris@6 427
Chris@6 428 paint.restore();
Chris@6 429
Chris@6 430 // looks like save/restore doesn't deal with this:
Chris@6 431 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 432 }
Chris@6 433
Chris@6 434 QString
Chris@6 435 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 436 {
Chris@6 437 return Layer::toXmlString(indent, extraAttributes +
Chris@6 438 QString(" colour=\"%1\" plotStyle=\"%2\"")
Chris@6 439 .arg(encodeColour(m_colour)).arg(m_plotStyle));
Chris@0 440 }
Chris@0 441
Chris@11 442 void
Chris@11 443 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 444 {
Chris@11 445 QString colourSpec = attributes.value("colour");
Chris@11 446 if (colourSpec != "") {
Chris@11 447 QColor colour(colourSpec);
Chris@11 448 if (colour.isValid()) {
Chris@11 449 setBaseColour(QColor(colourSpec));
Chris@11 450 }
Chris@11 451 }
Chris@11 452
Chris@11 453 bool ok;
Chris@11 454 PlotStyle style = (PlotStyle)
Chris@11 455 attributes.value("plotStyle").toInt(&ok);
Chris@11 456 if (ok) setPlotStyle(style);
Chris@11 457 }
Chris@11 458
Chris@0 459
Chris@0 460 #ifdef INCLUDE_MOCFILES
Chris@0 461 #include "TimeValueLayer.moc.cpp"
Chris@0 462 #endif
Chris@0 463