diff --git a/source/gui/CChart.cpp b/source/gui/CChart.cpp
new file mode 100644
index 0000000000..ddfa845f28
--- /dev/null
+++ b/source/gui/CChart.cpp
@@ -0,0 +1,144 @@
+/* Copyright (C) 2016 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+#include "CChart.h"
+
+#include "graphics/ShaderManager.h"
+#include "lib/ogl.h"
+#include "ps/CLogger.h"
+#include "renderer/Renderer.h"
+
+#include
+
+CChart::CChart()
+{
+ AddSetting(GUIST_CGUIList, "series_color");
+ AddSetting(GUIST_CGUISeries, "series");
+}
+
+CChart::~CChart()
+{
+}
+
+void CChart::HandleMessage(SGUIMessage& Message)
+{
+ // TODO: implement zoom
+}
+
+void CChart::Draw()
+{
+ PROFILE3("render chart");
+
+ if (!GetGUI())
+ return;
+
+ UpdateSeries();
+
+ const float bz = GetBufferedZ();
+ CRect rect = GetChartRect();
+ const float width = rect.GetWidth();
+ const float height = rect.GetHeight();
+
+ if (m_Series.empty())
+ return;
+
+ // Disable depth updates to prevent apparent z-fighting-related issues
+ // with some drivers causing units to get drawn behind the texture.
+ glDepthMask(0);
+
+ // Setup the render state
+ CMatrix3D transform = GetDefaultGuiMatrix();
+ CShaderDefines lineDefines;
+ CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid, g_Renderer.GetSystemShaderDefines(), lineDefines);
+ tech->BeginPass();
+ CShaderProgramPtr shader = tech->GetShader();
+ shader->Uniform(str_transform, transform);
+
+ CVector2D leftBottom, rightTop;
+ leftBottom = rightTop = m_Series[0].m_Points[0];
+ for (const CChartData& data : m_Series)
+ for (const CVector2D& point : data.m_Points)
+ {
+ if (point.X < leftBottom.X)
+ leftBottom.X = point.X;
+ if (point.Y < leftBottom.Y)
+ leftBottom.Y = point.Y;
+
+ if (point.X > rightTop.X)
+ rightTop.X = point.X;
+ if (point.Y > rightTop.Y)
+ rightTop.Y = point.Y;
+ }
+
+ CVector2D scale(width / (rightTop.X - leftBottom.X), height / (rightTop.Y - leftBottom.Y));
+
+ for (const CChartData& data : m_Series)
+ {
+ if (data.m_Points.empty())
+ continue;
+
+ std::vector vertices;
+ for (const CVector2D& point : data.m_Points)
+ {
+ vertices.push_back(rect.left + (point.X - leftBottom.X) * scale.X);
+ vertices.push_back(rect.bottom - (point.Y - leftBottom.Y) * scale.Y);
+ vertices.push_back(bz + 0.5f);
+ }
+ shader->Uniform(str_color, data.m_Color);
+ shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
+ shader->AssertPointersBound();
+
+ glEnable(GL_LINE_SMOOTH);
+ glLineWidth(1.1f);
+ if (!g_Renderer.m_SkipSubmit)
+ glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3);
+ glLineWidth(1.0f);
+ glDisable(GL_LINE_SMOOTH);
+ }
+
+ tech->EndPass();
+
+ // Reset depth mask
+ glDepthMask(1);
+}
+
+CRect CChart::GetChartRect() const
+{
+ return m_CachedActualSize;
+}
+
+void CChart::UpdateSeries()
+{
+ CGUISeries* pSeries;
+ GUI::GetSettingPointer(this, "series", pSeries);
+
+ CGUIList* pSeriesColor;
+ GUI::GetSettingPointer(this, "series_color", pSeriesColor);
+
+ m_Series.clear();
+ for (size_t i = 0; i < pSeries->m_Series.size(); ++i)
+ {
+ m_Series.resize(m_Series.size() + 1);
+ CChartData& data = m_Series.back();
+
+ if (i < pSeriesColor->m_Items.size() && !GUI::ParseColor(pSeriesColor->m_Items[i].GetOriginalString(), data.m_Color, 0))
+ LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(pSeriesColor->m_Items[i].GetOriginalString()));
+
+ data.m_Points = pSeries->m_Series[i];
+ }
+}
diff --git a/source/gui/CChart.h b/source/gui/CChart.h
new file mode 100644
index 0000000000..7b1574f503
--- /dev/null
+++ b/source/gui/CChart.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2016 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCHART
+#define INCLUDED_CCHART
+
+#include "GUI.h"
+#include "graphics/Color.h"
+#include "maths/Vector2D.h"
+#include
+
+
+struct CChartData
+{
+ CColor m_Color;
+ std::vector m_Points;
+};
+
+/**
+* Chart for a data visualization as lines or points
+*
+* @see IGUIObject
+*/
+class CChart : public IGUIObject
+{
+ GUI_OBJECT(CChart)
+
+public:
+ CChart();
+ virtual ~CChart();
+
+protected:
+ /**
+ * @see IGUIObject#HandleMessage()
+ */
+ virtual void HandleMessage(SGUIMessage& Message);
+
+ /**
+ * Draws the Chart
+ */
+ virtual void Draw();
+
+ virtual CRect GetChartRect() const;
+
+ void UpdateSeries();
+
+ std::vector m_Series;
+};
+
+#endif // INCLUDED_CCHART
diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp
index 09c33868f8..a78d7b1b35 100644
--- a/source/gui/CGUI.cpp
+++ b/source/gui/CGUI.cpp
@@ -24,6 +24,7 @@
// Types - when including them into the engine.
#include "CButton.h"
+#include "CChart.h"
#include "CCheckBox.h"
#include "CDropDown.h"
#include "CImage.h"
@@ -312,6 +313,7 @@ void CGUI::Initialize()
AddObjectType("olist", &COList::ConstructObject);
AddObjectType("dropdown", &CDropDown::ConstructObject);
AddObjectType("tooltip", &CTooltip::ConstructObject);
+ AddObjectType("chart", &CChart::ConstructObject);
}
void CGUI::Draw()
diff --git a/source/gui/CGUISeries.h b/source/gui/CGUISeries.h
new file mode 100644
index 0000000000..2a0f9e171d
--- /dev/null
+++ b/source/gui/CGUISeries.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2016 Wildfire Games.
+* This file is part of 0 A.D.
+*
+* 0 A.D. is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* 0 A.D. is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with 0 A.D. If not, see .
+*/
+
+
+#ifndef INCLUDED_CGUISERIES
+#define INCLUDED_CGUISERIES
+
+#include "GUItext.h"
+#include "maths/Vector2D.h"
+
+
+class CGUISeries
+{
+public:
+ std::vector> m_Series;
+};
+
+#endif
diff --git a/source/gui/GUI.h b/source/gui/GUI.h
index d2c5657507..801f85e3aa 100644
--- a/source/gui/GUI.h
+++ b/source/gui/GUI.h
@@ -40,6 +40,7 @@ GUI Inclusion file
#include "ps/CStr.h"
#include "CGUIList.h"
+#include "CGUISeries.h"
#include "GUIbase.h"
#include "GUItext.h"
#include "GUIutil.h"
diff --git a/source/gui/GUItypes.h b/source/gui/GUItypes.h
index 6f363ca2a9..0daab15795 100644
--- a/source/gui/GUItypes.h
+++ b/source/gui/GUItypes.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -40,3 +40,4 @@ TYPE(EAlign)
TYPE(EVAlign)
TYPE(CPos)
TYPE(CGUIList)
+TYPE(CGUISeries)
diff --git a/source/gui/GUIutil.cpp b/source/gui/GUIutil.cpp
index 2c0f63f5a4..11aea048f2 100644
--- a/source/gui/GUIutil.cpp
+++ b/source/gui/GUIutil.cpp
@@ -253,6 +253,11 @@ bool __ParseString(const CStrW& UNUSED(Value), CGUIList& UNUSED(Output
return false;
}
+template <>
+bool __ParseString(const CStrW& UNUSED(Value), CGUISeries& UNUSED(Output))
+{
+ return false;
+}
//--------------------------------------------------------
diff --git a/source/gui/scripting/JSInterface_IGUIObject.cpp b/source/gui/scripting/JSInterface_IGUIObject.cpp
index cfee79a069..471b00ce12 100644
--- a/source/gui/scripting/JSInterface_IGUIObject.cpp
+++ b/source/gui/scripting/JSInterface_IGUIObject.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -298,6 +298,35 @@ bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Handle
break;
}
+ case GUIST_CGUISeries:
+ {
+ CGUISeries value;
+ GUI::GetSetting(e, propName, value);
+
+ JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
+ vp.setObject(*obj);
+
+ for (u32 i = 0; i < value.m_Series.size(); ++i)
+ {
+ JS::RootedObject inner_obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
+ for (u32 j = 0; j < value.m_Series[i].size(); ++j)
+ {
+ JS::RootedObject val(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
+
+ JS::RootedValue val_x(cx), val_y(cx);
+ ScriptInterface::ToJSVal(cx, &val_x, value.m_Series[i][j].X);
+ ScriptInterface::ToJSVal(cx, &val_y, value.m_Series[i][j].Y);
+ JS_SetElement(cx, val, 0, val_x);
+ JS_SetElement(cx, val, 1, val_y);
+
+ JS_SetElement(cx, inner_obj, j, val);
+ }
+ JS_SetElement(cx, obj, i, inner_obj);
+ }
+
+ break;
+ }
+
default:
JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
DEBUG_WARN_ERR(ERR::LOGIC);
@@ -588,6 +617,69 @@ bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Handle
break;
}
+ case GUIST_CGUISeries:
+ {
+ u32 length;
+ if (!vp.isObject() || !JS_GetArrayLength(cx, vpObj, &length))
+ {
+ JS_ReportError(cx, "Table only accepts a GUISeries object");
+ return false;
+ }
+
+ CGUISeries series;
+ series.m_Series.resize(length);
+ for (u32 i = 0; i < length; ++i)
+ {
+ JS::RootedValue data_value(cx);
+ if (!JS_GetElement(cx, vpObj, i, &data_value))
+ {
+ JS_ReportError(cx, "Failed to get a data of series");
+ return false;
+ }
+
+ JS::RootedObject data(cx, data_value.toObjectOrNull());
+ u32 data_length;
+ if (!JS_GetArrayLength(cx, data, &data_length))
+ {
+ JS_ReportError(cx, "Series only accepts a chart data");
+ return false;
+ }
+
+ series.m_Series[i].resize(data_length);
+ for (u32 j = 0; j < data_length; ++j)
+ {
+ JS::RootedValue element_value(cx);
+ if (!JS_GetElement(cx, data, j, &element_value))
+ {
+ JS_ReportError(cx, "Failed to get a chart data element");
+ return false;
+ }
+
+ JS::RootedObject element(cx, element_value.toObjectOrNull());
+ u32 element_length;
+ if (!JS_GetArrayLength(cx, element, &element_length) || element_length < 2)
+ {
+ JS_ReportError(cx, "Chart data only accepts a point");
+ return false;
+ }
+
+ JS::RootedValue element_x(cx), element_y(cx);
+ if (!JS_GetElement(cx, element, 0, &element_x) || !JS_GetElement(cx, element, 1, &element_y))
+ {
+ JS_ReportError(cx, "Failed to get a chart point");
+ return false;
+ }
+
+ if (!ScriptInterface::FromJSVal(cx, element_x, series.m_Series[i][j].X) ||
+ !ScriptInterface::FromJSVal(cx, element_y, series.m_Series[i][j].Y))
+ return false;
+ }
+ }
+
+ GUI::SetSetting(e, propName, series);
+ break;
+ }
+
default:
JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
break;