]> 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/Graphics.hpp>\r
11 \r
12 #include <boost/foreach.hpp>\r
13 #include <boost/optional.hpp>\r
14 #include <boost/circular_buffer.hpp>\r
15 #include <boost/range/algorithm_ext/erase.hpp>\r
16 \r
17 #include <numeric>\r
18 #include <map>\r
19 #include <array>\r
20 \r
21 namespace caspar { namespace diagnostics {\r
22 \r
23 struct drawable : public sf::Drawable\r
24 {\r
25         virtual ~drawable(){}\r
26         virtual void render(sf::RenderTarget& target) = 0;\r
27         virtual void Render(sf::RenderTarget& target) const { const_cast<drawable*>(this)->render(target);}\r
28 };\r
29 \r
30 class context : public drawable\r
31 {       \r
32         timer timer_;\r
33         sf::RenderWindow window_;\r
34         \r
35         std::list<std::shared_ptr<drawable>> drawables_;\r
36         std::map<size_t, sf::Font> fonts_;\r
37                 \r
38         executor executor_;\r
39 public:                                 \r
40 \r
41         template<typename Func>\r
42         static auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept\r
43         {       \r
44                 return get_instance().executor_.begin_invoke(std::forward<Func>(func)); \r
45         }\r
46 \r
47         static void register_drawable(const std::shared_ptr<drawable>& drawable)\r
48         {\r
49                 begin_invoke([=]\r
50                 {\r
51                         get_instance().drawables_.push_back(drawable);\r
52                 });\r
53         }\r
54                         \r
55 private:\r
56 \r
57         void tick()\r
58         {\r
59                 sf::Event e;\r
60                 while(window_.GetEvent(e)){}            \r
61                 glClear(GL_COLOR_BUFFER_BIT);\r
62                 window_.Draw(*this);\r
63                 window_.Display();\r
64                 timer_.tick(1.0/50.0);\r
65                 executor_.begin_invoke([this]{tick();});\r
66         }\r
67 \r
68         void render(sf::RenderTarget& target)\r
69         {\r
70                 auto count = std::max<size_t>(5, drawables_.size());\r
71 \r
72                 int n = 0;\r
73                 for(auto it = drawables_.begin(); it != drawables_.end(); ++n)\r
74                 {\r
75                         auto& drawable = *it;\r
76                         if(!drawable.unique())\r
77                         {\r
78                                 drawable->SetScale(window_.GetWidth(), window_.GetHeight()/count);\r
79                                 drawable->SetPosition(0.0f, n* window_.GetHeight()/count);\r
80                                 target.Draw(*drawable);                         \r
81                                 ++it;           \r
82                         }\r
83                         else                    \r
84                                 it = drawables_.erase(it);                      \r
85                 }               \r
86         }       \r
87         \r
88         static context& get_instance()\r
89         {\r
90                 static context impl;\r
91                 return impl;\r
92         }\r
93 \r
94         context()\r
95         {\r
96                 executor_.start();\r
97                 executor_.begin_invoke([this]\r
98                 {\r
99                         window_.Create(sf::VideoMode(600, 1000), "CasparCG Diagnostics");\r
100                         window_.SetPosition(0, 0);\r
101                         window_.SetActive();\r
102                         glEnable(GL_BLEND);\r
103                         glEnable(GL_LINE_SMOOTH);\r
104                         glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);\r
105                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
106                         tick();\r
107                 });\r
108         }\r
109 };\r
110         \r
111 class guide : public drawable\r
112 {\r
113         float value_;\r
114         color c_;\r
115 public:\r
116         guide(color c = color(1.0f, 1.0f, 1.0f, 0.6f)) \r
117                 : value_(0.0f)\r
118                 , c_(c){}\r
119 \r
120         guide(float value, color c = color(1.0f, 1.0f, 1.0f, 0.6f)) \r
121                 : value_(value)\r
122                 , c_(c){}\r
123                         \r
124         void set_color(color c) {c_ = c;}\r
125 \r
126         void render(sf::RenderTarget&)\r
127         {               \r
128                 glEnable(GL_LINE_STIPPLE);\r
129                 glLineStipple(3, 0xAAAA);\r
130                 glBegin(GL_LINE_STRIP); \r
131                         glColor4f(c_.red, c_.green, c_.blue+0.2f, c_.alpha);            \r
132                         glVertex3f(0.0f, (1.0f-value_) * 0.8f + 0.1f, 0.0f);            \r
133                         glVertex3f(1.0f, (1.0f-value_) * 0.8f + 0.1f, 0.0f);    \r
134                 glEnd();\r
135                 glDisable(GL_LINE_STIPPLE);\r
136         }\r
137 };\r
138 \r
139 class line : public drawable\r
140 {\r
141         boost::optional<diagnostics::guide> guide_;\r
142         boost::circular_buffer<float> line_data_;\r
143         std::vector<float> tick_data_;\r
144         color c_;\r
145 public:\r
146         line(size_t res = 600)\r
147                 : line_data_(res)\r
148                 , c_(1.0f, 1.0f, 1.0f){}\r
149         \r
150         void update(float value)\r
151         {\r
152                 tick_data_.push_back(value);\r
153         }\r
154 \r
155         void guide(const guide& guide)\r
156         {\r
157                 guide_ = guide;\r
158                 guide_->set_color(c_);\r
159         }\r
160         \r
161         void set_color(color c)\r
162         {\r
163                 c_ = c;\r
164                 if(guide_)\r
165                         guide_->set_color(c_);\r
166         }\r
167 \r
168         color get_color() const { return c_; }\r
169         \r
170         void render(sf::RenderTarget& target)\r
171         {\r
172                 float dx = 1.0f/static_cast<float>(line_data_.capacity());\r
173                 float x = static_cast<float>(line_data_.capacity()-line_data_.size())*dx;\r
174 \r
175                 if(!tick_data_.empty())\r
176                 {\r
177                         float sum = std::accumulate(tick_data_.begin(), tick_data_.end(), 0.0) + std::numeric_limits<float>::min();\r
178                         line_data_.push_back(static_cast<float>(sum)/static_cast<float>(tick_data_.size()));\r
179                         tick_data_.clear();\r
180                 }\r
181                 else if(!line_data_.empty())\r
182                 {\r
183                         line_data_.push_back(line_data_.back());\r
184                 }\r
185 \r
186                 if(guide_)\r
187                         target.Draw(*guide_);\r
188                 \r
189                 glBegin(GL_LINE_STRIP);\r
190                 glColor4f(c_.red, c_.green, c_.blue, 1.0f);             \r
191                 for(size_t n = 0; n < line_data_.size(); ++n)                           \r
192                         glVertex3f(x+n*dx, std::max(0.05f, std::min(0.95f, (1.0f-line_data_[n])*0.8f + 0.1f)), 0.0f);           \r
193                 glEnd();\r
194         }\r
195 };\r
196 \r
197 struct graph::implementation : public drawable\r
198 {\r
199         std::map<std::string, diagnostics::line> lines_;\r
200         std::string name_;\r
201 \r
202         implementation(const std::string& name) : name_(name){}\r
203 \r
204         void update(const std::string& name, float value)\r
205         {\r
206                 context::begin_invoke([=]\r
207                 {\r
208                         lines_[name].update(value);\r
209                 });\r
210         }\r
211 \r
212         void set_color(const std::string& name, color c)\r
213         {\r
214                 context::begin_invoke([=]\r
215                 {\r
216                         lines_[name].set_color(c);\r
217                 });\r
218         }\r
219         \r
220         void guide(const std::string& name, float value)\r
221         {\r
222                 context::begin_invoke([=]\r
223                 {\r
224                         lines_[name].guide(diagnostics::guide(value));\r
225                 });\r
226         }\r
227 \r
228 private:\r
229         void render(sf::RenderTarget& target)\r
230         {\r
231                 const size_t text_size = 15;\r
232                 const size_t text_margin = 2;\r
233                 const size_t text_offset = text_size+text_margin*2;\r
234 \r
235                 sf::String text(name_.c_str(), sf::Font::GetDefaultFont(), text_size);\r
236                 text.SetStyle(sf::String::Italic);\r
237                 text.Move(text_margin, text_margin);\r
238                 \r
239                 glPushMatrix();\r
240                 glScaled(1.0f/GetScale().x, 1.0f/GetScale().y, 1.0f);\r
241                 target.Draw(text);\r
242                 float x_offset = text.GetPosition().x + text.GetRect().Right + text_margin*4;\r
243                 for(auto it = lines_.begin(); it != lines_.end(); ++it)\r
244                 {                                               \r
245                         sf::String line_text(it->first, sf::Font::GetDefaultFont(), text_size);\r
246                         line_text.SetPosition(x_offset, text_margin);\r
247                         auto c = it->second.get_color();\r
248                         line_text.SetColor(sf::Color(c.red*255.0f, c.green*255.0f, c.blue*255.0f, c.alpha*255.0f));\r
249                         target.Draw(line_text);\r
250                         x_offset = line_text.GetRect().Right + text_margin*2;\r
251                 }\r
252 \r
253                 glDisable(GL_TEXTURE_2D);\r
254                 glPopMatrix();\r
255 \r
256                 glBegin(GL_QUADS);\r
257                         glColor4f(1.0f, 1.0f, 1.0f, 0.2f);      \r
258                         glVertex2f(1.0f, 0.99f);\r
259                         glVertex2f(0.0f, 0.99f);\r
260                         glVertex2f(0.0f, 0.01f);        \r
261                         glVertex2f(1.0f, 0.01f);        \r
262                 glEnd();\r
263 \r
264                 glPushMatrix();\r
265                 glTranslated(0.0f, text_offset/GetScale().y, 1.0f);\r
266                 glScaled(1.0f, 1.0-text_offset/GetScale().y, 1.0f);\r
267                 \r
268                 target.Draw(diagnostics::guide(1.0f, color(1.0f, 1.0f, 1.0f, 0.6f)));\r
269                 target.Draw(diagnostics::guide(0.0f, color(1.0f, 1.0f, 1.0f, 0.6f)));\r
270 \r
271                 for(auto it = lines_.begin(); it != lines_.end(); ++it)         \r
272                         target.Draw(it->second);\r
273                 \r
274                 glPopMatrix();\r
275 \r
276         }\r
277 \r
278         implementation(implementation&);\r
279         implementation& operator=(implementation&);\r
280 };\r
281         \r
282 graph::graph(const std::string& name) : impl_(new implementation(name))\r
283 {\r
284         context::register_drawable(impl_);\r
285 }\r
286 void graph::update(const std::string& name, float value){impl_->update(name, value);}\r
287 void graph::guide(const std::string& name, float value){impl_->guide(name, value);}\r
288 void graph::set_color(const std::string& name, color c){impl_->set_color(name, c);}\r
289 \r
290 safe_ptr<graph> create_graph(const std::string& name)\r
291 {\r
292         return safe_ptr<graph>(new graph(name));\r
293 }\r
294 \r
295 }}