/** * @file TimerCallback.cpp * @author Ulrich Kemloh <kemlohulrich@gmail.com> * @version 0.1 * Copyright (C) <2009-2010> * * @section LICENSE * This file is part of OpenPedSim. * * OpenPedSim is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * OpenPedSim is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenPedSim. If not, see <http://www.gnu.org/licenses/>. * * @section DESCRIPTION * This class is called by the timer and update all elements on the screen * * \brief Callback class for the visualisation thread. Triggered by the timer * * * * Created on: 11.05.2009 * */ #include <string> #include <vector> #include <algorithm> #include <cstdlib> #include <iostream> #ifdef TRAVISTO_FFMPEG #ifdef _WIN32 #include <vtkAVIWriter.h> #include <windows.h> #else #include <vtkFFMPEGWriter.h> #endif #endif #include "general/Macros.h" #include <QObject> #include <QString> #include <QTime> #include <QDir> #include <qwaitcondition.h> #include <vtkCommand.h> #include <vtkPolyData.h> #include <vtkWindowToImageFilter.h> #include <vtkRenderer.h> #include <vtkRendererCollection.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkPNGWriter.h> #include <vtkPostScriptWriter.h> #include <vtkActor2DCollection.h> #include <vtkTextActor.h> #include <vtkCamera.h> #include <vtkTextProperty.h> #include <vtkSphereSource.h> #include <vtkMapper2D.h> #include <vtkMapper.h> #include <vtkSmartPointer.h> #include <vtkPolyDataMapper.h> #include <vtkLabeledDataMapper.h> #include <vtkMath.h> #include <vtkPolyLine.h> #include <vtkTextProperty.h> #include <vtkProperty.h> #include <vtkLineSource.h> #include <vtkVectorText.h> #include <vtkFollower.h> #include <vtkLine.h> #include <vtkTubeFilter.h> #include "geometry/FacilityGeometry.h" #include "geometry/Point.h" #include "Pedestrian.h" #include "Frame.h" #include "FrameElement.h" #include "TrajectoryPoint.h" #include "SyncData.h" #include "SystemSettings.h" #include "TrailPlotter.h" #include "geometry/PointPlotter.h" #include "TimerCallback.h" #include <vtkTextActor3D.h> #define VTK_CREATE(type, name) \ vtkSmartPointer<type> name = vtkSmartPointer<type>::New() using namespace std; static int once=1; TimerCallback* TimerCallback::New() { TimerCallback *cb = new TimerCallback; cb->RenderTimerId = 0; cb->windowToImageFilter=NULL; cb->runningTime=NULL; return cb; } void TimerCallback::Execute(vtkObject *caller, unsigned long eventId, void *callData) { if (vtkCommand::TimerEvent == eventId) { int frameNumber=0; int minFrame=0; int nPeds=0; static bool isRecording =false; int tid = * static_cast<int *>(callData); if (tid == this->RenderTimerId) { //dont update anything if the system is actually paused //if(extern_is_pause) return; vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::SafeDownCast(caller); vtkRenderWindow *renderWindow = iren->GetRenderWindow(); vtkRenderer *renderer =renderWindow->GetRenderers()->GetFirstRenderer(); if (iren && renderWindow && renderer) { //first pedestrian group if(extern_first_dataset_loaded) { Frame * frame=NULL; // return the same frame if the system is paused // in fact you could just return, but in this case no update will be made // e.g showing captions/trails... if(extern_is_pause) { frame=extern_trajectories_firstSet.getFrame(extern_trajectories_firstSet.getFrameCursor()); } else { frame = extern_trajectories_firstSet.getNextFrame(); } frameNumber=extern_trajectories_firstSet.getFrameCursor(); double now = frameNumber*iren->GetTimerDuration(tid)/1000; // { // for (auto tab: extern_trainTimeTables) // { // VTK_CREATE(vtkPolyDataMapper, mapper); // VTK_CREATE(vtkActor, actor); // } // } int countTrains = 0; for (auto tab: extern_trainTimeTables) { // VTK_CREATE(vtkTextActor, textActor); VTK_CREATE(vtkTextActor3D, textActor); auto trainType = tab.second->type; auto trainId = tab.second->id; auto trackStart = tab.second->pstart; auto trackEnd = tab.second->pend; auto trainStart = tab.second->tstart; auto trainEnd = tab.second->tend; auto train = extern_trainTypes[trainType]; auto doors = train->doors; std::vector<Point> doorPoints; auto mapper = tab.second->mapper; auto actor = tab.second->actor; auto txtActor = tab.second->textActor; for(auto door: doors) { doorPoints.push_back(door.GetPoint1()); doorPoints.push_back(door.GetPoint2()); }//doors if(once) { auto data = getTrainData(trainStart, trainEnd, doorPoints); mapper->SetInputData(data); actor->SetMapper(mapper); actor->GetProperty()->SetLineWidth(10); actor->GetProperty()->SetOpacity(0.1);//feels cool! actor->GetProperty()->SetColor( std::abs(0.9-renderer->GetBackground()[0]), std::abs(0.9-renderer->GetBackground()[1]), std::abs(1.0-renderer->GetBackground()[2]) ); // text txtActor->GetTextProperty()->SetOpacity(0.7); double pos_x = 50*(trainStart._x + trainEnd._x); double pos_y = 50*(trainStart._y + trainEnd._y); txtActor->SetPosition (pos_x, pos_y+2, 20); txtActor->SetInput (trainType.c_str()); txtActor->GetTextProperty()->SetFontSize (30); txtActor->GetTextProperty()->SetBold (true); txtActor->GetTextProperty()->SetColor ( std::abs(0.9-renderer->GetBackground()[0]), std::abs(0.9-renderer->GetBackground()[1]), std::abs(0.5-renderer->GetBackground()[2]) ); txtActor->SetVisibility(false); } if((now >= tab.second->tin) && (now <= tab.second->tout)) { actor->SetVisibility(true); txtActor->SetVisibility(true); } else { actor->SetVisibility(false); txtActor->SetVisibility(false); } if(once) { renderer->AddActor(actor); renderer->AddActor(txtActor); if(countTrains == extern_trainTimeTables.size()) once = 0; } countTrains++; }// time table if(frame==NULL) { } else { nPeds= frame->getSize(); if(SystemSettings::get2D()==true) { vtkPolyData* pData=frame->GetPolyData2D(); #if VTK_MAJOR_VERSION <= 5 extern_glyphs_pedestrians->SetInput(pData); ((vtkLabeledDataMapper*)extern_pedestrians_labels->GetMapper())->SetInput(pData); #else extern_glyphs_pedestrians->SetInputData(pData); extern_pedestrians_labels->GetMapper()->SetInputDataObject(pData); #endif extern_glyphs_pedestrians->Update(); } else { vtkPolyData* pData=frame->GetPolyData3D(); #if VTK_MAJOR_VERSION <= 5 extern_glyphs_pedestrians_3D->SetInput(pData); ((vtkLabeledDataMapper*)extern_pedestrians_labels->GetMapper())->SetInput(pData); #else extern_glyphs_pedestrians_3D->SetInputData(pData); extern_pedestrians_labels->GetMapper()->SetInputDataObject(pData); #endif extern_glyphs_pedestrians_3D->Update(); } auto FrameElements = frame->GetFrameElements(); if(FrameElements.size()) minFrame = frame->GetFrameElements()[0]->GetMinFrame(); else minFrame = 0; frameNumber += minFrame; if(SystemSettings::getShowTrajectories()) { const std::vector<FrameElement *> &elements=frame->GetFrameElements(); for(unsigned int i=0; i<elements.size(); i++) { FrameElement* el = elements[i]; double pos[3]; double color; el->GetPos(pos); el->GetColor(&color); extern_trail_plotter->PlotPoint(pos,color); } } //virtual cam if(SystemSettings::getVirtualAgent()!=-1){ updateVirtualCamera(frame,renderer); } } } int* winSize=renderWindow->GetSize(); static int lastWinX=winSize[0]+1; // +1 to trigger a first change static int lastWinY=winSize[1]; // HHHHHH sprintf(runningTimeText,"Pedestrians: %d Time: %ld Sec",nPeds,frameNumber*iren->GetTimerDuration(tid)/1000); runningTime->SetInput(runningTimeText); runningTime->Modified(); if((lastWinX!=winSize[0]) || (lastWinY!=winSize[1]) /*|| (frameNumber<10)*/) { static std::string winBaseName(renderWindow->GetWindowName()); std::string winName=winBaseName; std::string s; winName.append(" [ "); s=QString::number(winSize[0]).toStdString(); winName.append(s); winName.append(" x "); s=QString::number(winSize[1]).toStdString(); winName.append(s); winName.append(" ] -->"); int posY=winSize[1]*(1.0-30.0/536.0); int posX=winSize[0]*(1.0-450.0/720.0); runningTime->SetPosition(posX,posY); renderWindow->SetWindowName(winName.c_str()); lastWinX=winSize[0]; lastWinY=winSize[1]; } iren->Render(); if(extern_force_system_update) { updateSettings(renderWindow); } if(extern_take_screenshot) { takeScreenshot(renderWindow); } if(SystemSettings::getRecordPNGsequence()) { takeScreenshotSequence(renderWindow); } if (frameNumber!=0) { int desiredfps=1000.0/iren->GetTimerDuration(tid); int effectivefps=1/(renderer->GetLastRenderTimeInSeconds()); effectivefps = (effectivefps>desiredfps)?desiredfps:effectivefps; emit signalFrameNumber(frameNumber, minFrame); emit signalRunningTime(frameNumber*iren->GetTimerDuration(tid)); emit signalRenderingTime(effectivefps); } #ifdef TRAVISTO_FFMPEG if(extern_launch_recording) { extern_launch_recording=false; //reset windowToImageFilter=vtkWindowToImageFilter::New(); #ifdef _WIN32 pAVIWriter=vtkAVIWriter::New(); #endif #ifdef __linux__ pAVIWriter=vtkFFMPEGWriter::New(); #endif pAVIWriter->SetQuality(2); pAVIWriter->SetRate(1000.0/iren->GetTimerDuration(tid)); QString videoName; SystemSettings::getOutputDirectory(videoName); //create directory if not exits if(!QDir(videoName).exists()) { QDir dir; if(!dir.mkpath (videoName )) { cerr<<"could not create directory: "<< videoName.toStdString(); videoName=""; // current } } videoName += "/tvtvid_"+QDateTime::currentDateTime().toString("yyMMdd_hh_mm_").append(SystemSettings::getFilenamePrefix()).append(".avi"); pAVIWriter->SetFileName(videoName.toStdString().c_str()); if(windowToImageFilter!=NULL) if(windowToImageFilter->GetInput()==NULL) { //should be the case by first call windowToImageFilter->SetInput(renderWindow); #if VTK_MAJOR_VERSION <= 5 pAVIWriter->SetInput(windowToImageFilter->GetOutput()); #else pAVIWriter->SetInputConnection(windowToImageFilter->GetOutputPort()); #endif pAVIWriter->Start(); } extern_recording_enable=true; isRecording=true; } if(isRecording) { windowToImageFilter->Modified(); // only write when not paused if(!extern_is_pause) pAVIWriter->Write(); if(extern_recording_enable==false) { //stop the recording pAVIWriter->End(); windowToImageFilter->Delete(); pAVIWriter->Delete(); isRecording=false; } } #endif //TRAVISTO_FFMPEG if(extern_shutdown_visual_thread) { emit signalFrameNumber(0, 0); // this will force an update of the windows lastWinX=0; lastWinY=0; //exit if and only if the recording process is terminated if(isRecording) extern_recording_enable=false; else iren->ExitCallback(); } } } } } void TimerCallback::updateSettings(vtkRenderWindow* renderWindow) { static bool fullscreen=false; extern_glyphs_pedestrians_actor_2D->SetVisibility(SystemSettings::getShowAgents()&& SystemSettings::get2D()); extern_glyphs_pedestrians_actor_3D->SetVisibility(SystemSettings::getShowAgents()&& !SystemSettings::get2D()); extern_trail_plotter->SetVisibility(SystemSettings::getShowTrajectories()); //agents captions extern_pedestrians_labels->SetVisibility(SystemSettings::getShowAgentsCaptions()); //geometry captions //enable / disable full screen if(fullscreen!=extern_fullscreen_enable) { renderWindow->SetFullScreen(extern_fullscreen_enable); //renderWindow->GetRenderers()->GetFirstRenderer()->ResetCamera(); fullscreen=extern_fullscreen_enable; } // take extern_force_system_update=false; } void TimerCallback::getTrail(int datasetID, int frameNumber) { int trailCount=0; int trailType=0; int trailForm=0; int tcMin=0; int tcMax=0; SystemSettings::getTrailsInfo(&trailCount,&trailType,&trailForm); switch(trailType) { case 0://backward tcMin=frameNumber-trailCount; tcMax=frameNumber; break; case 1://symetric tcMin=frameNumber-trailCount/2; tcMax=frameNumber+trailCount/2; break; case 2://forward tcMin=frameNumber; tcMax=frameNumber+trailCount; break; } for (int i=tcMin; i<tcMax; i++) { Frame* frame = extern_trajectories_firstSet.getFrame(i); if(frame==NULL) { // cerr<<"Trajectory not available in getTrail(), first data set"<<endl; } else { FrameElement* point=NULL; while(NULL!=(point=frame->getNextElement())) { //extern_pedestrians_firstSet[point->getIndex()]->plotTrail(point->getX(),point->getY(),point->getZ()); //extern_pedestrians_firstSet[point->getIndex()]->setTrailGeometry(trailForm); } frame->resetCursor(); } } } void TimerCallback::updateVirtualCamera(Frame *frame, vtkRenderer *renderer) { //look for the position of that agent const std::vector <FrameElement *> elements= frame->GetFrameElements(); for(unsigned int i=0;i<elements.size();i++){ FrameElement * el= elements[i]; if(el->GetId()==SystemSettings::getVirtualAgent()) { static bool cam=true; double pos[3]={0,0,0}; double orien[3]={0,0,0}; double pedSize=170; el->GetPos(pos); el->GetOrientation(orien); double angle=vtkMath::RadiansFromDegrees(orien[2]); //cout<<"new Pos: "<<pos[0]<<" " <<pos[1] <<" "<<pos[2]<<endl; vtkCamera* virtualCam=renderer->GetActiveCamera(); virtualCam->Print(std::cout); exit(0); virtualCam->SetPosition(pos[0]+15,pos[1],pos[2]+pedSize); //new focal point double eyeRange=150;//15m double x=pos[0]+eyeRange*cos(angle); double y=pos[1]+eyeRange*sin(angle); double z = pos[2]; //virtualCam->SetFocalPoint(pos[0]+15,pos[1],pos[2]+pedSize+8); virtualCam->SetFocalPoint(x,y,z+pedSize+8); //virtualCam->Azimuth(pedestrianOrienation); //virtualCam->Azimuth(pedestrianOrienation); //virtualCam->Yaw(pedestrianOrienation); virtualCam->SetDistance(eyeRange); if(cam)virtualCam->SetRoll(90); cam=false; virtualCam->Modified(); return; } } } void TimerCallback::takeScreenshot(vtkRenderWindow *renderWindow) { static int imageID=0; vtkWindowToImageFilter * winToImFilter = vtkWindowToImageFilter::New(); winToImFilter->SetInput( renderWindow ); //winToImFilter->SetMagnification(4); //renderWindow->Delete(); //vtkPostScriptWriter * image = vtkPostScriptWriter::New(); vtkPNGWriter * image = vtkPNGWriter::New(); image->SetInputConnection(winToImFilter->GetOutputPort()); winToImFilter->Delete(); QString screenshots; SystemSettings::getOutputDirectory(screenshots); //create directory if not exits if(!QDir(screenshots).exists()) { QDir dir; if(!dir.mkpath (screenshots )) { //Debug::Error("could not create directory: %s",screenshots.toStdString().c_str()); //try with the current directory screenshots=""; } } char filename[256]= {0}; // sprintf(filename,"travisto_video_%d.png",imageID++); std::string date= QString(QDateTime::currentDateTime().toString("yyMMdd_hh")).toStdString(); sprintf(filename,"travisto_snap_%sh_%d.png",date.c_str(),imageID++); //append the prefix screenshots+=SystemSettings::getFilenamePrefix(); screenshots+=QString(filename); image->SetFileName(screenshots.toStdString().c_str()); winToImFilter->Modified(); image->Write (); image->Delete(); extern_take_screenshot=false; } /// take png screenshot sequence void TimerCallback::takeScreenshotSequence(vtkRenderWindow* renderWindow) { static int imageID=0; vtkWindowToImageFilter * winToImFilter = vtkWindowToImageFilter::New(); winToImFilter->SetInput( renderWindow ); //renderWindow->Delete(); vtkPNGWriter * image = vtkPNGWriter::New(); //vtkPostScriptWriter * image = vtkPostScriptWriter::New(); image->SetInputConnection( winToImFilter->GetOutputPort()); winToImFilter->Delete(); QString screenshots; SystemSettings::getOutputDirectory(screenshots); screenshots.append("./png_seq_"+QDateTime::currentDateTime().toString("yyMMddhh")+"_"+SystemSettings::getFilenamePrefix()); screenshots.truncate(screenshots.size()-1); //create directory if not exits if(!QDir(screenshots).exists()) { QDir dir; if(!dir.mkpath (screenshots )) { cerr<<"could not create directory: "<< screenshots.toStdString(); //try with the current directory screenshots="./png_seq_"+QDateTime::currentDateTime().toString("yyMMdd")+"_"+SystemSettings::getFilenamePrefix(); screenshots.truncate(screenshots.size()-1); } } char filename[30]= {0}; sprintf(filename,"/tmp_%07d.png",imageID++); screenshots.append(filename); image->SetFileName(screenshots.toStdString().c_str()); winToImFilter->Modified(); image->Write (); image->Delete(); } void TimerCallback::SetRenderTimerId(int tid) { this->RenderTimerId = tid; } void TimerCallback::setTextActor(vtkTextActor* ra) { runningTime=ra; } // https://vtk.org/Wiki/VTK/Examples/Cxx/GeometricObjects/ColoredLines vtkSmartPointer<vtkPolyData> TimerCallback::getTrainData( Point trainStart, Point trainEnd, std::vector<Point> doorPoints) { float factor = 100.0; double pt[3] = { 1.0, 0.0, 0.0 }; // to convert from Point // Create the polydata where we will store all the geometric data vtkSmartPointer<vtkPolyData> linesPolyData = vtkSmartPointer<vtkPolyData>::New(); // Create a vtkPoints container and store the points in it vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New(); pt[0] = factor*trainStart._x; pt[1] = factor*trainStart._y; pts->InsertNextPoint(pt); for(auto p: doorPoints) { pt[0] = factor*p._x; pt[1] = factor*p._y; pts->InsertNextPoint(pt); } pt[0] = factor*trainEnd._x; pt[1] = factor*trainEnd._y; pts->InsertNextPoint(pt); // Add the points to the polydata container linesPolyData->SetPoints(pts); vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // Create the first line (between Origin and P0) for(int i = 0; i<= doorPoints.size(); i+=2 ) { vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New(); line->GetPointIds()->SetId(0, i); line->GetPointIds()->SetId(1, i+1); lines->InsertNextCell(line); lines->InsertNextCell(line); line = nullptr; } // Add the lines to the polydata container linesPolyData->SetLines(lines); return linesPolyData; }