1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    GUITLLogicPhasesTrackerWindow.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Oct/Nov 2003
15 /// @version $Id$
16 ///
17 // A window displaying the phase diagram of a tl-logic
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <vector>
27 #include <iostream>
28 #include <utils/gui/windows/GUIMainWindow.h>
29 #include <utils/gui/div/GLHelper.h>
30 #include "GUITLLogicPhasesTrackerWindow.h"
31 #include <microsim/traffic_lights/MSTrafficLightLogic.h>
32 #include <microsim/MSLink.h>
33 #include <utils/common/ToString.h>
34 #include <utils/common/MsgHandler.h>
35 #include <guisim/GUITrafficLightLogicWrapper.h>
36 #include <utils/gui/windows/GUIAppEnum.h>
37 #include <utils/gui/images/GUIIconSubSys.h>
38 #include <utils/gui/settings/GUIVisualizationSettings.h>
39 #include <utils/gui/div/GUIDesigns.h>
40 #include <foreign/fontstash/fontstash.h>
41 #include <utils/gui/globjects/GLIncludes.h>
42 
43 
44 // ===========================================================================
45 // member method definitions
46 // ===========================================================================
47 /* -------------------------------------------------------------------------
48  * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-callbacks
49  * ----------------------------------------------------------------------- */
50 FXDEFMAP(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel) GUITLLogicPhasesTrackerPanelMap[] = {
51     FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure),
52     FXMAPFUNC(SEL_PAINT,     0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint),
53 
54 };
55 
56 // Macro for the GLTestApp class hierarchy implementation
FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel,FXGLCanvas,GUITLLogicPhasesTrackerPanelMap,ARRAYNUMBER (GUITLLogicPhasesTrackerPanelMap))57 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel, FXGLCanvas, GUITLLogicPhasesTrackerPanelMap, ARRAYNUMBER(GUITLLogicPhasesTrackerPanelMap))
58 
59 
60 
61 /* -------------------------------------------------------------------------
62  * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-methods
63  * ----------------------------------------------------------------------- */
64 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::GUITLLogicPhasesTrackerPanel(
65     FXComposite* c, GUIMainWindow& app,
66     GUITLLogicPhasesTrackerWindow& parent)
67     : FXGLCanvas(c, app.getGLVisual(), app.getBuildGLCanvas(), (FXObject*) nullptr, (FXSelector) 0, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y/*, 0, 0, 300, 200*/),
68       myParent(&parent) {}
69 
70 
~GUITLLogicPhasesTrackerPanel()71 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::~GUITLLogicPhasesTrackerPanel() {}
72 
73 
74 long
onConfigure(FXObject *,FXSelector,void *)75 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure(
76     FXObject*, FXSelector, void*) {
77     if (makeCurrent()) {
78         int widthInPixels = getWidth();
79         int heightInPixels = getHeight();
80         if (widthInPixels != 0 && heightInPixels != 0) {
81             glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
82             glClearColor(0, 0, 0, 1);
83             glDisable(GL_DEPTH_TEST);
84             glDisable(GL_LIGHTING);
85             glDisable(GL_LINE_SMOOTH);
86             glEnable(GL_BLEND);
87             glEnable(GL_ALPHA_TEST);
88             glDisable(GL_COLOR_MATERIAL);
89             glLineWidth(1);
90             glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
91         }
92     }
93     return 1;
94 }
95 
96 
97 long
onPaint(FXObject *,FXSelector,void *)98 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint(
99     FXObject*, FXSelector, void*) {
100     if (!isEnabled()) {
101         return 1;
102     }
103     if (makeCurrent()) {
104         int widthInPixels = getWidth();
105         int heightInPixels = getHeight();
106         if (widthInPixels != 0 && heightInPixels != 0) {
107             glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
108             glClearColor(0, 0, 0, 1);
109             glDisable(GL_DEPTH_TEST);
110             glDisable(GL_LIGHTING);
111             glDisable(GL_LINE_SMOOTH);
112             glEnable(GL_BLEND);
113             glEnable(GL_ALPHA_TEST);
114             glDisable(GL_COLOR_MATERIAL);
115             glLineWidth(1);
116             glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
117             // draw
118             glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
119             myParent->drawValues(*this);
120             swapBuffers();
121         }
122         makeNonCurrent();
123     }
124     return 1;
125 }
126 
127 
128 
129 /* -------------------------------------------------------------------------
130  * GUITLLogicPhasesTrackerWindow - FOX callback mapping
131  * ----------------------------------------------------------------------- */
132 FXDEFMAP(GUITLLogicPhasesTrackerWindow) GUITLLogicPhasesTrackerWindowMap[] = {
133     FXMAPFUNC(SEL_CONFIGURE, 0,           GUITLLogicPhasesTrackerWindow::onConfigure),
134     FXMAPFUNC(SEL_PAINT,     0,           GUITLLogicPhasesTrackerWindow::onPaint),
135     FXMAPFUNC(SEL_COMMAND,   MID_SIMSTEP, GUITLLogicPhasesTrackerWindow::onSimStep),
136 
137 };
138 
FXIMPLEMENT(GUITLLogicPhasesTrackerWindow,FXMainWindow,GUITLLogicPhasesTrackerWindowMap,ARRAYNUMBER (GUITLLogicPhasesTrackerWindowMap))139 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow, FXMainWindow, GUITLLogicPhasesTrackerWindowMap, ARRAYNUMBER(GUITLLogicPhasesTrackerWindowMap))
140 
141 
142 /* -------------------------------------------------------------------------
143  * GUITLLogicPhasesTrackerWindow-methods
144  * ----------------------------------------------------------------------- */
145 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(
146     GUIMainWindow& app,
147     MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& wrapper,
148     ValueSource<std::pair<SUMOTime, MSPhaseDefinition> >* src)
149     : FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL,
150                    20, 20, 300, 200),
151       myApplication(&app), myTLLogic(&logic), myAmInTrackingMode(true) {
152     // build the toolbar
153     myToolBarDrag = new FXToolBarShell(this, GUIDesignToolBar);
154     myToolBar = new FXToolBar(this, myToolBarDrag, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | FRAME_RAISED);
155     new FXToolBarGrip(myToolBar, myToolBar, FXToolBar::ID_TOOLBARGRIP, GUIDesignToolBarGrip);
156     // interval manipulation
157     myBeginOffset = new FXRealSpinner(myToolBar, 10, this, MID_SIMSTEP, LAYOUT_TOP | FRAME_SUNKEN | FRAME_THICK);
158     //myBeginOffset->setFormatString("%.0f");
159     //myBeginOffset->setIncrements(1, 10, 100);
160     myBeginOffset->setIncrement(10);
161     myBeginOffset->setRange(60, 3600);
162     myBeginOffset->setValue(240);
163     new FXLabel(myToolBar, "(s)", nullptr, LAYOUT_CENTER_Y);
164     //
165     myConnector = new GLObjectValuePassConnector<std::pair<SUMOTime, MSPhaseDefinition> >(wrapper, src, this);
166     FXint height = (FXint)(myTLLogic->getLinks().size() * 20 + 30 + 8 + 30);
167     app.addChild(this);
168     for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
169         myLinkNames.push_back(toString<int>(i));
170     }
171     FXVerticalFrame* glcanvasFrame =
172         new FXVerticalFrame(this,
173                             FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
174                             0, 0, 0, 0, 0, 0, 0, 0);
175     myPanel = new
176     GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
177     setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());
178     setIcon(GUIIconSubSys::getIcon(ICON_APP_TLSTRACKER));
179     setHeight(height);
180 }
181 
182 
GUITLLogicPhasesTrackerWindow(GUIMainWindow & app,MSTrafficLightLogic & logic,GUITrafficLightLogicWrapper &,const MSSimpleTrafficLightLogic::Phases &)183 GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(
184     GUIMainWindow& app,
185     MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& /*wrapper*/,
186     const MSSimpleTrafficLightLogic::Phases& /*phases*/)
187     : FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL,
188                    20, 20, 300, 200),
189       myApplication(&app), myTLLogic(&logic), myAmInTrackingMode(false),
190       myToolBarDrag(nullptr), myBeginOffset(nullptr) {
191     myConnector = nullptr;
192     FXint height = (FXint)(myTLLogic->getLinks().size() * 20 + 30 + 8);
193     setTitle("TLS-Tracker");
194     app.addChild(this);
195     for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
196         myLinkNames.push_back(toString<int>(i));
197     }
198     FXVerticalFrame* glcanvasFrame =
199         new FXVerticalFrame(this,
200                             FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
201                             0, 0, 0, 0, 0, 0, 0, 0);
202     myPanel = new
203     GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
204     setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());
205     setIcon(GUIIconSubSys::getIcon(ICON_APP_TLSTRACKER));
206     setHeight(height);
207 }
208 
209 
~GUITLLogicPhasesTrackerWindow()210 GUITLLogicPhasesTrackerWindow::~GUITLLogicPhasesTrackerWindow() {
211     myApplication->removeChild(this);
212     delete myConnector;
213     // just to quit cleanly on a failure
214     if (myLock.locked()) {
215         myLock.unlock();
216     }
217     delete myToolBarDrag;
218 }
219 
220 
221 void
create()222 GUITLLogicPhasesTrackerWindow::create() {
223     FXMainWindow::create();
224     if (myToolBarDrag != nullptr) {
225         myToolBarDrag->create();
226     }
227 }
228 
229 
230 void
drawValues(GUITLLogicPhasesTrackerPanel & caller)231 GUITLLogicPhasesTrackerWindow::drawValues(GUITLLogicPhasesTrackerPanel& caller) {
232     // compute what shall be shown (what is visible)
233     myFirstPhase2Show = 0;
234     myFirstPhaseOffset = 0;
235     SUMOTime leftOffset = 0;
236     myFirstTime2Show = 0;
237     if (!myAmInTrackingMode) {
238         myPhases.clear();
239         myDurations.clear();
240         // insert phases
241         MSSimpleTrafficLightLogic* simpleTLLogic = dynamic_cast<MSSimpleTrafficLightLogic*>(myTLLogic);
242         if (simpleTLLogic == nullptr) {
243             return;
244         }
245         const MSSimpleTrafficLightLogic::Phases& phases = simpleTLLogic->getPhases();
246         MSSimpleTrafficLightLogic::Phases::const_iterator j;
247         myLastTime = 0;
248         myBeginTime = 0;
249         for (j = phases.begin(); j != phases.end(); ++j) {
250             myPhases.push_back(*(*j));
251             myDurations.push_back((*j)->duration);
252             myLastTime += (*j)->duration;
253         }
254         if (myLastTime <= myBeginTime) {
255             WRITE_ERROR("Overflow in time computation occurred.");
256             return;
257         }
258     } else {
259         SUMOTime beginOffset = TIME2STEPS(myBeginOffset->getValue());
260         myBeginTime = myLastTime - beginOffset;
261         myFirstTime2Show = myBeginTime;
262         // check whether no phases are known at all
263         if (myDurations.size() != 0) {
264             SUMOTime durs = 0;
265             int phaseOffset = (int)myDurations.size() - 1;
266             DurationsVector::reverse_iterator i = myDurations.rbegin();
267             while (i != myDurations.rend()) {
268                 if (durs + (*i) > beginOffset) {
269                     myFirstPhase2Show = phaseOffset;
270                     myFirstPhaseOffset = (durs + (*i)) - beginOffset;
271                     break;
272                 }
273                 durs += (*i);
274                 phaseOffset--;
275                 ++i;
276             }
277             if (i == myDurations.rend()) {
278                 // there are too few information stored;
279                 myFirstPhase2Show = 0;
280                 myFirstPhaseOffset = 0;
281                 leftOffset = beginOffset - durs;
282             }
283         }
284     }
285     // begin drawing
286     glMatrixMode(GL_PROJECTION);
287     glLoadIdentity();
288     glMatrixMode(GL_MODELVIEW);
289     glLoadIdentity();
290     glTranslated(-1, -1, 0);
291     glScaled(2, 2, 1);
292     glDisable(GL_TEXTURE_2D);
293     // draw the horizontal lines dividing the signal groups
294     glColor3d(1, 1, 1);
295     // compute some values needed more than once
296     const double height = (double) caller.getHeight();
297     const double width = (double) caller.getWidth();
298     const double barWidth = MAX2(1.0, width - 31);
299     const double fontHeight = 0.08 * 300. / height;
300     const double fontWidth = 0.08 * 300. / width;
301     const double h9 = ((double) 9 / height);
302     const double h10 = ((double) 10 / height);
303     const double h11 = ((double) 11 / height);
304     const double h16 = ((double) 16 / height);
305     const double h20 = ((double) 20 / height);
306     // draw the link names and the lines dividing them
307     double h = (double)(1.0 - h10);
308     double h2 = 12;
309     for (int i = 0; i < (int)myTLLogic->getLinks().size() + 1; ++i) {
310         // draw the bar
311         glBegin(GL_LINES);
312         glVertex2d(0, h);
313         glVertex2d((double)(30. / width), h);
314         glEnd();
315         // draw the name
316         if (i < (int)myTLLogic->getLinks().size()) {
317             glTranslated(0, h - h20, 0);
318             GLHelper::drawText(myLinkNames[i], Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
319             glTranslated(0, -h + h20, 0);
320             h2 += 20;
321         }
322         h -= h20;
323     }
324     glBegin(GL_LINES);
325     glVertex2d(0, h + h20);
326     glVertex2d(1.0, h + h20);
327     glEnd();
328 
329     // draw the names closure (vertical line)
330     h += (double) 20 / height;
331     glColor3d(1, 1, 1);
332     glBegin(GL_LINES);
333     glVertex2d((double) 30 / width, 1.0);
334     glVertex2d((double) 30 / width, h);
335     glEnd();
336 
337 
338     // draw the phases
339     // disable value addition while drawing
340     myLock.lock();
341     // determine the initial offset
342     double x = ((double) 31. / width);
343     double ta = (double) leftOffset / width;
344     ta *= (double)((barWidth / ((double)(myLastTime - myBeginTime))));
345     x += ta;
346 
347     // and the initial phase information
348     PhasesVector::iterator pi = myPhases.begin() + myFirstPhase2Show;
349 
350     SUMOTime fpo = myFirstPhaseOffset;
351 
352     // start drawing
353     for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {
354         SUMOTime i = 30;
355         // the first phase may be drawn incompletely
356         SUMOTime duration = *pd - fpo;
357         // compute the heigh and the width of the phase
358         h = (double)(1.0 - h10);
359         double a = (double) duration / width;
360         a *= (double)((barWidth / ((double)(myLastTime - myBeginTime))));
361         const double x2 = x + a;
362 
363         // go through the links
364         for (int j = 0; j < (int) myTLLogic->getLinks().size(); ++j) {
365             // determine the current link's color
366             LinkState state = pi->getSignalState(j);
367             // draw the bar (red is drawn as a line)
368             GLHelper::setColor(GUIVisualizationSettings::getLinkColor(state));
369             switch (state) {
370                 case LINKSTATE_TL_RED:
371                 case LINKSTATE_TL_REDYELLOW:
372                     // draw a thin line
373                     glBegin(GL_QUADS);
374                     glVertex2d(x, h - h11);
375                     glVertex2d(x, h - h9);
376                     glVertex2d(x2, h - h9);
377                     glVertex2d(x2, h - h11);
378                     glEnd();
379                     break;
380                 default:
381                     // draw a thick block
382                     glBegin(GL_QUADS);
383                     glVertex2d(x, h - h16);
384                     glVertex2d(x, h);
385                     glVertex2d(x2, h);
386                     glVertex2d(x2, h - h16);
387                     glEnd();
388                     break;
389             }
390             // proceed to next link
391             h -= h20;
392         }
393         // proceed to next phase
394         i += duration;
395         ++pi;
396         x = x2;
397         // all further phases are drawn in full
398         fpo = 0;
399     }
400     // allow value addition
401     myLock.unlock();
402 
403     glColor3d(1, 1, 1);
404     if (myPhases.size() != 0) {
405         SUMOTime tickDist = TIME2STEPS(10);
406         // patch distances - hack
407         double t = myBeginOffset != nullptr ? (double) myBeginOffset->getValue() : STEPS2TIME(myLastTime - myBeginTime);
408         while (t > barWidth / 4.) {
409             tickDist += TIME2STEPS(10);
410             t -= (double)(barWidth / 4.);
411         }
412         // draw time information
413         //h = (double)(myTLLogic->getLinks().size() * 20 + 12);
414         double glh = (double)(1.0 - myTLLogic->getLinks().size() * h20 - h10);
415         // current begin time
416         // time ticks
417         SUMOTime currTime = myFirstTime2Show;
418         int pos = 31;// + /*!!!currTime*/ - myFirstTime2Show;
419         double glpos = (double) pos / width;
420         const double ticSize = 4 / height;
421         while (pos < width + 50) {
422             const std::string timeStr = time2string(currTime);
423             const double w = 50 / width;
424             glTranslated(glpos - w / 2., glh - h20, 0);
425             GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
426             glTranslated(-glpos + w / 2., -glh + h20, 0);
427 
428             glBegin(GL_LINES);
429             glVertex2d(glpos, glh);
430             glVertex2d(glpos, glh - ticSize);
431             glEnd();
432 
433             const double a = STEPS2TIME(tickDist) * barWidth / STEPS2TIME(myLastTime - myBeginTime);
434             pos += (int) a;
435             glpos += a / width;
436             currTime += tickDist;
437         }
438     }
439 }
440 
441 
442 void
addValue(std::pair<SUMOTime,MSPhaseDefinition> def)443 GUITLLogicPhasesTrackerWindow::addValue(std::pair<SUMOTime, MSPhaseDefinition> def) {
444     // do not draw while adding
445     myLock.lock();
446     // set the first time if not set before
447     if (myPhases.size() == 0) {
448         myBeginTime = def.first;
449     }
450     // append or set the phase
451     if (myPhases.size() == 0 || *(myPhases.end() - 1) != def.second) {
452         myPhases.push_back(def.second);
453         myDurations.push_back(DELTA_T);
454     } else {
455         *(myDurations.end() - 1) += DELTA_T;
456     }
457     // set the last time a phase was added at
458     myLastTime = def.first;
459     // allow drawing
460     myLock.unlock();
461 }
462 
463 
464 long
onConfigure(FXObject * sender,FXSelector sel,void * data)465 GUITLLogicPhasesTrackerWindow::onConfigure(FXObject* sender,
466         FXSelector sel, void* data) {
467     myPanel->onConfigure(sender, sel, data);
468     return FXMainWindow::onConfigure(sender, sel, data);
469 }
470 
471 
472 long
onPaint(FXObject * sender,FXSelector sel,void * data)473 GUITLLogicPhasesTrackerWindow::onPaint(FXObject* sender,
474                                        FXSelector sel, void* data) {
475     myPanel->onPaint(sender, sel, data);
476     return FXMainWindow::onPaint(sender, sel, data);
477 }
478 
479 
480 long
onSimStep(FXObject *,FXSelector,void *)481 GUITLLogicPhasesTrackerWindow::onSimStep(FXObject*,
482         FXSelector, void*) {
483     update();
484     return 1;
485 }
486 
487 
488 void
setBeginTime(SUMOTime time)489 GUITLLogicPhasesTrackerWindow::setBeginTime(SUMOTime time) {
490     myBeginTime = time;
491 }
492 
493 
494 /****************************************************************************/
495 
496