source: ps/trunk/source/gui/ObjectTypes/CChart.cpp@ 25261

Last change on this file since 25261 was 25261, checked in by Vladislav Belov, 3 years ago

Removes unused shadow settings forgotten in rP13877, removes direct SkipSubmit access.

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1/* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "precompiled.h"
19
20#include "CChart.h"
21
22#include "graphics/ShaderManager.h"
23#include "gui/GUIMatrix.h"
24#include "gui/SettingTypes/CGUIList.h"
25#include "gui/SettingTypes/CGUISeries.h"
26#include "gui/SettingTypes/CGUIString.h"
27#include "ps/CLogger.h"
28#include "ps/Profile.h"
29#include "renderer/Renderer.h"
30
31#include <cmath>
32
33CChart::CChart(CGUI& pGUI)
34 : IGUIObject(pGUI),
35 IGUITextOwner(*static_cast<IGUIObject*>(this)),
36 m_AxisColor(),
37 m_AxisWidth(),
38 m_BufferZone(),
39 m_Font(),
40 m_FormatX(),
41 m_FormatY(),
42 m_SeriesColor(),
43 m_SeriesSetting(),
44 m_TextAlign()
45{
46 RegisterSetting("axis_color", m_AxisColor);
47 RegisterSetting("axis_width", m_AxisWidth);
48 RegisterSetting("buffer_zone", m_BufferZone);
49 RegisterSetting("font", m_Font);
50 RegisterSetting("format_x", m_FormatX);
51 RegisterSetting("format_y", m_FormatY);
52 RegisterSetting("series_color", m_SeriesColor);
53 RegisterSetting("series", m_SeriesSetting);
54 RegisterSetting("text_align", m_TextAlign);
55}
56
57CChart::~CChart()
58{
59}
60
61void CChart::UpdateCachedSize()
62{
63 IGUIObject::UpdateCachedSize();
64 IGUITextOwner::UpdateCachedSize();
65}
66
67void CChart::HandleMessage(SGUIMessage& Message)
68{
69 IGUIObject::HandleMessage(Message);
70 // IGUITextOwner::HandleMessage(Message); performed in UpdateSeries
71
72 // TODO: implement zoom
73 if(Message.type == GUIM_SETTINGS_UPDATED)
74 UpdateSeries();
75}
76
77void CChart::DrawLine(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector<float>& vertices) const
78{
79 shader->Uniform(str_color, color);
80 shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
81 shader->AssertPointersBound();
82
83#if !CONFIG2_GLES
84 glEnable(GL_LINE_SMOOTH);
85#endif
86 glLineWidth(1.1f);
87 if (!g_Renderer.DoSkipSubmit())
88 glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3);
89 glLineWidth(1.0f);
90#if !CONFIG2_GLES
91 glDisable(GL_LINE_SMOOTH);
92#endif
93}
94
95void CChart::DrawTriangleStrip(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector<float>& vertices) const
96{
97 shader->Uniform(str_color, color);
98 shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
99 shader->AssertPointersBound();
100
101 if (!g_Renderer.DoSkipSubmit())
102 glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size() / 3);
103}
104
105void CChart::DrawAxes(const CShaderProgramPtr& shader) const
106{
107 const float bz = GetBufferedZ();
108 CRect rect = GetChartRect();
109 std::vector<float> vertices;
110 vertices.reserve(30);
111#define ADD(x, y) vertices.push_back(x); vertices.push_back(y); vertices.push_back(bz + 0.5f);
112 ADD(m_CachedActualSize.right, m_CachedActualSize.bottom);
113 ADD(rect.right + m_AxisWidth, rect.bottom);
114 ADD(m_CachedActualSize.left, m_CachedActualSize.bottom);
115 ADD(rect.left, rect.bottom);
116 ADD(m_CachedActualSize.left, m_CachedActualSize.top);
117 ADD(rect.left, rect.top - m_AxisWidth);
118#undef ADD
119 DrawTriangleStrip(shader, m_AxisColor, vertices);
120}
121
122void CChart::Draw()
123{
124 PROFILE3("render chart");
125
126 if (m_Series.empty())
127 return;
128
129 const float bz = GetBufferedZ();
130 CRect rect = GetChartRect();
131 const float width = rect.GetWidth();
132 const float height = rect.GetHeight();
133
134 // Setup the render state
135 CMatrix3D transform = GetDefaultGuiMatrix();
136 CShaderDefines lineDefines;
137 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid, g_Renderer.GetSystemShaderDefines(), lineDefines);
138 tech->BeginPass();
139 CShaderProgramPtr shader = tech->GetShader();
140 shader->Uniform(str_transform, transform);
141
142 CVector2D scale(width / (m_RightTop.X - m_LeftBottom.X), height / (m_RightTop.Y - m_LeftBottom.Y));
143 for (const CChartData& data : m_Series)
144 {
145 if (data.m_Points.empty())
146 continue;
147
148 std::vector<float> vertices;
149 for (const CVector2D& point : data.m_Points)
150 {
151 if (fabs(point.X) != std::numeric_limits<float>::infinity() && fabs(point.Y) != std::numeric_limits<float>::infinity())
152 {
153 vertices.push_back(rect.left + (point.X - m_LeftBottom.X) * scale.X);
154 vertices.push_back(rect.bottom - (point.Y - m_LeftBottom.Y) * scale.Y);
155 vertices.push_back(bz + 0.5f);
156 }
157 else
158 {
159 DrawLine(shader, data.m_Color, vertices);
160 vertices.clear();
161 }
162 }
163 if (!vertices.empty())
164 DrawLine(shader, data.m_Color, vertices);
165 }
166
167 if (m_AxisWidth > 0)
168 DrawAxes(shader);
169
170 tech->EndPass();
171
172 for (size_t i = 0; i < m_TextPositions.size(); ++i)
173 DrawText(i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i], bz + 0.5f);
174}
175
176CRect CChart::GetChartRect() const
177{
178 return CRect(
179 m_CachedActualSize.TopLeft() + CVector2D(m_AxisWidth, m_AxisWidth),
180 m_CachedActualSize.BottomRight() - CVector2D(m_AxisWidth, m_AxisWidth)
181 );
182}
183
184void CChart::UpdateSeries()
185{
186 m_Series.clear();
187 m_Series.resize(m_SeriesSetting.m_Series.size());
188
189 for (size_t i = 0; i < m_SeriesSetting.m_Series.size(); ++i)
190 {
191 CChartData& data = m_Series[i];
192
193 if (i < m_SeriesColor.m_Items.size() && !data.m_Color.ParseString(m_pGUI, m_SeriesColor.m_Items[i].GetOriginalString().ToUTF8(), 0))
194 LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(m_SeriesColor.m_Items[i].GetOriginalString()));
195
196 data.m_Points = m_SeriesSetting.m_Series[i];
197 }
198 UpdateBounds();
199
200 SetupText();
201}
202
203void CChart::SetupText()
204{
205 m_GeneratedTexts.clear();
206 m_TextPositions.clear();
207
208 if (m_Series.empty())
209 return;
210
211 // Add Y-axis
212 const float height = GetChartRect().GetHeight();
213 // TODO: split values depend on the format;
214 if (m_EqualY)
215 {
216 // We don't need to generate many items for equal values
217 AddFormattedValue(m_FormatY, m_RightTop.Y, m_Font, m_BufferZone);
218 m_TextPositions.emplace_back(GetChartRect().TopLeft());
219 }
220 else
221 for (int i = 0; i < 3; ++i)
222 {
223 AddFormattedValue(m_FormatY, m_RightTop.Y - (m_RightTop.Y - m_LeftBottom.Y) / 3.f * i, m_Font, m_BufferZone);
224 m_TextPositions.emplace_back(GetChartRect().TopLeft() + CVector2D(0.f, height / 3.f * i));
225 }
226
227 // Add X-axis
228 const float width = GetChartRect().GetWidth();
229 if (m_EqualX)
230 {
231 CSize2D text_size = AddFormattedValue(m_FormatX, m_RightTop.X, m_Font, m_BufferZone);
232 m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size);
233 }
234 else
235 for (int i = 0; i < 3; ++i)
236 {
237 CSize2D text_size = AddFormattedValue(m_FormatX, m_RightTop.X - (m_RightTop.X - m_LeftBottom.X) / 3 * i, m_Font, m_BufferZone);
238 m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size - CVector2D(width / 3 * i, 0.f));
239 }
240}
241
242CSize2D CChart::AddFormattedValue(const CStrW& format, const float value, const CStrW& font, const float buffer_zone)
243{
244 // TODO: we need to catch cases with equal formatted values.
245 CGUIString gui_str;
246 if (format == L"DECIMAL2")
247 {
248 wchar_t buffer[64];
249 swprintf(buffer, 64, L"%.2f", value);
250 gui_str.SetValue(buffer);
251 }
252 else if (format == L"INTEGER")
253 {
254 wchar_t buffer[64];
255 swprintf(buffer, 64, L"%d", std::lround(value));
256 gui_str.SetValue(buffer);
257 }
258 else if (format == L"DURATION_SHORT")
259 {
260 const int seconds = value;
261 wchar_t buffer[64];
262 swprintf(buffer, 64, L"%d:%02d", seconds / 60, seconds % 60);
263 gui_str.SetValue(buffer);
264 }
265 else if (format == L"PERCENTAGE")
266 {
267 wchar_t buffer[64];
268 swprintf(buffer, 64, L"%d%%", std::lround(value));
269 gui_str.SetValue(buffer);
270 }
271 else
272 {
273 LOGERROR("Unsupported chart format: " + format.EscapeToPrintableASCII());
274 return CSize2D();
275 }
276
277 return AddText(gui_str, font, 0, buffer_zone).GetSize();
278}
279
280void CChart::UpdateBounds()
281{
282 if (m_Series.empty() || m_Series[0].m_Points.empty())
283 {
284 m_LeftBottom = m_RightTop = CVector2D(0.f, 0.f);
285 return;
286 }
287
288 m_LeftBottom = m_RightTop = m_Series[0].m_Points[0];
289 for (const CChartData& data : m_Series)
290 for (const CVector2D& point : data.m_Points)
291 {
292 if (fabs(point.X) != std::numeric_limits<float>::infinity() && point.X < m_LeftBottom.X)
293 m_LeftBottom.X = point.X;
294 if (fabs(point.Y) != std::numeric_limits<float>::infinity() && point.Y < m_LeftBottom.Y)
295 m_LeftBottom.Y = point.Y;
296
297 if (fabs(point.X) != std::numeric_limits<float>::infinity() && point.X > m_RightTop.X)
298 m_RightTop.X = point.X;
299 if (fabs(point.Y) != std::numeric_limits<float>::infinity() && point.Y > m_RightTop.Y)
300 m_RightTop.Y = point.Y;
301 }
302
303 m_EqualY = m_RightTop.Y == m_LeftBottom.Y;
304 if (m_EqualY)
305 m_RightTop.Y += 1;
306 m_EqualX = m_RightTop.X == m_LeftBottom.X;
307 if (m_EqualX)
308 m_RightTop.X += 1;
309}
Note: See TracBrowser for help on using the repository browser.