annotate layer/TimeValueLayer.cpp @ 1386:fc3d89f88690 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:34:34 +0000
parents a1393b4384a5
children c39f2d439d59
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "TimeValueLayer.h"
Chris@0 17
Chris@128 18 #include "data/model/Model.h"
Chris@0 19 #include "base/RealTime.h"
Chris@0 20 #include "base/Profiler.h"
Chris@197 21 #include "base/LogRange.h"
Chris@437 22 #include "base/RangeMapper.h"
Chris@960 23 #include "base/Pitch.h"
Chris@128 24 #include "view/View.h"
Chris@0 25
Chris@128 26 #include "data/model/SparseTimeValueModel.h"
Chris@340 27 #include "data/model/Labeller.h"
Chris@0 28
Chris@70 29 #include "widgets/ItemEditDialog.h"
Chris@125 30 #include "widgets/ListInputDialog.h"
Chris@701 31 #include "widgets/TextAbbrev.h"
Chris@70 32
Chris@1078 33 #include "ColourDatabase.h"
Chris@376 34 #include "ColourMapper.h"
Chris@691 35 #include "PianoScale.h"
Chris@698 36 #include "LinearNumericalScale.h"
Chris@698 37 #include "LogNumericalScale.h"
Chris@699 38 #include "LinearColourScale.h"
Chris@699 39 #include "LogColourScale.h"
Chris@1078 40 #include "PaintAssistant.h"
Chris@66 41
Chris@0 42 #include <QPainter>
Chris@6 43 #include <QPainterPath>
Chris@21 44 #include <QMouseEvent>
Chris@125 45 #include <QRegExp>
Chris@316 46 #include <QTextStream>
Chris@360 47 #include <QMessageBox>
Chris@340 48 #include <QInputDialog>
Chris@0 49
Chris@0 50 #include <iostream>
Chris@0 51 #include <cmath>
Chris@0 52
Chris@526 53 //#define DEBUG_TIME_VALUE_LAYER 1
Chris@526 54
Chris@44 55 TimeValueLayer::TimeValueLayer() :
Chris@287 56 SingleColourLayer(),
Chris@0 57 m_model(0),
Chris@21 58 m_editing(false),
Chris@23 59 m_originalPoint(0, 0.0, tr("New Point")),
Chris@21 60 m_editingPoint(0, 0.0, tr("New Point")),
Chris@22 61 m_editingCommand(0),
Chris@197 62 m_colourMap(0),
Chris@1362 63 m_colourInverted(false),
Chris@66 64 m_plotStyle(PlotConnectedPoints),
Chris@437 65 m_verticalScale(AutoAlignScale),
Chris@513 66 m_drawSegmentDivisions(true),
Chris@553 67 m_derivative(false),
Chris@437 68 m_scaleMinimum(0),
Chris@437 69 m_scaleMaximum(0)
Chris@0 70 {
Chris@44 71
Chris@0 72 }
Chris@0 73
Chris@0 74 void
Chris@0 75 TimeValueLayer::setModel(SparseTimeValueModel *model)
Chris@0 76 {
Chris@0 77 if (m_model == model) return;
Chris@0 78 m_model = model;
Chris@0 79
Chris@320 80 connectSignals(m_model);
Chris@0 81
Chris@438 82 m_scaleMinimum = 0;
Chris@438 83 m_scaleMaximum = 0;
Chris@438 84
Chris@494 85 if (m_model && m_model->getRDFTypeURI().endsWith("Segment")) {
Chris@494 86 setPlotStyle(PlotSegmentation);
Chris@494 87 }
Chris@494 88 if (m_model && m_model->getRDFTypeURI().endsWith("Change")) {
Chris@494 89 setPlotStyle(PlotSegmentation);
Chris@494 90 }
Chris@494 91
Chris@526 92 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 93 cerr << "TimeValueLayer::setModel(" << model << ")" << endl;
Chris@526 94 #endif
Chris@0 95
Chris@0 96 emit modelReplaced();
Chris@0 97 }
Chris@0 98
Chris@0 99 Layer::PropertyList
Chris@0 100 TimeValueLayer::getProperties() const
Chris@0 101 {
Chris@287 102 PropertyList list = SingleColourLayer::getProperties();
Chris@87 103 list.push_back("Plot Type");
Chris@87 104 list.push_back("Vertical Scale");
Chris@100 105 list.push_back("Scale Units");
Chris@513 106 list.push_back("Draw Segment Division Lines");
Chris@553 107 list.push_back("Show Derivative");
Chris@0 108 return list;
Chris@0 109 }
Chris@0 110
Chris@87 111 QString
Chris@87 112 TimeValueLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 113 {
Chris@87 114 if (name == "Plot Type") return tr("Plot Type");
Chris@87 115 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@100 116 if (name == "Scale Units") return tr("Scale Units");
Chris@513 117 if (name == "Draw Segment Division Lines") return tr("Draw Segment Division Lines");
Chris@553 118 if (name == "Show Derivative") return tr("Show Derivative");
Chris@287 119 return SingleColourLayer::getPropertyLabel(name);
Chris@87 120 }
Chris@87 121
Chris@515 122 QString
Chris@515 123 TimeValueLayer::getPropertyIconName(const PropertyName &name) const
Chris@515 124 {
Chris@515 125 if (name == "Draw Segment Division Lines") return "lines";
Chris@553 126 if (name == "Show Derivative") return "derivative";
Chris@515 127 return "";
Chris@515 128 }
Chris@515 129
Chris@0 130 Layer::PropertyType
Chris@0 131 TimeValueLayer::getPropertyType(const PropertyName &name) const
Chris@0 132 {
Chris@287 133 if (name == "Plot Type") return ValueProperty;
Chris@287 134 if (name == "Vertical Scale") return ValueProperty;
Chris@100 135 if (name == "Scale Units") return UnitsProperty;
Chris@1198 136 if (name == "Colour" && m_plotStyle == PlotSegmentation) return ColourMapProperty;
Chris@513 137 if (name == "Draw Segment Division Lines") return ToggleProperty;
Chris@553 138 if (name == "Show Derivative") return ToggleProperty;
Chris@287 139 return SingleColourLayer::getPropertyType(name);
Chris@0 140 }
Chris@0 141
Chris@198 142 QString
Chris@198 143 TimeValueLayer::getPropertyGroupName(const PropertyName &name) const
Chris@198 144 {
Chris@198 145 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@198 146 return tr("Scale");
Chris@198 147 }
Chris@553 148 if (name == "Plot Type" || name == "Draw Segment Division Lines" ||
Chris@553 149 name == "Show Derivative") {
Chris@513 150 return tr("Plot Type");
Chris@513 151 }
Chris@287 152 return SingleColourLayer::getPropertyGroupName(name);
Chris@198 153 }
Chris@198 154
Chris@698 155 QString
Chris@698 156 TimeValueLayer::getScaleUnits() const
Chris@698 157 {
Chris@698 158 if (m_model) return m_model->getScaleUnits();
Chris@698 159 else return "";
Chris@698 160 }
Chris@698 161
Chris@0 162 int
Chris@0 163 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@1266 164 int *min, int *max, int *deflt) const
Chris@0 165 {
Chris@216 166 int val = 0;
Chris@0 167
Chris@287 168 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@197 169
Chris@287 170 if (min) *min = 0;
Chris@287 171 if (max) *max = ColourMapper::getColourMapCount() - 1;
Chris@287 172 if (deflt) *deflt = 0;
Chris@197 173
Chris@287 174 val = m_colourMap;
Chris@0 175
Chris@87 176 } else if (name == "Plot Type") {
Chris@1266 177
Chris@1266 178 if (min) *min = 0;
Chris@1266 179 if (max) *max = 6;
Chris@216 180 if (deflt) *deflt = int(PlotConnectedPoints);
Chris@1266 181
Chris@1266 182 val = int(m_plotStyle);
Chris@0 183
Chris@87 184 } else if (name == "Vertical Scale") {
Chris@1266 185
Chris@1266 186 if (min) *min = 0;
Chris@1266 187 if (max) *max = 3;
Chris@216 188 if (deflt) *deflt = int(AutoAlignScale);
Chris@1266 189
Chris@1266 190 val = int(m_verticalScale);
Chris@66 191
Chris@100 192 } else if (name == "Scale Units") {
Chris@100 193
Chris@216 194 if (deflt) *deflt = 0;
Chris@100 195 if (m_model) {
Chris@216 196 val = UnitDatabase::getInstance()->getUnitId
Chris@698 197 (getScaleUnits());
Chris@100 198 }
Chris@100 199
Chris@513 200 } else if (name == "Draw Segment Division Lines") {
Chris@513 201
Chris@513 202 if (min) *min = 0;
Chris@1365 203 if (max) *max = 1;
Chris@513 204 if (deflt) *deflt = 1;
Chris@513 205 val = (m_drawSegmentDivisions ? 1.0 : 0.0);
Chris@513 206
Chris@553 207 } else if (name == "Show Derivative") {
Chris@553 208
Chris@553 209 if (min) *min = 0;
Chris@1365 210 if (max) *max = 1;
Chris@553 211 if (deflt) *deflt = 0;
Chris@553 212 val = (m_derivative ? 1.0 : 0.0);
Chris@553 213
Chris@0 214 } else {
Chris@1266 215
Chris@1266 216 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 217 }
Chris@0 218
Chris@216 219 return val;
Chris@0 220 }
Chris@0 221
Chris@0 222 QString
Chris@0 223 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1266 224 int value) const
Chris@0 225 {
Chris@287 226 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@1362 227 return ColourMapper::getColourMapLabel(value);
Chris@87 228 } else if (name == "Plot Type") {
Chris@1266 229 switch (value) {
Chris@1266 230 default:
Chris@1266 231 case 0: return tr("Points");
Chris@1266 232 case 1: return tr("Stems");
Chris@1266 233 case 2: return tr("Connected Points");
Chris@1266 234 case 3: return tr("Lines");
Chris@1266 235 case 4: return tr("Curve");
Chris@1266 236 case 5: return tr("Segmentation");
Chris@1266 237 case 6: return tr("Discrete Curves");
Chris@1266 238 }
Chris@87 239 } else if (name == "Vertical Scale") {
Chris@1266 240 switch (value) {
Chris@1266 241 default:
Chris@1266 242 case 0: return tr("Auto-Align");
Chris@1266 243 case 1: return tr("Linear");
Chris@1266 244 case 2: return tr("Log");
Chris@1266 245 case 3: return tr("+/-1");
Chris@1266 246 }
Chris@0 247 }
Chris@287 248 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 249 }
Chris@0 250
Chris@0 251 void
Chris@0 252 TimeValueLayer::setProperty(const PropertyName &name, int value)
Chris@0 253 {
Chris@287 254 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@287 255 setFillColourMap(value);
Chris@87 256 } else if (name == "Plot Type") {
Chris@1266 257 setPlotStyle(PlotStyle(value));
Chris@87 258 } else if (name == "Vertical Scale") {
Chris@1266 259 setVerticalScale(VerticalScale(value));
Chris@100 260 } else if (name == "Scale Units") {
Chris@100 261 if (m_model) {
Chris@100 262 m_model->setScaleUnits
Chris@100 263 (UnitDatabase::getInstance()->getUnitById(value));
Chris@100 264 emit modelChanged();
Chris@100 265 }
Chris@513 266 } else if (name == "Draw Segment Division Lines") {
Chris@513 267 setDrawSegmentDivisions(value > 0.5);
Chris@553 268 } else if (name == "Show Derivative") {
Chris@553 269 setShowDerivative(value > 0.5);
Chris@287 270 } else {
Chris@287 271 SingleColourLayer::setProperty(name, value);
Chris@0 272 }
Chris@0 273 }
Chris@0 274
Chris@0 275 void
Chris@197 276 TimeValueLayer::setFillColourMap(int map)
Chris@197 277 {
Chris@197 278 if (m_colourMap == map) return;
Chris@197 279 m_colourMap = map;
Chris@197 280 emit layerParametersChanged();
Chris@197 281 }
Chris@197 282
Chris@197 283 void
Chris@0 284 TimeValueLayer::setPlotStyle(PlotStyle style)
Chris@0 285 {
Chris@0 286 if (m_plotStyle == style) return;
Chris@197 287 bool colourTypeChanged = (style == PlotSegmentation ||
Chris@197 288 m_plotStyle == PlotSegmentation);
Chris@0 289 m_plotStyle = style;
Chris@197 290 if (colourTypeChanged) {
Chris@197 291 emit layerParameterRangesChanged();
Chris@197 292 }
Chris@0 293 emit layerParametersChanged();
Chris@0 294 }
Chris@0 295
Chris@66 296 void
Chris@66 297 TimeValueLayer::setVerticalScale(VerticalScale scale)
Chris@66 298 {
Chris@66 299 if (m_verticalScale == scale) return;
Chris@66 300 m_verticalScale = scale;
Chris@66 301 emit layerParametersChanged();
Chris@66 302 }
Chris@66 303
Chris@513 304 void
Chris@513 305 TimeValueLayer::setDrawSegmentDivisions(bool draw)
Chris@513 306 {
Chris@513 307 if (m_drawSegmentDivisions == draw) return;
Chris@513 308 m_drawSegmentDivisions = draw;
Chris@513 309 emit layerParametersChanged();
Chris@513 310 }
Chris@513 311
Chris@553 312 void
Chris@553 313 TimeValueLayer::setShowDerivative(bool show)
Chris@553 314 {
Chris@553 315 if (m_derivative == show) return;
Chris@553 316 m_derivative = show;
Chris@553 317 emit layerParametersChanged();
Chris@553 318 }
Chris@553 319
Chris@0 320 bool
Chris@918 321 TimeValueLayer::isLayerScrollable(const LayerGeometryProvider *v) const
Chris@0 322 {
Chris@6 323 // We don't illuminate sections in the line or curve modes, so
Chris@6 324 // they're always scrollable
Chris@6 325
Chris@6 326 if (m_plotStyle == PlotLines ||
Chris@1266 327 m_plotStyle == PlotCurve ||
Chris@615 328 m_plotStyle == PlotDiscreteCurves) return true;
Chris@6 329
Chris@0 330 QPoint discard;
Chris@44 331 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@0 332 }
Chris@0 333
Chris@79 334 bool
Chris@908 335 TimeValueLayer::getValueExtents(double &min, double &max,
Chris@101 336 bool &logarithmic, QString &unit) const
Chris@79 337 {
Chris@101 338 if (!m_model) return false;
Chris@668 339
Chris@79 340 min = m_model->getValueMinimum();
Chris@79 341 max = m_model->getValueMaximum();
Chris@668 342
Chris@101 343 logarithmic = (m_verticalScale == LogScale);
Chris@668 344
Chris@698 345 unit = getScaleUnits();
Chris@668 346
Chris@553 347 if (m_derivative) {
Chris@908 348 max = std::max(fabs(min), fabs(max));
Chris@553 349 min = -max;
Chris@553 350 }
Chris@629 351
Chris@629 352 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 353 cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << endl;
Chris@629 354 #endif
Chris@629 355
Chris@668 356 if (!shouldAutoAlign() && !logarithmic && !m_derivative) {
Chris@668 357
Chris@668 358 if (max == min) {
Chris@668 359 max = max + 0.5;
Chris@668 360 min = min - 0.5;
Chris@668 361 } else {
Chris@908 362 double margin = (max - min) / 10.0;
Chris@668 363 max = max + margin;
Chris@668 364 min = min - margin;
Chris@668 365 }
Chris@668 366
Chris@668 367 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 368 cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << " (after adjustment)" << endl;
Chris@668 369 #endif
Chris@668 370 }
Chris@668 371
Chris@79 372 return true;
Chris@79 373 }
Chris@79 374
Chris@101 375 bool
Chris@908 376 TimeValueLayer::getDisplayExtents(double &min, double &max) const
Chris@101 377 {
Chris@296 378 if (!m_model || shouldAutoAlign()) return false;
Chris@101 379
Chris@437 380 if (m_scaleMinimum == m_scaleMaximum) {
Chris@668 381 bool log;
Chris@668 382 QString unit;
Chris@668 383 getValueExtents(min, max, log, unit);
Chris@553 384 } else {
Chris@553 385 min = m_scaleMinimum;
Chris@553 386 max = m_scaleMaximum;
Chris@437 387 }
Chris@437 388
Chris@553 389 if (m_derivative) {
Chris@908 390 max = std::max(fabs(min), fabs(max));
Chris@553 391 min = -max;
Chris@553 392 }
Chris@437 393
Chris@526 394 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 395 cerr << "TimeValueLayer::getDisplayExtents: min = " << min << ", max = " << max << endl;
Chris@526 396 #endif
Chris@437 397
Chris@101 398 return true;
Chris@101 399 }
Chris@101 400
Chris@437 401 bool
Chris@908 402 TimeValueLayer::setDisplayExtents(double min, double max)
Chris@437 403 {
Chris@437 404 if (!m_model) return false;
Chris@437 405
Chris@437 406 if (min == max) {
Chris@437 407 if (min == 0.f) {
Chris@437 408 max = 1.f;
Chris@437 409 } else {
Chris@437 410 max = min * 1.0001;
Chris@437 411 }
Chris@437 412 }
Chris@437 413
Chris@437 414 m_scaleMinimum = min;
Chris@437 415 m_scaleMaximum = max;
Chris@437 416
Chris@526 417 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 418 cerr << "TimeValueLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
Chris@526 419 #endif
Chris@437 420
Chris@437 421 emit layerParametersChanged();
Chris@437 422 return true;
Chris@437 423 }
Chris@437 424
Chris@437 425 int
Chris@437 426 TimeValueLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@437 427 {
Chris@437 428 if (shouldAutoAlign()) return 0;
Chris@437 429 if (!m_model) return 0;
Chris@437 430
Chris@439 431 defaultStep = 0;
Chris@437 432 return 100;
Chris@437 433 }
Chris@437 434
Chris@437 435 int
Chris@437 436 TimeValueLayer::getCurrentVerticalZoomStep() const
Chris@437 437 {
Chris@437 438 if (shouldAutoAlign()) return 0;
Chris@437 439 if (!m_model) return 0;
Chris@437 440
Chris@437 441 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@437 442 if (!mapper) return 0;
Chris@437 443
Chris@908 444 double dmin, dmax;
Chris@437 445 getDisplayExtents(dmin, dmax);
Chris@437 446
Chris@437 447 int nr = mapper->getPositionForValue(dmax - dmin);
Chris@526 448
Chris@526 449 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 450 cerr << "TimeValueLayer::getCurrentVerticalZoomStep: dmin = " << dmin << ", dmax = " << dmax << ", nr = " << nr << endl;
Chris@526 451 #endif
Chris@526 452
Chris@437 453 delete mapper;
Chris@437 454
Chris@437 455 return 100 - nr;
Chris@437 456 }
Chris@437 457
Chris@437 458 void
Chris@437 459 TimeValueLayer::setVerticalZoomStep(int step)
Chris@437 460 {
Chris@437 461 if (shouldAutoAlign()) return;
Chris@437 462 if (!m_model) return;
Chris@437 463
Chris@437 464 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@437 465 if (!mapper) return;
Chris@437 466
Chris@908 467 double min, max;
Chris@437 468 bool logarithmic;
Chris@437 469 QString unit;
Chris@437 470 getValueExtents(min, max, logarithmic, unit);
Chris@437 471
Chris@908 472 double dmin, dmax;
Chris@437 473 getDisplayExtents(dmin, dmax);
Chris@437 474
Chris@908 475 double newdist = mapper->getValueForPosition(100 - step);
Chris@437 476
Chris@908 477 double newmin, newmax;
Chris@437 478
Chris@437 479 if (logarithmic) {
Chris@437 480
Chris@437 481 // see SpectrogramLayer::setVerticalZoomStep
Chris@437 482
Chris@908 483 newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@437 484 newmin = newmax - newdist;
Chris@437 485
Chris@526 486 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 487 cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
Chris@526 488 #endif
Chris@437 489
Chris@437 490 } else {
Chris@908 491 double dmid = (dmax + dmin) / 2;
Chris@437 492 newmin = dmid - newdist / 2;
Chris@437 493 newmax = dmid + newdist / 2;
Chris@437 494 }
Chris@437 495
Chris@437 496 if (newmin < min) {
Chris@437 497 newmax += (min - newmin);
Chris@437 498 newmin = min;
Chris@437 499 }
Chris@437 500 if (newmax > max) {
Chris@437 501 newmax = max;
Chris@437 502 }
Chris@437 503
Chris@526 504 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 505 cerr << "TimeValueLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
Chris@526 506 #endif
Chris@437 507
Chris@437 508 setDisplayExtents(newmin, newmax);
Chris@437 509 }
Chris@437 510
Chris@437 511 RangeMapper *
Chris@437 512 TimeValueLayer::getNewVerticalZoomRangeMapper() const
Chris@437 513 {
Chris@437 514 if (!m_model) return 0;
Chris@437 515
Chris@437 516 RangeMapper *mapper;
Chris@437 517
Chris@908 518 double min, max;
Chris@437 519 bool logarithmic;
Chris@437 520 QString unit;
Chris@437 521 getValueExtents(min, max, logarithmic, unit);
Chris@437 522
Chris@437 523 if (min == max) return 0;
Chris@437 524
Chris@437 525 if (logarithmic) {
Chris@437 526 mapper = new LogRangeMapper(0, 100, min, max, unit);
Chris@437 527 } else {
Chris@437 528 mapper = new LinearRangeMapper(0, 100, min, max, unit);
Chris@437 529 }
Chris@437 530
Chris@437 531 return mapper;
Chris@437 532 }
Chris@437 533
Chris@0 534 SparseTimeValueModel::PointList
Chris@918 535 TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
Chris@0 536 {
Chris@0 537 if (!m_model) return SparseTimeValueModel::PointList();
Chris@0 538
Chris@908 539 sv_frame_t frame = v->getFrameForX(x);
Chris@0 540
Chris@0 541 SparseTimeValueModel::PointList onPoints =
Chris@1266 542 m_model->getPoints(frame);
Chris@0 543
Chris@0 544 if (!onPoints.empty()) {
Chris@1266 545 return onPoints;
Chris@0 546 }
Chris@0 547
Chris@0 548 SparseTimeValueModel::PointList prevPoints =
Chris@1266 549 m_model->getPreviousPoints(frame);
Chris@0 550 SparseTimeValueModel::PointList nextPoints =
Chris@1266 551 m_model->getNextPoints(frame);
Chris@0 552
Chris@0 553 SparseTimeValueModel::PointList usePoints = prevPoints;
Chris@0 554
Chris@0 555 if (prevPoints.empty()) {
Chris@1266 556 usePoints = nextPoints;
Chris@631 557 } else if (nextPoints.empty()) {
Chris@631 558 // stick with prevPoints
Chris@908 559 } else if (prevPoints.begin()->frame < v->getStartFrame() &&
Chris@1266 560 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@1266 561 usePoints = nextPoints;
Chris@0 562 } else if (nextPoints.begin()->frame - frame <
Chris@1266 563 frame - prevPoints.begin()->frame) {
Chris@1266 564 usePoints = nextPoints;
Chris@0 565 }
Chris@0 566
Chris@28 567 if (!usePoints.empty()) {
Chris@1266 568 int fuzz = 2;
Chris@1266 569 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@1266 570 if ((px > x && px - x > fuzz) ||
Chris@1266 571 (px < x && x - px > fuzz + 3)) {
Chris@1266 572 usePoints.clear();
Chris@1266 573 }
Chris@28 574 }
Chris@28 575
Chris@0 576 return usePoints;
Chris@0 577 }
Chris@0 578
Chris@25 579 QString
Chris@909 580 TimeValueLayer::getLabelPreceding(sv_frame_t frame) const
Chris@552 581 {
Chris@552 582 if (!m_model) return "";
Chris@552 583 SparseTimeValueModel::PointList points = m_model->getPreviousPoints(frame);
Chris@552 584 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@552 585 i != points.end(); ++i) {
Chris@552 586 if (i->label != "") return i->label;
Chris@552 587 }
Chris@552 588 return "";
Chris@552 589 }
Chris@552 590
Chris@552 591 QString
Chris@918 592 TimeValueLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@0 593 {
Chris@25 594 int x = pos.x();
Chris@0 595
Chris@25 596 if (!m_model || !m_model->getSampleRate()) return "";
Chris@0 597
Chris@44 598 SparseTimeValueModel::PointList points = getLocalPoints(v, x);
Chris@0 599
Chris@0 600 if (points.empty()) {
Chris@1266 601 if (!m_model->isReady()) {
Chris@1266 602 return tr("In progress");
Chris@1266 603 } else {
Chris@1266 604 return tr("No local points");
Chris@1266 605 }
Chris@0 606 }
Chris@0 607
Chris@908 608 sv_frame_t useFrame = points.begin()->frame;
Chris@0 609
Chris@0 610 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@25 611
Chris@960 612 QString valueText;
Chris@960 613 float value = points.begin()->value;
Chris@960 614 QString unit = getScaleUnits();
Chris@960 615
Chris@960 616 if (unit == "Hz") {
Chris@960 617 valueText = tr("%1 Hz (%2, %3)")
Chris@960 618 .arg(value)
Chris@960 619 .arg(Pitch::getPitchLabelForFrequency(value))
Chris@960 620 .arg(Pitch::getPitchForFrequency(value));
Chris@960 621 } else if (unit != "") {
Chris@960 622 valueText = tr("%1 %2").arg(value).arg(unit);
Chris@960 623 } else {
Chris@960 624 valueText = tr("%1").arg(value);
Chris@960 625 }
Chris@960 626
Chris@25 627 QString text;
Chris@0 628
Chris@25 629 if (points.begin()->label == "") {
Chris@1266 630 text = QString(tr("Time:\t%1\nValue:\t%2\nNo label"))
Chris@1266 631 .arg(rt.toText(true).c_str())
Chris@1266 632 .arg(valueText);
Chris@101 633 } else {
Chris@1266 634 text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4"))
Chris@1266 635 .arg(rt.toText(true).c_str())
Chris@1266 636 .arg(valueText)
Chris@1266 637 .arg(points.begin()->label);
Chris@25 638 }
Chris@0 639
Chris@44 640 pos = QPoint(v->getXForFrame(useFrame),
Chris@1266 641 getYForValue(v, points.begin()->value));
Chris@25 642 return text;
Chris@0 643 }
Chris@0 644
Chris@28 645 bool
Chris@918 646 TimeValueLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@1266 647 int &resolution,
Chris@1266 648 SnapType snap) const
Chris@13 649 {
Chris@13 650 if (!m_model) {
Chris@1266 651 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@13 652 }
Chris@13 653
Chris@13 654 resolution = m_model->getResolution();
Chris@28 655 SparseTimeValueModel::PointList points;
Chris@13 656
Chris@28 657 if (snap == SnapNeighbouring) {
Chris@1266 658
Chris@1266 659 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@1266 660 if (points.empty()) return false;
Chris@1266 661 frame = points.begin()->frame;
Chris@1266 662 return true;
Chris@28 663 }
Chris@28 664
Chris@28 665 points = m_model->getPoints(frame, frame);
Chris@908 666 sv_frame_t snapped = frame;
Chris@28 667 bool found = false;
Chris@13 668
Chris@13 669 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@1266 670 i != points.end(); ++i) {
Chris@13 671
Chris@1266 672 if (snap == SnapRight) {
Chris@28 673
Chris@1266 674 if (i->frame > frame) {
Chris@1266 675 snapped = i->frame;
Chris@1266 676 found = true;
Chris@1266 677 break;
Chris@1266 678 }
Chris@28 679
Chris@1266 680 } else if (snap == SnapLeft) {
Chris@28 681
Chris@1266 682 if (i->frame <= frame) {
Chris@1266 683 snapped = i->frame;
Chris@1266 684 found = true; // don't break, as the next may be better
Chris@1266 685 } else {
Chris@1266 686 break;
Chris@1266 687 }
Chris@28 688
Chris@1266 689 } else { // nearest
Chris@28 690
Chris@1266 691 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@1266 692 ++j;
Chris@28 693
Chris@1266 694 if (j == points.end()) {
Chris@28 695
Chris@1266 696 snapped = i->frame;
Chris@1266 697 found = true;
Chris@1266 698 break;
Chris@28 699
Chris@1266 700 } else if (j->frame >= frame) {
Chris@28 701
Chris@1266 702 if (j->frame - frame < frame - i->frame) {
Chris@1266 703 snapped = j->frame;
Chris@1266 704 } else {
Chris@1266 705 snapped = i->frame;
Chris@1266 706 }
Chris@1266 707 found = true;
Chris@1266 708 break;
Chris@1266 709 }
Chris@1266 710 }
Chris@13 711 }
Chris@13 712
Chris@28 713 frame = snapped;
Chris@28 714 return found;
Chris@13 715 }
Chris@13 716
Chris@517 717 bool
Chris@918 718 TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@805 719 int &resolution,
Chris@517 720 SnapType snap) const
Chris@517 721 {
Chris@517 722 if (!m_model) {
Chris@1266 723 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
Chris@517 724 }
Chris@517 725
Chris@517 726 resolution = m_model->getResolution();
Chris@517 727
Chris@517 728 const SparseTimeValueModel::PointList &points = m_model->getPoints();
Chris@517 729 SparseTimeValueModel::PointList close = m_model->getPoints(frame, frame);
Chris@517 730
Chris@517 731 SparseTimeValueModel::PointList::const_iterator i;
Chris@517 732
Chris@908 733 sv_frame_t matchframe = frame;
Chris@908 734 double matchvalue = 0.0;
Chris@517 735
Chris@517 736 for (i = close.begin(); i != close.end(); ++i) {
Chris@517 737 if (i->frame > frame) break;
Chris@517 738 matchvalue = i->value;
Chris@517 739 matchframe = i->frame;
Chris@517 740 }
Chris@517 741
Chris@908 742 sv_frame_t snapped = frame;
Chris@517 743 bool found = false;
Chris@517 744 bool distant = false;
Chris@908 745 double epsilon = 0.0001;
Chris@517 746
Chris@517 747 i = close.begin();
Chris@517 748
Chris@517 749 // Scan through the close points first, then the more distant ones
Chris@851 750 // if no suitable close one is found. So the while-termination
Chris@851 751 // condition here can only happen once i has passed through the
Chris@851 752 // whole of the close container and then the whole of the separate
Chris@851 753 // points container. The two iterators are totally distinct, but
Chris@851 754 // have the same type so we cheekily use the same variable and a
Chris@851 755 // single loop for both.
Chris@517 756
Chris@517 757 while (i != points.end()) {
Chris@517 758
Chris@851 759 if (!distant) {
Chris@851 760 if (i == close.end()) {
Chris@851 761 // switch from the close container to the points container
Chris@851 762 i = points.begin();
Chris@851 763 distant = true;
Chris@851 764 }
Chris@517 765 }
Chris@517 766
Chris@1266 767 if (snap == SnapRight) {
Chris@517 768
Chris@1266 769 if (i->frame > matchframe &&
Chris@908 770 fabs(i->value - matchvalue) < epsilon) {
Chris@1266 771 snapped = i->frame;
Chris@1266 772 found = true;
Chris@1266 773 break;
Chris@1266 774 }
Chris@517 775
Chris@1266 776 } else if (snap == SnapLeft) {
Chris@517 777
Chris@1266 778 if (i->frame < matchframe) {
Chris@908 779 if (fabs(i->value - matchvalue) < epsilon) {
Chris@517 780 snapped = i->frame;
Chris@517 781 found = true; // don't break, as the next may be better
Chris@517 782 }
Chris@1266 783 } else if (found || distant) {
Chris@1266 784 break;
Chris@1266 785 }
Chris@517 786
Chris@1266 787 } else {
Chris@517 788 // no other snap types supported
Chris@1266 789 }
Chris@517 790
Chris@517 791 ++i;
Chris@517 792 }
Chris@517 793
Chris@517 794 frame = snapped;
Chris@517 795 return found;
Chris@517 796 }
Chris@517 797
Chris@101 798 void
Chris@918 799 TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
Chris@101 800 {
Chris@101 801 min = 0.0;
Chris@101 802 max = 0.0;
Chris@101 803 log = false;
Chris@101 804
Chris@296 805 if (shouldAutoAlign()) {
Chris@101 806
Chris@698 807 if (!v->getValueExtents(getScaleUnits(), min, max, log)) {
Chris@101 808 min = m_model->getValueMinimum();
Chris@101 809 max = m_model->getValueMaximum();
Chris@668 810 } else if (log) {
Chris@668 811 LogRange::mapRange(min, max);
Chris@101 812 }
Chris@101 813
Chris@101 814 } else if (m_verticalScale == PlusMinusOneScale) {
Chris@101 815
Chris@101 816 min = -1.0;
Chris@101 817 max = 1.0;
Chris@101 818
Chris@101 819 } else {
Chris@101 820
Chris@437 821 getDisplayExtents(min, max);
Chris@632 822
Chris@101 823 if (m_verticalScale == LogScale) {
Chris@197 824 LogRange::mapRange(min, max);
Chris@101 825 log = true;
Chris@101 826 }
Chris@101 827 }
Chris@101 828
Chris@629 829 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 830 cerr << "TimeValueLayer::getScaleExtents: min = " << min << ", max = " << max << endl;
Chris@629 831 #endif
Chris@101 832 }
Chris@101 833
Chris@21 834 int
Chris@918 835 TimeValueLayer::getYForValue(LayerGeometryProvider *v, double val) const
Chris@21 836 {
Chris@908 837 double min = 0.0, max = 0.0;
Chris@101 838 bool logarithmic = false;
Chris@918 839 int h = v->getPaintHeight();
Chris@79 840
Chris@101 841 getScaleExtents(v, min, max, logarithmic);
Chris@101 842
Chris@526 843 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 844 cerr << "getYForValue(" << val << "): min " << min << ", max "
Chris@682 845 << max << ", log " << logarithmic << endl;
Chris@526 846 #endif
Chris@101 847
Chris@101 848 if (logarithmic) {
Chris@197 849 val = LogRange::map(val);
Chris@79 850 }
Chris@79 851
Chris@66 852 return int(h - ((val - min) * h) / (max - min));
Chris@21 853 }
Chris@21 854
Chris@908 855 double
Chris@918 856 TimeValueLayer::getValueForY(LayerGeometryProvider *v, int y) const
Chris@21 857 {
Chris@908 858 double min = 0.0, max = 0.0;
Chris@101 859 bool logarithmic = false;
Chris@918 860 int h = v->getPaintHeight();
Chris@21 861
Chris@101 862 getScaleExtents(v, min, max, logarithmic);
Chris@101 863
Chris@908 864 double val = min + (double(h - y) * double(max - min)) / h;
Chris@101 865
Chris@101 866 if (logarithmic) {
Chris@437 867 val = LogRange::map(val);
Chris@101 868 }
Chris@101 869
Chris@101 870 return val;
Chris@21 871 }
Chris@21 872
Chris@296 873 bool
Chris@296 874 TimeValueLayer::shouldAutoAlign() const
Chris@296 875 {
Chris@296 876 if (!m_model) return false;
Chris@698 877 QString unit = getScaleUnits();
Chris@296 878 return (m_verticalScale == AutoAlignScale && unit != "");
Chris@296 879 }
Chris@296 880
Chris@68 881 QColor
Chris@918 882 TimeValueLayer::getColourForValue(LayerGeometryProvider *v, double val) const
Chris@68 883 {
Chris@908 884 double min, max;
Chris@101 885 bool log;
Chris@101 886 getScaleExtents(v, min, max, log);
Chris@68 887
Chris@197 888 if (min > max) std::swap(min, max);
Chris@197 889 if (max == min) max = min + 1;
Chris@197 890
Chris@101 891 if (log) {
Chris@197 892 val = LogRange::map(val);
Chris@68 893 }
Chris@68 894
Chris@526 895 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 896 cerr << "TimeValueLayer::getColourForValue: min " << min << ", max "
Chris@682 897 << max << ", log " << log << ", value " << val << endl;
Chris@526 898 #endif
Chris@68 899
Chris@1362 900 QColor solid = ColourMapper(m_colourMap, m_colourInverted, min, max).map(val);
Chris@197 901 return QColor(solid.red(), solid.green(), solid.blue(), 120);
Chris@68 902 }
Chris@68 903
Chris@287 904 int
Chris@287 905 TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 906 {
Chris@287 907 impose = false;
Chris@287 908 return ColourDatabase::getInstance()->getColourIndex
Chris@287 909 (QString(darkbg ? "Bright Green" : "Green"));
Chris@287 910 }
Chris@287 911
Chris@0 912 void
Chris@916 913 TimeValueLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 914 {
Chris@0 915 if (!m_model || !m_model->isOK()) return;
Chris@0 916
Chris@908 917 sv_samplerate_t sampleRate = m_model->getSampleRate();
Chris@0 918 if (!sampleRate) return;
Chris@0 919
Chris@353 920 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@353 921
Chris@0 922 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 923
Chris@0 924 int x0 = rect.left(), x1 = rect.right();
Chris@908 925 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@908 926 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@553 927 if (m_derivative) --frame0;
Chris@0 928
Chris@0 929 SparseTimeValueModel::PointList points(m_model->getPoints
Chris@1266 930 (frame0, frame1));
Chris@11 931 if (points.empty()) return;
Chris@0 932
Chris@287 933 paint.setPen(getBaseQColor());
Chris@0 934
Chris@287 935 QColor brushColour(getBaseQColor());
Chris@0 936 brushColour.setAlpha(80);
Chris@0 937 paint.setBrush(brushColour);
Chris@0 938
Chris@526 939 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 940 cerr << "TimeValueLayer::paint: resolution is "
Chris@1266 941 << m_model->getResolution() << " frames" << endl;
Chris@526 942 #endif
Chris@0 943
Chris@908 944 double min = m_model->getValueMinimum();
Chris@908 945 double max = m_model->getValueMaximum();
Chris@0 946 if (max == min) max = min + 1.0;
Chris@0 947
Chris@918 948 int origin = int(nearbyint(v->getPaintHeight() -
Chris@1266 949 (-min * v->getPaintHeight()) / (max - min)));
Chris@0 950
Chris@0 951 QPoint localPos;
Chris@908 952 sv_frame_t illuminateFrame = -1;
Chris@0 953
Chris@44 954 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1266 955 SparseTimeValueModel::PointList localPoints =
Chris@1266 956 getLocalPoints(v, localPos.x());
Chris@526 957 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 958 cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl;
Chris@526 959 #endif
Chris@1266 960 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 961 }
Chris@6 962
Chris@20 963 int w =
Chris@1266 964 v->getXForFrame(frame0 + m_model->getResolution()) -
Chris@1266 965 v->getXForFrame(frame0);
Chris@7 966
Chris@629 967 if (m_plotStyle == PlotStems) {
Chris@629 968 if (w < 2) w = 2;
Chris@629 969 } else {
Chris@629 970 if (w < 1) w = 1;
Chris@629 971 }
Chris@629 972
Chris@6 973 paint.save();
Chris@6 974
Chris@6 975 QPainterPath path;
Chris@55 976 int pointCount = 0;
Chris@79 977
Chris@79 978 int textY = 0;
Chris@79 979 if (m_plotStyle == PlotSegmentation) {
Chris@79 980 textY = v->getTextLabelHeight(this, paint);
Chris@554 981 } else {
Chris@554 982 int originY = getYForValue(v, 0.f);
Chris@918 983 if (originY > 0 && originY < v->getPaintHeight()) {
Chris@554 984 paint.save();
Chris@554 985 paint.setPen(getPartialShades(v)[1]);
Chris@554 986 paint.drawLine(x0, originY, x1, originY);
Chris@554 987 paint.restore();
Chris@554 988 }
Chris@79 989 }
Chris@6 990
Chris@908 991 sv_frame_t prevFrame = 0;
Chris@615 992
Chris@0 993 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@1266 994 i != points.end(); ++i) {
Chris@0 995
Chris@553 996 if (m_derivative && i == points.begin()) continue;
Chris@721 997
Chris@1266 998 const SparseTimeValueModel::Point &p(*i);
Chris@0 999
Chris@908 1000 double value = p.value;
Chris@553 1001 if (m_derivative) {
Chris@553 1002 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@553 1003 --j;
Chris@553 1004 value -= j->value;
Chris@553 1005 }
Chris@553 1006
Chris@1266 1007 int x = v->getXForFrame(p.frame);
Chris@1266 1008 int y = getYForValue(v, value);
Chris@0 1009
Chris@615 1010 bool gap = false;
Chris@615 1011 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1012 if (value == 0.0) {
Chris@721 1013 // Treat zeros as gaps
Chris@721 1014 continue;
Chris@721 1015 }
Chris@615 1016 gap = (p.frame > prevFrame &&
Chris@615 1017 (p.frame - prevFrame >= m_model->getResolution() * 2));
Chris@615 1018 }
Chris@615 1019
Chris@79 1020 if (m_plotStyle != PlotSegmentation) {
Chris@79 1021 textY = y - paint.fontMetrics().height()
Chris@631 1022 + paint.fontMetrics().ascent() - 1;
Chris@372 1023 if (textY < paint.fontMetrics().ascent() + 1) {
Chris@372 1024 textY = paint.fontMetrics().ascent() + 1;
Chris@372 1025 }
Chris@79 1026 }
Chris@79 1027
Chris@1266 1028 bool haveNext = false;
Chris@908 1029 double nvalue = 0.f;
Chris@908 1030 sv_frame_t nf = v->getModelsEndFrame();
Chris@1266 1031 int nx = v->getXForFrame(nf);
Chris@1266 1032 int ny = y;
Chris@34 1033
Chris@1266 1034 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@1266 1035 ++j;
Chris@34 1036
Chris@1266 1037 if (j != points.end()) {
Chris@1266 1038 const SparseTimeValueModel::Point &q(*j);
Chris@721 1039 nvalue = q.value;
Chris@553 1040 if (m_derivative) nvalue -= p.value;
Chris@615 1041 nf = q.frame;
Chris@1266 1042 nx = v->getXForFrame(nf);
Chris@1266 1043 ny = getYForValue(v, nvalue);
Chris@1266 1044 haveNext = true;
Chris@76 1045 }
Chris@76 1046
Chris@682 1047 // cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext
Chris@682 1048 // << ", nx = " << nx << endl;
Chris@34 1049
Chris@1229 1050 QPen pen(getBaseQColor());
Chris@1229 1051 QBrush brush(brushColour);
Chris@1229 1052
Chris@615 1053 if (m_plotStyle == PlotDiscreteCurves) {
Chris@1229 1054 pen = QPen(getBaseQColor(), 3);
Chris@1229 1055 brush = QBrush(Qt::NoBrush);
Chris@615 1056 } else if (m_plotStyle == PlotSegmentation) {
Chris@1229 1057 pen = QPen(getForegroundQColor(v));
Chris@1229 1058 brush = QBrush(getColourForValue(v, value));
Chris@1266 1059 } else if (m_plotStyle == PlotLines ||
Chris@1266 1060 m_plotStyle == PlotCurve) {
Chris@1229 1061 brush = QBrush(Qt::NoBrush);
Chris@1266 1062 }
Chris@1229 1063
Chris@1229 1064 paint.setPen(PaintAssistant::scalePen(pen));
Chris@1229 1065 paint.setBrush(brush);
Chris@1229 1066
Chris@1266 1067 if (m_plotStyle == PlotStems) {
Chris@1266 1068 if (y < origin - 1) {
Chris@1266 1069 paint.drawLine(x + w/2, y + 1, x + w/2, origin);
Chris@1266 1070 } else if (y > origin + 1) {
Chris@1266 1071 paint.drawLine(x + w/2, origin, x + w/2, y - 1);
Chris@1266 1072 }
Chris@1266 1073 }
Chris@0 1074
Chris@631 1075 bool illuminate = false;
Chris@631 1076
Chris@1266 1077 if (illuminateFrame == p.frame) {
Chris@6 1078
Chris@1266 1079 // not equipped to illuminate the right section in line
Chris@1266 1080 // or curve mode
Chris@6 1081
Chris@1266 1082 if (m_plotStyle != PlotCurve &&
Chris@615 1083 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1084 m_plotStyle != PlotLines) {
Chris@631 1085 illuminate = true;
Chris@631 1086 }
Chris@631 1087 }
Chris@0 1088
Chris@1266 1089 if (m_plotStyle != PlotLines &&
Chris@1266 1090 m_plotStyle != PlotCurve &&
Chris@615 1091 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1092 m_plotStyle != PlotSegmentation) {
Chris@631 1093 if (illuminate) {
Chris@631 1094 paint.save();
Chris@1266 1095 paint.setPen(PaintAssistant::scalePen(getForegroundQColor(v)));
Chris@631 1096 paint.setBrush(getForegroundQColor(v));
Chris@631 1097 }
Chris@326 1098 if (m_plotStyle != PlotStems ||
Chris@326 1099 w > 1) {
Chris@326 1100 paint.drawRect(x, y - 1, w, 2);
Chris@326 1101 }
Chris@631 1102 if (illuminate) {
Chris@631 1103 paint.restore();
Chris@631 1104 }
Chris@1266 1105 }
Chris@0 1106
Chris@1266 1107 if (m_plotStyle == PlotConnectedPoints ||
Chris@1266 1108 m_plotStyle == PlotLines ||
Chris@615 1109 m_plotStyle == PlotDiscreteCurves ||
Chris@1266 1110 m_plotStyle == PlotCurve) {
Chris@0 1111
Chris@1266 1112 if (haveNext) {
Chris@3 1113
Chris@1266 1114 if (m_plotStyle == PlotConnectedPoints) {
Chris@1266 1115
Chris@79 1116 paint.save();
Chris@1266 1117 paint.setPen(PaintAssistant::scalePen(brushColour));
Chris@1266 1118 paint.drawLine(x + w, y, nx, ny);
Chris@79 1119 paint.restore();
Chris@6 1120
Chris@1266 1121 } else if (m_plotStyle == PlotLines) {
Chris@430 1122
Chris@430 1123 if (pointCount == 0) {
Chris@430 1124 path.moveTo(x + w/2, y);
Chris@430 1125 }
Chris@6 1126
Chris@1266 1127 // paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@430 1128 path.lineTo(nx + w/2, ny);
Chris@6 1129
Chris@1266 1130 } else {
Chris@6 1131
Chris@1266 1132 double x0 = x + double(w)/2;
Chris@1266 1133 double x1 = nx + double(w)/2;
Chris@1266 1134
Chris@1266 1135 double y0 = y;
Chris@1266 1136 double y1 = ny;
Chris@55 1137
Chris@615 1138 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1139 bool nextGap =
Chris@721 1140 (nvalue == 0.0) ||
Chris@721 1141 (nf - p.frame >= m_model->getResolution() * 2);
Chris@615 1142 if (nextGap) {
Chris@615 1143 x1 = x0;
Chris@615 1144 y1 = y0;
Chris@615 1145 }
Chris@615 1146 }
Chris@615 1147
Chris@1266 1148 if (pointCount == 0 || gap) {
Chris@1266 1149 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1150 }
Chris@6 1151
Chris@1266 1152 if (nx - x > 5) {
Chris@1266 1153 path.cubicTo(x0, y0,
Chris@1266 1154 x0, y0,
Chris@1266 1155 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1156
Chris@1266 1157 // // or
Chris@1266 1158 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1159
Chris@1266 1160 } else {
Chris@883 1161 path.lineTo(x0, y0);
Chris@1266 1162 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1163 }
Chris@1266 1164 }
Chris@1266 1165 }
Chris@1266 1166 }
Chris@0 1167
Chris@1266 1168 if (m_plotStyle == PlotSegmentation) {
Chris@76 1169
Chris@526 1170 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1171 cerr << "drawing rect" << endl;
Chris@526 1172 #endif
Chris@1266 1173
Chris@1266 1174 if (nx <= x) continue;
Chris@26 1175
Chris@1229 1176 paint.setPen(PaintAssistant::scalePen(QPen(getForegroundQColor(v), 2)));
Chris@559 1177
Chris@631 1178 if (!illuminate) {
Chris@513 1179 if (!m_drawSegmentDivisions ||
Chris@513 1180 nx < x + 5 ||
Chris@918 1181 x >= v->getPaintWidth() - 1) {
Chris@513 1182 paint.setPen(Qt::NoPen);
Chris@513 1183 }
Chris@1266 1184 }
Chris@26 1185
Chris@1266 1186 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@1266 1187 }
Chris@26 1188
Chris@741 1189 if (v->shouldShowFeatureLabels()) {
Chris@629 1190
Chris@741 1191 QString label = p.label;
Chris@741 1192 bool italic = false;
Chris@741 1193
Chris@741 1194 if (label == "" &&
Chris@741 1195 (m_plotStyle == PlotPoints ||
Chris@741 1196 m_plotStyle == PlotSegmentation ||
Chris@741 1197 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1198 char lc[20];
Chris@741 1199 snprintf(lc, 20, "%.3g", p.value);
Chris@741 1200 label = lc;
Chris@741 1201 italic = true;
Chris@741 1202 }
Chris@741 1203
Chris@741 1204 if (label != "") {
Chris@741 1205 // Quick test for 20px before we do the slower test using metrics
Chris@741 1206 bool haveRoom = (nx > x + 20);
Chris@741 1207 haveRoom = (haveRoom &&
Chris@741 1208 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1209 if (haveRoom ||
Chris@741 1210 (!haveNext &&
Chris@741 1211 (pointCount == 0 || !italic))) {
Chris@1078 1212 PaintAssistant::drawVisibleText(v, paint, x + 5, textY, label,
Chris@741 1213 italic ?
Chris@1078 1214 PaintAssistant::OutlinedItalicText :
Chris@1078 1215 PaintAssistant::OutlinedText);
Chris@741 1216 }
Chris@741 1217 }
Chris@629 1218 }
Chris@629 1219
Chris@615 1220 prevFrame = p.frame;
Chris@632 1221 ++pointCount;
Chris@0 1222 }
Chris@6 1223
Chris@691 1224 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1225 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@1266 1226 paint.drawPath(path);
Chris@691 1227 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1228 && !path.isEmpty()) {
Chris@1266 1229 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
Chris@1266 1230 paint.drawPath(path);
Chris@6 1231 }
Chris@6 1232
Chris@6 1233 paint.restore();
Chris@6 1234
Chris@6 1235 // looks like save/restore doesn't deal with this:
Chris@6 1236 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1237 }
Chris@6 1238
Chris@42 1239 int
Chris@918 1240 TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@42 1241 {
Chris@1315 1242 if (!m_model) {
Chris@1315 1243 return 0;
Chris@1315 1244 } else if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
Chris@701 1245 return 0;
Chris@701 1246 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1247 if (m_verticalScale == LogScale) {
Chris@699 1248 return LogColourScale().getWidth(v, paint);
Chris@699 1249 } else {
Chris@699 1250 return LinearColourScale().getWidth(v, paint);
Chris@699 1251 }
Chris@698 1252 } else {
Chris@699 1253 if (m_verticalScale == LogScale) {
Chris@699 1254 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1255 } else {
Chris@699 1256 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1257 }
Chris@698 1258 }
Chris@42 1259 }
Chris@42 1260
Chris@42 1261 void
Chris@918 1262 TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@42 1263 {
Chris@717 1264 if (!m_model || m_model->getPoints().empty()) return;
Chris@42 1265
Chris@699 1266 QString unit;
Chris@908 1267 double min, max;
Chris@698 1268 bool logarithmic;
Chris@698 1269
Chris@698 1270 int w = getVerticalScaleWidth(v, false, paint);
Chris@918 1271 int h = v->getPaintHeight();
Chris@698 1272
Chris@698 1273 if (m_plotStyle == PlotSegmentation) {
Chris@698 1274
Chris@699 1275 getValueExtents(min, max, logarithmic, unit);
Chris@699 1276
Chris@699 1277 if (logarithmic) {
Chris@699 1278 LogRange::mapRange(min, max);
Chris@699 1279 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1280 } else {
Chris@699 1281 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1282 }
Chris@698 1283
Chris@698 1284 } else {
Chris@698 1285
Chris@699 1286 getScaleExtents(v, min, max, logarithmic);
Chris@699 1287
Chris@698 1288 if (logarithmic) {
Chris@698 1289 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1290 } else {
Chris@698 1291 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1292 }
Chris@698 1293
Chris@698 1294 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1295 PianoScale().paintPianoVertical
Chris@698 1296 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1297 LogRange::unmap(min),
Chris@698 1298 LogRange::unmap(max));
Chris@698 1299 paint.drawLine(w, 0, w, h);
Chris@698 1300 }
Chris@698 1301 }
Chris@698 1302
Chris@698 1303 if (getScaleUnits() != "") {
Chris@701 1304 int mw = w - 5;
Chris@701 1305 paint.drawText(5,
Chris@701 1306 5 + paint.fontMetrics().ascent(),
Chris@701 1307 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1308 paint.fontMetrics(),
Chris@701 1309 mw));
Chris@691 1310 }
Chris@42 1311 }
Chris@42 1312
Chris@21 1313 void
Chris@918 1314 TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1315 {
Chris@526 1316 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1317 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1318 #endif
Chris@21 1319
Chris@21 1320 if (!m_model) return;
Chris@21 1321
Chris@908 1322 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1323 int resolution = m_model->getResolution();
Chris@21 1324 if (frame < 0) frame = 0;
Chris@76 1325 frame = (frame / resolution) * resolution;
Chris@21 1326
Chris@908 1327 double value = getValueForY(v, e->y());
Chris@21 1328
Chris@76 1329 bool havePoint = false;
Chris@76 1330
Chris@76 1331 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1332 if (!points.empty()) {
Chris@76 1333 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1334 i != points.end(); ++i) {
Chris@76 1335 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1336 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1337 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1338 #endif
Chris@76 1339 continue;
Chris@76 1340 }
Chris@76 1341 m_editingPoint = *i;
Chris@76 1342 havePoint = true;
Chris@76 1343 }
Chris@76 1344 }
Chris@76 1345
Chris@76 1346 if (!havePoint) {
Chris@76 1347 m_editingPoint = SparseTimeValueModel::Point
Chris@908 1348 (frame, float(value), tr("New Point"));
Chris@76 1349 }
Chris@76 1350
Chris@23 1351 m_originalPoint = m_editingPoint;
Chris@22 1352
Chris@376 1353 if (m_editingCommand) finish(m_editingCommand);
Chris@22 1354 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@1266 1355 tr("Draw Point"));
Chris@76 1356 if (!havePoint) {
Chris@76 1357 m_editingCommand->addPoint(m_editingPoint);
Chris@76 1358 }
Chris@22 1359
Chris@21 1360 m_editing = true;
Chris@21 1361 }
Chris@21 1362
Chris@21 1363 void
Chris@918 1364 TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1365 {
Chris@526 1366 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1367 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1368 #endif
Chris@21 1369
Chris@21 1370 if (!m_model || !m_editing) return;
Chris@21 1371
Chris@908 1372 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1373 int resolution = m_model->getResolution();
Chris@21 1374 if (frame < 0) frame = 0;
Chris@76 1375 frame = (frame / resolution) * resolution;
Chris@21 1376
Chris@908 1377 double value = getValueForY(v, e->y());
Chris@21 1378
Chris@76 1379 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1380
Chris@526 1381 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1382 cerr << points.size() << " points" << endl;
Chris@526 1383 #endif
Chris@76 1384
Chris@76 1385 bool havePoint = false;
Chris@76 1386
Chris@76 1387 if (!points.empty()) {
Chris@76 1388 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1389 i != points.end(); ++i) {
Chris@76 1390 if (i->frame == m_editingPoint.frame &&
Chris@76 1391 i->value == m_editingPoint.value) {
Chris@526 1392 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1393 cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl;
Chris@526 1394 #endif
Chris@76 1395 continue;
Chris@76 1396 }
Chris@76 1397 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1398 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1399 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1400 #endif
Chris@76 1401 continue;
Chris@76 1402 }
Chris@526 1403 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1404 cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl;
Chris@526 1405 #endif
Chris@76 1406 m_editingPoint = *i;
Chris@76 1407 m_originalPoint = m_editingPoint;
Chris@76 1408 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1409 havePoint = true;
Chris@76 1410 }
Chris@76 1411 }
Chris@76 1412
Chris@76 1413 if (!havePoint) {
Chris@76 1414 if (frame == m_editingPoint.frame) {
Chris@76 1415 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1416 }
Chris@76 1417 }
Chris@76 1418
Chris@76 1419 // m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1420 m_editingPoint.frame = frame;
Chris@908 1421 m_editingPoint.value = float(value);
Chris@22 1422 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1423 }
Chris@21 1424
Chris@21 1425 void
Chris@918 1426 TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1427 {
Chris@526 1428 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1429 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1430 #endif
Chris@21 1431 if (!m_model || !m_editing) return;
Chris@376 1432 finish(m_editingCommand);
Chris@22 1433 m_editingCommand = 0;
Chris@21 1434 m_editing = false;
Chris@21 1435 }
Chris@21 1436
Chris@21 1437 void
Chris@918 1438 TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1439 {
Chris@335 1440 if (!m_model) return;
Chris@335 1441
Chris@335 1442 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1443 if (points.empty()) return;
Chris@335 1444
Chris@335 1445 m_editingPoint = *points.begin();
Chris@335 1446
Chris@335 1447 if (m_editingCommand) {
Chris@1266 1448 finish(m_editingCommand);
Chris@1266 1449 m_editingCommand = 0;
Chris@335 1450 }
Chris@335 1451
Chris@335 1452 m_editing = true;
Chris@335 1453 }
Chris@335 1454
Chris@335 1455 void
Chris@918 1456 TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 1457 {
Chris@335 1458 }
Chris@335 1459
Chris@335 1460 void
Chris@918 1461 TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1462 {
Chris@335 1463 if (!m_model || !m_editing) return;
Chris@335 1464
Chris@335 1465 m_editing = false;
Chris@335 1466
Chris@335 1467 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1468 if (points.empty()) return;
Chris@335 1469 if (points.begin()->frame != m_editingPoint.frame ||
Chris@335 1470 points.begin()->value != m_editingPoint.value) return;
Chris@335 1471
Chris@335 1472 m_editingCommand = new SparseTimeValueModel::EditCommand
Chris@335 1473 (m_model, tr("Erase Point"));
Chris@335 1474
Chris@335 1475 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 1476
Chris@376 1477 finish(m_editingCommand);
Chris@335 1478 m_editingCommand = 0;
Chris@335 1479 m_editing = false;
Chris@335 1480 }
Chris@335 1481
Chris@335 1482 void
Chris@918 1483 TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1484 {
Chris@526 1485 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1486 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1487 #endif
Chris@21 1488
Chris@21 1489 if (!m_model) return;
Chris@21 1490
Chris@44 1491 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@21 1492 if (points.empty()) return;
Chris@21 1493
Chris@21 1494 m_editingPoint = *points.begin();
Chris@23 1495 m_originalPoint = m_editingPoint;
Chris@22 1496
Chris@22 1497 if (m_editingCommand) {
Chris@1266 1498 finish(m_editingCommand);
Chris@1266 1499 m_editingCommand = 0;
Chris@22 1500 }
Chris@22 1501
Chris@21 1502 m_editing = true;
Chris@21 1503 }
Chris@21 1504
Chris@21 1505 void
Chris@918 1506 TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1507 {
Chris@526 1508 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1509 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1510 #endif
Chris@21 1511
Chris@21 1512 if (!m_model || !m_editing) return;
Chris@21 1513
Chris@908 1514 sv_frame_t frame = v->getFrameForX(e->x());
Chris@21 1515 if (frame < 0) frame = 0;
Chris@21 1516 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 1517
Chris@908 1518 double value = getValueForY(v, e->y());
Chris@21 1519
Chris@22 1520 if (!m_editingCommand) {
Chris@1266 1521 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@1266 1522 tr("Drag Point"));
Chris@22 1523 }
Chris@22 1524
Chris@22 1525 m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1526 m_editingPoint.frame = frame;
Chris@908 1527 m_editingPoint.value = float(value);
Chris@22 1528 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1529 }
Chris@21 1530
Chris@21 1531 void
Chris@918 1532 TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1533 {
Chris@526 1534 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1535 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1536 #endif
Chris@21 1537 if (!m_model || !m_editing) return;
Chris@23 1538
Chris@23 1539 if (m_editingCommand) {
Chris@23 1540
Chris@1266 1541 QString newName = m_editingCommand->getName();
Chris@23 1542
Chris@1266 1543 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@1266 1544 if (m_editingPoint.value != m_originalPoint.value) {
Chris@1266 1545 newName = tr("Edit Point");
Chris@1266 1546 } else {
Chris@1266 1547 newName = tr("Relocate Point");
Chris@1266 1548 }
Chris@1266 1549 } else {
Chris@1266 1550 newName = tr("Change Point Value");
Chris@1266 1551 }
Chris@23 1552
Chris@1266 1553 m_editingCommand->setName(newName);
Chris@1266 1554 finish(m_editingCommand);
Chris@23 1555 }
Chris@23 1556
Chris@22 1557 m_editingCommand = 0;
Chris@21 1558 m_editing = false;
Chris@21 1559 }
Chris@21 1560
Chris@255 1561 bool
Chris@918 1562 TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 1563 {
Chris@255 1564 if (!m_model) return false;
Chris@70 1565
Chris@70 1566 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@255 1567 if (points.empty()) return false;
Chris@70 1568
Chris@70 1569 SparseTimeValueModel::Point point = *points.begin();
Chris@70 1570
Chris@70 1571 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1572 (m_model->getSampleRate(),
Chris@70 1573 ItemEditDialog::ShowTime |
Chris@70 1574 ItemEditDialog::ShowValue |
Chris@73 1575 ItemEditDialog::ShowText,
Chris@698 1576 getScaleUnits());
Chris@70 1577
Chris@70 1578 dialog->setFrameTime(point.frame);
Chris@70 1579 dialog->setValue(point.value);
Chris@70 1580 dialog->setText(point.label);
Chris@70 1581
Chris@70 1582 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1583
Chris@70 1584 SparseTimeValueModel::Point newPoint = point;
Chris@70 1585 newPoint.frame = dialog->getFrameTime();
Chris@70 1586 newPoint.value = dialog->getValue();
Chris@70 1587 newPoint.label = dialog->getText();
Chris@70 1588
Chris@70 1589 SparseTimeValueModel::EditCommand *command =
Chris@70 1590 new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 1591 command->deletePoint(point);
Chris@70 1592 command->addPoint(newPoint);
Chris@376 1593 finish(command);
Chris@70 1594 }
Chris@70 1595
Chris@70 1596 delete dialog;
Chris@255 1597 return true;
Chris@70 1598 }
Chris@70 1599
Chris@70 1600 void
Chris@908 1601 TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1602 {
Chris@99 1603 if (!m_model) return;
Chris@99 1604
Chris@43 1605 SparseTimeValueModel::EditCommand *command =
Chris@1266 1606 new SparseTimeValueModel::EditCommand(m_model,
Chris@1266 1607 tr("Drag Selection"));
Chris@43 1608
Chris@43 1609 SparseTimeValueModel::PointList points =
Chris@1266 1610 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1611
Chris@43 1612 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@1266 1613 i != points.end(); ++i) {
Chris@43 1614
Chris@1266 1615 if (s.contains(i->frame)) {
Chris@1266 1616 SparseTimeValueModel::Point newPoint(*i);
Chris@1266 1617 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@1266 1618 command->deletePoint(*i);
Chris@1266 1619 command->addPoint(newPoint);
Chris@1266 1620 }
Chris@43 1621 }
Chris@43 1622
Chris@376 1623 finish(command);
Chris@43 1624 }
Chris@43 1625
Chris@43 1626 void
Chris@43 1627 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1628 {
Chris@99 1629 if (!m_model) return;
Chris@99 1630
Chris@43 1631 SparseTimeValueModel::EditCommand *command =
Chris@1266 1632 new SparseTimeValueModel::EditCommand(m_model,
Chris@1266 1633 tr("Resize Selection"));
Chris@43 1634
Chris@43 1635 SparseTimeValueModel::PointList points =
Chris@1266 1636 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1637
Chris@43 1638 double ratio =
Chris@1266 1639 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@1266 1640 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1641
Chris@43 1642 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@1266 1643 i != points.end(); ++i) {
Chris@43 1644
Chris@1266 1645 if (s.contains(i->frame)) {
Chris@43 1646
Chris@1266 1647 double target = double(i->frame);
Chris@1266 1648 target = double(newSize.getStartFrame()) +
Chris@1266 1649 target - double(s.getStartFrame()) * ratio;
Chris@43 1650
Chris@1266 1651 SparseTimeValueModel::Point newPoint(*i);
Chris@1266 1652 newPoint.frame = lrint(target);
Chris@1266 1653 command->deletePoint(*i);
Chris@1266 1654 command->addPoint(newPoint);
Chris@1266 1655 }
Chris@43 1656 }
Chris@43 1657
Chris@376 1658 finish(command);
Chris@43 1659 }
Chris@43 1660
Chris@76 1661 void
Chris@76 1662 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1663 {
Chris@99 1664 if (!m_model) return;
Chris@99 1665
Chris@76 1666 SparseTimeValueModel::EditCommand *command =
Chris@1266 1667 new SparseTimeValueModel::EditCommand(m_model,
Chris@1266 1668 tr("Delete Selected Points"));
Chris@76 1669
Chris@76 1670 SparseTimeValueModel::PointList points =
Chris@1266 1671 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1672
Chris@76 1673 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@1266 1674 i != points.end(); ++i) {
Chris@76 1675
Chris@76 1676 if (s.contains(i->frame)) {
Chris@76 1677 command->deletePoint(*i);
Chris@76 1678 }
Chris@76 1679 }
Chris@76 1680
Chris@376 1681 finish(command);
Chris@76 1682 }
Chris@76 1683
Chris@76 1684 void
Chris@918 1685 TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 1686 {
Chris@99 1687 if (!m_model) return;
Chris@99 1688
Chris@76 1689 SparseTimeValueModel::PointList points =
Chris@1266 1690 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1691
Chris@76 1692 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@1266 1693 i != points.end(); ++i) {
Chris@1266 1694 if (s.contains(i->frame)) {
Chris@83 1695 Clipboard::Point point(i->frame, i->value, i->label);
Chris@360 1696 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1697 to.addPoint(point);
Chris@76 1698 }
Chris@76 1699 }
Chris@76 1700 }
Chris@76 1701
Chris@125 1702 bool
Chris@918 1703 TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */,
Chris@125 1704 bool interactive)
Chris@76 1705 {
Chris@125 1706 if (!m_model) return false;
Chris@99 1707
Chris@76 1708 const Clipboard::PointList &points = from.getPoints();
Chris@76 1709
Chris@360 1710 bool realign = false;
Chris@360 1711
Chris@360 1712 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1713
Chris@360 1714 QMessageBox::StandardButton button =
Chris@919 1715 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 1716 tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"),
Chris@360 1717 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1718 QMessageBox::Yes);
Chris@360 1719
Chris@360 1720 if (button == QMessageBox::Cancel) {
Chris@360 1721 return false;
Chris@360 1722 }
Chris@360 1723
Chris@360 1724 if (button == QMessageBox::Yes) {
Chris@360 1725 realign = true;
Chris@360 1726 }
Chris@360 1727 }
Chris@360 1728
Chris@76 1729 SparseTimeValueModel::EditCommand *command =
Chris@1266 1730 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
Chris@76 1731
Chris@125 1732 enum ValueAvailability {
Chris@125 1733 UnknownAvailability,
Chris@125 1734 NoValues,
Chris@125 1735 SomeValues,
Chris@125 1736 AllValues
Chris@125 1737 };
Chris@125 1738
Chris@340 1739 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1740
Chris@125 1741 bool haveUsableLabels = false;
Chris@340 1742 Labeller labeller;
Chris@340 1743 labeller.setSampleRate(m_model->getSampleRate());
Chris@125 1744
Chris@125 1745 if (interactive) {
Chris@125 1746
Chris@125 1747 ValueAvailability availability = UnknownAvailability;
Chris@125 1748
Chris@125 1749 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 1750 i != points.end(); ++i) {
Chris@125 1751
Chris@125 1752 if (!i->haveFrame()) continue;
Chris@125 1753
Chris@125 1754 if (availability == UnknownAvailability) {
Chris@125 1755 if (i->haveValue()) availability = AllValues;
Chris@125 1756 else availability = NoValues;
Chris@125 1757 continue;
Chris@125 1758 }
Chris@125 1759
Chris@125 1760 if (i->haveValue()) {
Chris@125 1761 if (availability == NoValues) {
Chris@125 1762 availability = SomeValues;
Chris@125 1763 }
Chris@125 1764 } else {
Chris@125 1765 if (availability == AllValues) {
Chris@125 1766 availability = SomeValues;
Chris@125 1767 }
Chris@125 1768 }
Chris@125 1769
Chris@125 1770 if (!haveUsableLabels) {
Chris@125 1771 if (i->haveLabel()) {
Chris@125 1772 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1773 haveUsableLabels = true;
Chris@125 1774 }
Chris@125 1775 }
Chris@125 1776 }
Chris@125 1777
Chris@125 1778 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1779 }
Chris@125 1780
Chris@125 1781 if (availability == NoValues || availability == SomeValues) {
Chris@125 1782
Chris@125 1783 QString text;
Chris@125 1784 if (availability == NoValues) {
Chris@125 1785 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1786 } else {
Chris@125 1787 text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1788 }
Chris@125 1789
Chris@340 1790 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1791
Chris@125 1792 QStringList options;
Chris@340 1793 std::vector<Labeller::ValueType> genopts;
Chris@125 1794
Chris@340 1795 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1796 i != names.end(); ++i) {
Chris@340 1797 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1798 else options << i->second;
Chris@340 1799 genopts.push_back(i->first);
Chris@125 1800 }
Chris@125 1801
Chris@125 1802 static int prevSelection = 0;
Chris@125 1803
Chris@125 1804 bool ok = false;
Chris@125 1805 QString selected = ListInputDialog::getItem
Chris@125 1806 (0, tr("Choose value calculation"),
Chris@125 1807 text, options, prevSelection, &ok);
Chris@125 1808
Chris@852 1809 if (!ok) {
Chris@852 1810 delete command;
Chris@852 1811 return false;
Chris@852 1812 }
Chris@125 1813 int selection = 0;
Chris@340 1814 generation = Labeller::ValueNone;
Chris@125 1815
Chris@125 1816 for (QStringList::const_iterator i = options.begin();
Chris@125 1817 i != options.end(); ++i) {
Chris@125 1818 if (selected == *i) {
Chris@340 1819 generation = genopts[selection];
Chris@125 1820 break;
Chris@125 1821 }
Chris@125 1822 ++selection;
Chris@125 1823 }
Chris@340 1824
Chris@340 1825 labeller.setType(generation);
Chris@340 1826
Chris@340 1827 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1828 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1829 int cycleSize = QInputDialog::getInt
Chris@340 1830 (0, tr("Select cycle size"),
Chris@340 1831 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1832 labeller.setCounterCycleSize(cycleSize);
Chris@340 1833 }
Chris@125 1834
Chris@125 1835 prevSelection = selection;
Chris@125 1836 }
Chris@125 1837 }
Chris@125 1838
Chris@372 1839 SparseTimeValueModel::Point prevPoint(0);
Chris@125 1840
Chris@76 1841 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1842 i != points.end(); ++i) {
Chris@76 1843
Chris@76 1844 if (!i->haveFrame()) continue;
Chris@360 1845
Chris@908 1846 sv_frame_t frame = 0;
Chris@360 1847
Chris@360 1848 if (!realign) {
Chris@360 1849
Chris@360 1850 frame = i->getFrame();
Chris@360 1851
Chris@360 1852 } else {
Chris@360 1853
Chris@360 1854 if (i->haveReferenceFrame()) {
Chris@360 1855 frame = i->getReferenceFrame();
Chris@360 1856 frame = alignFromReference(v, frame);
Chris@360 1857 } else {
Chris@360 1858 frame = i->getFrame();
Chris@360 1859 }
Chris@76 1860 }
Chris@360 1861
Chris@76 1862 SparseTimeValueModel::Point newPoint(frame);
Chris@76 1863
Chris@125 1864 if (i->haveLabel()) {
Chris@125 1865 newPoint.label = i->getLabel();
Chris@125 1866 } else if (i->haveValue()) {
Chris@125 1867 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 1868 }
Chris@125 1869
Chris@372 1870 bool usePrev = false;
Chris@372 1871 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
Chris@372 1872
Chris@125 1873 if (i->haveValue()) {
Chris@125 1874 newPoint.value = i->getValue();
Chris@125 1875 } else {
Chris@526 1876 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1877 cerr << "Setting value on point at " << newPoint.frame << " from labeller";
Chris@526 1878 if (i == points.begin()) {
Chris@682 1879 cerr << ", no prev point" << endl;
Chris@526 1880 } else {
Chris@682 1881 cerr << ", prev point is at " << prevPoint.frame << endl;
Chris@526 1882 }
Chris@526 1883 #endif
Chris@340 1884 labeller.setValue<SparseTimeValueModel::Point>
Chris@371 1885 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
Chris@526 1886 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1887 cerr << "New point value = " << newPoint.value << endl;
Chris@526 1888 #endif
Chris@372 1889 if (labeller.actingOnPrevPoint() && i != points.begin()) {
Chris@372 1890 usePrev = true;
Chris@372 1891 }
Chris@372 1892 }
Chris@372 1893
Chris@372 1894 if (usePrev) {
Chris@372 1895 command->deletePoint(formerPrevPoint);
Chris@372 1896 command->addPoint(prevPoint);
Chris@340 1897 }
Chris@125 1898
Chris@340 1899 prevPoint = newPoint;
Chris@76 1900 command->addPoint(newPoint);
Chris@76 1901 }
Chris@76 1902
Chris@376 1903 finish(command);
Chris@125 1904 return true;
Chris@360 1905 }
Chris@76 1906
Chris@316 1907 void
Chris@316 1908 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1909 QString indent, QString extraAttributes) const
Chris@6 1910 {
Chris@1362 1911 QString s;
Chris@1362 1912
Chris@1362 1913 s += QString("plotStyle=\"%1\" "
Chris@1362 1914 "verticalScale=\"%2\" "
Chris@1362 1915 "scaleMinimum=\"%3\" "
Chris@1362 1916 "scaleMaximum=\"%4\" "
Chris@1362 1917 "drawDivisions=\"%5\" "
Chris@1362 1918 "derivative=\"%6\" ")
Chris@1362 1919 .arg(m_plotStyle)
Chris@1362 1920 .arg(m_verticalScale)
Chris@1362 1921 .arg(m_scaleMinimum)
Chris@1362 1922 .arg(m_scaleMaximum)
Chris@1362 1923 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@1362 1924 .arg(m_derivative ? "true" : "false");
Chris@1362 1925
Chris@1362 1926 // New-style colour map attribute, by string id rather than by
Chris@1362 1927 // number
Chris@1362 1928
Chris@1362 1929 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1930 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1931
Chris@1362 1932 // Old-style colour map attribute
Chris@1362 1933
Chris@1362 1934 s += QString("colourMap=\"%1\" ")
Chris@1362 1935 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1936
Chris@1362 1937 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@0 1938 }
Chris@0 1939
Chris@11 1940 void
Chris@11 1941 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1942 {
Chris@287 1943 SingleColourLayer::setProperties(attributes);
Chris@11 1944
Chris@445 1945 bool ok, alsoOk;
Chris@287 1946
Chris@1362 1947 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1948 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1949 if (colourMap >= 0) {
Chris@1362 1950 setFillColourMap(colourMap);
Chris@1362 1951 } else {
Chris@1362 1952 colourMap = attributes.value("colourMap").toInt(&ok);
Chris@1362 1953 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1954 setFillColourMap(colourMap);
Chris@1362 1955 }
Chris@1362 1956 }
Chris@287 1957
Chris@11 1958 PlotStyle style = (PlotStyle)
Chris@1266 1959 attributes.value("plotStyle").toInt(&ok);
Chris@11 1960 if (ok) setPlotStyle(style);
Chris@286 1961
Chris@286 1962 VerticalScale scale = (VerticalScale)
Chris@1266 1963 attributes.value("verticalScale").toInt(&ok);
Chris@286 1964 if (ok) setVerticalScale(scale);
Chris@445 1965
Chris@513 1966 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1967 setDrawSegmentDivisions(draw);
Chris@513 1968
Chris@553 1969 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1970 setShowDerivative(derivative);
Chris@553 1971
Chris@445 1972 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1973 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1974 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1975 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1976 #endif
Chris@526 1977 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1978 }
Chris@11 1979