annotate layer/TimeValueLayer.cpp @ 1548:bd6af89982d7

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