This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

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

Last change on this file was 27965, checked in by Vladislav Belov, 13 months ago

Revert non-ASCII characters from source and configuration files introduced in rP27786.

Fixes #6846

Differential Revision: https://code.wildfiregames.com/D5185

  • Property svn:eol-style set to native
File size: 6.9 KB
RevLine 
[26479]1/* Copyright (C) 2022 Wildfire Games.
[27965]2 * This file is part of 0 A.D.
[19027]3 *
[27965]4 * 0 A.D. is free software: you can redistribute it and/or modify
[19027]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 *
[27965]9 * 0 A.D. is distributed in the hope that it will be useful,
[19027]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
[27965]15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
[19027]16 */
17
18#include "precompiled.h"
[22558]19
[19027]20#include "CChart.h"
21
[25588]22#include "graphics/Canvas2D.h"
[23028]23#include "gui/SettingTypes/CGUIList.h"
24#include "gui/SettingTypes/CGUISeries.h"
25#include "gui/SettingTypes/CGUIString.h"
[19027]26#include "ps/CLogger.h"
[25457]27#include "ps/CStrInternStatic.h"
[22863]28#include "ps/Profile.h"
[19027]29
30#include <cmath>
31
[22741]32CChart::CChart(CGUI& pGUI)
[23005]33 : IGUIObject(pGUI),
[23020]34 IGUITextOwner(*static_cast<IGUIObject*>(this)),
[25392]35 m_AxisColor(this, "axis_color"),
36 m_AxisWidth(this, "axis_width"),
37 m_BufferZone(this, "buffer_zone"),
38 m_Font(this, "font"),
39 m_FormatX(this, "format_x"),
40 m_FormatY(this, "format_y"),
41 m_SeriesColor(this, "series_color"),
42 m_SeriesSetting(this, "series")
[19027]43{
44}
45
46CChart::~CChart()
47{
48}
49
[23020]50void CChart::UpdateCachedSize()
51{
52 IGUIObject::UpdateCachedSize();
53 IGUITextOwner::UpdateCachedSize();
54}
55
[19027]56void CChart::HandleMessage(SGUIMessage& Message)
57{
[23020]58 IGUIObject::HandleMessage(Message);
59 // IGUITextOwner::HandleMessage(Message); performed in UpdateSeries
60
[19027]61 // TODO: implement zoom
[24268]62 if(Message.type == GUIM_SETTINGS_UPDATED)
[19115]63 UpdateSeries();
[19027]64}
65
[25605]66void CChart::DrawAxes(CCanvas2D& canvas) const
[21429]67{
[25605]68 canvas.DrawRect(CRect(
69 m_CachedActualSize.TopLeft(),
70 m_CachedActualSize.BottomLeft() + CVector2D(m_AxisWidth, 0.0f)), m_AxisColor);
71 canvas.DrawRect(CRect(
72 m_CachedActualSize.BottomLeft() - CVector2D(0.0f, m_AxisWidth),
73 m_CachedActualSize.BottomRight()), m_AxisColor);
[21429]74}
75
[25588]76void CChart::Draw(CCanvas2D& canvas)
[19027]77{
78 PROFILE3("render chart");
79
[19115]80 if (m_Series.empty())
81 return;
[19027]82
83 CRect rect = GetChartRect();
84 const float width = rect.GetWidth();
85 const float height = rect.GetHeight();
86
[21429]87 CVector2D scale(width / (m_RightTop.X - m_LeftBottom.X), height / (m_RightTop.Y - m_LeftBottom.Y));
[25588]88 std::vector<CVector2D> linePoints;
[19027]89 for (const CChartData& data : m_Series)
90 {
91 if (data.m_Points.empty())
92 continue;
[25588]93
94 linePoints.clear();
[19027]95 for (const CVector2D& point : data.m_Points)
96 {
[20544]97 if (fabs(point.X) != std::numeric_limits<float>::infinity() && fabs(point.Y) != std::numeric_limits<float>::infinity())
[19408]98 {
[25588]99 linePoints.emplace_back(
100 rect.left + (point.X - m_LeftBottom.X) * scale.X,
101 rect.bottom - (point.Y - m_LeftBottom.Y) * scale.Y);
[19408]102 }
103 else
104 {
[26479]105 canvas.DrawLine(linePoints, 2.0f, data.m_Color);
[25588]106 linePoints.clear();
[19408]107 }
[19027]108 }
[25588]109 if (!linePoints.empty())
[26479]110 canvas.DrawLine(linePoints, 2.0f, data.m_Color);
[19027]111 }
112
[21429]113 if (m_AxisWidth > 0)
[25605]114 DrawAxes(canvas);
[21429]115
116 for (size_t i = 0; i < m_TextPositions.size(); ++i)
[25591]117 DrawText(canvas, i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i]);
[19027]118}
119
120CRect CChart::GetChartRect() const
121{
[21429]122 return CRect(
[25152]123 m_CachedActualSize.TopLeft() + CVector2D(m_AxisWidth, m_AxisWidth),
124 m_CachedActualSize.BottomRight() - CVector2D(m_AxisWidth, m_AxisWidth)
[21429]125 );
[19027]126}
127
128void CChart::UpdateSeries()
129{
[23005]130 m_Series.clear();
[25392]131 m_Series.resize(m_SeriesSetting->m_Series.size());
[19027]132
[25392]133 for (size_t i = 0; i < m_SeriesSetting->m_Series.size(); ++i)
[19027]134 {
[19115]135 CChartData& data = m_Series[i];
[19027]136
[25392]137 if (i < m_SeriesColor->m_Items.size() && !data.m_Color.ParseString(m_pGUI, m_SeriesColor->m_Items[i].GetOriginalString().ToUTF8(), 0))
138 LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(m_SeriesColor->m_Items[i].GetOriginalString()));
[19027]139
[25392]140 data.m_Points = m_SeriesSetting->m_Series[i];
[19027]141 }
[21429]142 UpdateBounds();
143
144 SetupText();
[19027]145}
[21429]146
147void CChart::SetupText()
148{
149 m_GeneratedTexts.clear();
150 m_TextPositions.clear();
151
152 if (m_Series.empty())
153 return;
154
155 // Add Y-axis
156 const float height = GetChartRect().GetHeight();
157 // TODO: split values depend on the format;
158 if (m_EqualY)
159 {
160 // We don't need to generate many items for equal values
[23005]161 AddFormattedValue(m_FormatY, m_RightTop.Y, m_Font, m_BufferZone);
[21429]162 m_TextPositions.emplace_back(GetChartRect().TopLeft());
163 }
164 else
165 for (int i = 0; i < 3; ++i)
166 {
[23005]167 AddFormattedValue(m_FormatY, m_RightTop.Y - (m_RightTop.Y - m_LeftBottom.Y) / 3.f * i, m_Font, m_BufferZone);
[25152]168 m_TextPositions.emplace_back(GetChartRect().TopLeft() + CVector2D(0.f, height / 3.f * i));
[21429]169 }
170
171 // Add X-axis
172 const float width = GetChartRect().GetWidth();
173 if (m_EqualX)
174 {
[25143]175 CSize2D text_size = AddFormattedValue(m_FormatX, m_RightTop.X, m_Font, m_BufferZone);
[21429]176 m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size);
177 }
178 else
179 for (int i = 0; i < 3; ++i)
180 {
[25143]181 CSize2D text_size = AddFormattedValue(m_FormatX, m_RightTop.X - (m_RightTop.X - m_LeftBottom.X) / 3 * i, m_Font, m_BufferZone);
[25152]182 m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size - CVector2D(width / 3 * i, 0.f));
[21429]183 }
184}
185
[25143]186CSize2D CChart::AddFormattedValue(const CStrW& format, const float value, const CStrW& font, const float buffer_zone)
[21429]187{
188 // TODO: we need to catch cases with equal formatted values.
189 CGUIString gui_str;
190 if (format == L"DECIMAL2")
191 {
192 wchar_t buffer[64];
193 swprintf(buffer, 64, L"%.2f", value);
194 gui_str.SetValue(buffer);
195 }
196 else if (format == L"INTEGER")
197 {
198 wchar_t buffer[64];
[22282]199 swprintf(buffer, 64, L"%d", std::lround(value));
[21429]200 gui_str.SetValue(buffer);
201 }
202 else if (format == L"DURATION_SHORT")
203 {
204 const int seconds = value;
205 wchar_t buffer[64];
206 swprintf(buffer, 64, L"%d:%02d", seconds / 60, seconds % 60);
207 gui_str.SetValue(buffer);
208 }
[22282]209 else if (format == L"PERCENTAGE")
210 {
211 wchar_t buffer[64];
212 swprintf(buffer, 64, L"%d%%", std::lround(value));
213 gui_str.SetValue(buffer);
214 }
[21429]215 else
216 {
217 LOGERROR("Unsupported chart format: " + format.EscapeToPrintableASCII());
[25143]218 return CSize2D();
[21429]219 }
[22679]220
[23009]221 return AddText(gui_str, font, 0, buffer_zone).GetSize();
[21429]222}
223
224void CChart::UpdateBounds()
225{
226 if (m_Series.empty() || m_Series[0].m_Points.empty())
227 {
228 m_LeftBottom = m_RightTop = CVector2D(0.f, 0.f);
229 return;
230 }
231
232 m_LeftBottom = m_RightTop = m_Series[0].m_Points[0];
233 for (const CChartData& data : m_Series)
234 for (const CVector2D& point : data.m_Points)
235 {
236 if (fabs(point.X) != std::numeric_limits<float>::infinity() && point.X < m_LeftBottom.X)
237 m_LeftBottom.X = point.X;
238 if (fabs(point.Y) != std::numeric_limits<float>::infinity() && point.Y < m_LeftBottom.Y)
239 m_LeftBottom.Y = point.Y;
240
241 if (fabs(point.X) != std::numeric_limits<float>::infinity() && point.X > m_RightTop.X)
242 m_RightTop.X = point.X;
243 if (fabs(point.Y) != std::numeric_limits<float>::infinity() && point.Y > m_RightTop.Y)
244 m_RightTop.Y = point.Y;
245 }
246
247 m_EqualY = m_RightTop.Y == m_LeftBottom.Y;
248 if (m_EqualY)
249 m_RightTop.Y += 1;
250 m_EqualX = m_RightTop.X == m_LeftBottom.X;
251 if (m_EqualX)
252 m_RightTop.X += 1;
253}
Note: See TracBrowser for help on using the repository browser.