428 lines
13 KiB
C++
Executable File
428 lines
13 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* calculates, converts, and draws events by entering time or pixel position as value
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Mike Wille, SLSE, 2014-10-01
|
|
*/
|
|
|
|
#include "EventTools.h"
|
|
#include "EventGraphic.h"
|
|
#include "Event.h"
|
|
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
#include <math.h>
|
|
#include <QPainter>
|
|
#include <QDebug>
|
|
|
|
|
|
namespace LtsTimelineClassic
|
|
{
|
|
|
|
#define logger NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.EventTools") // for static functions
|
|
#ifdef LTS_DEBUG
|
|
//#define VERBOSE
|
|
#endif
|
|
#ifdef VERBOSE
|
|
#define dbg_print(x) qWarning() << #x << x
|
|
#else
|
|
#define dbg_print(x) (void)x;
|
|
#endif
|
|
|
|
qint64 EventTools::s_minAdjusted = 0;
|
|
qint64 EventTools::s_maxAdjusted = 1;
|
|
qint64 EventTools::s_minTimeline = 0;
|
|
qint64 EventTools::s_maxTimeline = 1;
|
|
qint64 EventTools::s_sbPos = 0;
|
|
qint64 EventTools::s_zoomFactor = 1;
|
|
int EventTools::s_paintMin = 0;
|
|
int EventTools::s_paintDiff = 0;
|
|
bool EventTools::s_isValid = false;
|
|
qint64 EventTools::s_diff = 0;
|
|
int EventTools::s_sectionHeight = 0;
|
|
int EventTools::s_test = 0;
|
|
qint64 EventTools::s_blockSizeTime = 0;
|
|
bool EventTools::s_inUse = false;
|
|
|
|
|
|
// creates relative event tools
|
|
// do NOT use references, as setValues HAS TO be called when values change or all calculations will probably fail
|
|
void EventTools::setValues(const qint64 min, const qint64 max, const qint64 sbPos, const qint64 zoomFactor)
|
|
{
|
|
if (s_minTimeline == min && s_maxTimeline == max && s_sbPos == sbPos && s_zoomFactor == zoomFactor) // no need to recalc same values
|
|
return;
|
|
|
|
s_sbPos = sbPos;
|
|
s_zoomFactor = zoomFactor;
|
|
|
|
//qWarning() << "setValues: sbPos =" << s_sbPos;
|
|
//qWarning() << "setValues: zoomFactor =" << s_zoomFactor;
|
|
|
|
s_isValid = false;
|
|
|
|
s_minAdjusted = s_sbPos;
|
|
|
|
double d_max = s_minAdjusted;
|
|
d_max += s_zoomFactor;
|
|
|
|
s_maxAdjusted = d_max;
|
|
s_diff = s_maxAdjusted - s_minAdjusted;
|
|
|
|
s_minTimeline = min;
|
|
s_maxTimeline = max - EventTools::s_diff + (EventTools::getBlockSizeTime() * 2); // add one event width to the end, so that the last event is not cut off when live
|
|
|
|
recalcBlockSizeTime();
|
|
|
|
if (min > 0 && max > 1)
|
|
{
|
|
s_isValid = true;
|
|
//#warning TODO: ERST AB HIER IST DIE TIMELINE ZU GEBRAUCHEN !
|
|
//qWarning() << "EventTools: min/max okay => "<<min<<"/"<<max;
|
|
}
|
|
else
|
|
{
|
|
//qWarning() << "EventTools: min/max weird => "<<min<<"/"<<max;
|
|
}
|
|
|
|
#ifdef VERBOSE
|
|
qWarning() << "\r\n\r\nEventTools::setValues: " << "\r\n"
|
|
<< "s_sbPos" << s_sbPos << "\r\n"
|
|
<< "s_zoomFactor" << s_zoomFactor << "\r\n"
|
|
<< "s_minTimeline" << s_minTimeline << "\r\n"
|
|
<< "s_maxTimeline" << s_maxTimeline << "\r\n"
|
|
<< "s_isValid" << s_isValid << "\r\n"
|
|
<< "s_minAdjusted" << s_minAdjusted << "\r\n"
|
|
<< "s_maxAdjusted" << (long)s_maxAdjusted << "\r\n"
|
|
<< "s_diff" << s_diff << "\r\n"
|
|
;
|
|
#endif
|
|
}
|
|
|
|
|
|
void EventTools::setSection(const int x, const int length)
|
|
{
|
|
s_paintMin = x;
|
|
s_paintDiff = length;
|
|
//qWarning() << "EventTools::setSection: x" << x << "length" << length;
|
|
}
|
|
|
|
|
|
bool EventTools::isValid()
|
|
{
|
|
#ifdef VERBOSE
|
|
if (!s_isValid)
|
|
qWarning() << "<<<< INVALID EVENT TOOLS!!";
|
|
|
|
qWarning() << "\r\nEventTools::isValid: " << "\r\n"
|
|
<< "s_isValid" << s_isValid << "\r\n"
|
|
;
|
|
#endif
|
|
|
|
return s_isValid;
|
|
}
|
|
|
|
qint64 EventTools::getAdjustedMin()
|
|
{
|
|
if (!isValid())
|
|
return 0;
|
|
|
|
return s_minAdjusted;
|
|
}
|
|
|
|
qint64 EventTools::getAdjustedMax()
|
|
{
|
|
if (!isValid())
|
|
return 1;
|
|
|
|
return s_maxAdjusted;
|
|
}
|
|
|
|
qint64 EventTools::getTimelineMin()
|
|
{
|
|
if (!isValid())
|
|
return 0;
|
|
|
|
return s_minTimeline;
|
|
}
|
|
|
|
qint64 EventTools::getScrollbarPosition()
|
|
{
|
|
return s_sbPos;
|
|
}
|
|
|
|
qint64 EventTools::getTimelineMax()
|
|
{
|
|
if (!isValid())
|
|
return 1;
|
|
|
|
return s_maxTimeline;
|
|
}
|
|
|
|
int EventTools::getEventPosition(qint64 eventTime) // time to pixels
|
|
{
|
|
qint64 min = s_minAdjusted;
|
|
//dbg_print(min);
|
|
qint64 diff = s_diff; // = max - min
|
|
|
|
//dbg_print(s_maxTimeline);
|
|
//dbg_print(s_maxAdjusted);
|
|
|
|
if (diff == 0)
|
|
{
|
|
#ifdef VERBOSE
|
|
qWarning() << "\n\ngetEventPosition: diff == 0\n";
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
double pDiff = s_paintDiff; // 500px (700[right] - 200[left])
|
|
double pMin = s_paintMin; // => 200[left]
|
|
|
|
double x1 = eventTime; // => 1443000000
|
|
x1 -= min; // => 1443000000 - 1440000000 => 3.000.000
|
|
x1 /= diff; // => (max - min) => 1445000000 - 1440000000 => 5.000.000 => 3/5 = 0,6
|
|
|
|
double x2 = x1; // => 0,6
|
|
x2 *= pDiff; // => 0,6 * 500px => 300
|
|
x2 += pMin; // => 300 + 200 x (pixel) relative position of event in widget (set reference as valid)
|
|
|
|
#ifdef VERBOSE
|
|
qWarning() << "\r\n\r\nEventTools::getEventPosition: " << "\r\n"
|
|
<< "min" << min << "\r\n"
|
|
<< "diff" << diff << "\r\n"
|
|
<< "pDiff" << pDiff << "\r\n"
|
|
<< "pMin" << pMin << "\r\n"
|
|
<< (long)x1 << "[x1]" << "= (" << eventTime << "[eventTime]" << "-" << min << "[min]" << ") /" << diff << "[diff]" << "\r\n"
|
|
<< (long)x2 << "[x2]" << "= (" << (long)x1 << "[x1]" << "*" << pDiff << "[pDiff]" << ") +" << pMin << "[pMin]" << "\r\n"
|
|
;
|
|
|
|
dbg_print(min);
|
|
#endif
|
|
|
|
return qRound(x2);
|
|
}
|
|
|
|
qint64 EventTools::getEventTime(double eventPosition) // pixels to qint64 (time), starting from minimum
|
|
{
|
|
if (!isValid())
|
|
{
|
|
//qWarning() << "! getEventTime: invalid ET";
|
|
return 0;
|
|
}
|
|
|
|
if (eventPosition > 999999)
|
|
{
|
|
//WARNING_LOG(GET_LOGGER_ET) << "eventPosition > 999999: returning 0" ;
|
|
WARNING_LOG(logger) << "getEventTime: pos > 999999 => returning 0" ;
|
|
return 0;
|
|
}
|
|
|
|
double min = s_minAdjusted;
|
|
double diff = s_diff;
|
|
double pDiff = s_paintDiff;
|
|
|
|
if (pDiff < 1)
|
|
{
|
|
//WARNING_LOG(GET_LOGGER_ET) << "pDiff = " << pDiff << ", which is smaller than 1! avoiding 0-division now! returning 0" ;
|
|
WARNING_LOG(logger) << "getEventTime: pDiff < 1" ;
|
|
return 0;
|
|
}
|
|
|
|
double pMin = s_paintMin;
|
|
|
|
double x1 = eventPosition;
|
|
x1 -= pMin;
|
|
x1 /= pDiff;
|
|
|
|
double x2 = x1;
|
|
x2 *= diff;
|
|
x2 += min;
|
|
|
|
return qRound64(x2);
|
|
}
|
|
|
|
|
|
void EventTools::drawMarkLines(QPainter *painter, const EventList &eventList, int viewHeight)
|
|
{
|
|
if (!isValid())
|
|
return;
|
|
|
|
//painter->setPen(QColor("red"));
|
|
|
|
for (int k = 0; k < eventList.count(); k++)
|
|
{
|
|
Event *e = eventList.at(k);
|
|
int pos = getEventPosition(e->startTime());
|
|
|
|
if (pos < (s_paintMin) || pos > (s_paintMin + s_paintDiff)) // check boundaries
|
|
continue;
|
|
|
|
painter->drawLine(pos, 0, pos, viewHeight);
|
|
}
|
|
}
|
|
|
|
|
|
void EventTools::recalcBlockSizeTime()
|
|
{
|
|
s_blockSizeTime = _getBlockSizeTime();
|
|
}
|
|
|
|
|
|
qint64 EventTools::_getBlockSizeTime()
|
|
{
|
|
// TODO: use s_diff instead:
|
|
int startPos = getEventPosition(s_minAdjusted); // => 50px (WARNING: why is minAdjusted's position not 0, but is going to be hidden by section 0 ????
|
|
int endPos = getEventPosition(s_maxAdjusted); // => 350px the diff between min & max is 300px
|
|
|
|
int posDiff = endPos - startPos;
|
|
|
|
if (posDiff < 1)
|
|
{
|
|
//qWarning() << "posDiff < 1" << posDiff;
|
|
return 0; // event tools are not ready yet..
|
|
}
|
|
|
|
qint64 diff = s_maxAdjusted - s_minAdjusted; // => 50.000s
|
|
|
|
double pixelWidthInSeconds = diff;
|
|
pixelWidthInSeconds /= posDiff; // => 50.000s / 300px = 167s 1 pixel "has"/"contains"/"is" 167 seconds ("in width")
|
|
|
|
int eventGraphicsWidth = eventWidth(); // usually 8px
|
|
qint64 result = eventGraphicsWidth; result *= pixelWidthInSeconds; // the block is 1337 :) seconds long
|
|
|
|
#ifdef VERBOSE
|
|
qWarning() << "\r\n\r\nEventTools::recalcBlockSizeTime: " << "\r\n"
|
|
<< "minAdj" << s_minAdjusted << "\r\n"
|
|
<< "maxAdj" << s_maxAdjusted << "\r\n"
|
|
<< "endPos" << endPos << "\r\n"
|
|
<< "diff" << diff << "\r\n"
|
|
<< "pixelWidthInSeconds" << pixelWidthInSeconds << "\r\n"
|
|
<< "eventGraphicsWidth" << eventGraphicsWidth << "\r\n"
|
|
<< "result" << result << "\r\n"
|
|
<< "(duration > result) ?" << (duration > result)
|
|
;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
qint64 EventTools::getBlockSizeTime(qint64 duration)
|
|
{
|
|
if (duration < s_blockSizeTime)
|
|
return s_blockSizeTime;
|
|
else
|
|
return duration;
|
|
}
|
|
|
|
|
|
void EventTools::drawEvents(QPainter *painter, const EventGraphicList &eventGraphicList, const int verticalPosition /* y(0) = event line to draw on */)
|
|
{
|
|
painter->save();
|
|
|
|
//painter->setRenderHint(QPainter::RenderHint::Antialiasing); looks super ugly ^^
|
|
painter->setClipping(true); // do not draw outside of the section & line of the events ..
|
|
painter->setClipRect(QRect(s_paintMin, verticalPosition, s_paintDiff, s_sectionHeight)); // clip the complete timeline view to that single event line & section
|
|
|
|
//WARNING_LOG(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.EventTools")) << "drawEvents > eventList.count = " << eventList.count() ;
|
|
for (int i = 0; i < eventGraphicList.count(); i++)
|
|
{
|
|
EventGraphic *eg = eventGraphicList.at(i);
|
|
|
|
if (!eg)
|
|
{
|
|
//WARNING_LOG(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.")) << "" ;
|
|
//WARNING_LOG(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.EventTools")) << "drawEvents > (!event) !!" ;
|
|
continue;
|
|
}
|
|
|
|
qint64 eventStart = eg->lowestTime();
|
|
|
|
if (eventStart == 0)
|
|
continue;
|
|
|
|
|
|
|
|
#ifdef VERBOSE
|
|
qWarning() << "\r\n\r\nEventTools::drawEvents" << "\r\n"
|
|
<< "eventStart" << eventStart << "\r\n";
|
|
#endif
|
|
|
|
qint64 eventDuration = eg->getDuration(); // take the highest duration
|
|
|
|
int newEventPosition = getEventPosition(eventStart);
|
|
|
|
int newEventEndPosition = 0;
|
|
int finalEventEndPos = 0;
|
|
|
|
int eventGraphicsWidth = eventWidth(); // event graphic width of ALL event graphics (DO NOT RESIZE EVENT GRAPHICS WHEN A SIZE HAS ALREADY BEEN CHOSEN & USED!!!)
|
|
|
|
if (eventDuration > 1) // if events have a duration, calculate its end position before checking for boundaries
|
|
{
|
|
qint64 eventPosition = eventStart;
|
|
eventPosition += eventDuration;
|
|
newEventEndPosition = getEventPosition(eventPosition);
|
|
finalEventEndPos = GetEndPos(newEventEndPosition, eventGraphicsWidth); // move the event graphics, so that the middle point of the event graphic is at the start-position (s_start)
|
|
}
|
|
else
|
|
{
|
|
newEventEndPosition = newEventPosition; // events without an duration end at the same position as they start
|
|
}
|
|
|
|
|
|
if (newEventPosition + (newEventEndPosition - newEventPosition) < ((-eventGraphicsWidth) + s_paintMin) || // eventGraphicsWidth makes sure that event is visible until it is out of scope
|
|
newEventPosition > (s_paintMin + s_paintDiff)) // check boundaries
|
|
{
|
|
#ifdef VERBOSE
|
|
qWarning() << "\n\ndrawEvents: boundary check result was negative\n" <<
|
|
" newEventPosition=" << newEventPosition << "\n" <<
|
|
" newEventEndPosition=" << newEventPosition << "\n" <<
|
|
" eventGraphicsWidth=" << eventGraphicsWidth << "\n" <<
|
|
" s_paintMin=" << s_paintMin << "\n" <<
|
|
" s_paintDiff=" << s_paintDiff << "\n";
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
|
|
// draw event graphics first, so that the duration background will overlap it!
|
|
int finalEventPos = newEventPosition;
|
|
|
|
QRect eventGraphicRect(finalEventPos, verticalPosition + 2, eventGraphicsWidth, s_sectionHeight - 6); // position on which the passive drawer can draw its event graphic on
|
|
|
|
//qWarning() << ">> DRAW: " << eventGraphicRect;
|
|
|
|
eg->drawEventGraphic(painter, eventGraphicRect); // tell the passive drawer to draw his event graphic on this painter at the given position (QRect)
|
|
|
|
#ifdef VERBOSE
|
|
qWarning() << "drawEvents > actually draw an event at "
|
|
"x=" << eventGraphicRect.x() << " "
|
|
"y=" << eventGraphicRect.y() << " "
|
|
"eventStart=" << eventStart ;
|
|
#endif
|
|
|
|
if (eventDuration > 1 && // if there is a duration, get another event position (eventPosition + duration) for the terminatory event graphic
|
|
(finalEventEndPos - 1) > finalEventPos) // draw only if scale is ideal to show duration events
|
|
{
|
|
QRect eventGraphicEndRect(finalEventEndPos, verticalPosition + 2, eventGraphicsWidth, s_sectionHeight - 6);
|
|
eg->drawEventGraphic(painter, eventGraphicEndRect); // draw terminatory event graphic
|
|
|
|
// this is a event with a duration - so calculate the rect for the whole event and draw the duration background
|
|
// overlap event graphics now!
|
|
QRect eventWholeRect(newEventPosition + (eventGraphicsWidth / 2) /* because the drawings start pos is exactly
|
|
at the event start position and we must draw the duration background on the middle of the drawing
|
|
(because of symetrics - see the geometric forms the timeline uses to draw events) */
|
|
, verticalPosition + 2
|
|
, (newEventEndPosition - newEventPosition) - (eventGraphicsWidth) /* s.a.a. */
|
|
, s_sectionHeight - 6); // this is the WHOLE rect of the event with duration
|
|
|
|
eg->drawDurationBackground(painter, eventWholeRect);
|
|
}
|
|
}
|
|
|
|
painter->restore();
|
|
}
|
|
|
|
} // namespace LtsTimelineClassic
|