Ticket #3403: 3403_charts.5.patch

File 3403_charts.5.patch, 18.3 KB (added by Vladislav Belov, 7 years ago)

Adds CChart, CChartData, CGUISeries

  • binaries/data/mods/public/gui/summary/summary.js

     
    3535var g_GameData;
    3636var g_ResourceData = new Resources();
    3737
     38var g_Charts = ["population", "explored"];
     39
    3840function selectPanel(panel)
    3941{
    4042    // TODO: move panel buttons to a custom parent object
     
    4749
    4850    adjustTabDividers(panel.size);
    4951
    50     updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]);
     52    let generalPanel = Engine.GetGUIObjectByName("generalPanel");
     53    let chartsPanel = Engine.GetGUIObjectByName("chartsPanel");
     54    if (panel.name != "chartsPanelButton")
     55    {
     56        generalPanel.hidden = false;
     57        chartsPanel.hidden = true;
     58        updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]);
     59    }
     60    else
     61    {
     62        generalPanel.hidden = true;
     63        chartsPanel.hidden = false;
     64        updateCharts();
     65    }
    5166}
    5267
     68function initCharts()
     69{
     70    let player_colors = [];
     71    for (let i = 0; i < g_PlayerCount; ++i)
     72    {
     73        let playerState = g_GameData.sim.playerStates[i+1];
     74        player_colors.push(
     75            Math.floor(playerState.color.r * 255) + " " +
     76            Math.floor(playerState.color.g * 255) + " " +
     77            Math.floor(playerState.color.b * 255)
     78        );
     79    }
     80    for (let i = 0; i < g_Charts.length; ++i)
     81    {
     82        let chart = Engine.GetGUIObjectByName(g_Charts[i] + "Chart");
     83        chart.series_color = player_colors;
     84    }
     85}
     86
     87function updateCharts()
     88{
     89    for (let i = 0; i < g_Charts.length; ++i)
     90    {
     91        let chart = Engine.GetGUIObjectByName(g_Charts[i] + "Chart");
     92        let series = [];
     93        for (let j = 0; j < g_PlayerCount; ++j)
     94        {
     95            let playerState = g_GameData.sim.playerStates[j+1];
     96            series.push(playerState.statistics.sequences[g_Charts[i]]);
     97        }
     98        chart.series = series;
     99    }
     100}
     101
    53102function adjustTabDividers(tabSize)
    54103{
    55104    let leftSpacer = Engine.GetGUIObjectByName("tabDividerLeft");
     
    266315            g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0;
    267316    }
    268317
     318    initCharts();
     319
    269320    selectPanel(Engine.GetGUIObjectByName("scorePanelButton"));
    270321}
  • binaries/data/mods/public/gui/summary/summary.xml

     
    9898            </object>
    9999        </object>
    100100
     101        <object name="chartsPanelButton" type="button" sprite="BackgroundTab" style="TabButton" size="762 92 880 120">
     102            <action on="Press">selectPanel(this);</action>
     103            <object type="text" style="ModernLabelText" ghost="true">
     104                <translatableAttribute id="caption">Charts</translatableAttribute>
     105            </object>
     106        </object>
     107
    101108        <object name="generalPanel" type="image" sprite="ForegroundBody" size="20 120 100%-20 100%-54">
    102109            <object size="0 0 100% 100%-50">
    103110                <object name="playerNameHeading" type="text" style="ModernLeftTabLabelText">
     
    151158                </repeat>
    152159            </object>
    153160        </object>
     161       
     162        <object name="chartsPanel" type="image" sprite="ForegroundBody" size="20 120 100%-20 100%-54">
     163            <object
     164                name="populationChartCaption"
     165                type="text"
     166                style="ModernTabLabelText"
     167                size="20 20 50%-20 40"
     168            >
     169                <translatableAttribute id="caption">Population</translatableAttribute>
     170            </object>
     171            <object
     172                name="populationChart"
     173                type="chart"
     174                ghost="true"
     175                size="20 60 50%-20 50%-20"
     176            />
     177            <object
     178                name="exploredChartCaption"
     179                type="text"
     180                style="ModernTabLabelText"
     181                size="50%+20 20 100%-20 40"
     182            >
     183                <translatableAttribute id="caption">Explored</translatableAttribute>
     184            </object>
     185            <object
     186                name="exploredChart"
     187                type="chart"
     188                ghost="true"
     189                size="50%+20 60 100%-20 50%-20"
     190            />
     191        </object>
    154192
    155193        <object type="button" name="replayButton" style="ModernButtonRed" size="100%-310 100%-48 100%-170 100%-20">
    156194            <translatableAttribute id="caption">Replay</translatableAttribute>
  • binaries/data/mods/public/simulation/components/StatisticsTracker.js

     
    11function StatisticsTracker() {}
    22
     3const UPDATE_SEQUENCE_INTERVAL = 30000; // 30 seconds
     4
    35StatisticsTracker.prototype.Schema =
    46    "<a:component type='system'/><empty/>";
    57
     
    142144    this.lootCollected = 0;
    143145    this.peakPercentMapControlled = 0;
    144146    this.teamPeakPercentMapControlled = 0;
     147
     148    this.sequences = {
     149        "population": [],
     150        "explored": []
     151    };
     152
     153    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     154    if (!cmpTimer)
     155        return;
     156    this.updateTimer = cmpTimer.SetInterval(
     157        this.entity, IID_StatisticsTracker, "updateSequences", 0, UPDATE_SEQUENCE_INTERVAL, {
     158            "startTime": cmpTimer.GetTime()
     159        }
     160    );
    145161};
    146162
    147163/**
     
    191207        "percentMapControlled": this.GetPercentMapControlled(),
    192208        "teamPercentMapControlled": this.GetTeamPercentMapControlled(),
    193209        "peakPercentMapControlled": this.peakPercentMapControlled,
    194         "teamPeakPercentMapControlled": this.teamPeakPercentMapControlled
     210        "teamPeakPercentMapControlled": this.teamPeakPercentMapControlled,
     211        "sequences": this.sequences
    195212    };
    196213};
    197214
     
    492509        this.teamPeakPercentMapControlled = newPercent;
    493510};
    494511
     512StatisticsTracker.prototype.updateSequences = function(data)
     513{
     514    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     515    if (!cmpTimer)
     516        return;
     517    let deltaTime = (cmpTimer.GetTime() - data["startTime"]) / 1000;
     518
     519    let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     520    if (!cmpPlayer)
     521        return;
     522    this.sequences.population.push([deltaTime, cmpPlayer.GetPopulationCount()]);
     523
     524    let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     525    if (!cmpRangeManager)
     526        return;
     527    this.sequences.explored.push([
     528        deltaTime,
     529        cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID())
     530    ]);
     531}
     532
    495533Engine.RegisterComponentType(IID_StatisticsTracker, "StatisticsTracker", StatisticsTracker);
  • source/gui/CChart.cpp

     
     1/* Copyright (C) 2016 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#include "CChart.h"
     20
     21#include "graphics/ShaderManager.h"
     22#include "lib/ogl.h"
     23#include "ps/CLogger.h"
     24#include "renderer/Renderer.h"
     25
     26#include <cmath>
     27
     28CChart::CChart()
     29{
     30    AddSetting(GUIST_CGUIList, "series_color");
     31    AddSetting(GUIST_CGUISeries, "series");
     32}
     33
     34CChart::~CChart()
     35{
     36}
     37
     38void CChart::HandleMessage(SGUIMessage& Message)
     39{
     40    // TODO: implement zoom
     41}
     42
     43void CChart::Draw()
     44{
     45    PROFILE3("render chart");
     46
     47    if (!GetGUI())
     48        return;
     49
     50    UpdateSeries();
     51
     52    const float bz = GetBufferedZ();
     53    CRect rect = GetChartRect();
     54    const float width = rect.GetWidth();
     55    const float height = rect.GetHeight();
     56
     57    if (m_Series.empty())
     58        return;
     59
     60    // Disable depth updates to prevent apparent z-fighting-related issues
     61    //  with some drivers causing units to get drawn behind the texture.
     62    glDepthMask(0);
     63
     64    // Setup the render state
     65    CMatrix3D transform = GetDefaultGuiMatrix();
     66    CShaderDefines lineDefines;
     67    CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid, g_Renderer.GetSystemShaderDefines(), lineDefines);
     68    tech->BeginPass();
     69    CShaderProgramPtr shader = tech->GetShader();
     70    shader->Uniform(str_transform, transform);
     71
     72    CVector2D leftBottom, rightTop;
     73    leftBottom = rightTop = m_Series[0].m_Points[0];
     74    for (const CChartData& data : m_Series)
     75        for (const CVector2D& point : data.m_Points)
     76        {
     77            if (point.X < leftBottom.X)
     78                leftBottom.X = point.X;
     79            if (point.Y < leftBottom.Y)
     80                leftBottom.Y = point.Y;
     81
     82            if (point.X > rightTop.X)
     83                rightTop.X = point.X;
     84            if (point.Y > rightTop.Y)
     85                rightTop.Y = point.Y;
     86        }
     87
     88    CVector2D scale(width / (rightTop.X - leftBottom.X), height / (rightTop.Y - leftBottom.Y));
     89
     90    for (const CChartData& data : m_Series)
     91    {
     92        if (data.m_Points.empty())
     93            continue;
     94
     95        std::vector<float> vertices;
     96        for (const CVector2D& point : data.m_Points)
     97        {
     98            vertices.push_back(rect.left + (point.X - leftBottom.X) * scale.X);
     99            vertices.push_back(rect.bottom - (point.Y - leftBottom.Y) * scale.Y);
     100            vertices.push_back(bz + 0.5f);
     101        }
     102        shader->Uniform(str_color, data.m_Color);
     103        shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
     104        shader->AssertPointersBound();
     105
     106        glEnable(GL_LINE_SMOOTH);
     107        glLineWidth(1.1f);
     108        if (!g_Renderer.m_SkipSubmit)
     109            glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3);
     110        glLineWidth(1.0f);
     111        glDisable(GL_LINE_SMOOTH);
     112    }
     113
     114    tech->EndPass();
     115
     116    // Reset depth mask
     117    glDepthMask(1);
     118}
     119
     120CRect CChart::GetChartRect() const
     121{
     122    return m_CachedActualSize;
     123}
     124
     125void CChart::UpdateSeries()
     126{
     127    CGUISeries* pSeries;
     128    GUI<CGUISeries>::GetSettingPointer(this, "series", pSeries);
     129
     130    CGUIList* pSeriesColor;
     131    GUI<CGUIList>::GetSettingPointer(this, "series_color", pSeriesColor);
     132
     133    m_Series.clear();
     134    for (size_t i = 0; i < pSeries->m_Series.size(); ++i)
     135    {
     136        m_Series.resize(m_Series.size() + 1);
     137        CChartData& data = m_Series.back();
     138
     139        if (i < pSeriesColor->m_Items.size() && !GUI<int>::ParseColor(pSeriesColor->m_Items[i].GetOriginalString(), data.m_Color, 0))
     140            LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(pSeriesColor->m_Items[i].GetOriginalString()));
     141
     142        data.m_Points = pSeries->m_Series[i];
     143    }
     144}
  • source/gui/CChart.h

     
     1/* Copyright (C) 2016 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#ifndef INCLUDED_CCHART
     19#define INCLUDED_CCHART
     20
     21#include "GUI.h"
     22#include "graphics/Color.h"
     23#include "maths/Vector2D.h"
     24#include <vector>
     25
     26
     27struct CChartData
     28{
     29    CColor m_Color;
     30    std::vector<CVector2D> m_Points;
     31};
     32
     33/**
     34* Chart for a data visualization as lines or points
     35*
     36* @see IGUIObject
     37*/
     38class CChart : public IGUIObject
     39{
     40    GUI_OBJECT(CChart)
     41
     42public:
     43    CChart();
     44    virtual ~CChart();
     45
     46protected:
     47    /**
     48    * @see IGUIObject#HandleMessage()
     49    */
     50    virtual void HandleMessage(SGUIMessage& Message);
     51
     52    /**
     53    * Draws the Chart
     54    */
     55    virtual void Draw();
     56
     57    virtual CRect GetChartRect() const;
     58
     59    void UpdateSeries();
     60
     61    std::vector<CChartData> m_Series;
     62};
     63
     64#endif // INCLUDED_CCHART
  • source/gui/CGUI.cpp

     
    2424
    2525// Types - when including them into the engine.
    2626#include "CButton.h"
     27#include "CChart.h"
    2728#include "CCheckBox.h"
    2829#include "CDropDown.h"
    2930#include "CImage.h"
     
    312313    AddObjectType("olist",          &COList::ConstructObject);
    313314    AddObjectType("dropdown",       &CDropDown::ConstructObject);
    314315    AddObjectType("tooltip",        &CTooltip::ConstructObject);
     316    AddObjectType("chart",          &CChart::ConstructObject);
    315317}
    316318
    317319void CGUI::Draw()
  • source/gui/CGUISeries.h

     
     1/* Copyright (C) 2016 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
     19#ifndef INCLUDED_CGUISERIES
     20#define INCLUDED_CGUISERIES
     21
     22#include "GUItext.h"
     23#include "maths/Vector2D.h"
     24
     25
     26class CGUISeries
     27{
     28public:
     29    std::vector<std::vector<CVector2D>> m_Series;
     30};
     31
     32#endif
  • source/gui/GUI.h

     
    4040#include "ps/CStr.h"
    4141
    4242#include "CGUIList.h"
     43#include "CGUISeries.h"
    4344#include "GUIbase.h"
    4445#include "GUItext.h"
    4546#include "GUIutil.h"
  • source/gui/GUItypes.h

     
    1 /* Copyright (C) 2009 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    4040TYPE(EVAlign)
    4141TYPE(CPos)
    4242TYPE(CGUIList)
     43TYPE(CGUISeries)
  • source/gui/GUIutil.cpp

     
    253253    return false;
    254254}
    255255
     256template <>
     257bool __ParseString<CGUISeries>(const CStrW& UNUSED(Value), CGUISeries& UNUSED(Output))
     258{
     259    return false;
     260}
    256261
    257262//--------------------------------------------------------
    258263
  • source/gui/scripting/JSInterface_IGUIObject.cpp

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    298298            break;
    299299        }
    300300
     301        case GUIST_CGUISeries:
     302        {
     303            CGUISeries value;
     304            GUI<CGUISeries>::GetSetting(e, propName, value);
     305
     306            JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
     307            vp.setObject(*obj);
     308
     309            for (u32 i = 0; i < value.m_Series.size(); ++i)
     310            {
     311                JS::RootedObject inner_obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
     312                for (u32 j = 0; j < value.m_Series[i].size(); ++j)
     313                {
     314                    JS::RootedObject val(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
     315
     316                    JS::RootedValue val_x(cx), val_y(cx);
     317                    ScriptInterface::ToJSVal(cx, &val_x, value.m_Series[i][j].X);
     318                    ScriptInterface::ToJSVal(cx, &val_y, value.m_Series[i][j].Y);
     319                    JS_SetElement(cx, val, 0, val_x);
     320                    JS_SetElement(cx, val, 1, val_y);
     321
     322                    JS_SetElement(cx, inner_obj, j, val);
     323                }
     324                JS_SetElement(cx, obj, i, inner_obj);
     325            }
     326
     327            break;
     328        }
     329
    301330        default:
    302331            JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
    303332            DEBUG_WARN_ERR(ERR::LOGIC);
     
    588617        break;
    589618    }
    590619
     620    case GUIST_CGUISeries:
     621    {
     622        u32 length;
     623        if (!vp.isObject() || !JS_GetArrayLength(cx, vpObj, &length))
     624        {
     625            JS_ReportError(cx, "Table only accepts a GUISeries object");
     626            return false;
     627        }
     628
     629        CGUISeries series;
     630        series.m_Series.resize(length);
     631        for (u32 i = 0; i < length; ++i)
     632        {
     633            JS::RootedValue data_value(cx);
     634            if (!JS_GetElement(cx, vpObj, i, &data_value))
     635            {
     636                JS_ReportError(cx, "Failed to get a data of series");
     637                return false;
     638            }
     639
     640            JS::RootedObject data(cx, data_value.toObjectOrNull());
     641            u32 data_length;
     642            if (!JS_GetArrayLength(cx, data, &data_length))
     643            {
     644                JS_ReportError(cx, "Series only accepts a chart data");
     645                return false;
     646            }
     647
     648            series.m_Series[i].resize(data_length);
     649            for (u32 j = 0; j < data_length; ++j)
     650            {
     651                JS::RootedValue element_value(cx);
     652                if (!JS_GetElement(cx, data, j, &element_value))
     653                {
     654                    JS_ReportError(cx, "Failed to get a chart data element");
     655                    return false;
     656                }
     657
     658                JS::RootedObject element(cx, element_value.toObjectOrNull());
     659                u32 element_length;
     660                if (!JS_GetArrayLength(cx, element, &element_length) || element_length < 2)
     661                {
     662                    JS_ReportError(cx, "Chart data only accepts a point");
     663                    return false;
     664                }
     665
     666                JS::RootedValue element_x(cx), element_y(cx);
     667                if (!JS_GetElement(cx, element, 0, &element_x) || !JS_GetElement(cx, element, 1, &element_y))
     668                {
     669                    JS_ReportError(cx, "Failed to get a chart point");
     670                    return false;
     671                }
     672
     673                if (!ScriptInterface::FromJSVal(cx, element_x, series.m_Series[i][j].X) ||
     674                    !ScriptInterface::FromJSVal(cx, element_y, series.m_Series[i][j].Y))
     675                    return false;
     676            }
     677        }
     678
     679        GUI<CGUISeries>::SetSetting(e, propName, series);
     680        break;
     681    }
     682
    591683    default:
    592684        JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
    593685        break;