annotate layer/TimeValueLayer.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +0100
parents e95cefd4aa81
children
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@1550 918 int x0 = rect.left();
Chris@1550 919 int x1 = x0 + rect.width();
Chris@908 920 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@908 921 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@553 922 if (m_derivative) --frame0;
Chris@0 923
Chris@1474 924 EventVector points(model->getEventsWithin(frame0, frame1 - frame0, 1));
Chris@1492 925
Chris@1492 926 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1492 927 SVCERR << "TimeValueLayer[" << this << "]::paint in " << v->getId()
Chris@1492 928 << ": pixel extents " << x0 << " to " << x1 << ", frame extents "
Chris@1492 929 << frame0 << " to " << frame1 << " yielding " << points.size()
Chris@1492 930 << " points (of " << model->getAllEvents().size()
Chris@1492 931 << " from frames " << model->getStartFrame() << " to "
Chris@1492 932 << model->getEndFrame() << ")" << endl;
Chris@1492 933 #endif
Chris@1492 934
Chris@11 935 if (points.empty()) return;
Chris@0 936
Chris@287 937 paint.setPen(getBaseQColor());
Chris@0 938
Chris@287 939 QColor brushColour(getBaseQColor());
Chris@0 940 brushColour.setAlpha(80);
Chris@0 941 paint.setBrush(brushColour);
Chris@0 942
Chris@526 943 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 944 cerr << "TimeValueLayer::paint: resolution is "
Chris@1474 945 << model->getResolution() << " frames" << endl;
Chris@526 946 #endif
Chris@0 947
Chris@1474 948 double min = model->getValueMinimum();
Chris@1474 949 double max = model->getValueMaximum();
Chris@0 950 if (max == min) max = min + 1.0;
Chris@0 951
Chris@918 952 int origin = int(nearbyint(v->getPaintHeight() -
Chris@1266 953 (-min * v->getPaintHeight()) / (max - min)));
Chris@0 954
Chris@0 955 QPoint localPos;
Chris@908 956 sv_frame_t illuminateFrame = -1;
Chris@0 957
Chris@44 958 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1429 959 EventVector localPoints = getLocalPoints(v, localPos.x());
Chris@526 960 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 961 cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl;
Chris@526 962 #endif
Chris@1429 963 if (!localPoints.empty()) {
Chris@1429 964 illuminateFrame = localPoints.begin()->getFrame();
Chris@1429 965 }
Chris@0 966 }
Chris@6 967
Chris@20 968 int w =
Chris@1474 969 v->getXForFrame(frame0 + model->getResolution()) -
Chris@1266 970 v->getXForFrame(frame0);
Chris@7 971
Chris@629 972 if (m_plotStyle == PlotStems) {
Chris@629 973 if (w < 2) w = 2;
Chris@629 974 } else {
Chris@629 975 if (w < 1) w = 1;
Chris@629 976 }
Chris@629 977
Chris@6 978 paint.save();
Chris@6 979
Chris@6 980 QPainterPath path;
Chris@55 981 int pointCount = 0;
Chris@79 982
Chris@79 983 int textY = 0;
Chris@79 984 if (m_plotStyle == PlotSegmentation) {
Chris@1537 985 textY = v->getTextLabelYCoord(this, paint);
Chris@554 986 } else {
Chris@554 987 int originY = getYForValue(v, 0.f);
Chris@918 988 if (originY > 0 && originY < v->getPaintHeight()) {
Chris@554 989 paint.save();
Chris@554 990 paint.setPen(getPartialShades(v)[1]);
Chris@554 991 paint.drawLine(x0, originY, x1, originY);
Chris@554 992 paint.restore();
Chris@554 993 }
Chris@79 994 }
Chris@6 995
Chris@908 996 sv_frame_t prevFrame = 0;
Chris@615 997
Chris@1429 998 for (EventVector::const_iterator i = points.begin();
Chris@1266 999 i != points.end(); ++i) {
Chris@0 1000
Chris@553 1001 if (m_derivative && i == points.begin()) continue;
Chris@721 1002
Chris@1429 1003 Event p(*i);
Chris@0 1004
Chris@1429 1005 double value = p.getValue();
Chris@553 1006 if (m_derivative) {
Chris@1429 1007 EventVector::const_iterator j = i;
Chris@553 1008 --j;
Chris@1429 1009 value -= j->getValue();
Chris@553 1010 }
Chris@553 1011
Chris@1429 1012 int x = v->getXForFrame(p.getFrame());
Chris@1266 1013 int y = getYForValue(v, value);
Chris@0 1014
Chris@615 1015 bool gap = false;
Chris@615 1016 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1017 if (value == 0.0) {
Chris@721 1018 // Treat zeros as gaps
Chris@721 1019 continue;
Chris@721 1020 }
Chris@1429 1021 gap = (p.getFrame() > prevFrame &&
Chris@1474 1022 (p.getFrame() - prevFrame >= model->getResolution() * 2));
Chris@615 1023 }
Chris@615 1024
Chris@79 1025 if (m_plotStyle != PlotSegmentation) {
Chris@79 1026 textY = y - paint.fontMetrics().height()
Chris@631 1027 + paint.fontMetrics().ascent() - 1;
Chris@372 1028 if (textY < paint.fontMetrics().ascent() + 1) {
Chris@372 1029 textY = paint.fontMetrics().ascent() + 1;
Chris@372 1030 }
Chris@79 1031 }
Chris@79 1032
Chris@1266 1033 bool haveNext = false;
Chris@908 1034 double nvalue = 0.f;
Chris@908 1035 sv_frame_t nf = v->getModelsEndFrame();
Chris@1266 1036 int nx = v->getXForFrame(nf);
Chris@1266 1037 int ny = y;
Chris@34 1038
Chris@1429 1039 EventVector::const_iterator j = i;
Chris@1266 1040 ++j;
Chris@34 1041
Chris@1266 1042 if (j != points.end()) {
Chris@1429 1043 Event q(*j);
Chris@1429 1044 nvalue = q.getValue();
Chris@1429 1045 if (m_derivative) nvalue -= p.getValue();
Chris@1429 1046 nf = q.getFrame();
Chris@1266 1047 nx = v->getXForFrame(nf);
Chris@1266 1048 ny = getYForValue(v, nvalue);
Chris@1266 1049 haveNext = true;
Chris@76 1050 }
Chris@76 1051
Chris@1429 1052 // cout << "frame = " << p.getFrame() << ", x = " << x << ", haveNext = " << haveNext
Chris@682 1053 // << ", nx = " << nx << endl;
Chris@34 1054
Chris@1229 1055 QPen pen(getBaseQColor());
Chris@1229 1056 QBrush brush(brushColour);
Chris@1229 1057
Chris@615 1058 if (m_plotStyle == PlotDiscreteCurves) {
Chris@1229 1059 pen = QPen(getBaseQColor(), 3);
Chris@1229 1060 brush = QBrush(Qt::NoBrush);
Chris@615 1061 } else if (m_plotStyle == PlotSegmentation) {
Chris@1229 1062 pen = QPen(getForegroundQColor(v));
Chris@1229 1063 brush = QBrush(getColourForValue(v, value));
Chris@1266 1064 } else if (m_plotStyle == PlotLines ||
Chris@1266 1065 m_plotStyle == PlotCurve) {
Chris@1229 1066 brush = QBrush(Qt::NoBrush);
Chris@1266 1067 }
Chris@1229 1068
Chris@1401 1069 paint.setPen(v->scalePen(pen));
Chris@1229 1070 paint.setBrush(brush);
Chris@1229 1071
Chris@1266 1072 if (m_plotStyle == PlotStems) {
Chris@1266 1073 if (y < origin - 1) {
Chris@1266 1074 paint.drawLine(x + w/2, y + 1, x + w/2, origin);
Chris@1266 1075 } else if (y > origin + 1) {
Chris@1266 1076 paint.drawLine(x + w/2, origin, x + w/2, y - 1);
Chris@1266 1077 }
Chris@1266 1078 }
Chris@0 1079
Chris@631 1080 bool illuminate = false;
Chris@631 1081
Chris@1429 1082 if (illuminateFrame == p.getFrame()) {
Chris@6 1083
Chris@1266 1084 // not equipped to illuminate the right section in line
Chris@1266 1085 // or curve mode
Chris@6 1086
Chris@1266 1087 if (m_plotStyle != PlotCurve &&
Chris@615 1088 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1089 m_plotStyle != PlotLines) {
Chris@631 1090 illuminate = true;
Chris@631 1091 }
Chris@631 1092 }
Chris@0 1093
Chris@1266 1094 if (m_plotStyle != PlotLines &&
Chris@1266 1095 m_plotStyle != PlotCurve &&
Chris@615 1096 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1097 m_plotStyle != PlotSegmentation) {
Chris@631 1098 if (illuminate) {
Chris@631 1099 paint.save();
Chris@1401 1100 paint.setPen(v->scalePen(getForegroundQColor(v)));
Chris@631 1101 paint.setBrush(getForegroundQColor(v));
Chris@631 1102 }
Chris@326 1103 if (m_plotStyle != PlotStems ||
Chris@326 1104 w > 1) {
Chris@326 1105 paint.drawRect(x, y - 1, w, 2);
Chris@326 1106 }
Chris@631 1107 if (illuminate) {
Chris@631 1108 paint.restore();
Chris@631 1109 }
Chris@1266 1110 }
Chris@0 1111
Chris@1266 1112 if (m_plotStyle == PlotConnectedPoints ||
Chris@1266 1113 m_plotStyle == PlotLines ||
Chris@615 1114 m_plotStyle == PlotDiscreteCurves ||
Chris@1266 1115 m_plotStyle == PlotCurve) {
Chris@0 1116
Chris@1266 1117 if (haveNext) {
Chris@3 1118
Chris@1266 1119 if (m_plotStyle == PlotConnectedPoints) {
Chris@1266 1120
Chris@79 1121 paint.save();
Chris@1401 1122 paint.setPen(v->scalePen(brushColour));
Chris@1266 1123 paint.drawLine(x + w, y, nx, ny);
Chris@79 1124 paint.restore();
Chris@6 1125
Chris@1266 1126 } else if (m_plotStyle == PlotLines) {
Chris@430 1127
Chris@430 1128 if (pointCount == 0) {
Chris@430 1129 path.moveTo(x + w/2, y);
Chris@430 1130 }
Chris@6 1131
Chris@1266 1132 // paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@430 1133 path.lineTo(nx + w/2, ny);
Chris@6 1134
Chris@1266 1135 } else {
Chris@6 1136
Chris@1266 1137 double x0 = x + double(w)/2;
Chris@1266 1138 double x1 = nx + double(w)/2;
Chris@1266 1139
Chris@1266 1140 double y0 = y;
Chris@1266 1141 double y1 = ny;
Chris@55 1142
Chris@615 1143 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1144 bool nextGap =
Chris@721 1145 (nvalue == 0.0) ||
Chris@1474 1146 (nf - p.getFrame() >= model->getResolution() * 2);
Chris@615 1147 if (nextGap) {
Chris@615 1148 x1 = x0;
Chris@615 1149 y1 = y0;
Chris@615 1150 }
Chris@615 1151 }
Chris@615 1152
Chris@1266 1153 if (pointCount == 0 || gap) {
Chris@1266 1154 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1155 }
Chris@6 1156
Chris@1266 1157 if (nx - x > 5) {
Chris@1266 1158 path.cubicTo(x0, y0,
Chris@1266 1159 x0, y0,
Chris@1266 1160 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1161
Chris@1266 1162 // // or
Chris@1266 1163 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1164
Chris@1266 1165 } else {
Chris@883 1166 path.lineTo(x0, y0);
Chris@1266 1167 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1168 }
Chris@1266 1169 }
Chris@1266 1170 }
Chris@1266 1171 }
Chris@0 1172
Chris@1266 1173 if (m_plotStyle == PlotSegmentation) {
Chris@76 1174
Chris@526 1175 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1176 cerr << "drawing rect" << endl;
Chris@526 1177 #endif
Chris@1266 1178
Chris@1266 1179 if (nx <= x) continue;
Chris@26 1180
Chris@1401 1181 paint.setPen(v->scalePen(QPen(getForegroundQColor(v), 2)));
Chris@559 1182
Chris@631 1183 if (!illuminate) {
Chris@513 1184 if (!m_drawSegmentDivisions ||
Chris@513 1185 nx < x + 5 ||
Chris@918 1186 x >= v->getPaintWidth() - 1) {
Chris@513 1187 paint.setPen(Qt::NoPen);
Chris@513 1188 }
Chris@1266 1189 }
Chris@26 1190
Chris@1266 1191 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@1266 1192 }
Chris@26 1193
Chris@741 1194 if (v->shouldShowFeatureLabels()) {
Chris@629 1195
Chris@1429 1196 QString label = p.getLabel();
Chris@741 1197 bool italic = false;
Chris@741 1198
Chris@741 1199 if (label == "" &&
Chris@741 1200 (m_plotStyle == PlotPoints ||
Chris@741 1201 m_plotStyle == PlotSegmentation ||
Chris@741 1202 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1203 char lc[20];
Chris@1429 1204 snprintf(lc, 20, "%.3g", p.getValue());
Chris@741 1205 label = lc;
Chris@741 1206 italic = true;
Chris@741 1207 }
Chris@741 1208
Chris@1474 1209 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1474 1210 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1474 1211 // which is too new for us
Chris@1474 1212 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1474 1213
Chris@741 1214 if (label != "") {
Chris@741 1215 // Quick test for 20px before we do the slower test using metrics
Chris@741 1216 bool haveRoom = (nx > x + 20);
Chris@741 1217 haveRoom = (haveRoom &&
Chris@741 1218 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1219 if (haveRoom ||
Chris@741 1220 (!haveNext &&
Chris@741 1221 (pointCount == 0 || !italic))) {
Chris@1429 1222 PaintAssistant::drawVisibleText
Chris@1429 1223 (v, paint, x + 5, textY, label,
Chris@1429 1224 italic ?
Chris@1429 1225 PaintAssistant::OutlinedItalicText :
Chris@1429 1226 PaintAssistant::OutlinedText);
Chris@741 1227 }
Chris@741 1228 }
Chris@629 1229 }
Chris@629 1230
Chris@1429 1231 prevFrame = p.getFrame();
Chris@632 1232 ++pointCount;
Chris@0 1233 }
Chris@6 1234
Chris@691 1235 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1236 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@1266 1237 paint.drawPath(path);
Chris@691 1238 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1239 && !path.isEmpty()) {
Chris@1266 1240 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
Chris@1266 1241 paint.drawPath(path);
Chris@6 1242 }
Chris@6 1243
Chris@6 1244 paint.restore();
Chris@6 1245
Chris@6 1246 // looks like save/restore doesn't deal with this:
Chris@6 1247 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1248 }
Chris@6 1249
Chris@42 1250 int
Chris@918 1251 TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@42 1252 {
Chris@1474 1253 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1254 if (!model) {
Chris@1315 1255 return 0;
Chris@1315 1256 } else if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
Chris@701 1257 return 0;
Chris@701 1258 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1259 if (m_verticalScale == LogScale) {
Chris@699 1260 return LogColourScale().getWidth(v, paint);
Chris@699 1261 } else {
Chris@699 1262 return LinearColourScale().getWidth(v, paint);
Chris@699 1263 }
Chris@698 1264 } else {
Chris@699 1265 if (m_verticalScale == LogScale) {
Chris@699 1266 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1267 } else {
Chris@699 1268 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1269 }
Chris@698 1270 }
Chris@42 1271 }
Chris@42 1272
Chris@42 1273 void
Chris@918 1274 TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@42 1275 {
Chris@1474 1276 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1277 if (!model || model->isEmpty()) return;
Chris@42 1278
Chris@699 1279 QString unit;
Chris@908 1280 double min, max;
Chris@698 1281 bool logarithmic;
Chris@698 1282
Chris@698 1283 int w = getVerticalScaleWidth(v, false, paint);
Chris@918 1284 int h = v->getPaintHeight();
Chris@698 1285
Chris@698 1286 if (m_plotStyle == PlotSegmentation) {
Chris@698 1287
Chris@699 1288 getValueExtents(min, max, logarithmic, unit);
Chris@699 1289
Chris@699 1290 if (logarithmic) {
Chris@699 1291 LogRange::mapRange(min, max);
Chris@699 1292 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1293 } else {
Chris@699 1294 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1295 }
Chris@698 1296
Chris@698 1297 } else {
Chris@698 1298
Chris@699 1299 getScaleExtents(v, min, max, logarithmic);
Chris@699 1300
Chris@698 1301 if (logarithmic) {
Chris@698 1302 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1303 } else {
Chris@698 1304 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1305 }
Chris@698 1306
Chris@698 1307 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1308 PianoScale().paintPianoVertical
Chris@698 1309 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1310 LogRange::unmap(min),
Chris@698 1311 LogRange::unmap(max));
Chris@698 1312 paint.drawLine(w, 0, w, h);
Chris@698 1313 }
Chris@698 1314 }
Chris@698 1315
Chris@698 1316 if (getScaleUnits() != "") {
Chris@701 1317 int mw = w - 5;
Chris@701 1318 paint.drawText(5,
Chris@701 1319 5 + paint.fontMetrics().ascent(),
Chris@701 1320 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1321 paint.fontMetrics(),
Chris@701 1322 mw));
Chris@691 1323 }
Chris@42 1324 }
Chris@42 1325
Chris@21 1326 void
Chris@918 1327 TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1328 {
Chris@526 1329 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1330 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1331 #endif
Chris@21 1332
Chris@1474 1333 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1334 if (!model) return;
Chris@21 1335
Chris@908 1336 sv_frame_t frame = v->getFrameForX(e->x());
Chris@1474 1337 int resolution = model->getResolution();
Chris@21 1338 if (frame < 0) frame = 0;
Chris@76 1339 frame = (frame / resolution) * resolution;
Chris@21 1340
Chris@908 1341 double value = getValueForY(v, e->y());
Chris@21 1342
Chris@76 1343 bool havePoint = false;
Chris@76 1344
Chris@1429 1345 EventVector points = getLocalPoints(v, e->x());
Chris@76 1346 if (!points.empty()) {
Chris@1429 1347 for (EventVector::iterator i = points.begin();
Chris@76 1348 i != points.end(); ++i) {
Chris@1429 1349 if (((i->getFrame() / resolution) * resolution) != frame) {
Chris@526 1350 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1351 cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
Chris@526 1352 #endif
Chris@76 1353 continue;
Chris@76 1354 }
Chris@76 1355 m_editingPoint = *i;
Chris@76 1356 havePoint = true;
Chris@76 1357 }
Chris@76 1358 }
Chris@76 1359
Chris@76 1360 if (!havePoint) {
Chris@1429 1361 m_editingPoint = Event(frame, float(value), tr("New Point"));
Chris@76 1362 }
Chris@76 1363
Chris@23 1364 m_originalPoint = m_editingPoint;
Chris@22 1365
Chris@376 1366 if (m_editingCommand) finish(m_editingCommand);
Chris@1474 1367 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point"));
Chris@76 1368 if (!havePoint) {
Chris@1429 1369 m_editingCommand->add(m_editingPoint);
Chris@76 1370 }
Chris@22 1371
Chris@21 1372 m_editing = true;
Chris@21 1373 }
Chris@21 1374
Chris@21 1375 void
Chris@918 1376 TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1377 {
Chris@526 1378 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1379 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1380 #endif
Chris@21 1381
Chris@1474 1382 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1383 if (!model || !m_editing) return;
Chris@21 1384
Chris@908 1385 sv_frame_t frame = v->getFrameForX(e->x());
Chris@1474 1386 int resolution = model->getResolution();
Chris@21 1387 if (frame < 0) frame = 0;
Chris@76 1388 frame = (frame / resolution) * resolution;
Chris@21 1389
Chris@908 1390 double value = getValueForY(v, e->y());
Chris@21 1391
Chris@1429 1392 EventVector points = getLocalPoints(v, e->x());
Chris@76 1393
Chris@526 1394 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1395 cerr << points.size() << " points" << endl;
Chris@526 1396 #endif
Chris@76 1397
Chris@76 1398 bool havePoint = false;
Chris@76 1399
Chris@76 1400 if (!points.empty()) {
Chris@1429 1401 for (EventVector::iterator i = points.begin();
Chris@76 1402 i != points.end(); ++i) {
Chris@1429 1403 if (i->getFrame() == m_editingPoint.getFrame() &&
Chris@1429 1404 i->getValue() == m_editingPoint.getValue()) {
Chris@526 1405 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1406 cerr << "ignoring current editing point at " << i->getFrame() << ", " << i->getValue() << endl;
Chris@526 1407 #endif
Chris@76 1408 continue;
Chris@76 1409 }
Chris@1429 1410 if (((i->getFrame() / resolution) * resolution) != frame) {
Chris@526 1411 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1412 cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
Chris@526 1413 #endif
Chris@76 1414 continue;
Chris@76 1415 }
Chris@526 1416 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1417 cerr << "adjusting to new point at " << i->getFrame() << ", " << i->getValue() << endl;
Chris@526 1418 #endif
Chris@76 1419 m_editingPoint = *i;
Chris@76 1420 m_originalPoint = m_editingPoint;
Chris@1429 1421 m_editingCommand->remove(m_editingPoint);
Chris@76 1422 havePoint = true;
Chris@76 1423 }
Chris@76 1424 }
Chris@76 1425
Chris@76 1426 if (!havePoint) {
Chris@1429 1427 if (frame == m_editingPoint.getFrame()) {
Chris@1429 1428 m_editingCommand->remove(m_editingPoint);
Chris@76 1429 }
Chris@76 1430 }
Chris@76 1431
Chris@1429 1432 m_editingPoint = m_editingPoint
Chris@1429 1433 .withFrame(frame)
Chris@1429 1434 .withValue(float(value));
Chris@1429 1435 m_editingCommand->add(m_editingPoint);
Chris@21 1436 }
Chris@21 1437
Chris@21 1438 void
Chris@918 1439 TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1440 {
Chris@526 1441 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1442 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1443 #endif
Chris@1474 1444 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1445 if (!model || !m_editing) return;
Chris@376 1446 finish(m_editingCommand);
Chris@1408 1447 m_editingCommand = nullptr;
Chris@21 1448 m_editing = false;
Chris@21 1449 }
Chris@21 1450
Chris@21 1451 void
Chris@918 1452 TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1453 {
Chris@1474 1454 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1455 if (!model) return;
Chris@335 1456
Chris@1429 1457 EventVector points = getLocalPoints(v, e->x());
Chris@335 1458 if (points.empty()) return;
Chris@335 1459
Chris@335 1460 m_editingPoint = *points.begin();
Chris@335 1461
Chris@335 1462 if (m_editingCommand) {
Chris@1266 1463 finish(m_editingCommand);
Chris@1408 1464 m_editingCommand = nullptr;
Chris@335 1465 }
Chris@335 1466
Chris@335 1467 m_editing = true;
Chris@335 1468 }
Chris@335 1469
Chris@335 1470 void
Chris@918 1471 TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 1472 {
Chris@335 1473 }
Chris@335 1474
Chris@335 1475 void
Chris@918 1476 TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1477 {
Chris@1474 1478 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1479 if (!model || !m_editing) return;
Chris@335 1480
Chris@335 1481 m_editing = false;
Chris@335 1482
Chris@1429 1483 EventVector points = getLocalPoints(v, e->x());
Chris@335 1484 if (points.empty()) return;
Chris@1429 1485 if (points.begin()->getFrame() != m_editingPoint.getFrame() ||
Chris@1429 1486 points.begin()->getValue() != m_editingPoint.getValue()) return;
Chris@335 1487
Chris@1474 1488 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Erase Point"));
Chris@1429 1489 m_editingCommand->remove(m_editingPoint);
Chris@376 1490 finish(m_editingCommand);
Chris@1408 1491 m_editingCommand = nullptr;
Chris@335 1492 m_editing = false;
Chris@335 1493 }
Chris@335 1494
Chris@335 1495 void
Chris@918 1496 TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1497 {
Chris@526 1498 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1499 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1500 #endif
Chris@21 1501
Chris@1474 1502 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1503 if (!model) return;
Chris@21 1504
Chris@1429 1505 EventVector points = getLocalPoints(v, e->x());
Chris@21 1506 if (points.empty()) return;
Chris@21 1507
Chris@21 1508 m_editingPoint = *points.begin();
Chris@23 1509 m_originalPoint = m_editingPoint;
Chris@22 1510
Chris@22 1511 if (m_editingCommand) {
Chris@1266 1512 finish(m_editingCommand);
Chris@1408 1513 m_editingCommand = nullptr;
Chris@22 1514 }
Chris@22 1515
Chris@21 1516 m_editing = true;
Chris@21 1517 }
Chris@21 1518
Chris@21 1519 void
Chris@918 1520 TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1521 {
Chris@526 1522 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1523 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1524 #endif
Chris@21 1525
Chris@1474 1526 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1527 if (!model || !m_editing) return;
Chris@21 1528
Chris@908 1529 sv_frame_t frame = v->getFrameForX(e->x());
Chris@21 1530 if (frame < 0) frame = 0;
Chris@1474 1531 frame = frame / model->getResolution() * model->getResolution();
Chris@21 1532
Chris@908 1533 double value = getValueForY(v, e->y());
Chris@21 1534
Chris@22 1535 if (!m_editingCommand) {
Chris@1474 1536 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Drag Point"));
Chris@22 1537 }
Chris@22 1538
Chris@1429 1539 m_editingCommand->remove(m_editingPoint);
Chris@1429 1540 m_editingPoint = m_editingPoint
Chris@1429 1541 .withFrame(frame)
Chris@1429 1542 .withValue(float(value));
Chris@1429 1543 m_editingCommand->add(m_editingPoint);
Chris@21 1544 }
Chris@21 1545
Chris@21 1546 void
Chris@918 1547 TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1548 {
Chris@526 1549 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1550 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1551 #endif
Chris@1474 1552 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1553 if (!model || !m_editing) return;
Chris@23 1554
Chris@23 1555 if (m_editingCommand) {
Chris@23 1556
Chris@1266 1557 QString newName = m_editingCommand->getName();
Chris@23 1558
Chris@1429 1559 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1429 1560 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1266 1561 newName = tr("Edit Point");
Chris@1266 1562 } else {
Chris@1266 1563 newName = tr("Relocate Point");
Chris@1266 1564 }
Chris@1266 1565 } else {
Chris@1266 1566 newName = tr("Change Point Value");
Chris@1266 1567 }
Chris@23 1568
Chris@1266 1569 m_editingCommand->setName(newName);
Chris@1266 1570 finish(m_editingCommand);
Chris@23 1571 }
Chris@23 1572
Chris@1408 1573 m_editingCommand = nullptr;
Chris@21 1574 m_editing = false;
Chris@21 1575 }
Chris@21 1576
Chris@255 1577 bool
Chris@918 1578 TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 1579 {
Chris@1474 1580 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1581 if (!model) return false;
Chris@70 1582
Chris@1429 1583 EventVector points = getLocalPoints(v, e->x());
Chris@255 1584 if (points.empty()) return false;
Chris@70 1585
Chris@1429 1586 Event point = *points.begin();
Chris@70 1587
Chris@70 1588 ItemEditDialog *dialog = new ItemEditDialog
Chris@1474 1589 (model->getSampleRate(),
Chris@70 1590 ItemEditDialog::ShowTime |
Chris@70 1591 ItemEditDialog::ShowValue |
Chris@73 1592 ItemEditDialog::ShowText,
Chris@698 1593 getScaleUnits());
Chris@70 1594
Chris@1429 1595 dialog->setFrameTime(point.getFrame());
Chris@1429 1596 dialog->setValue(point.getValue());
Chris@1429 1597 dialog->setText(point.getLabel());
Chris@70 1598
Chris@70 1599 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1600
Chris@1429 1601 Event newPoint = point
Chris@1429 1602 .withFrame(dialog->getFrameTime())
Chris@1429 1603 .withValue(dialog->getValue())
Chris@1429 1604 .withLabel(dialog->getText());
Chris@70 1605
Chris@1429 1606 ChangeEventsCommand *command =
Chris@1474 1607 new ChangeEventsCommand(m_model.untyped, tr("Edit Point"));
Chris@1429 1608 command->remove(point);
Chris@1429 1609 command->add(newPoint);
Chris@376 1610 finish(command);
Chris@70 1611 }
Chris@70 1612
Chris@70 1613 delete dialog;
Chris@255 1614 return true;
Chris@70 1615 }
Chris@70 1616
Chris@70 1617 void
Chris@908 1618 TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1619 {
Chris@1474 1620 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1621 if (!model) return;
Chris@99 1622
Chris@1429 1623 ChangeEventsCommand *command =
Chris@1474 1624 new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
Chris@43 1625
Chris@1429 1626 EventVector points =
Chris@1474 1627 model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 1628
Chris@1429 1629 for (Event p: points) {
Chris@43 1630
Chris@1429 1631 Event newPoint = p.withFrame
Chris@1429 1632 (p.getFrame() + newStartFrame - s.getStartFrame());
Chris@1429 1633 command->remove(p);
Chris@1429 1634 command->add(newPoint);
Chris@43 1635 }
Chris@43 1636
Chris@376 1637 finish(command);
Chris@43 1638 }
Chris@43 1639
Chris@43 1640 void
Chris@43 1641 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1642 {
Chris@1474 1643 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1644 if (!model || !s.getDuration()) return;
Chris@99 1645
Chris@1429 1646 ChangeEventsCommand *command =
Chris@1474 1647 new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
Chris@43 1648
Chris@1429 1649 EventVector points =
Chris@1474 1650 model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 1651
Chris@1429 1652 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1429 1653 double oldStart = double(s.getStartFrame());
Chris@1429 1654 double newStart = double(newSize.getStartFrame());
Chris@43 1655
Chris@1429 1656 for (Event p: points) {
Chris@1429 1657
Chris@1429 1658 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@43 1659
Chris@1429 1660 Event newPoint = p
Chris@1429 1661 .withFrame(lrint(newFrame));
Chris@1429 1662 command->remove(p);
Chris@1429 1663 command->add(newPoint);
Chris@43 1664 }
Chris@43 1665
Chris@376 1666 finish(command);
Chris@43 1667 }
Chris@43 1668
Chris@76 1669 void
Chris@76 1670 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1671 {
Chris@1474 1672 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1673 if (!model) return;
Chris@99 1674
Chris@1429 1675 ChangeEventsCommand *command =
Chris@1474 1676 new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
Chris@76 1677
Chris@1429 1678 EventVector points =
Chris@1474 1679 model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@76 1680
Chris@1429 1681 for (Event p: points) {
Chris@1429 1682 command->remove(p);
Chris@76 1683 }
Chris@76 1684
Chris@376 1685 finish(command);
Chris@76 1686 }
Chris@76 1687
Chris@76 1688 void
Chris@918 1689 TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 1690 {
Chris@1474 1691 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1692 if (!model) return;
Chris@99 1693
Chris@1429 1694 EventVector points =
Chris@1474 1695 model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@76 1696
Chris@1429 1697 for (Event p: points) {
Chris@1429 1698 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@76 1699 }
Chris@76 1700 }
Chris@76 1701
Chris@125 1702 bool
Chris@1533 1703 TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
Chris@1533 1704 sv_frame_t /* frameOffset */, bool interactive)
Chris@76 1705 {
Chris@1474 1706 auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
Chris@1474 1707 if (!model) return false;
Chris@99 1708
Chris@1433 1709 EventVector points = from.getPoints();
Chris@76 1710
Chris@360 1711 bool realign = false;
Chris@360 1712
Chris@360 1713 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1714
Chris@360 1715 QMessageBox::StandardButton button =
Chris@919 1716 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 1717 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 1718 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1719 QMessageBox::Yes);
Chris@360 1720
Chris@360 1721 if (button == QMessageBox::Cancel) {
Chris@360 1722 return false;
Chris@360 1723 }
Chris@360 1724
Chris@360 1725 if (button == QMessageBox::Yes) {
Chris@360 1726 realign = true;
Chris@360 1727 }
Chris@360 1728 }
Chris@360 1729
Chris@1429 1730 ChangeEventsCommand *command =
Chris@1474 1731 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@76 1732
Chris@125 1733 enum ValueAvailability {
Chris@125 1734 UnknownAvailability,
Chris@125 1735 NoValues,
Chris@125 1736 SomeValues,
Chris@125 1737 AllValues
Chris@125 1738 };
Chris@125 1739
Chris@340 1740 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1741
Chris@125 1742 bool haveUsableLabels = false;
Chris@340 1743 Labeller labeller;
Chris@1474 1744 labeller.setSampleRate(model->getSampleRate());
Chris@125 1745
Chris@125 1746 if (interactive) {
Chris@125 1747
Chris@125 1748 ValueAvailability availability = UnknownAvailability;
Chris@125 1749
Chris@1423 1750 for (EventVector::const_iterator i = points.begin();
Chris@125 1751 i != points.end(); ++i) {
Chris@125 1752
Chris@125 1753 if (availability == UnknownAvailability) {
Chris@1423 1754 if (i->hasValue()) availability = AllValues;
Chris@125 1755 else availability = NoValues;
Chris@125 1756 continue;
Chris@125 1757 }
Chris@125 1758
Chris@1423 1759 if (i->hasValue()) {
Chris@125 1760 if (availability == NoValues) {
Chris@125 1761 availability = SomeValues;
Chris@125 1762 }
Chris@125 1763 } else {
Chris@125 1764 if (availability == AllValues) {
Chris@125 1765 availability = SomeValues;
Chris@125 1766 }
Chris@125 1767 }
Chris@125 1768
Chris@125 1769 if (!haveUsableLabels) {
Chris@1423 1770 if (i->hasLabel()) {
Chris@125 1771 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1772 haveUsableLabels = true;
Chris@125 1773 }
Chris@125 1774 }
Chris@125 1775 }
Chris@125 1776
Chris@125 1777 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1778 }
Chris@125 1779
Chris@125 1780 if (availability == NoValues || availability == SomeValues) {
Chris@125 1781
Chris@125 1782 QString text;
Chris@125 1783 if (availability == NoValues) {
Chris@125 1784 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1785 } else {
Chris@125 1786 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 1787 }
Chris@125 1788
Chris@340 1789 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1790
Chris@125 1791 QStringList options;
Chris@340 1792 std::vector<Labeller::ValueType> genopts;
Chris@125 1793
Chris@340 1794 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1795 i != names.end(); ++i) {
Chris@340 1796 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1797 else options << i->second;
Chris@340 1798 genopts.push_back(i->first);
Chris@125 1799 }
Chris@125 1800
Chris@125 1801 static int prevSelection = 0;
Chris@125 1802
Chris@125 1803 bool ok = false;
Chris@125 1804 QString selected = ListInputDialog::getItem
Chris@1408 1805 (nullptr, tr("Choose value calculation"),
Chris@125 1806 text, options, prevSelection, &ok);
Chris@125 1807
Chris@852 1808 if (!ok) {
Chris@852 1809 delete command;
Chris@852 1810 return false;
Chris@852 1811 }
Chris@125 1812 int selection = 0;
Chris@340 1813 generation = Labeller::ValueNone;
Chris@125 1814
Chris@125 1815 for (QStringList::const_iterator i = options.begin();
Chris@125 1816 i != options.end(); ++i) {
Chris@125 1817 if (selected == *i) {
Chris@340 1818 generation = genopts[selection];
Chris@125 1819 break;
Chris@125 1820 }
Chris@125 1821 ++selection;
Chris@125 1822 }
Chris@340 1823
Chris@340 1824 labeller.setType(generation);
Chris@340 1825
Chris@340 1826 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1827 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1828 int cycleSize = QInputDialog::getInt
Chris@1408 1829 (nullptr, tr("Select cycle size"),
Chris@340 1830 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1831 labeller.setCounterCycleSize(cycleSize);
Chris@340 1832 }
Chris@125 1833
Chris@125 1834 prevSelection = selection;
Chris@125 1835 }
Chris@125 1836 }
Chris@125 1837
Chris@1429 1838 Event prevPoint;
Chris@125 1839
Chris@1423 1840 for (EventVector::const_iterator i = points.begin();
Chris@76 1841 i != points.end(); ++i) {
Chris@76 1842
Chris@908 1843 sv_frame_t frame = 0;
Chris@360 1844
Chris@360 1845 if (!realign) {
Chris@360 1846
Chris@360 1847 frame = i->getFrame();
Chris@360 1848
Chris@360 1849 } else {
Chris@360 1850
Chris@1423 1851 if (i->hasReferenceFrame()) {
Chris@360 1852 frame = i->getReferenceFrame();
Chris@360 1853 frame = alignFromReference(v, frame);
Chris@360 1854 } else {
Chris@360 1855 frame = i->getFrame();
Chris@360 1856 }
Chris@76 1857 }
Chris@360 1858
Chris@1533 1859 Event newPoint = i->withFrame(frame);
Chris@1533 1860
Chris@1429 1861 if (!i->hasLabel() && i->hasValue()) {
Chris@1429 1862 newPoint = newPoint.withLabel(QString("%1").arg(i->getValue()));
Chris@125 1863 }
Chris@125 1864
Chris@372 1865 bool usePrev = false;
Chris@1429 1866 Event formerPrevPoint = prevPoint;
Chris@372 1867
Chris@1429 1868 if (!i->hasValue()) {
Chris@526 1869 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1870 cerr << "Setting value on point at " << newPoint.getFrame() << " from labeller";
Chris@526 1871 if (i == points.begin()) {
Chris@682 1872 cerr << ", no prev point" << endl;
Chris@526 1873 } else {
Chris@1429 1874 cerr << ", prev point is at " << prevPoint.getFrame() << endl;
Chris@526 1875 }
Chris@526 1876 #endif
Chris@1429 1877
Chris@1429 1878 Labeller::Revaluing valuing =
Chris@1429 1879 labeller.revalue
Chris@1408 1880 (newPoint, (i == points.begin()) ? nullptr : &prevPoint);
Chris@1429 1881
Chris@526 1882 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1883 cerr << "New point value = " << newPoint.getValue() << endl;
Chris@526 1884 #endif
Chris@1429 1885 if (valuing.first == Labeller::AppliesToPreviousEvent) {
Chris@372 1886 usePrev = true;
Chris@1429 1887 prevPoint = valuing.second;
Chris@1429 1888 } else {
Chris@1429 1889 newPoint = valuing.second;
Chris@372 1890 }
Chris@372 1891 }
Chris@372 1892
Chris@372 1893 if (usePrev) {
Chris@1429 1894 command->remove(formerPrevPoint);
Chris@1429 1895 command->add(prevPoint);
Chris@340 1896 }
Chris@125 1897
Chris@340 1898 prevPoint = newPoint;
Chris@1429 1899 command->add(newPoint);
Chris@76 1900 }
Chris@76 1901
Chris@376 1902 finish(command);
Chris@125 1903 return true;
Chris@360 1904 }
Chris@76 1905
Chris@316 1906 void
Chris@316 1907 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1908 QString indent, QString extraAttributes) const
Chris@6 1909 {
Chris@1362 1910 QString s;
Chris@1362 1911
Chris@1362 1912 s += QString("plotStyle=\"%1\" "
Chris@1362 1913 "verticalScale=\"%2\" "
Chris@1362 1914 "scaleMinimum=\"%3\" "
Chris@1362 1915 "scaleMaximum=\"%4\" "
Chris@1362 1916 "drawDivisions=\"%5\" "
Chris@1362 1917 "derivative=\"%6\" ")
Chris@1362 1918 .arg(m_plotStyle)
Chris@1362 1919 .arg(m_verticalScale)
Chris@1362 1920 .arg(m_scaleMinimum)
Chris@1362 1921 .arg(m_scaleMaximum)
Chris@1362 1922 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@1362 1923 .arg(m_derivative ? "true" : "false");
Chris@1362 1924
Chris@1362 1925 // New-style colour map attribute, by string id rather than by
Chris@1362 1926 // number
Chris@1362 1927
Chris@1362 1928 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1929 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1930
Chris@1362 1931 // Old-style colour map attribute
Chris@1362 1932
Chris@1362 1933 s += QString("colourMap=\"%1\" ")
Chris@1362 1934 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1935
Chris@1362 1936 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@0 1937 }
Chris@0 1938
Chris@11 1939 void
Chris@11 1940 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1941 {
Chris@287 1942 SingleColourLayer::setProperties(attributes);
Chris@11 1943
Chris@445 1944 bool ok, alsoOk;
Chris@287 1945
Chris@1362 1946 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1947 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1948 if (colourMap >= 0) {
Chris@1362 1949 setFillColourMap(colourMap);
Chris@1362 1950 } else {
Chris@1362 1951 colourMap = attributes.value("colourMap").toInt(&ok);
Chris@1362 1952 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1953 setFillColourMap(colourMap);
Chris@1362 1954 }
Chris@1362 1955 }
Chris@287 1956
Chris@11 1957 PlotStyle style = (PlotStyle)
Chris@1266 1958 attributes.value("plotStyle").toInt(&ok);
Chris@11 1959 if (ok) setPlotStyle(style);
Chris@286 1960
Chris@286 1961 VerticalScale scale = (VerticalScale)
Chris@1266 1962 attributes.value("verticalScale").toInt(&ok);
Chris@286 1963 if (ok) setVerticalScale(scale);
Chris@445 1964
Chris@513 1965 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1966 setDrawSegmentDivisions(draw);
Chris@513 1967
Chris@553 1968 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1969 setShowDerivative(derivative);
Chris@553 1970
Chris@445 1971 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1972 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1973 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1974 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1975 #endif
Chris@526 1976 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1977 }
Chris@11 1978