/** * @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 #include #include #include 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 => "< "< 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