Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/gui/tracker/GUIParameterTracker.cpp
169685 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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
/****************************************************************************/
14
/// @file GUIParameterTracker.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @date Sept 2002
19
///
20
// A window which displays the time line of one (or more) value(s)
21
/****************************************************************************/
22
#include <config.h>
23
24
#include <string>
25
#include <fstream>
26
#include <utils/common/MsgHandler.h>
27
#include <utils/common/ToString.h>
28
#include <utils/common/StringUtils.h>
29
#include <utils/common/SUMOTime.h>
30
#include <utils/foxtools/MFXUtils.h>
31
#include <utils/iodevices/OutputDevice.h>
32
#include <utils/gui/div/GLHelper.h>
33
#include <utils/gui/globjects/GUIGlObject.h>
34
#include <utils/gui/div/GUIIOGlobals.h>
35
#include <utils/gui/div/GUIDesigns.h>
36
#include <utils/gui/windows/GUIAppEnum.h>
37
#include <utils/gui/windows/GUIMainWindow.h>
38
#include <utils/gui/images/GUIIconSubSys.h>
39
#include <foreign/fontstash/fontstash.h>
40
#include <utils/gui/globjects/GLIncludes.h>
41
#include "GUIParameterTracker.h"
42
43
44
// ===========================================================================
45
// FOX callback mapping
46
// ===========================================================================
47
FXDEFMAP(GUIParameterTracker) GUIParameterTrackerMap[] = {
48
FXMAPFUNC(SEL_CONFIGURE, 0, GUIParameterTracker::onConfigure),
49
FXMAPFUNC(SEL_PAINT, 0, GUIParameterTracker::onPaint),
50
FXMAPFUNC(SEL_COMMAND, MID_SIMSTEP, GUIParameterTracker::onSimStep),
51
FXMAPFUNC(SEL_COMMAND, GUIParameterTracker::MID_MULTIPLOT, GUIParameterTracker::onMultiPlot),
52
FXMAPFUNC(SEL_COMMAND, GUIParameterTracker::MID_AGGREGATIONINTERVAL, GUIParameterTracker::onCmdChangeAggregation),
53
FXMAPFUNC(SEL_COMMAND, GUIParameterTracker::MID_SAVE, GUIParameterTracker::onCmdSave),
54
55
};
56
57
// Macro for the GLTestApp class hierarchy implementation
58
FXIMPLEMENT(GUIParameterTracker, FXMainWindow, GUIParameterTrackerMap, ARRAYNUMBER(GUIParameterTrackerMap))
59
60
// ===========================================================================
61
// static value definitions
62
// ===========================================================================
63
std::set<GUIParameterTracker*> GUIParameterTracker::myMultiPlots;
64
std::vector<RGBColor> GUIParameterTracker::myColors;
65
66
67
// ===========================================================================
68
// method definitions
69
// ===========================================================================
70
GUIParameterTracker::GUIParameterTracker(GUIMainWindow& app,
71
const std::string& name)
72
: FXMainWindow(app.getApp(), "Tracker", nullptr, nullptr, DECOR_ALL, 20, 20, 300, 200),
73
myApplication(&app) {
74
buildToolBar();
75
app.addChild(this);
76
FXVerticalFrame* glcanvasFrame = new FXVerticalFrame(this, FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 0, 0, 0, 0, 0, 0);
77
myPanel = new GUIParameterTrackerPanel(glcanvasFrame, *myApplication, *this);
78
setTitle(name.c_str());
79
setIcon(GUIIconSubSys::getIcon(GUIIcon::APP_TRACKER));
80
81
if (myColors.size() == 0) {
82
myColors = {RGBColor::BLACK, RGBColor::GREEN, RGBColor::RED, RGBColor::BLUE, RGBColor::ORANGE, RGBColor::CYAN, RGBColor::MAGENTA};
83
84
}
85
}
86
87
88
GUIParameterTracker::~GUIParameterTracker() {
89
myMultiPlots.erase(this);
90
myApplication->removeChild(this);
91
for (std::vector<TrackerValueDesc*>::iterator i1 = myTracked.begin(); i1 != myTracked.end(); i1++) {
92
delete (*i1);
93
}
94
// deleted by GUINet
95
for (std::vector<GLObjectValuePassConnector<double>*>::iterator i2 = myValuePassers.begin(); i2 != myValuePassers.end(); i2++) {
96
delete (*i2);
97
}
98
delete myToolBarDrag;
99
delete myToolBar;
100
}
101
102
103
void
104
GUIParameterTracker::create() {
105
FXMainWindow::create();
106
myToolBarDrag->create();
107
}
108
109
110
void
111
GUIParameterTracker::buildToolBar() {
112
myToolBarDrag = new FXToolBarShell(this, GUIDesignToolBar);
113
myToolBar = new FXToolBar(this, myToolBarDrag, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | FRAME_RAISED);
114
new FXToolBarGrip(myToolBar, myToolBar, FXToolBar::ID_TOOLBARGRIP, GUIDesignToolBarGrip);
115
// save button
116
GUIDesigns::buildFXButton(myToolBar, "", "", + TL("Save the data..."),
117
GUIIconSubSys::getIcon(GUIIcon::SAVE), this, GUIParameterTracker::MID_SAVE, GUIDesignButtonToolbar);
118
119
// aggregation interval combo
120
myAggregationInterval = new MFXComboBoxIcon(myToolBar, nullptr, false, GUIDesignComboBoxVisibleItems,
121
this, MID_AGGREGATIONINTERVAL, GUIDesignComboBoxStatic);
122
myAggregationInterval->appendIconItem("1s");
123
myAggregationInterval->appendIconItem("1min");
124
myAggregationInterval->appendIconItem("5min");
125
myAggregationInterval->appendIconItem("15min");
126
myAggregationInterval->appendIconItem("30min");
127
myAggregationInterval->appendIconItem("60min");
128
129
myMultiPlot = new FXCheckButton(myToolBar, TL("Multiplot"), this, MID_MULTIPLOT);
130
myMultiPlot->setCheck(false);
131
}
132
133
134
bool
135
GUIParameterTracker::addTrackedMultiplot(GUIGlObject& o, ValueSource<double>* src, TrackerValueDesc* newTracked) {
136
bool first = true;
137
for (GUIParameterTracker* tr : myMultiPlots) {
138
if (first) {
139
first = false;
140
} else {
141
// each Tracker gets its own copy to simplify cleanup
142
newTracked = new TrackerValueDesc(newTracked->getName(), RGBColor::BLACK, newTracked->getRecordingBegin(),
143
STEPS2TIME(newTracked->getAggregationSpan()));
144
src = src->copy();
145
}
146
tr->addTracked(o, src, newTracked);
147
}
148
return myMultiPlots.size() > 0;
149
}
150
151
152
void
153
GUIParameterTracker::addTracked(GUIGlObject& o, ValueSource<double>* src,
154
TrackerValueDesc* newTracked) {
155
myTracked.push_back(newTracked);
156
// build connection (is automatically set into an execution map)
157
myValuePassers.push_back(new GLObjectValuePassConnector<double>(o, src, newTracked));
158
update();
159
}
160
161
162
long
163
GUIParameterTracker::onConfigure(FXObject* sender, FXSelector sel, void* ptr) {
164
myPanel->onConfigure(sender, sel, ptr);
165
return FXMainWindow::onConfigure(sender, sel, ptr);
166
}
167
168
169
long
170
GUIParameterTracker::onPaint(FXObject* sender, FXSelector sel, void* ptr) {
171
myPanel->onPaint(sender, sel, ptr);
172
return FXMainWindow::onPaint(sender, sel, ptr);
173
}
174
175
176
long
177
GUIParameterTracker::onSimStep(FXObject*, FXSelector, void*) {
178
update();
179
return 1;
180
}
181
182
long
183
GUIParameterTracker::onCmdChangeAggregation(FXObject*, FXSelector, void*) {
184
int index = myAggregationInterval->getCurrentItem();
185
int aggInt = 0;
186
switch (index) {
187
case 0:
188
aggInt = 1;
189
break;
190
case 1:
191
aggInt = 60;
192
break;
193
case 2:
194
aggInt = 60 * 5;
195
break;
196
case 3:
197
aggInt = 60 * 15;
198
break;
199
case 4:
200
aggInt = 60 * 30;
201
break;
202
case 5:
203
aggInt = 60 * 60;
204
break;
205
default:
206
throw 1;
207
}
208
for (TrackerValueDesc* const tvd : myTracked) {
209
tvd->setAggregationSpan(TIME2STEPS(aggInt));
210
}
211
return 1;
212
}
213
214
215
long
216
GUIParameterTracker::onCmdSave(FXObject*, FXSelector, void*) {
217
FXString file = MFXUtils::getFilename2Write(this, TL("Save Data"),
218
SUMOXMLDefinitions::CSVFileExtensions.getMultilineString().c_str(),
219
GUIIconSubSys::getIcon(GUIIcon::EMPTY), gCurrentFolder);
220
if (file == "") {
221
return 1;
222
}
223
try {
224
OutputDevice& dev = OutputDevice::getDevice(file.text());
225
// write header
226
std::vector<TrackerValueDesc*>::iterator i;
227
dev << "# Time";
228
for (i = myTracked.begin(); i != myTracked.end(); ++i) {
229
TrackerValueDesc* tvd = *i;
230
dev << ';' << tvd->getName();
231
}
232
dev << '\n';
233
// count entries
234
int max = 0;
235
for (i = myTracked.begin(); i != myTracked.end(); ++i) {
236
TrackerValueDesc* tvd = *i;
237
int sizei = (int)tvd->getAggregatedValues().size();
238
if (max < sizei) {
239
max = sizei;
240
}
241
tvd->unlockValues();
242
}
243
// write entries
244
SUMOTime t = myTracked.empty() ? 0 : myTracked.front()->getRecordingBegin();
245
SUMOTime dt = myTracked.empty() ? DELTA_T : myTracked.front()->getAggregationSpan();
246
for (int j = 0; j < max; j++) {
247
dev << time2string(t);
248
for (i = myTracked.begin(); i != myTracked.end(); ++i) {
249
TrackerValueDesc* tvd = *i;
250
dev << ';' << tvd->getAggregatedValues()[j];
251
tvd->unlockValues();
252
}
253
dev << '\n';
254
t += dt;
255
}
256
dev.close();
257
} catch (IOError& e) {
258
FXMessageBox::error(this, MBOX_OK, TL("Storing failed!"), "%s", e.what());
259
}
260
return 1;
261
}
262
263
264
long
265
GUIParameterTracker::onMultiPlot(FXObject*, FXSelector, void*) {
266
if (myMultiPlot->getCheck()) {
267
myMultiPlots.insert(this);
268
} else {
269
myMultiPlots.erase(this);
270
}
271
return 1;
272
}
273
274
/* -------------------------------------------------------------------------
275
* GUIParameterTracker::GUIParameterTrackerPanel-methods
276
* ----------------------------------------------------------------------- */
277
FXDEFMAP(GUIParameterTracker::GUIParameterTrackerPanel) GUIParameterTrackerPanelMap[] = {
278
FXMAPFUNC(SEL_CONFIGURE, 0, GUIParameterTracker::GUIParameterTrackerPanel::onConfigure),
279
FXMAPFUNC(SEL_MOTION, 0, GUIParameterTracker::GUIParameterTrackerPanel::onMouseMove),
280
FXMAPFUNC(SEL_PAINT, 0, GUIParameterTracker::GUIParameterTrackerPanel::onPaint),
281
282
};
283
284
// Macro for the GLTestApp class hierarchy implementation
285
FXIMPLEMENT(GUIParameterTracker::GUIParameterTrackerPanel, FXGLCanvas, GUIParameterTrackerPanelMap, ARRAYNUMBER(GUIParameterTrackerPanelMap))
286
287
288
289
GUIParameterTracker::GUIParameterTrackerPanel::GUIParameterTrackerPanel(
290
FXComposite* c, GUIMainWindow& app,
291
GUIParameterTracker& parent)
292
: FXGLCanvas(c, app.getGLVisual(), app.getBuildGLCanvas(), (FXObject*) nullptr, (FXSelector) 0, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 300, 200),
293
myParent(&parent) {}
294
295
296
GUIParameterTracker::GUIParameterTrackerPanel::~GUIParameterTrackerPanel() {}
297
298
299
void
300
GUIParameterTracker::GUIParameterTrackerPanel::drawValues() {
301
glMatrixMode(GL_PROJECTION);
302
glLoadIdentity();
303
glMatrixMode(GL_MODELVIEW);
304
glLoadIdentity();
305
glDisable(GL_TEXTURE_2D);
306
for (int i = 0; i < (int)myParent->myTracked.size(); i++) {
307
TrackerValueDesc* desc = myParent->myTracked[i];
308
glPushMatrix();
309
drawValue(*desc, myColors[i % myColors.size()], i);
310
glPopMatrix();
311
}
312
}
313
314
315
void
316
GUIParameterTracker::GUIParameterTrackerPanel::drawValue(TrackerValueDesc& desc,
317
const RGBColor& col,
318
int index) {
319
const double fontWidth = 0.1 * 300. / myWidthInPixels;
320
const double fontHeight = 0.1 * 300. / myHeightInPixels;
321
const bool isMultiPlot = myParent->myTracked.size() > 1;
322
const std::vector<double>& values = desc.getAggregatedValues();
323
if (values.size() < 2) {
324
// draw name
325
glTranslated(-.9, 0.9, 0);
326
GLHelper::drawText(desc.getName(), Position((double)index / (double)myParent->myTracked.size(), 0.), 1, fontHeight, col, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
327
desc.unlockValues();
328
return;
329
}
330
//
331
// apply scaling
332
GLHelper::pushMatrix();
333
334
// apply the positiopn offset of the display
335
glScaled(0.8, 0.8, 1);
336
// apply value range scaling
337
double ys = (double) 2.0 / (double) desc.getRange();
338
glScaled(1.0, ys, 1.0);
339
glTranslated(-1.0, -desc.getYCenter(), 0);
340
341
// draw value bounderies
342
// draw minimum boundary
343
glBegin(GL_LINES);
344
glVertex2d(0, desc.getMin());
345
glVertex2d(2.0, desc.getMin());
346
glEnd();
347
glBegin(GL_LINES);
348
glVertex2d(0, desc.getMax());
349
glVertex2d(2.0, desc.getMax());
350
glEnd();
351
GLHelper::setColor(col.changedAlpha(-178));
352
for (int a = 1; a < 6; a++) {
353
const double yp = desc.getRange() / 6.0 * (double) a + desc.getMin();
354
glBegin(GL_LINES);
355
glVertex2d(0, yp);
356
glVertex2d(2.0, yp);
357
glEnd();
358
}
359
360
double latest = 0;
361
double mx = (2 * myMouseX / myWidthInPixels - 1) / 0.8 + 1;
362
int mIndex = 0;
363
double mouseValue = std::numeric_limits<double>::max();
364
latest = values.back();
365
// init values
366
const double xStep = 2.0 / (double) values.size();
367
std::vector<double>::const_iterator i = values.begin();
368
double yp = (*i);
369
double xp = 0;
370
i++;
371
GLHelper::setColor(col);
372
for (; i != values.end(); i++) {
373
double yn = (*i);
374
double xn = xp + xStep;
375
if (xp < mx && mx < xn) {
376
mouseValue = yp;
377
mIndex = (int)(i - values.begin()) - 1;
378
glPushMatrix();
379
GLHelper::setColor(isMultiPlot ? col.changedBrightness(-40).changedAlpha(-100) : RGBColor::BLUE);
380
glTranslated(xn, yn, 0);
381
glScaled(20.0 / myWidthInPixels, 10.0 * desc.getRange() / myHeightInPixels, 0);
382
GLHelper::drawFilledCircle(1, 8);
383
GLHelper::setColor(col);
384
glPopMatrix();
385
}
386
glBegin(GL_LINES);
387
glVertex2d(xp, yp);
388
glVertex2d(xn, yn);
389
glEnd();
390
yp = yn;
391
xp = xn;
392
}
393
desc.unlockValues();
394
GLHelper::popMatrix();
395
396
// draw value bounderies and descriptions
397
GLHelper::setColor(col);
398
399
// draw min time
400
SUMOTime beginStep = desc.getRecordingBegin();
401
std::string begStr = time2string(beginStep);
402
double w = 50 / myWidthInPixels;
403
glTranslated(-0.8 - w / 2., -0.88, 0);
404
GLHelper::drawText(begStr, Position(0, 0), 1, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
405
glTranslated(0.8 + w / 2., 0.88, 0);
406
407
// draw max time
408
glTranslated(0.75, -0.88, 0);
409
GLHelper::drawText(time2string(beginStep + static_cast<SUMOTime>(values.size() * desc.getAggregationSpan())),
410
Position(0, 0), 1, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
411
glTranslated(-0.75, 0.88, 0);
412
413
// draw min value
414
glTranslated(-0.98, -0.82, 0);
415
GLHelper::drawText(toString(desc.getMin()), Position(0, index * fontHeight), 1, fontHeight, col, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
416
glTranslated(0.98, 0.82, 0);
417
418
// draw max value
419
glTranslated(-0.98, 0.78, 0);
420
GLHelper::drawText(toString(desc.getMax()), Position(0, -index * fontHeight), 1, fontHeight, col, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
421
glTranslated(0.98, -0.78, 0);
422
423
// draw name
424
glTranslated(-0.98, .92, 0);
425
GLHelper::drawText(desc.getName(), Position((double)index / (double)myParent->myTracked.size(), 0.), 1, fontHeight, col, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
426
glTranslated(0.98, -.92, 0);
427
428
// draw current value (with contrasting color)
429
double p = (double) 0.8 -
430
((double) 1.6 / (desc.getMax() - desc.getMin()) * (latest - desc.getMin()));
431
glTranslated(-0.98, -(p + .02), 0);
432
GLHelper::drawText(toString(latest), Position(isMultiPlot ? 0.1 : 0, 0), 1, fontHeight, isMultiPlot ? col.changedBrightness(50) : RGBColor::RED, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
433
glTranslated(0.98, p + .02, 0);
434
435
// draw moused value
436
if (mouseValue != std::numeric_limits<double>::max()) {
437
p = (double) 0.8 -
438
((double) 1.6 / (desc.getMax() - desc.getMin()) * (mouseValue - desc.getMin()));
439
glTranslated(-0.98, -(p + .02), 0);
440
GLHelper::drawText(toString(mouseValue), Position(isMultiPlot ? 0.1 : 0, 0), 1, fontHeight, isMultiPlot ? col.changedBrightness(-40) : RGBColor::BLUE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
441
glTranslated(0.98, p + .02, 0);
442
443
if (index == 0) {
444
// time is the same for all plots so we only draw it once
445
const std::string mouseTime = time2string(beginStep + static_cast<SUMOTime>(mIndex * desc.getAggregationSpan()));
446
glTranslated(1.6 * (double)mIndex / (double)values.size() - 0.8, -0.9, 0);
447
GLHelper::drawText(mouseTime, Position(0, 0), 1, fontHeight, isMultiPlot ? col.changedBrightness(-40) : RGBColor::BLUE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);
448
}
449
}
450
451
}
452
453
454
long
455
GUIParameterTracker::GUIParameterTrackerPanel::onConfigure(FXObject*,
456
FXSelector, void*) {
457
if (makeCurrent()) {
458
myWidthInPixels = myParent->getWidth();
459
myHeightInPixels = myParent->getHeight();
460
if (myWidthInPixels != 0 && myHeightInPixels != 0) {
461
glViewport(0, 0, myWidthInPixels - 1, myHeightInPixels - 1);
462
glClearColor(1.0, 1.0, 1.0, 1);
463
glDisable(GL_DEPTH_TEST);
464
glDisable(GL_LIGHTING);
465
glDisable(GL_LINE_SMOOTH);
466
glEnable(GL_BLEND);
467
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
468
glEnable(GL_ALPHA_TEST);
469
glDisable(GL_COLOR_MATERIAL);
470
glLineWidth(1);
471
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
472
}
473
makeNonCurrent();
474
}
475
return 1;
476
}
477
478
479
long
480
GUIParameterTracker::GUIParameterTrackerPanel::onPaint(FXObject*,
481
FXSelector, void*) {
482
if (!isEnabled()) {
483
return 1;
484
}
485
if (makeCurrent()) {
486
myWidthInPixels = getWidth();
487
myHeightInPixels = getHeight();
488
if (myWidthInPixels != 0 && myHeightInPixels != 0) {
489
glViewport(0, 0, myWidthInPixels - 1, myHeightInPixels - 1);
490
glClearColor(1.0, 1.0, 1.0, 1);
491
glDisable(GL_DEPTH_TEST);
492
glDisable(GL_LIGHTING);
493
glDisable(GL_LINE_SMOOTH);
494
glEnable(GL_BLEND);
495
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496
glEnable(GL_ALPHA_TEST);
497
glDisable(GL_COLOR_MATERIAL);
498
glLineWidth(1);
499
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
500
// draw
501
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
502
drawValues();
503
swapBuffers();
504
}
505
makeNonCurrent();
506
}
507
return 1;
508
}
509
510
511
long
512
GUIParameterTracker::GUIParameterTrackerPanel::onMouseMove(FXObject*, FXSelector, void* ptr) {
513
FXEvent* event = (FXEvent*) ptr;
514
myMouseX = event->win_x;
515
update();
516
return 1;
517
}
518
519
520
521
/****************************************************************************/
522
523