Changeset 208


Ignore:
Timestamp:
17/08/09 20:42:41 (5 years ago)
Author:
guyru
Message:

Render legend in the SimplePiePlot? widget

Location:
trunk/src
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/simple_pie_plot.cpp

    r207 r208  
    2222#include <boost/foreach.hpp> 
    2323#include <memory> 
     24#include <algorithm> 
    2425#include <cmath> 
    2526using namespace std; 
     
    5960{ 
    6061        m_highlight = -1; 
     62 
     63        m_max_legend_width = 0; 
     64        m_max_legend_height = 0; 
     65        m_legend_width = 0; 
     66 
    6167        Connect(this->GetId(), wxEVT_PAINT, wxPaintEventHandler(SimplePiePlot::OnPaint)); 
    6268        Connect(this->GetId(), wxEVT_SIZE, wxSizeEventHandler(SimplePiePlot::OnResize)); 
     
    7177        auto_ptr<wxGraphicsContext> dc(wxGraphicsContext::Create(pdc)); 
    7278        wxBrush color_brush; 
    73         int width, height; 
    74  
    75         GetClientSize(&width, &height); 
    76  
    77         // radius should be set so it fits exactly 
    78         double radius = (width>height? height : width)/2.0; 
    79  
     79 
     80        if (!m_max_legend_width || !m_max_legend_height) { 
     81                CalculateLegendDimensions(dc.get()); 
     82                // This might have caused a new minimum size to occure, tell 
     83                // parent about it by refitting. See also comment in 
     84                // GetMinSize() 
     85                if (this->GetParent() && this->GetParent()->GetSizer()) 
     86                        this->GetParent()->GetSizer()->SetSizeHints(this->GetParent()); 
     87        } 
     88        CalculatePiePlotLocation(); 
     89         
     90 
     91        dc->SetPen(*wxTRANSPARENT_PEN); 
    8092        for (int i = 1; i<m_angles.size(); i++) { 
    8193                // create brush 
     
    8395                dc->SetBrush(color_brush); 
    8496 
    85                 DrawPieSlice(width/2.0, height/2.0, radius, m_angles[i-1], 
     97                DrawPieSlice(m_pie_x, m_pie_y, m_radius, m_angles[i-1], 
    8698                             m_angles[i], dc.get()); 
    8799        } 
     100 
     101        DrawLegend(dc.get()); 
     102} 
     103 
     104void SimplePiePlot::CalculatePiePlotLocation() 
     105{ 
     106        double pie_width; 
     107        int width, height; 
     108 
     109        assert(m_legend_width); 
     110 
     111        GetClientSize(&width, &height); 
     112        // m_max_legend_height* for the color box and it's right padding 
     113        pie_width = width - m_legend_width - m_legend_left_padding; 
     114        m_pie_x = pie_width/2.0; 
     115        m_pie_y = height/2.0; 
     116        m_radius = min(pie_width,(double)height)/2.0; 
    88117} 
    89118 
     
    94123} 
    95124 
    96 void SimplePiePlot::SetData(vector<double> d) 
    97 { 
     125void SimplePiePlot::SetData(vector<double> d, vector<wxString> labels) 
     126{ 
     127        // number of values should match number of labels 
     128        assert(labels.size() == d.size()); 
     129 
    98130        m_data.clear(); 
    99131        m_data_total = 0; 
    100132        BOOST_FOREACH(double tmp, d) { 
     133                // we can't plot negative values 
     134                assert(tmp >= 0); 
    101135                m_data_total += tmp; 
    102                 if (tmp!=0) 
    103                         m_data.push_back(tmp); 
    104         } 
     136                m_data.push_back(tmp); 
     137        } 
     138        m_labels.assign(labels.begin(), labels.end()); 
     139        CalculateAngles(); 
     140 
     141        // this will cause the legend size to be re-calculated 
     142        m_max_legend_width = 0; 
     143        m_max_legend_height = 0; 
     144} 
     145 
     146void SimplePiePlot::CalculateAngles() 
     147{ 
     148        double new_angle = 0; 
    105149 
    106150        m_angles.clear(); 
    107         double new_angle = 0; 
    108151        m_angles.push_back(new_angle); 
    109152        BOOST_FOREACH(double tmp, m_data) { 
     
    113156} 
    114157 
     158void SimplePiePlot::CalculateLegendDimensions(wxGraphicsContext *dc) 
     159{ 
     160        double tmp_height; 
     161        double tmp_width; 
     162        m_max_legend_width = 0; 
     163        m_max_legend_height = 0; 
     164 
     165        dc->SetFont(*wxNORMAL_FONT, *wxBLACK); 
     166         
     167        BOOST_FOREACH(wxString label, m_labels) { 
     168                dc->GetTextExtent(label, &tmp_width, &tmp_height, NULL, NULL); 
     169                m_max_legend_width = max(m_max_legend_width, tmp_width); 
     170                m_max_legend_height = max(m_max_legend_height, tmp_height); 
     171        } 
     172 
     173        m_legend_width = m_max_legend_width + 2*m_max_legend_height; 
     174        m_legend_line_height = 1.5 * m_max_legend_height; 
     175} 
     176 
     177void SimplePiePlot::DrawLegend(wxGraphicsContext *dc) 
     178{ 
     179        wxBrush color_brush; 
     180        int width, height; 
     181        GetClientSize(&width, &height); 
     182 
     183        assert(m_legend_width); 
     184        double legend_x = width - m_legend_width; 
     185 
     186        dc->SetPen(*wxTRANSPARENT_PEN); 
     187        dc->SetFont(*wxNORMAL_FONT, *wxBLACK); 
     188 
     189        for (int i = 0; i<m_labels.size(); i++) { 
     190                color_brush.SetColour(GetSegmentColor(i)); 
     191                dc->SetBrush(color_brush); 
     192 
     193                dc->DrawRectangle(legend_x, i * m_legend_line_height, 
     194                        m_max_legend_height, m_max_legend_height); 
     195                dc->DrawText(m_labels[i], legend_x + 2*m_max_legend_height, 
     196                        i * m_legend_line_height); 
     197        } 
     198 
     199} 
     200 
    115201wxColour SimplePiePlot::GetSegmentColor(int i) 
    116202{ 
    117203        double hue_step = 1.0/(m_data.size()); 
    118204        double hue = i * hue_step; 
    119         double value = m_highlight == i ? 1.0 : 0.8; 
     205        double value = m_highlight == i ? 1.0 : 0.9; 
    120206        wxColour color; 
    121207        wxImage::RGBValue rgb; 
     
    132218        int width, height; 
    133219        GetClientSize(&width, &height); 
    134         // radius should be set so it fits exactly 
    135         const double radius = (width>height? height : width)/2.0; 
    136  
    137         const double dist_mouse = (width/2.0-event.m_x)*(width/2.0-event.m_x) +  
    138                             (height/2.0-event.m_y)*(height/2.0-event.m_y); 
    139         if (dist_mouse > (radius*radius)) {  
    140                 ClearHighlight(); 
     220 
     221        const double dist_mouse = (m_pie_x-event.m_x)*(m_pie_x-event.m_x) +  
     222                            (m_pie_y-event.m_y)*(m_pie_y-event.m_y); 
     223        if (dist_mouse <= (m_radius*m_radius)) {  
     224                //angles are clockwise from the x-axis 
     225                double angle = acos((event.m_x-m_pie_x)/sqrt(dist_mouse)); 
     226                if (event.m_y < m_pie_y) 
     227                        angle = 2*PI - angle; 
     228                int i; 
     229                for (i = 1; i<m_angles.size(); i++) { 
     230                        if (angle<=m_angles[i]) 
     231                                break; 
     232                } 
     233                Highlight(i-1); 
    141234                return; 
    142         } 
    143  
    144         //angles are clockwise from the x-axis 
    145         double angle = acos((event.m_x-width/2.0)/sqrt(dist_mouse)); 
    146         if (event.m_y < height/2.0) 
    147                 angle = 2*PI - angle; 
    148         int i; 
    149         for (i = 1; i<m_angles.size(); i++) { 
    150                 if (angle<=m_angles[i]) 
    151                         break; 
    152         } 
    153         Highlight(i-1); 
     235        } else if (event.m_x >= width-m_legend_width){ 
     236                int i = (int) (event.m_y / m_legend_line_height); 
     237                if (i<m_labels.size()){ 
     238                        Highlight(i); 
     239                        return; 
     240                } 
     241        } 
     242 
     243        ClearHighlight(); 
    154244} 
    155245 
     
    172262        } 
    173263} 
     264 
     265wxSize SimplePiePlot::GetMinSize() const 
     266{ 
     267        // In the first call to GetMinSize, m_legend_line_height and 
     268        // m_legend_width may not be populated yet (set to zero), this 
     269        // is a result of not being able to call CalculateLegendDimensions() 
     270        // at this point due to constness. In order to workaround this issue, 
     271        // if CalculateLegendDimensions() gets called in OnPaint() (this 
     272        // happens also after setting new data), OnPaint() also notifies the 
     273        // parent's sizer (is such exist) to update the size hints 
     274        // accordingly. 
     275        const double total_legend_height = m_labels.size() * m_legend_line_height; 
     276        const double total_width = total_legend_height + m_legend_width + 
     277                                   m_legend_left_padding; 
     278        return wxSize((int)ceil(total_width),(int)ceil(total_legend_height)); 
     279} 
     280 
  • trunk/src/simple_pie_plot.h

    r207 r208  
    3232                 const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
    3333                 long style = wxNO_BORDER, const wxString& name = wxPanelNameStr); 
    34         void SetData(std::vector<double> d); 
     34        void SetData(std::vector<double> d, std::vector<wxString> labels); 
    3535        void OnPaint(wxPaintEvent& event); 
    3636        void OnResize(wxSizeEvent& event); 
    3737        void OnMouseLeaveWindow(wxMouseEvent& event); 
    3838        void OnMouseMove(wxMouseEvent& event); 
     39        wxSize GetMinSize() const; 
    3940private: 
    4041        /** 
     
    4445        wxColour GetSegmentColor(int i); 
    4546 
     47        /** 
     48         * Calculate the angles in the plot and populate the m_angles array. 
     49         */ 
     50        void CalculateAngles(); 
     51 
     52        /** 
     53         * Calculate the maximum widht and height for a label in the legend. 
     54         */ 
     55        void CalculateLegendDimensions(wxGraphicsContext *dc); 
     56 
     57        /** 
     58         * Calculate the location and radius of the pie plot. 
     59         */ 
     60        void CalculatePiePlotLocation(); 
     61 
     62        void DrawLegend(wxGraphicsContext *dc); 
     63 
    4664        void Highlight(int i); 
    4765        void ClearHighlight(); 
    4866 
    4967        std::vector<double> m_data; 
     68        std::vector<wxString> m_labels; 
    5069        std::vector<double> m_angles; 
    5170        double m_data_total; 
    5271        int m_highlight; 
     72 
     73        /// maximum width for a label in the legend. 
     74        double m_max_legend_width; 
     75        /// maximum height for a label in the legend. 
     76        double m_max_legend_height; 
     77        /// total width of the legend 
     78        double m_legend_width; 
     79        /// the line height of each legend entry including padding 
     80        double m_legend_line_height; 
     81 
     82        static const double m_legend_left_padding = 10; 
     83 
     84        // The center point of the pie plot 
     85        double m_pie_x; 
     86        double m_pie_y; 
     87        double m_radius; 
     88 
    5389}; 
    5490 
  • trunk/src/statistics_dialog.cpp

    r206 r208  
    4646        games_started = new wxStaticText(this, wxID_ANY, wxT("")); 
    4747        games_finished = new wxStaticText(this, wxID_ANY, wxT("")); 
    48         histogram = new simple_pie_plot::SimplePiePlot(this, wxID_ANY); 
     48        pie_plot = new simple_pie_plot::SimplePiePlot(this, wxID_ANY); 
    4949        statistics_reset_date = new wxStaticText(this, wxID_ANY, wxT("")); 
    5050} 
     
    6464 
    6565        vector<int> score_dist = m_stats->score_distribution(); 
    66         vector<double> dist (score_dist.begin(), score_dist.end()); 
    67         histogram->SetData(dist); 
     66        vector<double> dist; 
     67        vector<wxString> labels; 
     68        for (int i; i<score_dist.size(); i++) { 
     69                if (score_dist[i]) { 
     70                        assert(score_dist[i]>0); 
     71                        dist.push_back(score_dist[i]); 
     72                        tmp = wxString::Format(wxT("%i - %i"), 
     73                                i*statistics::score_distribution_granuality, 
     74                                (i+1)*statistics::score_distribution_granuality); 
     75                        labels.push_back(tmp); 
     76                } 
     77        } 
     78        pie_plot->SetData(dist, labels); 
    6879} 
    6980 
     
    7990 
    8091        wxStaticBoxSizer* score_distribution = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxT("Score Distribution") ), wxHORIZONTAL); 
    81         score_distribution->Add(histogram, 1, wxEXPAND, 10); 
     92        score_distribution->Add(pie_plot, 1, wxEXPAND, 10); 
    8293        top_sizer->Add(score_distribution,10,wxEXPAND, 10); 
    8394 
  • trunk/src/statistics_dialog.h

    r206 r208  
    4747        wxStaticText *games_finished; 
    4848        wxStaticText *statistics_reset_date; 
    49         simple_pie_plot::SimplePiePlot* histogram; 
     49        simple_pie_plot::SimplePiePlot* pie_plot; 
    5050 
    5151        statistics::Statistics* m_stats; 
Note: See TracChangeset for help on using the changeset viewer.