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.dev/sumo
3// Copyright (C) 2001-2023 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>
31#include <microsim/MSLink.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
62FXIMPLEMENT(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
80long
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
103long
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
135long
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 * ----------------------------------------------------------------------- */
147FXDEFMAP(GUITLLogicPhasesTrackerWindow) GUITLLogicPhasesTrackerWindowMap[] = {
148 FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::onConfigure),
149 FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::onPaint),
151
152};
153
154FXIMPLEMENT(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 }
230 delete myConnector;
231 // just to quit cleanly on a failure
232 if (myLock.locked()) {
233 myLock.unlock();
234 }
235 delete myToolBarDrag;
236}
237
238void
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);
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, TL("phase names"), this, MID_SIMSTEP);
270
271 if (myAmInTrackingMode) {
272 myDetectorMode = new FXCheckButton(myToolBar, TL("detectors"), this, MID_SIMSTEP);
273 myConditionMode = new FXCheckButton(myToolBar, TL("conditions"), this, MID_SIMSTEP);
274 } else {
275 myDetectorMode = nullptr;
276 myConditionMode = nullptr;
277 }
278}
279
280
281void
283 FXMainWindow::create();
284 if (myToolBarDrag != nullptr) {
285 myToolBarDrag->create();
286 }
287}
288
289int
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
304void
306 // compute what shall be shown (what is visible)
309 SUMOTime leftOffset = 0;
310 myFirstDet2Show = 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(TL("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;
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;
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;
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() == TRUE;
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 // the first phase may be drawn incompletely
520 SUMOTime duration = *pd - fpo;
521 // compute the height and the width of the phase
522 h = 1. - hTop;
523 double a = (double) duration / panelWidth;
524 a *= barWidth / ((double)(myLastTime - myBeginTime));
525 const double x2 = x + a;
526
527 // go through the links
528 for (int j = 0; j < (int) myTLLogic->getLinks().size(); ++j) {
529 // determine the current link's color
530 LinkState state = pi->getSignalState(j);
531 // draw the bar (red is drawn as a line)
533 switch (state) {
534 case LINKSTATE_TL_RED:
536 // draw a thin line
537 glBegin(GL_QUADS);
538 glVertex2d(x, h - h11);
539 glVertex2d(x, h - h9);
540 glVertex2d(x2, h - h9);
541 glVertex2d(x2, h - h11);
542 glEnd();
543 break;
544 default:
545 // draw a thick block
546 glBegin(GL_QUADS);
547 glVertex2d(x, h - stateHeight);
548 glVertex2d(x, h);
549 glVertex2d(x2, h);
550 glVertex2d(x2, h - stateHeight);
551 glEnd();
552 break;
553 }
554 if (myGreenMode->getCurrentItem() != 0) {
555 SUMOTime drawnDuration = 0;
556 double xOffset = 0;
557 if (state == LINKSTATE_TL_GREEN_MINOR || state == LINKSTATE_TL_GREEN_MAJOR) {
558 if (myGreenMode->getCurrentItem() == 1) {
559 drawnDuration = *pd;
560 } else {
561 runningGreen[j] += *pd;
562 if (pd + 1 == myDurations.end()) {
563 drawnDuration = runningGreen[j];
564 xOffset = -(double)(drawnDuration - *pd) / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));
565 }
566 }
567 } else {
568 if (runningGreen[j] > 0) {
569 drawnDuration = runningGreen[j];
570 xOffset = -(double)drawnDuration / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));
571 }
572 runningGreen[j] = 0;
573 }
574 if (drawnDuration > 0) {
575 GLHelper::drawText(toString((int)STEPS2TIME(drawnDuration)),
576 Position(x + xOffset, h - h9),
577 0, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
578 }
579 }
580 // proceed to next link
581 h -= h20;
582 }
583
584 // draw phase index / name (no names for intermediate)
585 std::string name = phaseNames ? pi->getName() : toString(*ii);
586 if (name != lastName) {
587 const double lastNameWidth = GLHelper::getTextWidth(lastName, fontWidth);
588 if (spaceForName < lastNameWidth) {
589 // clear space to avoid overdrawn text
590 glColor3d(0, 0, 0);
591 glBegin(GL_QUADS);
592 glVertex2d(x, 1 - fontHeight);
593 glVertex2d(x, 1);
594 glVertex2d(1, 1);
595 glVertex2d(1, 1 - fontHeight);
596 glEnd();
597 }
598 spaceForName = a;
599 GLHelper::drawText(name, Position(x, 1 - hTop), 0, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
600 } else {
601 spaceForName += a;
602 }
603 lastName = name;
604 // proceed to next phase
605 ++pi;
606 ++ii;
607 x = x2;
608 // all further phases are drawn in full
609 fpo = 0;
610 }
611
612 if (myAmInTrackingMode) {
613 h -= h75;
614 if (myDetectorMode->getCheck()) {
615 glColor3d(0.7, 0.7, 1.0);
617 panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);
618 h -= h35;
619 }
620 if (myConditionMode->getCheck()) {
621 glColor3d(0.9, 0.6, 0.9);
623 panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);
624 }
625 }
626 // allow value addition
627 myLock.unlock();
628
629 if (myPhases.size() != 0) {
630 const double timeRange = STEPS2TIME(myLastTime - myBeginTime);
631 SUMOTime tickDist = TIME2STEPS(10);
632 // patch distances - hack
633 double t = myBeginOffset != nullptr ? myBeginOffset->getValue() : timeRange;
634 while (t > barWidth / 4.) {
635 tickDist += TIME2STEPS(10);
636 t -= barWidth / 4.;
637 }
638 // draw time information
639 //h = (double)(myTLLogic->getLinks().size() * 20 + 12);
640 double glh = 1. - (double)myTLLogic->getLinks().size() * h20 - hTop;
641 // current begin time
642 // time ticks
643 SUMOTime currTime = myFirstTime2Show;
644 double glpos = 31. / panelWidth;
645 const double ticSize = 4. / panelHeight;
646 if (leftOffset > 0) {
647 const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;
648 glpos += a / panelWidth;
649 currTime += leftOffset;
650 } else if (myFirstPhaseOffset > 0) {
651 const double a = STEPS2TIME(-myFirstPhaseOffset) * barWidth / timeRange;
652 glpos += a / panelWidth;
653 currTime -= myFirstPhaseOffset;
654 }
655 int ticShift = myFirstPhase2Show;
656 const bool mmSS = myTimeMode->getCurrentItem() == 1;
657 const bool cycleTime = myTimeMode->getCurrentItem() == 2;
658 SUMOTime lastTimeInCycle = -1;
659 lastName = "";
660 pi = myPhases.begin() + myFirstPhase2Show;
661 for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {
662 const SUMOTime timeInCycle = myTimeInCycle[pd - myDurations.begin()];
663 // draw times at different heights
664 ticShift = (ticShift % 3) + 1;
665 const std::string timeStr = (mmSS
666 ? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"
667 + StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')
668 : toString((int)STEPS2TIME(cycleTime ? timeInCycle : currTime)));
669 const double w = 10 * (double)timeStr.size() / panelWidth;
670 glTranslated(glpos - w / 2., glh - h20 * ticShift, 0);
671 GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
672 glTranslated(-glpos + w / 2., -glh + h20 * ticShift, 0);
673
674 // draw tic
675 glColor3d(1, 1, 1);
676 glBegin(GL_LINES);
677 glVertex2d(glpos, glh);
678 glVertex2d(glpos, glh - ticSize * ticShift);
679 glEnd();
680
681 // draw vertical lines for names, detectors and conditions on each phase switch
682 if (myAmInTrackingMode) {
683 double hStart = 1;
684 if (!phaseNames || (pi->getName() != lastName)) {
685 glColor3d(0.4, 0.4, 0.4);
686 glBegin(GL_LINES);
687 glVertex2d(glpos, hStart);
688 hStart -= h20;
689 glVertex2d(glpos, hStart);
690 glEnd();
691 }
692 lastName = pi->getName();
693
694 hStart = glh - h60;
695 if (myDetectorMode->getCheck() && glpos >= w30) {
696 glColor3d(0.4, 0.4, 0.4);
697 glBegin(GL_LINES);
698 glVertex2d(glpos, hStart);
699 hStart -= (double)myDetectorNames.size() * h20;
700 glVertex2d(glpos, hStart);
701 glEnd();
702 hStart -= h35;
703 }
704 if (myConditionMode->getCheck() && glpos >= w30) {
705 glColor3d(0.4, 0.4, 0.4);
706 glBegin(GL_LINES);
707 glVertex2d(glpos, hStart);
708 glVertex2d(glpos, hStart - (double)myConditionNames.size() * h20);
709 glEnd();
710 }
711 }
712
713 // draw vertical line for cycle reset
714 if (timeInCycle == 0 || timeInCycle < lastTimeInCycle) {
715 const double cycle0pos = glpos - STEPS2TIME(timeInCycle) * barWidth / timeRange / panelWidth;
716 if (cycle0pos >= 31 / panelWidth) {
717 glColor3d(0.6, 0.6, 0.6);
718 glBegin(GL_LINES);
719 glVertex2d(cycle0pos, 1);
720 glVertex2d(cycle0pos, glh);
721 glEnd();
722 glColor3d(1, 1, 1);
723 }
724 }
725
726 lastTimeInCycle = timeInCycle;
727 tickDist = *pd;
728 const double a = STEPS2TIME(tickDist) * barWidth / timeRange;
729 glpos += a / panelWidth;
730 currTime += tickDist;
731 ++pi;
732 }
733
734 // draw bottom time bar with fixed spacing
735 if (myAmInTrackingMode && (myDetectorMode->getCheck() || myConditionMode->getCheck()) && glpos >= w30) {
736 glColor3d(1, 1, 1);
737 tickDist = TIME2STEPS(10);
738 // patch distances - hack
739 t = myBeginOffset != nullptr ? myBeginOffset->getValue() : STEPS2TIME(myLastTime - myBeginTime);
740 while (t > barWidth / 4.) {
741 tickDist += TIME2STEPS(10);
742 t -= barWidth / 4.;
743 }
744 glh = 1. - (double)myLinkNames.size() * h20 - h80;
745 glh -= h20 * (double)(myDetectorMode->getCheck() ? myDetectorNames.size() : myConditionNames.size());
746 currTime = myFirstTime2Show;
747 int pos = 31;
748 glpos = (double) pos / panelWidth;
749 if (leftOffset > 0) {
750 const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;
751 pos += (int)a;
752 glpos += a / panelWidth;
753 currTime += leftOffset;
754 } else if (myFirstPhaseOffset > 0) {
755 const double a = -STEPS2TIME(myBeginTime % tickDist) * barWidth / timeRange;
756 pos += (int)a;
757 glpos += a / panelWidth;
758 currTime = myBeginTime - (myBeginTime % tickDist);
759 }
760 while (pos < panelWidth + 50.) {
761 const std::string timeStr = (mmSS
762 ? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"
763 + StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')
764 : toString((int)STEPS2TIME(cycleTime ? findTimeInCycle(currTime) : currTime)));
765 const double w = 10. * (double)timeStr.size() / panelWidth;
766 glTranslated(glpos - w / 2., glh - h20, 0);
767 GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
768 glTranslated(-glpos + w / 2., -glh + h20, 0);
769
770 glBegin(GL_LINES);
771 glVertex2d(glpos, glh);
772 glVertex2d(glpos, glh - ticSize);
773 glEnd();
774
775 const double a = STEPS2TIME(tickDist) * barWidth / STEPS2TIME(myLastTime - myBeginTime);
776 pos += (int) a;
777 glpos += a / panelWidth;
778 currTime += tickDist;
779 }
780 }
781 }
782}
783
784
785void
786GUITLLogicPhasesTrackerWindow::drawNames(const std::vector<std::string>& names, double fontHeight, double fontWidth, double divHeight, double divWidth, double& h, int extraLines) {
787 int i = 0;
788 for (const std::string& name : names) {
789 // draw the bar
790 glBegin(GL_LINES);
791 glVertex2d(0, h);
792 glVertex2d(divWidth, h);
793 glEnd();
794 // draw the name
795 glTranslated(0, h - divHeight, 0);
796 GLHelper::drawText(name, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);
797 glTranslated(0, -h + divHeight, 0);
798
799 if (extraLines > 0 && i > 0 && i % extraLines == 0) {
800 glColor3d(0.4, 0.4, 0.4);
801 glBegin(GL_LINES);
802 glVertex2d(divWidth, h);
803 glVertex2d(1.0, h);
804 glEnd();
805 glColor3d(1, 1, 1);
806 }
807 h -= divHeight;
808 i++;
809 }
810 h -= divHeight;
811}
812
813
814void
816 const AdditionalStatesVector& states,
817 const DurationsVector& durations, SUMOTime firstOffset, int first2Show, double hStart,
818 double panelWidth, double leftOffset, double barWidth, double stateHeight, double h20, double& h) {
819 double x = 31. / panelWidth;
820 double ta = leftOffset / panelWidth;
821 ta *= barWidth / ((double)(myLastTime - myBeginTime));
822 x += ta;
823 auto di = states.begin() + first2Show;
824 SUMOTime fpo = firstOffset;
825
826 double mx = caller.getMousePos().x() / caller.getWidth();
827 double my = 1 - caller.getMousePos().y() / caller.getHeight();
828 std::string tooltip = "";
829 // start drawing
830 for (auto pd = durations.begin() + first2Show; pd != durations.end(); ++pd) {
831 // the first phase may be drawn incompletely
832 SUMOTime duration = *pd - fpo;
833 // compute the height and the width of the phase
834 h = hStart;
835 double a = (double) duration / panelWidth;
836 a *= barWidth / ((double)(myLastTime - myBeginTime));
837 const double x2 = x + a;
838 const bool tooltipX = x < mx && mx < x2;
839 //std::cout << SIMTIME << " detStates=" << toString(*di) << "\n";
840 // go through the detectors
841 for (double j : *di) {
842 if (j != 0) {
843 // draw a thick block
844 glBegin(GL_QUADS);
845 glVertex2d(x, h - stateHeight);
846 glVertex2d(x, h);
847 glVertex2d(x2, h);
848 glVertex2d(x2, h - stateHeight);
849 glEnd();
850 if (tooltipX) {
851 const bool tooltipY = (h - stateHeight) < my && my < h;
852 if (tooltipY) {
853 tooltip = toString((int)j);
854 }
855 }
856 }
857 // proceed to next link
858 h -= h20;
859 }
860 // proceed to next phase
861 ++di;
862 x = x2;
863 // all further phases are drawn in full
864 fpo = 0;
865 }
866 if (tooltip != "") {
867 // delay tool tip drawing until all bars are drawn to prevent overwriting
868 GLHelper::drawText(tooltip, Position(mx, my), 0, h20, RGBColor::YELLOW, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, 20 / caller.getWidth());
869 }
870}
871
874 // find latest cycle reset before t
875 int i = (int)myPhases.size() - 1;
876 SUMOTime lookBack = myLastTime - t - myDurations.back();
877 //std::cout << SIMTIME << " findTimeInCycle t=" << STEPS2TIME(t)
878 // << " last=" << STEPS2TIME(myLastTime)
879 // << " lastDur=" << STEPS2TIME(myDurations.back())
880 // << " lookBack=" << STEPS2TIME(lookBack)
881 // << " i0=" << i;
882 // look backwards through the phases until to the first cycle crossing before t
883 while (lookBack > 0 && i > 1) {
884 i--;
885 lookBack -= myDurations[i];
886 }
887 SUMOTime timeInCycle = myTimeInCycle[i < 0 ? 0 : i];
888 //std::cout << " iF=" << i << " lookBack2=" << STEPS2TIME(lookBack) << " tic=" << STEPS2TIME(timeInCycle) << "\n";
889 if (lookBack <= 0) {
890 return timeInCycle - lookBack;
891 }
892 return myTLLogic->mapTimeInCycle(t);
893}
894
895void
896GUITLLogicPhasesTrackerWindow::addValue(std::pair<SUMOTime, MSPhaseDefinition> def) {
897 // do not draw while adding
898 myLock.lock();
899 // set the first time if not set before
900 if (myPhases.size() == 0) {
901 myBeginTime = def.first;
902 }
903 // append or set the phase
904 if (myPhases.size() == 0 || myPhases.back() != def.second) {
905 myPhases.push_back(def.second);
906 myDurations.push_back(DELTA_T);
907 myTimeInCycle.push_back(myTLLogic->mapTimeInCycle(def.first - DELTA_T));
909 } else {
910 myDurations.back() += DELTA_T;
911 }
912 // updated detector states
913 std::vector<double> detectorStates;
914 for (auto item : myTLLogic->getDetectorStates()) {
915 detectorStates.push_back(item.second);
916 }
917 if (myDetectorStates.size() == 0 || myDetectorStates.back() != detectorStates) {
918 myDetectorStates.push_back(detectorStates);
919 myDetectorDurations.push_back(DELTA_T);
920 } else {
922 }
923 // updated condition states
924 std::vector<double> conditionStates;
925 for (auto item : myTLLogic->getConditions()) {
926 conditionStates.push_back(item.second);
927 }
928 if (myConditionStates.size() == 0 || myConditionStates.back() != conditionStates) {
929 myConditionStates.push_back(conditionStates);
930 myConditionDurations.push_back(DELTA_T);
931 } else {
933 }
934 // set the last time a phase was added at
935 myLastTime = def.first;
936 // allow drawing
937 myLock.unlock();
938}
939
940
941long
942GUITLLogicPhasesTrackerWindow::onConfigure(FXObject* sender, FXSelector sel, void* ptr) {
943 myPanel->onConfigure(sender, sel, ptr);
944 return FXMainWindow::onConfigure(sender, sel, ptr);
945}
946
947
948long
949GUITLLogicPhasesTrackerWindow::onPaint(FXObject* sender, FXSelector sel, void* ptr) {
950 myPanel->onPaint(sender, sel, ptr);
951 return FXMainWindow::onPaint(sender, sel, ptr);
952}
953
954
955long
956GUITLLogicPhasesTrackerWindow::onSimStep(FXObject* sender, FXSelector, void*) {
957 if (sender == myDetectorMode || sender == myConditionMode) {
958 resize(getWidth(), computeHeight());
959 }
960 update();
961 return 1;
962}
963
964
965void
967 myBeginTime = time;
968}
969
970
971void
973 getApp()->reg().writeIntEntry("TL_TRACKER", "x", getX());
974 getApp()->reg().writeIntEntry("TL_TRACKER", "y", getY());
975 getApp()->reg().writeIntEntry("TL_TRACKER", "width", getWidth());
976 getApp()->reg().writeIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue());
977 getApp()->reg().writeIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem());
978 getApp()->reg().writeIntEntry("TL_TRACKER", "greenMode", (myGreenMode->getCurrentItem()));
979 getApp()->reg().writeIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck()));
980 getApp()->reg().writeIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck()));
981 getApp()->reg().writeIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck()));
982}
983
984
985void
987 // ensure window is visible after switching screen resolutions
988 const FXint minSize = 400;
989 const FXint minTitlebarHeight = 20;
990 setX(MAX2(0, MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "x", 150),
991 getApp()->getRootWindow()->getWidth() - minSize)));
992 if (myLastY == -1) {
993 myLastY = MAX2(minTitlebarHeight,
994 MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "y", 150),
995 getApp()->getRootWindow()->getHeight() - minSize));
996 } else {
997 myLastY += getHeight() + 20;
998 }
999 setY(myLastY);
1000 setWidth(MAX2(getApp()->reg().readIntEntry("TL_TRACKER", "width", 700), minSize));
1001 myBeginOffset->setValue(getApp()->reg().readIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue()));
1002 myTimeMode->setCurrentItem(getApp()->reg().readIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem()));
1003 myGreenMode->setCurrentItem(getApp()->reg().readIntEntry("TL_TRACKER", "greenMode", myGreenMode->getCurrentItem()));
1004 myIndexMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck())));
1005 myDetectorMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck())));
1006 myConditionMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck())));
1007}
1008
1009/****************************************************************************/
long long int SUMOTime
Definition: GUI.h:36
@ MID_SIMSTEP
A Simulation step was performed.
Definition: GUIAppEnum.h:534
#define GUIDesignViewSettingsComboBox1
Combo boxes.
Definition: GUIDesigns.h:538
#define GUIDesignToolBarGrip
design for toolbar grip (used to change the position of toolbar with mouse)
Definition: GUIDesigns.h:438
#define GUIDesignToolBar
design for default toolbar
Definition: GUIDesigns.h:426
@ APP_TLSTRACKER
FXDEFMAP(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel) GUITLLogicPhasesTrackerPanelMap[]
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:279
#define TL(string)
Definition: MsgHandler.h:287
SUMOTime DELTA_T
Definition: SUMOTime.cpp:38
#define STEPS2TIME(x)
Definition: SUMOTime.h:55
#define TIME2STEPS(x)
Definition: SUMOTime.h:57
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:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
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:583
static double getTextWidth(const std::string &text, double size)
get required width of text
Definition: GLHelper.cpp:679
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:685
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 override
Returns the phases of this tls program.
The parent class for traffic light logics.
const LinkVectorVector & getLinks() const
Returns the list of lists of all affected links.
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 > getConditions() const
return all named conditions defined for this traffic light
const std::string & getProgramID() const
Returns this tl-logic's id.
virtual std::map< std::string, double > getDetectorStates() const
return activation state of all detectors that affect this traffic light
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
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
Definition: json.hpp:4471