]> git.sesse.net Git - casparcg/blob - common/diagnostics/graph.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / common / diagnostics / graph.cpp
1 #include "../stdafx.h"\r
2 \r
3 #include "graph.h"\r
4 \r
5 #pragma warning (disable : 4244)\r
6 \r
7 #include "../concurrency/executor.h"\r
8 #include "../utility/timer.h"\r
9 \r
10 #include <SFML/Window.hpp>\r
11 \r
12 #include <boost/foreach.hpp>\r
13 #include <boost/circular_buffer.hpp>\r
14 #include <boost/range/algorithm_ext/erase.hpp>\r
15 \r
16 #include <numeric>\r
17 #include <map>\r
18 #include <array>\r
19 \r
20 namespace caspar { namespace diagnostics {\r
21 \r
22 struct drawable\r
23 {\r
24         virtual ~drawable(){}\r
25         virtual void draw() = 0;\r
26 };\r
27 \r
28 class context\r
29 {       \r
30         timer timer_;\r
31         sf::Window window_;\r
32         \r
33         std::list<std::weak_ptr<drawable>> drawables_;\r
34                 \r
35         executor executor_;\r
36 public:                                 \r
37 \r
38         template<typename Func>\r
39         static auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept\r
40         {       \r
41                 return get_instance().executor_.begin_invoke(std::forward<Func>(func)); \r
42         }\r
43 \r
44         static void register_drawable(const std::shared_ptr<drawable>& drawable)\r
45         {\r
46                 begin_invoke([=]\r
47                 {\r
48                         get_instance().drawables_.push_back(drawable);\r
49                 });\r
50         }\r
51         \r
52 private:\r
53 \r
54         void tick()\r
55         {\r
56                 sf::Event e;\r
57                 while(window_.GetEvent(e)){}            \r
58                 glClear(GL_COLOR_BUFFER_BIT);\r
59                 render();\r
60                 window_.Display();\r
61                 timer_.tick(1.0/50.0);\r
62                 executor_.begin_invoke([this]{tick();});\r
63         }\r
64 \r
65         void render()\r
66         {\r
67                 glLoadIdentity();\r
68                 glTranslated(-1.0f, -1.0f, 0.0f);\r
69                 glScaled(2.0f, 2.0f, 1.0f);\r
70 \r
71                 float dy = 1.0/static_cast<float>(std::max<int>(5, drawables_.size()));\r
72 \r
73                 glTranslated(0.0f, (1.0-dy), 0.0f);\r
74                 for(auto it = drawables_.begin(); it != drawables_.end();)\r
75                 {\r
76                         auto drawable = it->lock();\r
77                         if(drawable)\r
78                         {\r
79                                 glPushMatrix();\r
80                                         glScaled(1.0f, dy, 1.0f);\r
81                                         drawable->draw();\r
82                                 glPopMatrix();\r
83                                 glTranslated(0.0f, -dy, 0.0f);\r
84                                 ++it;\r
85                         }\r
86                         else                    \r
87                                 it = drawables_.erase(it);                      \r
88                 }               \r
89         }       \r
90 \r
91         static context& get_instance()\r
92         {\r
93                 static context impl;\r
94                 return impl;\r
95         }\r
96 \r
97         context()\r
98         {\r
99                 executor_.start();\r
100                 executor_.begin_invoke([this]\r
101                 {\r
102                         window_.Create(sf::VideoMode(600, 1000), "CasparCG Diagnostics");\r
103                         window_.SetPosition(0, 0);\r
104                         window_.SetActive();\r
105                         glEnable(GL_BLEND);\r
106                         glEnable(GL_LINE_SMOOTH);\r
107                         glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);\r
108                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
109                         tick();\r
110                 });\r
111         }\r
112 };\r
113 \r
114 class line\r
115 {\r
116         boost::circular_buffer<float> line_data_;\r
117         std::vector<float> tick_data_;\r
118         color c_;\r
119 public:\r
120         line(size_t res = 600)\r
121                 : line_data_(res)\r
122                 , c_(1.0f, 1.0f, 1.0f){}\r
123         \r
124         void update(float value)\r
125         {\r
126                 tick_data_.push_back(value);\r
127         }\r
128         \r
129         void set_color(color c){c_ = c;}\r
130         \r
131         void draw()\r
132         {\r
133                 float dx = 1.0f/static_cast<float>(line_data_.capacity());\r
134                 float x = static_cast<float>(line_data_.capacity()-line_data_.size())*dx;\r
135 \r
136                 if(!tick_data_.empty())\r
137                 {\r
138                         float sum = std::accumulate(tick_data_.begin(), tick_data_.end(), 0.0) + std::numeric_limits<float>::min();\r
139                         line_data_.push_back(static_cast<float>(sum)/static_cast<float>(tick_data_.size()));\r
140                         tick_data_.clear();\r
141                 }\r
142                 else if(!line_data_.empty())\r
143                 {\r
144                         line_data_.push_back(line_data_.back());\r
145                 }\r
146                 \r
147                 glBegin(GL_LINE_STRIP);\r
148                 glColor4f(c_.red, c_.green, c_.blue, 1.0f);                     \r
149                 for(size_t n = 0; n < line_data_.size(); ++n)                           \r
150                         glVertex3f(x+n*dx, std::max(0.05f, std::min(0.95f, line_data_[n]*0.8f + 0.1f)), 0.0f);          \r
151                 glEnd();\r
152         }\r
153 };\r
154         \r
155 class guide\r
156 {\r
157         color c_;\r
158         float value_;\r
159 public:\r
160         guide() : value_(0.0f){}\r
161 \r
162         guide(float value, color c) \r
163                 : value_(value)\r
164                 , c_(c){}\r
165                         \r
166         void draw()\r
167         {               \r
168                 glEnable(GL_LINE_STIPPLE);\r
169                 glLineStipple(3, 0xAAAA);\r
170                 glBegin(GL_LINE_STRIP);\r
171                 glColor4f(c_.red, c_.green, c_.blue, 1.0f);                             \r
172                         glVertex3f(0.0f, value_ * 0.8f + 0.1f, 0.0f);           \r
173                         glVertex3f(1.0f, value_ * 0.8f + 0.1f, 0.0f);   \r
174                 glEnd();\r
175                 glDisable(GL_LINE_STIPPLE);\r
176         }\r
177 };\r
178 \r
179 struct graph::implementation : public drawable\r
180 {\r
181         std::map<std::string, diagnostics::line> lines_;\r
182         std::map<std::string, diagnostics::guide> guides_;\r
183 \r
184         implementation(const std::string&)\r
185         {\r
186                 guides_["max"] = diagnostics::guide(1.0f, color(0.4f, 0.4f, 0.4f));\r
187                 guides_["min"] = diagnostics::guide(0.0f, color(0.4f, 0.4f, 0.4f));\r
188         }\r
189 \r
190         void update(const std::string& name, float value)\r
191         {\r
192                 context::begin_invoke([=]\r
193                 {\r
194                         lines_[name].update(value);\r
195                 });\r
196         }\r
197 \r
198         void set_color(const std::string& name, color c)\r
199         {\r
200                 context::begin_invoke([=]\r
201                 {\r
202                         lines_[name].set_color(c);\r
203                 });\r
204         }\r
205         \r
206         void add_guide(const std::string& name, float value, color c)\r
207         {\r
208                 context::begin_invoke([=]\r
209                 {\r
210                         guides_[name] = diagnostics::guide(value, c);\r
211                 });\r
212         }\r
213 \r
214 private:\r
215         void draw()\r
216         {\r
217                 glBegin(GL_QUADS);\r
218                         glColor4f(1.0f, 1.0f, 1.0f, 0.2f);      \r
219                         glVertex2f(1.0f, 0.99f);\r
220                         glVertex2f(0.0f, 0.99f);\r
221                         glVertex2f(0.0f, 0.01f);        \r
222                         glVertex2f(1.0f, 0.01f);        \r
223                 glEnd();\r
224                 \r
225                 for(auto it = guides_.begin(); it != guides_.end(); ++it)\r
226                         it->second.draw();\r
227 \r
228                 for(auto it = lines_.begin(); it != lines_.end(); ++it)\r
229                         it->second.draw();\r
230         }\r
231 \r
232         implementation(implementation&);\r
233         implementation& operator=(implementation&);\r
234 };\r
235         \r
236 graph::graph(const std::string& name) : impl_(new implementation(name))\r
237 {\r
238         context::register_drawable(impl_);\r
239 }\r
240 void graph::update(const std::string& name, float value){impl_->update(name, value);}\r
241 void graph::set_color(const std::string& name, color c){impl_->set_color(name, c);}\r
242 void graph::add_guide(const std::string& name, float value, color c){impl_->add_guide(name, value, c);}\r
243 \r
244 safe_ptr<graph> create_graph(const std::string& name)\r
245 {\r
246         return safe_ptr<graph>(new graph(name));\r
247 }\r
248 \r
249 }}