annotate layer/TimeValueLayer.cpp @ 668:d52751e2728b

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