annotate layer/TimeValueLayer.cpp @ 1245:f0e291fa7b9c

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