Eclipse SUMO - Simulation of Urban MObility
GUITLLogicPhasesTrackerWindow.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2022 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
20 // A window displaying the phase diagram of a tl-logic
21 /****************************************************************************/
22 #include <config.h>
23 
24 #include <vector>
25 #include <iostream>
27 #include <utils/gui/div/GLHelper.h>
31 #include <microsim/MSLink.h>
32 #include <utils/common/ToString.h>
41 
42 
43 // ===========================================================================
44 // static member initialisation
45 // ===========================================================================
47 
48 // ===========================================================================
49 // member method definitions
50 // ===========================================================================
51 /* -------------------------------------------------------------------------
52  * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-callbacks
53  * ----------------------------------------------------------------------- */
58 
59 };
60 
61 // Macro for the GLTestApp class hierarchy implementation
62 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel, FXGLCanvas, GUITLLogicPhasesTrackerPanelMap, ARRAYNUMBER(GUITLLogicPhasesTrackerPanelMap))
63 
64 
65 
66 /* -------------------------------------------------------------------------
67  * GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-methods
68  * ----------------------------------------------------------------------- */
70  FXComposite* c, GUIMainWindow& app,
72  FXGLCanvas(c, app.getGLVisual(), app.getBuildGLCanvas(), (FXObject*) nullptr, (FXSelector) 0, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y/*, 0, 0, 300, 200*/),
73  myParent(&parent)
74 {}
75 
76 
78 
79 
80 long
82  if (makeCurrent()) {
83  int widthInPixels = getWidth();
84  int heightInPixels = getHeight();
85  if (widthInPixels != 0 && heightInPixels != 0) {
86  glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
87  glClearColor(0, 0, 0, 1);
88  glDisable(GL_DEPTH_TEST);
89  glDisable(GL_LIGHTING);
90  glDisable(GL_LINE_SMOOTH);
91  glEnable(GL_BLEND);
92  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
93  glEnable(GL_ALPHA_TEST);
94  glDisable(GL_COLOR_MATERIAL);
95  glLineWidth(1);
96  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
97  }
98  }
99  return 1;
100 }
101 
102 
103 long
105  FXObject*, FXSelector, void*) {
106  if (!isEnabled()) {
107  return 1;
108  }
109  if (makeCurrent()) {
110  int widthInPixels = getWidth();
111  int heightInPixels = getHeight();
112  if (widthInPixels != 0 && heightInPixels != 0) {
113  glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);
114  glClearColor(0, 0, 0, 1);
115  glDisable(GL_DEPTH_TEST);
116  glDisable(GL_LIGHTING);
117  glDisable(GL_LINE_SMOOTH);
118  glEnable(GL_BLEND);
119  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
120  glEnable(GL_ALPHA_TEST);
121  glDisable(GL_COLOR_MATERIAL);
122  glLineWidth(1);
123  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
124  // draw
125  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
126  myParent->drawValues(*this);
127  swapBuffers();
128  }
129  makeNonCurrent();
130  }
131  return 1;
132 }
133 
134 
135 long
137  FXEvent* event = (FXEvent*) ptr;
138  myMousePos.setx(event->win_x);
139  myMousePos.sety(event->win_y);
140  onPaint(nullptr, 0, nullptr);
141  return 1;
142 }
143 
144 /* -------------------------------------------------------------------------
145  * GUITLLogicPhasesTrackerWindow - FOX callback mapping
146  * ----------------------------------------------------------------------- */
147 FXDEFMAP(GUITLLogicPhasesTrackerWindow) GUITLLogicPhasesTrackerWindowMap[] = {
148  FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::onConfigure),
149  FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::onPaint),
150  FXMAPFUNC(SEL_COMMAND, MID_SIMSTEP, GUITLLogicPhasesTrackerWindow::onSimStep),
151 
152 };
153 
154 FXIMPLEMENT(GUITLLogicPhasesTrackerWindow, FXMainWindow, GUITLLogicPhasesTrackerWindowMap, ARRAYNUMBER(GUITLLogicPhasesTrackerWindowMap))
155 
156 
157 /* -------------------------------------------------------------------------
158  * GUITLLogicPhasesTrackerWindow-methods
159  * ----------------------------------------------------------------------- */
161  GUIMainWindow& app,
163  ValueSource<std::pair<SUMOTime, MSPhaseDefinition> >* src) :
164  FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL, 20, 20, 300, 200),
165  myApplication(&app),
166  myTLLogic(&logic),
167  myAmInTrackingMode(true) {
168  initToolBar();
170  app.addChild(this);
171  for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
172  myLinkNames.push_back(toString<int>(i));
173  }
174  for (auto item : myTLLogic->getDetectorStates()) {
175  std::string detID = item.first;
176  if (detID.size() > 4) {
177  detID = detID.substr(detID.size() - 4);
178  }
179  myDetectorNames.push_back(detID);
180  }
181  for (auto item : myTLLogic->getConditions()) {
182  myConditionNames.push_back(item.first);
183  }
184  FXVerticalFrame* glcanvasFrame =
185  new FXVerticalFrame(this,
186  FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
187  0, 0, 0, 0, 0, 0, 0, 0);
188  myPanel = new GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
189  setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());
191  loadSettings();
192  setHeight(computeHeight());
193 }
194 
195 
197  GUIMainWindow& app,
199  const MSSimpleTrafficLightLogic::Phases& /*phases*/) :
200  FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL, 20, 20, 300, 200),
201  myApplication(&app),
202  myTLLogic(&logic),
203  myAmInTrackingMode(false),
204  myToolBarDrag(nullptr),
205  myBeginOffset(nullptr) {
206  myConnector = nullptr;
207  initToolBar();
208  app.addChild(this);
209  for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {
210  myLinkNames.push_back(toString<int>(i));
211  }
212  FXVerticalFrame* glcanvasFrame =
213  new FXVerticalFrame(this,
214  FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,
215  0, 0, 0, 0, 0, 0, 0, 0);
216  myPanel = new GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);
217  setTitle((logic.getID() + " - " + logic.getProgramID() + " - phases").c_str());
219  setHeight(computeHeight());
220  setWidth(700);
221 }
222 
223 
225  if (myAmInTrackingMode) {
226  saveSettings();
227  myLastY = -1;
228  }
229  myApplication->removeChild(this);
230  delete myConnector;
231  // just to quit cleanly on a failure
232  if (myLock.locked()) {
233  myLock.unlock();
234  }
235  delete myToolBarDrag;
236 }
237 
238 void
240  myToolBarDrag = new FXToolBarShell(this, GUIDesignToolBar);
241  myToolBar = new FXToolBar(this, myToolBarDrag, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | FRAME_RAISED);
242  new FXToolBarGrip(myToolBar, myToolBar, FXToolBar::ID_TOOLBARGRIP, GUIDesignToolBarGrip);
243 
244  if (myAmInTrackingMode) {
245  // interval manipulation
246  new FXLabel(myToolBar, "range (s):", nullptr, LAYOUT_CENTER_Y);
247  myBeginOffset = new FXRealSpinner(myToolBar, 4, this, MID_SIMSTEP, LAYOUT_TOP | FRAME_SUNKEN | FRAME_THICK);
248  //myBeginOffset->setFormatString("%.0f");
249  //myBeginOffset->setIncrements(1, 10, 100);
250  myBeginOffset->setIncrement(10);
251  myBeginOffset->setRange(60, 3600);
252  myBeginOffset->setValue(240);
253  }
254 
255  new FXLabel(myToolBar, "time style:", nullptr, LAYOUT_CENTER_Y);
256  myTimeMode = new FXComboBox(myToolBar, 11, this, MID_SIMSTEP, GUIDesignViewSettingsComboBox1);
257  myTimeMode->appendItem("seconds");
258  myTimeMode->appendItem("MM:SS");
259  myTimeMode->appendItem("time in cycle");
260  myTimeMode->setNumVisible(3);
261 
262  new FXLabel(myToolBar, "green time", nullptr, LAYOUT_CENTER_Y);
264  myGreenMode->appendItem("off");
265  myGreenMode->appendItem("phase");
266  myGreenMode->appendItem("running");
267  myGreenMode->setNumVisible(3);
268 
269  myIndexMode = new FXCheckButton(myToolBar, "phase names", this, MID_SIMSTEP);
270 
271  if (myAmInTrackingMode) {
272  myDetectorMode = new FXCheckButton(myToolBar, "detectors", this, MID_SIMSTEP);
273  myConditionMode = new FXCheckButton(myToolBar, "conditions", this, MID_SIMSTEP);
274  } else {
275  myDetectorMode = nullptr;
276  myConditionMode = nullptr;
277  }
278 }
279 
280 
281 void
283  FXMainWindow::create();
284  if (myToolBarDrag != nullptr) {
285  myToolBarDrag->create();
286  }
287 }
288 
289 int
291  int newHeight = (int)myTLLogic->getLinks().size() * 20 + 30 + 8 + 30 + 60;
292  if (myAmInTrackingMode) {
293  newHeight += 20; // time bar
294  if (myDetectorMode->getCheck()) {
295  newHeight += (int)myTLLogic->getDetectorStates().size() * 20 + 5;
296  }
297  if (myConditionMode->getCheck()) {
298  newHeight += (int)myTLLogic->getConditions().size() * 20 + 5;
299  }
300  }
301  return newHeight;
302 }
303 
304 void
306  // compute what shall be shown (what is visible)
307  myFirstPhase2Show = 0;
308  myFirstPhaseOffset = 0;
309  SUMOTime leftOffset = 0;
310  myFirstDet2Show = 0;
311  myFirstDetOffset = 0;
312  myFirstCond2Show = 0;
313  myFirstCondOffset = 0;
314  myFirstTime2Show = 0;
315  if (!myAmInTrackingMode) {
316  myPhases.clear();
317  myDurations.clear();
318  myTimeInCycle.clear();
319  myPhaseIndex.clear();
320  // insert phases
321  MSSimpleTrafficLightLogic* simpleTLLogic = dynamic_cast<MSSimpleTrafficLightLogic*>(myTLLogic);
322  if (simpleTLLogic == nullptr) {
323  return;
324  }
325  myLastTime = 0;
326  myBeginTime = 0;
327  int idx = 0;
328  for (MSPhaseDefinition* const phase : simpleTLLogic->getPhases()) {
329  myPhases.push_back(*phase);
330  myDurations.push_back(phase->duration);
331  myTimeInCycle.push_back(myLastTime);
332  myPhaseIndex.push_back(idx++);
333  myLastTime += phase->duration;
334  }
335  if (myLastTime <= myBeginTime) {
336  WRITE_ERROR("Overflow in time computation occurred.");
337  return;
338  }
339  } else {
340  SUMOTime beginOffset = TIME2STEPS(myBeginOffset->getValue());
341  myBeginTime = myLastTime - beginOffset;
343  // check whether no phases are known at all
344  if (myDurations.size() != 0) {
345  SUMOTime durs = 0;
346  int phaseOffset = (int)myDurations.size() - 1;
347  DurationsVector::reverse_iterator i = myDurations.rbegin();
348  while (i != myDurations.rend()) {
349  if (durs + (*i) > beginOffset) {
350  myFirstPhase2Show = phaseOffset;
351  myFirstPhaseOffset = (durs + (*i)) - beginOffset;
352  break;
353  }
354  durs += (*i);
355  phaseOffset--;
356  ++i;
357  }
358  if (i == myDurations.rend()) {
359  // there are too few information stored;
360  myFirstPhase2Show = 0;
361  myFirstPhaseOffset = 0;
362  leftOffset = beginOffset - durs;
363  }
364  }
365  if (myDetectorDurations.size() != 0) {
366  SUMOTime durs = 0;
367  int phaseOffset = (int)myDetectorDurations.size() - 1;
368  DurationsVector::reverse_iterator i = myDetectorDurations.rbegin();
369  while (i != myDetectorDurations.rend()) {
370  if (durs + (*i) > beginOffset) {
371  myFirstDet2Show = phaseOffset;
372  myFirstDetOffset = (durs + (*i)) - beginOffset;
373  break;
374  }
375  durs += (*i);
376  phaseOffset--;
377  ++i;
378  }
379  if (i == myDetectorDurations.rend()) {
380  // there are too few information stored;
381  myFirstDet2Show = 0;
382  myFirstDetOffset = 0;
383  }
384  }
385  if (myConditionDurations.size() != 0) {
386  SUMOTime durs = 0;
387  int phaseOffset = (int)myConditionDurations.size() - 1;
388  DurationsVector::reverse_iterator i = myConditionDurations.rbegin();
389  while (i != myConditionDurations.rend()) {
390  if (durs + (*i) > beginOffset) {
391  myFirstCond2Show = phaseOffset;
392  myFirstCondOffset = (durs + (*i)) - beginOffset;
393  break;
394  }
395  durs += (*i);
396  phaseOffset--;
397  ++i;
398  }
399  if (i == myConditionDurations.rend()) {
400  // there are too few information stored;
401  myFirstCond2Show = 0;
402  myFirstCondOffset = 0;
403  }
404  }
405  }
406  // begin drawing
407  glMatrixMode(GL_PROJECTION);
408  glLoadIdentity();
409  glMatrixMode(GL_MODELVIEW);
410  glLoadIdentity();
411  glTranslated(-1, -1, 0);
412  glScaled(2, 2, 1);
413  glDisable(GL_TEXTURE_2D);
414  // draw the horizontal lines dividing the signal groups
415  glColor3d(1, 1, 1);
416  // compute some values needed more than once
417  const double panelHeight = (double) caller.getHeight();
418  const double panelWidth = (double) caller.getWidth();
419  const double barWidth = MAX2(1.0, panelWidth - 31);
420  const double fontHeight = 0.06 * 300. / panelHeight;
421  const double fontWidth = 0.06 * 300. / panelWidth;
422  const double h9 = 9. / panelHeight;
423  const double hTop = 20. / panelHeight;
424  const double h11 = 11. / panelHeight;
425  const double stateHeight = 16. / panelHeight;
426  const double h20 = 20. / panelHeight;
427  const double h30 = 15. / panelHeight;
428  const double h35 = 34. / panelHeight;
429  const double h60 = 70. / panelHeight;
430  const double h75 = 73. / panelHeight;
431  const double h80 = 90. / panelHeight;
432  const double w30 = 30 / panelWidth;
433  double h = 1. - hTop;
434  // draw the line below indices
435  glColor3d(1, 1, 1);
436  glBegin(GL_LINES);
437  glVertex2d(0, h);
438  glVertex2d(1, h);
439  glEnd();
440  // draw the link names and the lines dividing them
441  drawNames(myLinkNames, fontHeight, fontWidth, h20, w30, h, 0);
442  glBegin(GL_LINES);
443  glVertex2d(0, h + h20);
444  glVertex2d(1.0, h + h20);
445  glEnd();
446 
447  // draw the names closure (vertical line)
448  h += h20;
449  glColor3d(1, 1, 1);
450  glBegin(GL_LINES);
451  glVertex2d(w30, 1.);
452  glVertex2d(w30, h);
453  glEnd();
454 
455  if (myAmInTrackingMode) {
456  // optionally draw detector names
457  h -= h60;
458  if (myDetectorMode->getCheck()) {
459  const double top = h;
460  glBegin(GL_LINES);
461  glVertex2d(0, h);
462  glVertex2d(1.0, h);
463  glEnd();
464  drawNames(myDetectorNames, fontHeight * 0.7, fontWidth * 0.7, h20, w30, h, 3);
465  glBegin(GL_LINES);
466  glVertex2d(0, h + h20);
467  glVertex2d(1.0, h + h20);
468  glEnd();
469  // draw the names closure (vertical line)
470  glColor3d(1, 1, 1);
471  glBegin(GL_LINES);
472  glVertex2d(30. / panelWidth, top);
473  glVertex2d(30. / panelWidth, h + h20);
474  glEnd();
475  h -= h30;
476  }
477  // optionally draw condition names
478  if (myConditionMode->getCheck()) {
479  const double top = h;
480  glBegin(GL_LINES);
481  glVertex2d(0, h);
482  glVertex2d(1.0, h);
483  glEnd();
484  drawNames(myConditionNames, fontHeight * 0.7, fontWidth * 0.7, h20, w30, h, 3);
485  glBegin(GL_LINES);
486  glVertex2d(0, h + h20);
487  glVertex2d(1.0, h + h20);
488  glEnd();
489  // draw the names closure (vertical line)
490  glColor3d(1, 1, 1);
491  glBegin(GL_LINES);
492  glVertex2d(30. / panelWidth, top);
493  glVertex2d(30. / panelWidth, h + h20);
494  glEnd();
495  }
496  }
497 
498  // draw the phases
499  // disable value addition while drawing
500  myLock.lock();
501  // determine the initial offset
502  double x = 31. / panelWidth;
503  double ta = (double) leftOffset / panelWidth;
504  ta *= barWidth / ((double)(myLastTime - myBeginTime));
505  x += ta;
506 
507  // and the initial phase information
508  PhasesVector::iterator pi = myPhases.begin() + myFirstPhase2Show;
509  IndexVector::iterator ii = myPhaseIndex.begin() + myFirstPhase2Show;
510 
512  const bool phaseNames = myIndexMode->getCheck();
513  std::string lastName = "";
514  double spaceForName = 0;
515 
516  // start drawing
517  std::vector<SUMOTime> runningGreen(myTLLogic->getLinks().size(), 0);
518  for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {
519  SUMOTime i = 30;
520  // the first phase may be drawn incompletely
521  SUMOTime duration = *pd - fpo;
522  // compute the height and the width of the phase
523  h = 1. - hTop;
524  double a = (double) duration / panelWidth;
525  a *= barWidth / ((double)(myLastTime - myBeginTime));
526  const double x2 = x + a;
527 
528  // go through the links
529  for (int j = 0; j < (int) myTLLogic->getLinks().size(); ++j) {
530  // determine the current link's color
531  LinkState state = pi->getSignalState(j);
532  // draw the bar (red is drawn as a line)
534  switch (state) {
535  case LINKSTATE_TL_RED:
537  // draw a thin line
538  glBegin(GL_QUADS);
539  glVertex2d(x, h - h11);
540  glVertex2d(x, h - h9);
541  glVertex2d(x2, h - h9);
542  glVertex2d(x2, h - h11);
543  glEnd();
544  break;
545  default:
546  // draw a thick block
547  glBegin(GL_QUADS);
548  glVertex2d(x, h - stateHeight);
549  glVertex2d(x, h);
550  glVertex2d(x2, h);
551  glVertex2d(x2, h - stateHeight);
552  glEnd();
553  break;
554  }
555  if (myGreenMode->getCurrentItem() != 0) {
556  SUMOTime drawnDuration = 0;
557  double xOffset = 0;
558  if (state == LINKSTATE_TL_GREEN_MINOR || state == LINKSTATE_TL_GREEN_MAJOR) {
559  if (myGreenMode->getCurrentItem() == 1) {
560  drawnDuration = *pd;
561  } else {
562  runningGreen[j] += *pd;
563  if (pd + 1 == myDurations.end()) {
564  drawnDuration = runningGreen[j];
565  xOffset = -(drawnDuration - *pd) / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));
566  }
567  }
568  } else {
569  if (runningGreen[j] > 0) {
570  drawnDuration = runningGreen[j];
571  xOffset = -drawnDuration / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));
572  }
573  runningGreen[j] = 0;
574  }
575  if (drawnDuration > 0) {
576  GLHelper::drawText(toString((int)STEPS2TIME(drawnDuration)),
577  Position(x + xOffset, h - h9),
578  0, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
579  }
580  }
581  // proceed to next link
582  h -= h20;
583  }
584 
585  // draw phase index / name (no names for intermediate)
586  std::string name = phaseNames ? pi->getName() : toString(*ii);
587  if (name != lastName) {
588  const double lastNameWidth = GLHelper::getTextWidth(lastName, fontWidth);
589  if (spaceForName < lastNameWidth) {
590  // clear space to avoid overdrawn text
591  glColor3d(0, 0, 0);
592  glBegin(GL_QUADS);
593  glVertex2d(x, 1 - fontHeight);
594  glVertex2d(x, 1);
595  glVertex2d(1, 1);
596  glVertex2d(1, 1 - fontHeight);
597  glEnd();
598  }
599  spaceForName = a;
600  GLHelper::drawText(name, Position(x, 1 - hTop), 0, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
601  } else {
602  spaceForName += a;
603  }
604  lastName = name;
605  // proceed to next phase
606  i += duration;
607  ++pi;
608  ++ii;
609  x = x2;
610  // all further phases are drawn in full
611  fpo = 0;
612  }
613 
614  if (myAmInTrackingMode) {
615  h -= h75;
616  if (myDetectorMode->getCheck()) {
617  glColor3d(0.7, 0.7, 1.0);
619  panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);
620  h -= h35;
621  }
622  if (myConditionMode->getCheck()) {
623  glColor3d(0.9, 0.6, 0.9);
625  panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);
626  }
627  }
628  // allow value addition
629  myLock.unlock();
630 
631  if (myPhases.size() != 0) {
632  const double timeRange = STEPS2TIME(myLastTime - myBeginTime);
633  SUMOTime tickDist = TIME2STEPS(10);
634  // patch distances - hack
635  double t = myBeginOffset != nullptr ? myBeginOffset->getValue() : timeRange;
636  while (t > barWidth / 4.) {
637  tickDist += TIME2STEPS(10);
638  t -= barWidth / 4.;
639  }
640  // draw time information
641  //h = (double)(myTLLogic->getLinks().size() * 20 + 12);
642  double glh = (double)(1.0 - myTLLogic->getLinks().size() * h20 - hTop);
643  // current begin time
644  // time ticks
645  SUMOTime currTime = myFirstTime2Show;
646  double glpos = (double) 31 / panelWidth;
647  const double ticSize = 4. / panelHeight;
648  if (leftOffset > 0) {
649  const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;
650  glpos += a / panelWidth;
651  currTime += leftOffset;
652  } else if (myFirstPhaseOffset > 0) {
653  const double a = STEPS2TIME(-myFirstPhaseOffset) * barWidth / timeRange;
654  glpos += a / panelWidth;
655  currTime -= myFirstPhaseOffset;
656  }
657  int ticShift = myFirstPhase2Show;
658  const bool mmSS = myTimeMode->getCurrentItem() == 1;
659  const bool cycleTime = myTimeMode->getCurrentItem() == 2;
660  SUMOTime lastTimeInCycle = -1;
661  lastName = "";
662  pi = myPhases.begin() + myFirstPhase2Show;
663  for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {
664  const SUMOTime timeInCycle = myTimeInCycle[pd - myDurations.begin()];
665  // draw times at different heights
666  ticShift = (ticShift % 3) + 1;
667  const std::string timeStr = (mmSS
668  ? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"
669  + StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')
670  : toString((int)STEPS2TIME(cycleTime ? timeInCycle : currTime)));
671  const double w = 10 * timeStr.size() / panelWidth;
672  glTranslated(glpos - w / 2., glh - h20 * ticShift, 0);
673  GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
674  glTranslated(-glpos + w / 2., -glh + h20 * ticShift, 0);
675 
676  // draw tic
677  glColor3d(1, 1, 1);
678  glBegin(GL_LINES);
679  glVertex2d(glpos, glh);
680  glVertex2d(glpos, glh - ticSize * ticShift);
681  glEnd();
682 
683  // draw vertical lines for names, detectors and conditions on each phase switch
684  if (myAmInTrackingMode) {
685  double hStart = 1;
686  if (!phaseNames || (pi->getName() != lastName)) {
687  glColor3d(0.4, 0.4, 0.4);
688  glBegin(GL_LINES);
689  glVertex2d(glpos, hStart);
690  hStart -= h20;
691  glVertex2d(glpos, hStart);
692  glEnd();
693  }
694  lastName = pi->getName();
695 
696  hStart = glh - h60;
697  if (myDetectorMode->getCheck() && glpos >= w30) {
698  glColor3d(0.4, 0.4, 0.4);
699  glBegin(GL_LINES);
700  glVertex2d(glpos, hStart);
701  hStart -= myDetectorNames.size() * h20;
702  glVertex2d(glpos, hStart);
703  glEnd();
704  hStart -= h35;
705  }
706  if (myConditionMode->getCheck() && glpos >= w30) {
707  glColor3d(0.4, 0.4, 0.4);
708  glBegin(GL_LINES);
709  glVertex2d(glpos, hStart);
710  glVertex2d(glpos, hStart - myConditionNames.size() * h20);
711  glEnd();
712  }
713  }
714 
715  // draw vertical line for cycle reset
716  if (timeInCycle == 0 || timeInCycle < lastTimeInCycle) {
717  const double cycle0pos = glpos - STEPS2TIME(timeInCycle) * barWidth / timeRange / panelWidth;
718  if (cycle0pos >= 31 / panelWidth) {
719  glColor3d(0.6, 0.6, 0.6);
720  glBegin(GL_LINES);
721  glVertex2d(cycle0pos, 1);
722  glVertex2d(cycle0pos, glh);
723  glEnd();
724  glColor3d(1, 1, 1);
725  }
726  }
727 
728  lastTimeInCycle = timeInCycle;
729  tickDist = *pd;
730  const double a = STEPS2TIME(tickDist) * barWidth / timeRange;
731  glpos += a / panelWidth;
732  currTime += tickDist;
733  ++pi;
734  }
735 
736  // draw bottom time bar with fixed spacing
737  if (myAmInTrackingMode && (myDetectorMode->getCheck() || myConditionMode->getCheck()) && glpos >= w30) {
738  glColor3d(1, 1, 1);
739  tickDist = TIME2STEPS(10);
740  // patch distances - hack
741  t = myBeginOffset != nullptr ? myBeginOffset->getValue() : STEPS2TIME(myLastTime - myBeginTime);
742  while (t > barWidth / 4.) {
743  tickDist += TIME2STEPS(10);
744  t -= barWidth / 4.;
745  }
746  glh = (double)(1.0 - myLinkNames.size() * h20 - h80);
747  glh -= h20 * (myDetectorMode->getCheck() ? myDetectorNames.size() : myConditionNames.size());
748  currTime = myFirstTime2Show;
749  int pos = 31;
750  glpos = (double) pos / panelWidth;
751  if (leftOffset > 0) {
752  const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;
753  pos += (int)a;
754  glpos += a / panelWidth;
755  currTime += leftOffset;
756  } else if (myFirstPhaseOffset > 0) {
757  const double a = -STEPS2TIME(myBeginTime % tickDist) * barWidth / timeRange;
758  pos += (int)a;
759  glpos += a / panelWidth;
760  currTime = myBeginTime - (myBeginTime % tickDist);
761  }
762  while (pos < panelWidth + 50.) {
763  const std::string timeStr = (mmSS
764  ? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"
765  + StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')
766  : toString((int)STEPS2TIME(cycleTime ? findTimeInCycle(currTime) : currTime)));
767  const double w = 10 * timeStr.size() / panelWidth;
768  glTranslated(glpos - w / 2., glh - h20, 0);
769  GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
770  glTranslated(-glpos + w / 2., -glh + h20, 0);
771 
772  glBegin(GL_LINES);
773  glVertex2d(glpos, glh);
774  glVertex2d(glpos, glh - ticSize);
775  glEnd();
776 
777  const double a = STEPS2TIME(tickDist) * barWidth / STEPS2TIME(myLastTime - myBeginTime);
778  pos += (int) a;
779  glpos += a / panelWidth;
780  currTime += tickDist;
781  }
782  }
783  }
784 }
785 
786 
787 void
788 GUITLLogicPhasesTrackerWindow::drawNames(const std::vector<std::string>& names, double fontHeight, double fontWidth, double divHeight, double divWidth, double& h, int extraLines) {
789  int i = 0;
790  for (const std::string& name : names) {
791  // draw the bar
792  glBegin(GL_LINES);
793  glVertex2d(0, h);
794  glVertex2d(divWidth, h);
795  glEnd();
796  // draw the name
797  glTranslated(0, h - divHeight, 0);
798  GLHelper::drawText(name, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
799  glTranslated(0, -h + divHeight, 0);
800 
801  if (extraLines > 0 && i > 0 && i % extraLines == 0) {
802  glColor3d(0.4, 0.4, 0.4);
803  glBegin(GL_LINES);
804  glVertex2d(divWidth, h);
805  glVertex2d(1.0, h);
806  glEnd();
807  glColor3d(1, 1, 1);
808  }
809  h -= divHeight;
810  i++;
811  }
812  h -= divHeight;
813 }
814 
815 
816 void
818  const AdditionalStatesVector& states,
819  const DurationsVector& durations, SUMOTime firstOffset, int first2Show, double hStart,
820  double panelWidth, double leftOffset, double barWidth, double stateHeight, double h20, double& h) {
821  double x = 31. / panelWidth;
822  double ta = leftOffset / panelWidth;
823  ta *= barWidth / ((double)(myLastTime - myBeginTime));
824  x += ta;
825  auto di = states.begin() + first2Show;
826  SUMOTime fpo = firstOffset;
827 
828  double mx = caller.getMousePos().x() / caller.getWidth();
829  double my = 1 - caller.getMousePos().y() / caller.getHeight();
830  std::string tooltip = "";
831  // start drawing
832  for (auto pd = durations.begin() + first2Show; pd != durations.end(); ++pd) {
833  SUMOTime i = 30;
834  // the first phase may be drawn incompletely
835  SUMOTime duration = *pd - fpo;
836  // compute the height and the width of the phase
837  h = hStart;
838  double a = (double) duration / panelWidth;
839  a *= barWidth / ((double)(myLastTime - myBeginTime));
840  const double x2 = x + a;
841  const bool tooltipX = x < mx && mx < x2;
842  //std::cout << SIMTIME << " detStates=" << toString(*di) << "\n";
843  // go through the detectors
844  for (double j : *di) {
845  if (j != 0) {
846  // draw a thick block
847  glBegin(GL_QUADS);
848  glVertex2d(x, h - stateHeight);
849  glVertex2d(x, h);
850  glVertex2d(x2, h);
851  glVertex2d(x2, h - stateHeight);
852  glEnd();
853  if (tooltipX) {
854  const bool tooltipY = (h - stateHeight) < my && my < h;
855  if (tooltipY) {
856  tooltip = toString((int)j);
857  }
858  }
859  }
860  // proceed to next link
861  h -= h20;
862  }
863  // proceed to next phase
864  i += duration;
865  ++di;
866  x = x2;
867  // all further phases are drawn in full
868  fpo = 0;
869  }
870  if (tooltip != "") {
871  // delay tool tip drawing until all bars are drawn to prevent overwriting
872  GLHelper::drawText(tooltip, Position(mx, my), 0, h20, RGBColor::YELLOW, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, 20 / caller.getWidth());
873  }
874 }
875 
876 SUMOTime
878  // find latest cycle reset before t
879  int i = (int)myPhases.size() - 1;
880  SUMOTime lookBack = myLastTime - t - myDurations.back();
881  //std::cout << SIMTIME << " findTimeInCycle t=" << STEPS2TIME(t)
882  // << " last=" << STEPS2TIME(myLastTime)
883  // << " lastDur=" << STEPS2TIME(myDurations.back())
884  // << " lookBack=" << STEPS2TIME(lookBack)
885  // << " i0=" << i;
886  // look backwards through the phases until to the first cycle crossing before t
887  while (lookBack > 0 && i >= 0) {
888  i--;
889  lookBack -= myDurations[i];
890  }
891  SUMOTime timeInCycle = myTimeInCycle[i < 0 ? 0 : i];
892  //std::cout << " iF=" << i << " lookBack2=" << STEPS2TIME(lookBack) << " tic=" << STEPS2TIME(timeInCycle) << "\n";
893  if (lookBack <= 0) {
894  return timeInCycle - lookBack;
895  }
896  return myTLLogic->mapTimeInCycle(t);
897 }
898 
899 void
900 GUITLLogicPhasesTrackerWindow::addValue(std::pair<SUMOTime, MSPhaseDefinition> def) {
901  // do not draw while adding
902  myLock.lock();
903  // set the first time if not set before
904  if (myPhases.size() == 0) {
905  myBeginTime = def.first;
906  }
907  // append or set the phase
908  if (myPhases.size() == 0 || myPhases.back() != def.second) {
909  myPhases.push_back(def.second);
910  myDurations.push_back(DELTA_T);
911  myTimeInCycle.push_back(myTLLogic->mapTimeInCycle(def.first - DELTA_T));
913  } else {
914  myDurations.back() += DELTA_T;
915  }
916  // updated detector states
917  std::vector<double> detectorStates;
918  for (auto item : myTLLogic->getDetectorStates()) {
919  detectorStates.push_back(item.second);
920  }
921  if (myDetectorStates.size() == 0 || myDetectorStates.back() != detectorStates) {
922  myDetectorStates.push_back(detectorStates);
923  myDetectorDurations.push_back(DELTA_T);
924  } else {
925  myDetectorDurations.back() += DELTA_T;
926  }
927  // updated condition states
928  std::vector<double> conditionStates;
929  for (auto item : myTLLogic->getConditions()) {
930  conditionStates.push_back(item.second);
931  }
932  if (myConditionStates.size() == 0 || myConditionStates.back() != conditionStates) {
933  myConditionStates.push_back(conditionStates);
934  myConditionDurations.push_back(DELTA_T);
935  } else {
936  myConditionDurations.back() += DELTA_T;
937  }
938  // set the last time a phase was added at
939  myLastTime = def.first;
940  // allow drawing
941  myLock.unlock();
942 }
943 
944 
945 long
946 GUITLLogicPhasesTrackerWindow::onConfigure(FXObject* sender, FXSelector sel, void* ptr) {
947  myPanel->onConfigure(sender, sel, ptr);
948  return FXMainWindow::onConfigure(sender, sel, ptr);
949 }
950 
951 
952 long
953 GUITLLogicPhasesTrackerWindow::onPaint(FXObject* sender, FXSelector sel, void* ptr) {
954  myPanel->onPaint(sender, sel, ptr);
955  return FXMainWindow::onPaint(sender, sel, ptr);
956 }
957 
958 
959 long
960 GUITLLogicPhasesTrackerWindow::onSimStep(FXObject* sender, FXSelector, void*) {
961  if (sender == myDetectorMode || sender == myConditionMode) {
962  resize(getWidth(), computeHeight());
963  }
964  update();
965  return 1;
966 }
967 
968 
969 void
971  myBeginTime = time;
972 }
973 
974 
975 void
977  getApp()->reg().writeIntEntry("TL_TRACKER", "x", getX());
978  getApp()->reg().writeIntEntry("TL_TRACKER", "y", getY());
979  getApp()->reg().writeIntEntry("TL_TRACKER", "width", getWidth());
980  getApp()->reg().writeIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue());
981  getApp()->reg().writeIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem());
982  getApp()->reg().writeIntEntry("TL_TRACKER", "greenMode", (myGreenMode->getCurrentItem()));
983  getApp()->reg().writeIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck()));
984  getApp()->reg().writeIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck()));
985  getApp()->reg().writeIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck()));
986 }
987 
988 
989 void
991  // ensure window is visible after switching screen resolutions
992  const FXint minSize = 400;
993  const FXint minTitlebarHeight = 20;
994  setX(MAX2(0, MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "x", 150),
995  getApp()->getRootWindow()->getWidth() - minSize)));
996  if (myLastY == -1) {
997  myLastY = MAX2(minTitlebarHeight,
998  MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "y", 150),
999  getApp()->getRootWindow()->getHeight() - minSize));
1000  } else {
1001  myLastY += getHeight() + 20;
1002  }
1003  setY(myLastY);
1004  setWidth(MAX2(getApp()->reg().readIntEntry("TL_TRACKER", "width", 700), minSize));
1005  myBeginOffset->setValue(getApp()->reg().readIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue()));
1006  myTimeMode->setCurrentItem(getApp()->reg().readIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem()));
1007  myGreenMode->setCurrentItem((bool)getApp()->reg().readIntEntry("TL_TRACKER", "greenMode", (int)(myGreenMode->getCurrentItem())));
1008  myIndexMode->setCheck((bool)getApp()->reg().readIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck())));
1009  myDetectorMode->setCheck((bool)getApp()->reg().readIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck())));
1010  myConditionMode->setCheck((bool)getApp()->reg().readIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck())));
1011 }
1012 
1013 /****************************************************************************/
@ MID_SIMSTEP
A Simulation step was performed.
Definition: GUIAppEnum.h:495
#define GUIDesignViewSettingsComboBox1
Combo boxs.
Definition: GUIDesigns.h:478
#define GUIDesignToolBarGrip
design for toolbar grip (used to change the position of toolbar with mouse)
Definition: GUIDesigns.h:381
#define GUIDesignToolBar
design for default toolbar
Definition: GUIDesigns.h:369
@ APP_TLSTRACKER
FXDEFMAP(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel) GUITLLogicPhasesTrackerPanelMap[]
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:288
SUMOTime DELTA_T
Definition: SUMOTime.cpp:37
#define STEPS2TIME(x)
Definition: SUMOTime.h:53
#define TIME2STEPS(x)
Definition: SUMOTime.h:55
long long int SUMOTime
Definition: SUMOTime.h:32
LinkState
The right-of-way state of a link between two lanes used when constructing a NBTrafficLightLogic,...
@ LINKSTATE_TL_REDYELLOW
The link has red light (must brake) but indicates upcoming green.
@ LINKSTATE_TL_GREEN_MAJOR
The link has green light, may pass.
@ LINKSTATE_TL_RED
The link has red light (must brake)
@ LINKSTATE_TL_GREEN_MINOR
The link has green light, has to brake.
T MIN2(T a, T b)
Definition: StdDefs.h:74
T MAX2(T a, T b)
Definition: StdDefs.h:80
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static void setColor(const RGBColor &c)
Sets the gl-color to this value.
Definition: GLHelper.cpp:507
static double getTextWidth(const std::string &text, double size)
get required width of text
Definition: GLHelper.cpp:603
static void drawText(const std::string &text, const Position &pos, const double layer, const double size, const RGBColor &col=RGBColor::BLACK, const double angle=0, const int align=0, double width=-1)
Definition: GLHelper.cpp:609
Class passing values from a GUIGlObject to another object.
static FXIcon * getIcon(const GUIIcon which)
returns a icon previously defined in the enum GUIIcon
void removeChild(FXMainWindow *child)
removes the given child window from the list (FXMainWindow)
void addChild(FXMainWindow *child)
Adds a further child window to the list (FXMainWindow)
long onConfigure(FXObject *, FXSelector, void *)
called on size change
long onPaint(FXObject *, FXSelector, void *)
called if the widget shall be repainted
long onMouseMove(FXObject *, FXSelector, void *)
called on mouse movement (for updating tooltip)
This window displays a phase diagram for a chosen tl-logic.
GUITLLogicPhasesTrackerWindow()
protected constructor for FOX
AdditionalStatesVector myDetectorStates
The state of all used detectors of the current phase.
FXMutex myLock
A lock to avoid addition of new values while drawing.
SUMOTime myBeginTime
The first time a phase was added at.
static int myLastY
y-Position of previously opened window
DurationsVector myTimeInCycle
The time within the cycle for the current phase.
std::vector< std::string > myLinkNames
The names of links.
DurationsVector myDetectorDurations
The list of detector state durations.
FXCheckButton * myDetectorMode
Whether detector states are drawn.
FXRealSpinner * myBeginOffset
The offset changer (tracking mode)
long onSimStep(FXObject *sender, FXSelector sel, void *ptr)
called on a simulation step
GUITLLogicPhasesTrackerPanel * myPanel
The panel to draw on.
int computeHeight()
compute required windowHeight
void setBeginTime(SUMOTime time)
Sets the time the display shall be shown as beginning at.
FXToolBarShell * myToolBarDrag
The tool bar drag (tracking mode)
FXToolBar * myToolBar
The tool bar (tracking mode)
PhasesVector myPhases
The list of phases.
SUMOTime myFirstPhaseOffset
The offset to draw the first phase (left offset)
std::vector< std::string > myDetectorNames
FXComboBox * myGreenMode
Whether green durations are printed.
IndexVector myPhaseIndex
The index of the current phase.
void addValue(std::pair< SUMOTime, MSPhaseDefinition > def)
Adds a further phase definition.
GLObjectValuePassConnector< std::pair< SUMOTime, MSPhaseDefinition > > * myConnector
The connector for retrieval of further phases.
std::vector< std::string > myConditionNames
int myFirstPhase2Show
The index of the first phase that fits into the window.
long onPaint(FXObject *sender, FXSelector sel, void *ptr)
called if the widget shall be repainted
SUMOTime findTimeInCycle(SUMOTime t)
find time in cycle based on myTimeInCycle
FXCheckButton * myConditionMode
Whether detector states are drawn.
long onConfigure(FXObject *sender, FXSelector sel, void *ptr)
called on size change
SUMOTime myLastTime
The last time a phase was added at.
void create()
Creates the window (FOX-Toolkit)
MSTrafficLightLogic * myTLLogic
The logic to display.
std::vector< std::vector< double > > AdditionalStatesVector
Definition of a storage for detector and condition states.
void drawNames(const std::vector< std::string > &names, double fontHeight, double fontWidth, double divHeight, double divWidth, double &h, int extraLines)
draw row title
FXCheckButton * myIndexMode
Whether phase names shall be printed instead of indices.
std::vector< SUMOTime > DurationsVector
Definition of a storage for durations.
void drawAdditionalStates(GUITLLogicPhasesTrackerPanel &caller, const AdditionalStatesVector &states, const DurationsVector &durations, SUMOTime firstOffset, int first2Show, double hStart, double panelWidth, double leftOffset, double barWidth, double stateHeight, double h20, double &h)
draw detector and condition states
SUMOTime myFirstTime2Show
The time the diagram begins at.
GUIMainWindow * myApplication
The main application.
bool myAmInTrackingMode
Information whether the tracking mode is on.
void drawValues(GUITLLogicPhasesTrackerPanel &caller)
Draws all values.
DurationsVector myDurations
The list of phase durations.
static const RGBColor & getLinkColor(const LinkState &ls, bool realistic=false)
map from LinkState to color constants
The definition of a single phase of a tls logic.
A fixed traffic light logic.
const Phases & getPhases() const
Returns the phases of this tls program.
The parent class for traffic light logics.
virtual SUMOTime mapTimeInCycle(SUMOTime t) const
map the given time into the current cycle
virtual int getCurrentPhaseIndex() const =0
Returns the current index within the program.
virtual std::map< std::string, double > getDetectorStates() const
return activation state of all detectors that affect this traffic light
virtual std::map< std::string, double > getConditions() const
return all named conditions defined for this traffic light
const LinkVectorVector & getLinks() const
Returns the list of lists of all affected links.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
const std::string & getProgramID() const
Returns this tl-logic's id.
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double x() const
Returns the x-position.
Definition: Position.h:55
double y() const
Returns the y-position.
Definition: Position.h:60
static const RGBColor WHITE
Definition: RGBColor.h:192
static const RGBColor YELLOW
Definition: RGBColor.h:188
static const RGBColor BLACK
Definition: RGBColor.h:193
static std::string padFront(const std::string &str, int length, char padding)
@ FONS_ALIGN_MIDDLE
Definition: fontstash.h:47
@ FONS_ALIGN_LEFT
Definition: fontstash.h:42
@ FONS_ALIGN_BOTTOM
Definition: fontstash.h:48