1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36
37 #ifdef HAVE_NODEKITS
38
39 #include <Inventor/annex/Profiler/nodekits/SoScrollingGraphKit.h>
40
41 #include <cstdlib>
42 #include <cstdio>
43
44 #include <boost/scoped_ptr.hpp>
45 #include <boost/scoped_array.hpp>
46 #include <boost/intrusive_ptr.hpp>
47
48 #include <Inventor/SbTime.h>
49 #include <Inventor/SbColor.h>
50 #include <Inventor/errors/SoDebugError.h>
51 #include <Inventor/sensors/SoFieldSensor.h>
52 #include <Inventor/nodes/SoSeparator.h>
53 #include <Inventor/nodes/SoBaseColor.h>
54 #include <Inventor/nodes/SoCoordinate3.h>
55 #include <Inventor/nodes/SoTranslation.h>
56 #include <Inventor/nodes/SoText2.h>
57 #include <Inventor/nodes/SoScale.h>
58 #include <Inventor/nodes/SoLineSet.h>
59 #include <Inventor/nodes/SoIndexedFaceSet.h>
60
61 #include "misc/SbHash.h"
62 #include "nodekits/SoSubKitP.h"
63
64 // *************************************************************************
65
66 //namespace {
67
68 class Graph {
69 public:
70 SbName key;
71 int index;
72 SbColor color;
73 float baseval;
74 float basetarget;
75 };
76
77 class Datum {
78 public:
Datum(void)79 Datum(void) : next(NULL) { }
80 SbTime when;
81 SbList<float> datum;
82 Datum * next;
83 }; // Datum
84
85 //} // anonymous
86
87 // *************************************************************************
88
89 class SoScrollingGraphKitP {
90 public:
SoScrollingGraphKitP(void)91 SoScrollingGraphKitP(void) : kit(NULL), first(NULL), last(NULL) {
92 this->cachedmaxvalue = 0.0f;
93 this->cachedrealmaxvalue = 0.0f;
94 }
~SoScrollingGraphKitP(void)95 ~SoScrollingGraphKitP(void) {
96 SbList<const char *> keys;
97 this->graphs.makeKeyList(keys);
98 for (int c = 0; c < keys.getLength(); ++c) {
99 Graph * obj = NULL;
100 if (this->graphs.get(keys[c], obj)) { delete obj; }
101 }
102 this->graphs.clear();
103
104 while (this->first) {
105 Datum * datum = this->first;
106 this->first = this->first->next;
107 delete datum;
108 }
109 }
110
111 boost::intrusive_ptr<SoSeparator> chart;
112 boost::scoped_ptr<SoFieldSensor> addValuesSensor;
113
114 void pullStatistics(void);
115 Graph * getGraph(const SbName & key);
116 Graph * getGraph(int idx);
117 void addDatum(Datum * newDatum);
118
119 void generateStackedBarsChart(void);
120
121 SbHash<const char *, Graph *> graphs;
122
123 SoScrollingGraphKit * kit;
124 Datum * first;
125 Datum * last;
126
127 float cachedmaxvalue;
128 float cachedrealmaxvalue;
129 }; // SoScrollingGraphKitP
130
131 // *************************************************************************
132
133 #define PRIVATE(obj) ((obj)->pimpl)
134
135 SO_KIT_SOURCE(SoScrollingGraphKit);
136
137 void
initClass(void)138 SoScrollingGraphKit::initClass(void)
139 {
140 SO_KIT_INIT_CLASS(SoScrollingGraphKit, SoBaseKit, "BaseKit");
141 }
142
SoScrollingGraphKit(void)143 SoScrollingGraphKit::SoScrollingGraphKit(void)
144 {
145 PRIVATE(this)->kit = this;
146
147 SO_KIT_INTERNAL_CONSTRUCTOR(SoScrollingGraphKit);
148 SO_KIT_ADD_CATALOG_ENTRY(scene, SoSeparator, TRUE, this, "", FALSE);
149 SO_KIT_INIT_INSTANCE();
150
151 SO_KIT_DEFINE_ENUM_VALUE(GraphicsType, LINES);
152 SO_KIT_DEFINE_ENUM_VALUE(GraphicsType, STACKED_BARS);
153 SO_KIT_DEFINE_ENUM_VALUE(GraphicsType, DEFAULT_GRAPHICS);
154 SO_KIT_SET_SF_ENUM_TYPE(graphicsType, GraphicsType);
155
156 SO_KIT_DEFINE_ENUM_VALUE(RangeType, ABSOLUTE_ACCUMULATIVE);
157 //SO_KIT_DEFINE_ENUM_VALUE(RangeType, ABSOLUTE_OVERWRITE);
158 //SO_KIT_DEFINE_ENUM_VALUE(RangeType, RELATIVE_ACCUMULATIVE);
159 //SO_KIT_DEFINE_ENUM_VALUE(RangeType, RELATIVE_OVERWRITE);
160 SO_KIT_DEFINE_ENUM_VALUE(RangeType, DEFAULT_RANGETYPE);
161 SO_KIT_SET_SF_ENUM_TYPE(rangeType, RangeType);
162
163 SO_KIT_ADD_FIELD(graphicsType, (DEFAULT_GRAPHICS));
164 SO_KIT_ADD_FIELD(rangeType, (DEFAULT_RANGETYPE));
165 SO_KIT_ADD_FIELD(seconds, (SbTime(5.0)));
166 SO_KIT_ADD_FIELD(colors, (SbColor(0.0f, 0.0f, 0.0f)));
167 SbColor default_colors[] = {
168 SbColor(1.0f, 0.0f, 0.0f),
169 SbColor(0.0f, 1.0f, 0.0f),
170 SbColor(0.0f, 0.0f, 1.0f),
171 SbColor(1.0f, 0.0f, 1.0f),
172 SbColor(1.0f, 1.0f, 0.0f),
173 SbColor(0.0f, 1.0f, 1.0f)
174 };
175 const int numdefaultcolors =
176 sizeof(default_colors) / sizeof(default_colors[0]);
177 this->colors.setNum(numdefaultcolors);
178 this->colors.setValues(0, numdefaultcolors, default_colors);
179 this->colors.setDefault(TRUE);
180
181 SO_KIT_ADD_FIELD(viewportSize, (SbVec3f(512.0f, 512.0f, 0.0f)));
182 SO_KIT_ADD_FIELD(position, (SbVec3f(4.0f, 4.0f, 0.0f)));
183 SO_KIT_ADD_FIELD(size, (SbVec3f(256.0f, 100.0f, 0.0f)));
184
185 SO_KIT_ADD_FIELD(addKeys, (SbName::empty()));
186 this->addKeys.setNum(0);
187 this->addKeys.setDefault(TRUE);
188 SO_KIT_ADD_FIELD(addValues, (0.0f));
189 this->addValues.setNum(0);
190 this->addValues.setDefault(TRUE);
191
192 PRIVATE(this)->addValuesSensor.reset(new SoFieldSensor);
193 PRIVATE(this)->addValuesSensor->setFunction(SoScrollingGraphKit::addValuesCB);
194 PRIVATE(this)->addValuesSensor->setData(this);
195 PRIVATE(this)->addValuesSensor->attach(&(this->addValues));
196
197 PRIVATE(this)->chart = static_cast<SoSeparator *>(this->getAnyPart("scene", TRUE));
198 }
199
~SoScrollingGraphKit(void)200 SoScrollingGraphKit::~SoScrollingGraphKit(void)
201 {
202 }
203
204 void
addValuesCB(void * closure,SoSensor * COIN_UNUSED_ARG (sensor))205 SoScrollingGraphKit::addValuesCB(void * closure, SoSensor * COIN_UNUSED_ARG(sensor))
206 {
207 SoScrollingGraphKit * kit = static_cast<SoScrollingGraphKit *>(closure);
208
209 PRIVATE(kit)->pullStatistics();
210 switch (kit->graphicsType.getValue()) {
211 case SoScrollingGraphKit::LINES:
212 //PRIVATE(kit)->generateLineChart();
213 //break;
214 case SoScrollingGraphKit::STACKED_BARS:
215 PRIVATE(kit)->generateStackedBarsChart();
216 break;
217 default:
218 //PRIVATE(kit)->generateLineChart();
219 PRIVATE(kit)->generateStackedBarsChart();
220 break;
221 }
222 }
223
224 void
pullStatistics(void)225 SoScrollingGraphKitP::pullStatistics(void)
226 {
227 SbTime now(SbTime::getTimeOfDay());
228 const int numvalues = kit->addValues.getNum();
229 if (kit->addKeys.getNum() != numvalues) {
230 SoDebugError::post("SoScrollingGraphKitP::pullStatistics",
231 "data out of sync (%d keys, %d values)",
232 numvalues, kit->addKeys.getNum());
233 }
234
235 Datum * datum = new Datum;
236 datum->when = now;
237
238 int c;
239 for (c = 0; c < numvalues; ++c) { // make sure all the grraph structs needed are created
240 SbName key = kit->addKeys[c];
241 /*Graph * graph = */this->getGraph(key);
242 }
243 const int numgraphs = this->graphs.getNumElements();
244 for (c = 0; c < numgraphs; ++c) { // initialize default for all graphs
245 datum->datum.append(0.0f);
246 }
247 for (c = 0; c < numvalues; ++c) { // fill values for the graphs we have data for
248 SbName key = kit->addKeys[c];
249 Graph * graph = this->getGraph(key);
250 datum->datum[graph->index] = kit->addValues[c];
251 }
252 this->addDatum(datum);
253 }
254
255 void
addDatum(Datum * newDatum)256 SoScrollingGraphKitP::addDatum(Datum * newDatum)
257 {
258 if (this->first == NULL) {
259 this->first = this->last = newDatum;
260 } else {
261 this->last->next = newDatum;
262 this->last = newDatum;
263 }
264
265 SbTime maxtime(this->kit->seconds.getValue());
266 while ((this->first->next != NULL) &&
267 ((newDatum->when - this->first->next->when) > maxtime)) {
268 Datum * datum = this->first;
269 this->first = this->first->next;
270 delete datum;
271 }
272 }
273
274 Graph *
getGraph(const SbName & key)275 SoScrollingGraphKitP::getGraph(const SbName & key)
276 {
277 assert(key != SbName::empty());
278 Graph * graph = NULL;
279 if (!this->graphs.get(key.getString(), graph)) {
280 graph = new Graph;
281 graph->key = key;
282 graph->index = this->graphs.getNumElements();
283 graph->color = this->kit->colors[graph->index % this->kit->colors.getNum()];
284 graph->baseval = graph->basetarget = 0.0f;
285 this->graphs.put(key.getString(), graph);
286
287 printf("Adding graph #%d for '%s', color #%02x%02x%02x\n",
288 graph->index + 1, key.getString(), (uint8_t)(graph->color[0] * 255.0f),
289 (uint8_t) (graph->color[1] * 255.0), (uint8_t) (graph->color[2] * 255.0));
290 }
291 assert(graph);
292 return graph;
293 }
294
295 Graph *
getGraph(int idx)296 SoScrollingGraphKitP::getGraph(int idx)
297 {
298 SbList<const char *> keys;
299 this->graphs.makeKeyList(keys);
300 for (int i = 0; i < keys.getLength(); ++i) {
301 Graph * graph = NULL;
302 this->graphs.get(keys[i], graph);
303 if (graph->index == idx) return graph;
304 }
305 assert(!"serious problem - did not find graph index data");
306 return NULL;
307 }
308
309
310 #define LINEUP 1
311 #define SMOOTH_DECAY 1
312
313 template <class Type>
314 Type *
new_node(const char * buffer)315 new_node(const char * buffer)
316 {
317 Type * node = new Type;
318 if (!node->set(buffer)) {
319 assert(!"node field arguments error");
320 node->ref();
321 node->unref();
322 return NULL;
323 }
324 return node;
325 }
326
327 inline
328 float
decayvalue(float current,float target,float decay=0.95f)329 decayvalue(float current, float target, float decay = 0.95f)
330 {
331 #if SMOOTH_DECAY
332 return (current * decay) + (target * (1.0f - decay));
333 #else
334 return target;
335 #endif
336 }
337
338
339
340 void
generateStackedBarsChart(void)341 SoScrollingGraphKitP::generateStackedBarsChart(void)
342 {
343 const int numgraphs = this->graphs.getNumElements();
344 if (numgraphs == 0) return;
345
346 boost::scoped_array<SoBaseColor *> colors(new SoBaseColor * [numgraphs]);
347 boost::scoped_array<SoCoordinate3 *> coords(new SoCoordinate3 * [numgraphs]);
348 boost::scoped_array<SoLineSet *> lines(new SoLineSet * [numgraphs]);
349 boost::scoped_array<SoTranslation *> texttrans(new SoTranslation * [numgraphs]);
350 boost::scoped_array<SoText2 *> textnodes(new SoText2 * [numgraphs]);
351
352 if (this->chart->getNumChildren() != (numgraphs * 4 + 3) ||
353 !(this->chart->getChild(2+2)->isOfType(SoLineSet::getClassTypeId()))) {
354 this->chart->removeAllChildren();
355
356 this->chart->addChild(new_node<SoTranslation>("translation -1 -0.99 0"));
357 this->chart->addChild(new_node<SoScale>("scaleFactor 2 0.5 1"));
358
359 for (int c = 0; c < numgraphs; ++c) {
360 this->chart->addChild(colors[c] = new SoBaseColor);
361 { // the text for the graph name
362 SoSeparator * sep = new SoSeparator;
363 sep->addChild(texttrans[c] = new SoTranslation);
364 sep->addChild(textnodes[c] = new SoText2);
365 this->chart->addChild(sep);
366 }
367 this->chart->addChild(coords[c] = new SoCoordinate3);
368 this->chart->addChild(lines[c] = new SoLineSet);
369 }
370 {
371 SoSeparator * sep = new SoSeparator;
372 sep->addChild(new_node<SoBaseColor>("rgb 1 1 1"));
373 sep->addChild(new SoTranslation);
374 sep->addChild(new SoText2);
375 this->chart->addChild(sep);
376 }
377 } else {
378 for (int c = 0; c < numgraphs; ++c) {
379 colors[c] = static_cast<SoBaseColor *>(this->chart->getChild(2 + c * 4 + 0));
380 assert(colors[c]->isOfType(SoBaseColor::getClassTypeId()));
381 { // the text for the graph name
382 SoSeparator * sep = static_cast<SoSeparator *>(this->chart->getChild(2 + c * 4 + 1));
383 assert(sep->isOfType(SoSeparator::getClassTypeId()));
384 texttrans[c] = static_cast<SoTranslation *>(sep->getChild(0));
385 textnodes[c] = static_cast<SoText2 *>(sep->getChild(1));
386 }
387 coords[c] = static_cast<SoCoordinate3 *>(this->chart->getChild(2 + c * 4 + 2));
388 assert(coords[c]->isOfType(SoCoordinate3::getClassTypeId()));
389 lines[c] = static_cast<SoLineSet *>(this->chart->getChild(2 + c * 4 + 3));
390 assert(lines[c]->isOfType(SoLineSet::getClassTypeId()));
391 }
392 }
393
394 int numdata = 0;
395 SbTime end(SbTime::zero());
396 Datum * datum = this->first;
397 float maxvalue = 0.0f;
398 #if LINEUP
399 SbList<float> maxvalues;
400 {
401 while (datum) {
402 ++numdata;
403 while (maxvalues.getLength() < datum->datum.getLength()) {
404 maxvalues.append(0.0f);
405 }
406 for (int j = 0; j < datum->datum.getLength(); ++j) {
407 float val = datum->datum[j];
408 if (val > maxvalues[j]) { maxvalues[j] = val; }
409 }
410 end = datum->when;
411 datum = datum->next;
412 }
413 for (int j = 0; j < maxvalues.getLength(); ++j) {
414 Graph * graph = this->getGraph(j);
415 if (j == 0) {
416 graph->basetarget = 0.0f;
417 } else {
418 graph->basetarget = maxvalue; // maxvalues[j-1];
419 }
420 maxvalue += maxvalues[j];
421 graph->baseval = decayvalue(graph->baseval, graph->basetarget);
422 }
423 }
424 #else
425 while (datum) {
426 ++numdata;
427 float sum = 0.0f;
428 for (int j = 0; j < datum->datum.getLength(); ++j) {
429 sum += datum->datum[j];
430 }
431 if (sum > maxvalue) maxvalue = sum;
432 end = datum->when;
433 datum = datum->next;
434 }
435 #endif
436
437 this->cachedrealmaxvalue = maxvalue;
438 this->cachedmaxvalue = decayvalue(this->cachedmaxvalue, this->cachedrealmaxvalue);
439
440 // printf("values: %d, maxvalue: %f\n", numdata, maxvalue);
441
442 // printf("num data values: %d\n", numdata);
443 const float seconds = (float) this->kit->seconds.getValue().getValue();
444
445 for (int graphidx = 0; graphidx < numgraphs; ++graphidx) {
446 Graph * graph = this->getGraph(graphidx);
447
448 colors[graphidx]->rgb = graph->color;
449
450 coords[graphidx]->point.setNum(numdata * 2);
451 SbVec3f * coordarray = coords[graphidx]->point.startEditing();
452
453 datum = this->first;
454 int idx = 0;
455 float maxypos = 0.0f, maxprevypos = 0.0f;
456 while (datum) {
457 /*const int numvalues = */datum->datum.getLength();
458
459 float value = 0.0f, prev = 0.0f;
460 #if LINEUP
461 // prev = (graph->index == 0) ? 0.0f : maxvalues[graph->index - 1];
462 prev = graph->baseval;
463 value = prev + datum->datum[graph->index];
464 #else
465 for (int j = 0; j <= graph->index && j < numvalues; ++j) {
466 prev = value;
467 value += datum->datum[j];
468 }
469 #endif
470
471 const float xpos = 1.0f - ((float)((end - datum->when).getValue()) / seconds);
472 const float ypos = (this->cachedrealmaxvalue > 0.0f) ? (value / this->cachedrealmaxvalue) : value;
473 const float prevypos = (this->cachedrealmaxvalue > 0.0f) ? (prev / this->cachedrealmaxvalue) : prev;
474
475 //printf("coord %d: %f %f\n", idx, xpos, ypos);
476 coordarray[idx*2] = SbVec3f(xpos, prevypos, 0.0f);
477 coordarray[idx*2+1] = SbVec3f(xpos, ypos, 0.0f);
478
479 ++idx;
480 datum = datum->next;
481
482 if (maxypos < ypos) maxypos = ypos;
483 if (maxprevypos < prevypos) maxprevypos = prevypos;
484 }
485 coords[graphidx]->point.finishEditing();
486
487 lines[graphidx]->numVertices.setNum(idx);
488 int32_t * countarray = lines[graphidx]->numVertices.startEditing();
489 for (int j = 0; j < idx; ++j) {
490 countarray[j] = 2;
491 }
492 lines[graphidx]->numVertices.finishEditing();
493
494 textnodes[graphidx]->string.setValue(graph->key.getString());
495 texttrans[graphidx]->translation.setValue(SbVec3f(0.0f, (maxypos + graph->baseval * 2.0f) * 0.5f, 0.0f));
496 }
497
498
499 // graph space is now between 0-1 in both directions
500
501 {
502 SoSeparator * sep = static_cast<SoSeparator *>(this->chart->getChild(numgraphs * 4 + 2));
503 assert(sep && sep->isOfType(SoSeparator::getClassTypeId()));
504 assert(sep->getNumChildren() == 3);
505 SoTranslation * trans = static_cast<SoTranslation *>(sep->getChild(1));
506 assert(trans && trans->isOfType(SoTranslation::getClassTypeId()));
507 trans->translation.setValue(0.98f, 1.0f, 0.0f);
508 SoText2 * text = static_cast<SoText2 *>(sep->getChild(2));
509 assert(text && text->isOfType(SoText2::getClassTypeId()));
510
511 SbString content;
512 content.sprintf("%6.0f ms _", this->cachedmaxvalue * 1000.0f);
513 text->justification.setValue(SoText2::RIGHT);
514 text->string.setValue(content.getString());
515 }
516 }
517
518 // *************************************************************************
519
520 #undef PRIVATE
521 #undef LINEUP
522 #undef SMOOTH_DECAY
523
524 #endif // HAVE_NODEKITS
525