14 #include <vtkPiecewiseFunction.h>
17 #include <QMouseEvent>
28 QWidget(parent), mBorder(4), mStart(0), mStop(0), mPos(0), mCloseToGlyph(false), mTolerance_p(5)
30 mForward = vtkPiecewiseFunctionPtr::New();
31 mBackward = vtkPiecewiseFunctionPtr::New();
41 this->setFocusPolicy(Qt::StrongFocus);
42 this->setMouseTracking(
true);
65 double TimelineWidget::findCompactedTime(
double timeInterval,
double totalUsedTime,
double totalTime)
const
74 double compactingFactor0 = 0.2;
75 double compactingFactor1 = totalUsedTime/totalTime * 0.2;
78 double w = timeInterval/totalTime;
79 double c = w*compactingFactor1 + (1-w)*compactingFactor0;
81 double compactedTime = std::min(timeInterval * c, 0.2*totalUsedTime);
94 void TimelineWidget::createCompactingTransforms()
98 std::vector<TimelineEvent> temp = mEvents;
102 for (
unsigned i = 0; i < temp.size(); ++i)
104 temp[i].mDescription =
"merged";
105 temp[i].mStartTime = std::max(temp[i].mStartTime - fat, mStart);
106 temp[i].mEndTime = std::min(temp[i].mEndTime + fat, mStop);
110 for (
unsigned i = 0; i < temp.size(); ++i)
114 for (
unsigned j = i + 1; j < temp.size();)
116 if (temp[i].isOverlap(temp[j]))
118 temp[i].mStartTime = std::min(temp[i].mStartTime, temp[j].mStartTime);
119 temp[i].mEndTime = std::max(temp[i].mEndTime, temp[j].mEndTime);
120 temp.erase(temp.begin() + j);
130 std::sort(temp.begin(), temp.end());
132 double totalUsedTime = 0;
133 for (
unsigned i = 0; i < temp.size(); ++i)
134 totalUsedTime += temp[i].mEndTime - temp[i].mStartTime;
139 mForward = vtkPiecewiseFunctionPtr::New();
142 mForward->AddPoint(mStart, y);
143 double x_last = mStart;
144 for (
unsigned i = 0; i < temp.size(); ++i)
147 y = y + this->findCompactedTime(temp[i].mStartTime - x_last, totalUsedTime, mStop-mStart);
148 mForward->AddPoint(temp[i].mStartTime, y);
150 y = y + temp[i].mEndTime - temp[i].mStartTime;
151 mForward->AddPoint(temp[i].mEndTime, y);
153 x_last = temp[i].mEndTime;
157 y = y + this->findCompactedTime(mStop - x_last, totalUsedTime, mStop-mStart);
158 mForward->AddPoint(mStop, y);
162 mForward->GetRange(range);
163 double y_min = mForward->GetValue(range[0]);
164 double y_max = mForward->GetValue(range[1]);
165 for (
int i = 0; i < mForward->GetSize(); ++i)
168 mForward->GetNodeValue(i, val);
169 val[1] = (val[1] - y_min) / (y_max - y_min);
170 mForward->SetNodeValue(i, val);
174 mBackward = vtkPiecewiseFunctionPtr::New();
175 for (
int i = 0; i < mForward->GetSize(); ++i)
178 mForward->GetNodeValue(i, val);
179 mBackward->AddPoint(val[1], val[0]);
182 mNoncompactedIntervals = temp;
192 this->createCompactingTransforms();
198 for (
unsigned i = 0; i < mEvents.size(); ++i)
200 if (mEvents[i].isSingular())
204 if (!std::count(mContinousEvents.begin(), mContinousEvents.end(), mEvents[i].mGroup))
205 mContinousEvents.push_back(mEvents[i].mGroup);
219 int TimelineWidget::mapTime2PlotX(
double time)
const
221 double normalized = mForward->GetValue(time);
223 int retval = normalized * mPlotArea.width() + mPlotArea.left();
229 double TimelineWidget::mapPlotX2Time(
int plotX)
const
231 double normalized = double(plotX - mPlotArea.left()) / mPlotArea.width();
232 double retval = mBackward->GetValue(normalized);
241 QWidget::paintEvent(
event);
243 QPainter painter(
this);
245 painter.setRenderHint(QPainter::Antialiasing);
246 painter.setPen(Qt::NoPen);
248 QColor gray0(240, 240, 240);
249 QColor gray01(220, 220, 220);
250 QColor gray1(200, 200, 200);
251 QColor gray2(170, 170, 170);
252 QColor gray3(150, 150, 150);
253 QColor gray4(100, 100, 100);
254 QColor highlight(110, 214, 255);
257 QBrush brush(Qt::SolidPattern);
258 brush.setColor(gray2);
259 painter.setBrush(brush);
261 painter.drawRoundedRect(this->mFullArea, 4, 4);
263 brush.setColor(gray1);
264 painter.setBrush(brush);
265 painter.drawRoundedRect(this->mPlotArea, 4, 4);
270 for (
unsigned i = 0; i < mNoncompactedIntervals.size(); ++i)
272 int start_p = this->mapTime2PlotX(mNoncompactedIntervals[i].mStartTime);
273 int stop_p = this->mapTime2PlotX(mNoncompactedIntervals[i].mEndTime);
274 QColor color = gray01;
275 painter.fillRect(QRect(start_p, mPlotArea.top(), stop_p - start_p, mPlotArea.height()), color);
279 for (
unsigned i = 0; i < mEvents.size(); ++i)
281 if (!mContinousEvents.contains(mEvents[i].mGroup))
283 int start_p = this->mapTime2PlotX(mEvents[i].mStartTime);
284 int stop_p = this->mapTime2PlotX(mEvents[i].mEndTime);
285 int level = std::distance(mContinousEvents.begin(),
286 std::find(mContinousEvents.begin(), mContinousEvents.end(), mEvents[i].mGroup));
287 int level_max = mContinousEvents.size();
288 int thisHeight = (mPlotArea.height()) / level_max - margin * (level_max - 1) / level_max;
289 int thisTop = mPlotArea.top() + level * thisHeight + level * margin;
292 QColor color = mEvents[i].mColor;
294 painter.fillRect(QRect(start_p, thisTop, stop_p - start_p, thisHeight), color);
298 for (
unsigned i = 0; i < mEvents.size(); ++i)
300 if (mContinousEvents.contains(mEvents[i].mGroup))
303 int start_p = this->mapTime2PlotX(mEvents[i].mStartTime);
307 QRect rect(start_p - glyphWidth / 2, mPlotArea.top(), glyphWidth, mPlotArea.height());
309 brush.setColor(QColor(50, 50, 50));
310 painter.setBrush(brush);
311 painter.drawRoundedRect(rect, 2, 2);
314 if (rect.width() > 2 && rect.height() > 2)
316 rect.adjust(1, 1, -1, -1);
317 painter.fillRect(rect, gray2);
321 int offset_p = this->mapTime2PlotX(mPos);
324 int h = mPlotArea.height();
325 glyph.push_back(QPointF(-z, 0));
326 glyph.push_back(QPointF(z, 0));
327 glyph.push_back(QPointF(z, 0.7 * h));
328 glyph.push_back(QPointF(0, h));
329 glyph.push_back(QPointF(-z, 0.7 * h));
330 glyph.translate(offset_p, 0);
331 if (this->hasFocus() || mCloseToGlyph)
332 painter.setPen(highlight);
334 painter.setPen(gray4);
337 QRadialGradient radialGrad(QPointF(offset_p, h / 3), 2 * h / 3);
338 radialGrad.setColorAt(0, gray0);
340 radialGrad.setColorAt(1, gray2);
342 brush = QBrush(radialGrad);
344 painter.setBrush(brush);
345 painter.drawPolygon(glyph);
350 QWidget::resizeEvent(evt);
353 this->mFullArea = QRect(0, 0, width(), height());
354 this->mPlotArea = QRect(mBorder, mBorder, width() - mBorder * 2, height() - mBorder * 2);
357 void TimelineWidget::setPositionFromScreenPos(
int x,
int y)
359 mPos = this->mapPlotX2Time(x);
366 QWidget::mousePressEvent(
event);
368 if (
event->button() == Qt::LeftButton)
370 this->setPositionFromScreenPos(
event->x(),
event->y());
375 QWidget::mouseReleaseEvent(
event);
380 if (
event->type() == QEvent::ToolTip)
382 QHelpEvent *helpEvent =
static_cast<QHelpEvent *
>(
event);
383 if (!this->showHelp(helpEvent->pos()))
390 return QWidget::event(
event);
393 bool TimelineWidget::showHelp(QPoint pos)
395 double mouseTimePos = this->mapPlotX2Time(pos.x());
396 double tol_ms = this->mapPlotX2Time(pos.x()+mTolerance_p) - mouseTimePos;
400 for (
unsigned i = 0; i < mEvents.size(); ++i)
402 if (mEvents[i].isInside(mouseTimePos, tol_ms))
404 text << mEvents[i].mDescription;
411 QToolTip::hideText();
418 QToolTip::showText(this->mapToGlobal(pos), text.join(
"\n"),
this);
425 QWidget::mouseMoveEvent(
event);
427 if (
event->buttons() == Qt::LeftButton)
429 this->setPositionFromScreenPos(
event->x(),
event->y());
432 bool newCloseToGlyph = fabs((
double)(this->mapTime2PlotX(mPos) -
event->x())) < 5;
433 if (mCloseToGlyph != newCloseToGlyph)
438 mCloseToGlyph = newCloseToGlyph;
443 return QSize(100, 30);
448 return QSize(100, 20);