← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~borim/widelands/economyChart into lp:widelands

 

Borim has proposed merging lp:~borim/widelands/economyChart into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~borim/widelands/economyChart/+merge/81408

* collect ware consumption data for new charts

* add two new charts to the ware statistics menu
  1. Consumption Chart: display the consumed wares
  2. Economy Health Chart: display the difference between production and consumption

the work for this feature is not completed, some fine tuning is still needed.
e.g.: replace the tab icon place holder
-- 
https://code.launchpad.net/~borim/widelands/economyChart/+merge/81408
Your team Widelands Developers is requested to review the proposed merge of lp:~borim/widelands/economyChart into lp:widelands.
=== modified file 'src/game_io/game_player_info_data_packet.cc'
--- src/game_io/game_player_info_data_packet.cc	2011-09-24 20:41:50 +0000
+++ src/game_io/game_player_info_data_packet.cc	2011-11-06 17:03:27 +0000
@@ -30,7 +30,7 @@
 
 namespace Widelands {
 
-#define CURRENT_PACKET_VERSION 13
+#define CURRENT_PACKET_VERSION 14
 
 
 void Game_Player_Info_Data_Packet::Read
@@ -111,7 +111,9 @@
 					if (packet_version >= 6)
 						player.setAI(fr.CString());
 
-					if (packet_version >= 12)
+					if (packet_version >= 14)
+						player.ReadStatistics(fr, 2);
+					else if (packet_version >= 12)
 						player.ReadStatistics(fr, 1);
 					else
 						player.ReadStatistics(fr, 0);

=== modified file 'src/logic/constructionsite.cc'
--- src/logic/constructionsite.cc	2011-11-05 21:17:47 +0000
+++ src/logic/constructionsite.cc	2011-11-06 17:03:27 +0000
@@ -284,6 +284,9 @@
 			wq.set_filled(wq.get_filled() - 1);
 			wq.set_max_size(wq.get_max_size() - 1);
 
+			//update consumption statistic
+			owner().ware_consumed(wq.get_ware(), 1);
+
 			m_working = true;
 			m_work_steptime = game.get_gametime() + CONSTRUCTIONSITE_STEP_TIME;
 

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2011-11-05 18:19:37 +0000
+++ src/logic/player.cc	2011-11-06 17:03:27 +0000
@@ -91,8 +91,10 @@
 	m_allowed_worker_types  (tribe_descr.get_nrworkers  (), false),
 	m_allowed_building_types(tribe_descr.get_nrbuildings(), true),
 	m_ai(""),
-	m_current_statistics(tribe_descr.get_nrwares    ()),
-	m_ware_productions  (tribe_descr.get_nrwares    ())
+	m_current_produced_statistics(tribe_descr.get_nrwares    ()),
+	m_current_consumed_statistics(tribe_descr.get_nrwares    ()),
+	m_ware_productions  (tribe_descr.get_nrwares    ()),
+	m_ware_consumptions  (tribe_descr.get_nrwares    ())
 {
 	set_name(name);
 }
@@ -1013,10 +1015,14 @@
 void Player::sample_statistics()
 {
 	assert (m_ware_productions.size() == tribe().get_nrwares().value());
+	assert (m_ware_consumptions.size() == tribe().get_nrwares().value());
 
 	for (uint32_t i = 0; i < m_ware_productions.size(); ++i) {
-		m_ware_productions[i].push_back(m_current_statistics[i]);
-		m_current_statistics[i] = 0;
+		m_ware_productions[i].push_back(m_current_produced_statistics[i]);
+		m_current_produced_statistics[i] = 0;
+
+		m_ware_consumptions[i].push_back(m_current_consumed_statistics[i]);
+		m_current_consumed_statistics[i] = 0;
 	}
 }
 
@@ -1028,7 +1034,22 @@
 	assert (m_ware_productions.size() == tribe().get_nrwares().value());
 	assert(wareid.value() < tribe().get_nrwares().value());
 
-	++m_current_statistics[wareid];
+	++m_current_produced_statistics[wareid];
+}
+
+
+/**
+ * Some units from one kind of ware were consumed.
+ * Update the corresponding statistics
+ *
+ * \param wareid the ID of the consumed wares
+ * \param count the number of consumed wares
+ */
+void Player::ware_consumed(Ware_Index const wareid, uint8_t const count) {
+	assert (m_ware_consumptions.size() == tribe().get_nrwares().value());
+	assert(wareid.value() < tribe().get_nrwares().value());
+
+	m_current_consumed_statistics[wareid] += count;
 }
 
 
@@ -1045,6 +1066,18 @@
 
 
 /**
+ * Get current ware consumption statistics
+ */
+const std::vector<uint32_t> * Player::get_ware_consumption_statistics
+		(Ware_Index const ware) const {
+
+	assert(ware.value() < m_ware_consumptions.size());
+
+	return &m_ware_consumptions[ware];
+}
+
+
+/**
  * Add or remove the given building from building statistics.
  * Only to be called by \ref receive
  */
@@ -1118,14 +1151,17 @@
  * \param version indicates the kind of statistics file, which may be
  *   0 - old style statistics (before WiHack 2010)
  *   1 - statistics with ware names
+ *   2 - with consumption statistics
  */
 void Player::ReadStatistics(FileRead & fr, uint32_t const version)
 {
-	if (version == 1) {
+	 //version 1 and 2 only differs in an additional statistic.
+	 //Use version 1 code for both
+	if ((version == 2) || (version == 1)) {
 		uint16_t nr_wares = fr.Unsigned16();
 		uint16_t nr_entries = fr.Unsigned16();
 
-		for (uint32_t i = 0; i < m_current_statistics.size(); ++i)
+		for (uint32_t i = 0; i < m_current_produced_statistics.size(); ++i)
 			m_ware_productions[i].resize(nr_entries);
 
 		for (uint16_t i = 0; i < nr_wares; ++i) {
@@ -1138,11 +1174,36 @@
 				continue;
 			}
 
-			m_current_statistics[idx] = fr.Unsigned32();
+			m_current_produced_statistics[idx] = fr.Unsigned32();
 
 			for (uint32_t j = 0; j < nr_entries; ++j)
 				m_ware_productions[idx][j] = fr.Unsigned32();
 		}
+
+		//read consumption statistics if it exists
+		if (version == 2) {
+			nr_wares = fr.Unsigned16();
+			nr_entries = fr.Unsigned16();
+
+			for (uint32_t i = 0; i < m_current_consumed_statistics.size(); ++i)
+				m_ware_consumptions[i].resize(nr_entries);
+
+			for (uint16_t i = 0; i < nr_wares; ++i) {
+				std::string name = fr.CString();
+				Ware_Index idx = tribe().ware_index(name);
+				if (!idx) {
+					log
+						("Player %u statistics: unknown ware name %s",
+						player_number(), name.c_str());
+					continue;
+				}
+
+				m_current_consumed_statistics[idx] = fr.Unsigned32();
+
+				for (uint32_t j = 0; j < nr_entries; ++j)
+					m_ware_consumptions[idx][j] = fr.Unsigned32();
+			}
+		}
 	} else if (version == 0) {
 		uint16_t nr_wares = fr.Unsigned16();
 		uint16_t nr_entries = fr.Unsigned16();
@@ -1150,10 +1211,10 @@
 		if (nr_wares > 0) {
 			if (nr_wares == tribe().get_nrwares().value()) {
 				assert(m_ware_productions.size() == nr_wares);
-				assert(m_current_statistics.size() == nr_wares);
+				assert(m_current_produced_statistics.size() == nr_wares);
 
-				for (uint32_t i = 0; i < m_current_statistics.size(); ++i) {
-					m_current_statistics[i] = fr.Unsigned32();
+				for (uint32_t i = 0; i < m_current_produced_statistics.size(); ++i) {
+					m_current_produced_statistics[i] = fr.Unsigned32();
 					m_ware_productions[i].resize(nr_entries);
 
 					for (uint32_t j = 0; j < m_ware_productions[i].size(); ++j)
@@ -1177,6 +1238,22 @@
 		}
 	} else
 		throw wexception("Unsupported version %i", version);
+
+	//create empty consumption statistic if it is missing
+	if (version < 2) {
+		uint16_t nr_entries = m_ware_productions[0].size();
+
+		for (uint32_t i = 0; i < m_current_consumed_statistics.size(); ++i) {
+			m_ware_consumptions[i].resize(nr_entries);
+			m_current_consumed_statistics[i] = 0;
+
+			for (uint32_t j = 0; j < nr_entries; ++j)
+				m_ware_consumptions[i][j] = 0;
+		}
+	}
+
+	assert(m_ware_productions.size() == m_ware_consumptions.size());
+	assert(m_ware_productions[0].size() == m_ware_consumptions[0].size());
 }
 
 
@@ -1184,17 +1261,31 @@
  * Write statistics data to the give file
  */
 void Player::WriteStatistics(FileWrite & fw) const {
-	fw.Unsigned16(m_current_statistics.size());
+	//write produce statistics
+	fw.Unsigned16(m_current_produced_statistics.size());
 	fw.Unsigned16(m_ware_productions[0].size());
 
-	for (uint8_t i = 0; i < m_current_statistics.size(); ++i) {
+	for (uint8_t i = 0; i < m_current_produced_statistics.size(); ++i) {
 		fw.CString
 			(tribe().get_ware_descr
 			 (Ware_Index(static_cast<Ware_Index::value_t>(i)))->name());
-		fw.Unsigned32(m_current_statistics[i]);
+		fw.Unsigned32(m_current_produced_statistics[i]);
 		for (uint32_t j = 0; j < m_ware_productions[i].size(); ++j)
 			fw.Unsigned32(m_ware_productions[i][j]);
 	}
+
+	//write consume statistics
+	fw.Unsigned16(m_current_consumed_statistics.size());
+	fw.Unsigned16(m_ware_consumptions[0].size());
+
+	for (uint8_t i = 0; i < m_current_consumed_statistics.size(); ++i) {
+		fw.CString
+			(tribe().get_ware_descr
+			 (Ware_Index(static_cast<Ware_Index::value_t>(i)))->name());
+		fw.Unsigned32(m_current_consumed_statistics[i]);
+		for (uint32_t j = 0; j < m_ware_consumptions[i].size(); ++j)
+			fw.Unsigned32(m_ware_consumptions[i][j]);
+	}
 }
 
 }

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2011-11-05 18:19:37 +0000
+++ src/logic/player.h	2011-11-06 17:03:27 +0000
@@ -521,13 +521,19 @@
 	{
 		return m_building_stats[i];
 	}
+
 	std::vector<uint32_t> const * get_ware_production_statistics
 		(Ware_Index const) const;
 
+	std::vector<uint32_t> const * get_ware_consumption_statistics
+		(Ware_Index const) const;
+
 	void ReadStatistics(FileRead &, uint32_t version);
 	void WriteStatistics(FileWrite &) const;
 	void sample_statistics();
 	void ware_produced(Ware_Index);
+
+	void ware_consumed(Ware_Index, uint8_t);
 	void next_ware_production_period();
 
 	void receive(NoteImmovable const &);
@@ -589,13 +595,25 @@
 	/**
 	 * Wares produced (by ware id) since the last call to @ref sample_statistics
 	 */
-	std::vector<uint32_t> m_current_statistics;
+	std::vector<uint32_t> m_current_produced_statistics;
+
+	/**
+	 * Wares consumed (by ware id) since the last call to @ref sample_statistics
+	 */
+	std::vector<uint32_t> m_current_consumed_statistics;
 
 	/**
 	 * Statistics of wares produced over the life of the game, indexed as
 	 * m_ware_productions[ware id][time index]
 	 */
 	std::vector< std::vector<uint32_t> > m_ware_productions;
+
+	/**
+	 * Statistics of wares consumed over the life of the game, indexed as
+	 * m_ware_consumptions[ware_id][time_index]
+	 */
+	std::vector< std::vector<uint32_t> > m_ware_consumptions;
+
 	BuildingStats m_building_stats;
 };
 

=== modified file 'src/logic/production_program.cc'
--- src/logic/production_program.cc	2011-10-09 17:39:13 +0000
+++ src/logic/production_program.cc	2011-11-06 17:03:27 +0000
@@ -806,6 +806,9 @@
 			if (uint8_t const q = consumption_quantities[i]) {
 				assert(q <= warequeues[i]->get_filled());
 				warequeues[i]->set_filled(warequeues[i]->get_filled() - q);
+
+				//update consumption statistic
+				ps.owner().ware_consumed(warequeues[i]->get_ware(), q);
 			}
 		return ps.program_step(game);
 	}

=== modified file 'src/logic/worker.cc'
--- src/logic/worker.cc	2011-10-09 17:39:13 +0000
+++ src/logic/worker.cc	2011-11-06 17:03:27 +0000
@@ -1012,6 +1012,9 @@
 		return true;
 	}
 
+	//update consumption statistic
+	owner().ware_consumed(wareindex, 1);
+
 	item = fetch_carried_item(game);
 	item->remove(game);
 

=== added file 'src/ui_basic/wsm_checkbox.cc'
--- src/ui_basic/wsm_checkbox.cc	1970-01-01 00:00:00 +0000
+++ src/ui_basic/wsm_checkbox.cc	2011-11-06 17:03:27 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 by the Widelands Development Team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "graphic/rendertarget.h"
+#include "logic/item_ware_descr.h"
+#include "compile_assert.h"
+#include "checkbox.h"
+
+#include "wsm_checkbox.h"
+
+
+#define COLOR_BOX_HEIGHT 7
+#define WARES_DISPLAY_BG "pics/ware_list_bg.png"
+
+
+WSM_Checkbox::WSM_Checkbox
+	(UI::Panel * const parent,
+	 Point       const p,
+	 int32_t     const id,
+	 PictureID   const picid,
+	 RGBColor    const color)
+:
+UI::Checkbox(parent, p, g_gr->get_picture(PicMod_Game,  WARES_DISPLAY_BG)),
+m_pic       (picid),
+m_color     (color)
+{
+	set_id(id);
+}
+
+/**
+ * draw the normal checkbox, the picture and the color rectangle
+ */
+void WSM_Checkbox::draw(RenderTarget & dst) {
+	//  First, draw normal.
+	UI::Checkbox::draw(dst);
+
+	//  Now, draw a small box with the color.
+	assert(1 <= get_inner_w());
+	compile_assert(2 <= COLOR_BOX_HEIGHT);
+	dst.fill_rect
+		(Rect(Point(1, 1), get_inner_w() - 1, COLOR_BOX_HEIGHT - 2), m_color);
+
+	//  and the item
+	dst.blit
+		(Point((get_inner_w() - WARE_MENU_PIC_WIDTH) / 2, COLOR_BOX_HEIGHT),
+		 m_pic);
+}

=== added file 'src/ui_basic/wsm_checkbox.h'
--- src/ui_basic/wsm_checkbox.h	1970-01-01 00:00:00 +0000
+++ src/ui_basic/wsm_checkbox.h	2011-11-06 17:03:27 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 by the Widelands Development Team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WSM_CHECKBOX_H
+#define WSM_CHECKBOX_H
+
+/**
+ * This class is the same as an ordinary
+ * checkbox, the only difference is, it has
+ * a small rectangle on it with the color
+ * of the graph and it needs a picture
+ */
+struct WSM_Checkbox : public UI::Checkbox {
+	WSM_Checkbox(UI::Panel *, Point, int32_t id, PictureID picid, RGBColor);
+
+	virtual void draw(RenderTarget &);
+
+private:
+	PictureID  m_pic;
+	RGBColor   m_color;
+};
+
+
+#endif

=== added file 'src/wui/differential_plot_area.cc'
--- src/wui/differential_plot_area.cc	1970-01-01 00:00:00 +0000
+++ src/wui/differential_plot_area.cc	2011-11-06 17:03:27 +0000
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 by the Widelands Development Team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "differential_plot_area.h"
+#include "graphic/rendertarget.h"
+
+#define ZERO_LINE_COLOR RGBColor(255, 255, 255)
+
+DifferentialPlot_Area::DifferentialPlot_Area
+		(UI::Panel * const parent,
+		 int32_t const x, int32_t const y, int32_t const w, int32_t const h)
+:
+WUIPlot_Area (parent, x, y, w, h)
+{}
+
+void DifferentialPlot_Area::draw(RenderTarget & dst) {
+	float const xline_length = get_inner_w() - space_at_right  - spacing;
+	float const yline_length = get_inner_h() - space_at_bottom - spacing;
+	//yoffset of the zero line
+	float const yoffset = spacing + ((get_inner_h() - space_at_bottom) - spacing) / 2;
+
+	uint32_t time_in_ms_ = draw_diagram(dst, xline_length, yline_length);
+
+	//draw zero line
+	dst.draw_line
+		(get_inner_w() - space_at_right,
+		 yoffset,
+		 get_inner_w() - space_at_right - xline_length,
+		 yoffset,
+		 ZERO_LINE_COLOR);
+
+	//find max and min value
+	int32_t max = 0;
+	int32_t min = 0;
+
+	if (m_plotmode == PLOTMODE_ABSOLUTE)  {
+		for (uint32_t i = 0; i < m_plotdata.size(); ++i)
+			if (m_plotdata[i].showplot) {
+				for (uint32_t l = 0; l < m_plotdata[i].dataset->size(); ++l) {
+					int32_t temp = (*m_plotdata[i].dataset)[l] -
+								   (*m_negative_plotdata[i].dataset)[l];
+					if (max < temp) max = temp;
+					if (min > temp) min = temp;
+				}
+			}
+	} else {
+		for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
+			if (m_plotdata[plot].showplot) {
+
+				std::vector<uint32_t> const & dataset = *m_plotdata[plot].dataset;
+				std::vector<uint32_t> const & ndataset = *m_negative_plotdata[plot].dataset;
+
+				// How many do we take together
+				int32_t const how_many =
+					static_cast<int32_t>
+					((static_cast<float>(time_in_ms_)
+					  /
+					  static_cast<float>(nr_samples))
+					 /
+					 static_cast<float>(m_sample_rate));
+
+				int32_t add = 0;
+				//  Relative data, first entry is always zero.
+				for (uint32_t i = 0; i < dataset.size(); ++i) {
+					add += dataset[i] - ndataset[i];
+					if (0 == ((i + 1) % how_many)) {
+						if (max < add) max = add;
+						if (min > add) min = add;
+
+						add = 0;
+					}
+				}
+			}
+	}
+
+	//use equal positive and negative range
+	min = abs(min);
+	uint32_t highest_scale = 0;
+	if (min > max) {
+		highest_scale = min;
+	} else {
+		highest_scale = max;
+	}
+	//print the min and max values
+	char buffer[200];
+
+	sprintf(buffer, "%u", highest_scale);
+	draw_value
+		(dst, buffer, RGBColor(60, 125, 0),
+		 Point(get_inner_w() - space_at_right - 2, spacing + 2));
+
+	sprintf(buffer, "-%u", highest_scale);
+	draw_value
+		(dst, buffer, RGBColor(125, 0, 0),
+		 Point(get_inner_w() - space_at_right - 2, get_inner_h() - spacing - 15));
+
+	//  plot the pixels
+	float sub =
+		xline_length
+		/
+		(static_cast<float>(time_in_ms_)
+		 /
+		 static_cast<float>(m_sample_rate));
+	for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
+		if (m_plotdata[plot].showplot) {
+
+			RGBColor color = m_plotdata[plot].plotcolor;
+			std::vector<uint32_t> const * dataset = m_plotdata[plot].dataset;
+			std::vector<uint32_t> const * ndataset = m_negative_plotdata[plot].dataset;
+
+			std::vector<int32_t> m_data;
+			if (m_plotmode == PLOTMODE_RELATIVE) {
+				//  How many do we take together.
+				const int32_t how_many = static_cast<int32_t>
+				((static_cast<float>(time_in_ms_)
+				  /
+				  static_cast<float>(nr_samples))
+				 /
+				 static_cast<float>(m_sample_rate));
+
+				int32_t add = 0;
+				// Relative data, first entry is always zero
+				m_data.push_back(0);
+				for (uint32_t i = 0; i < dataset->size(); ++i) {
+					add += (*dataset)[i] - (*ndataset)[i];
+					if (0 == ((i + 1) % how_many)) {
+						m_data.push_back(add);
+						add = 0;
+					}
+				}
+
+				sub = xline_length / static_cast<float>(nr_samples);
+			}
+
+			float posx = get_inner_w() - space_at_right;
+
+			int32_t lx = get_inner_w() - space_at_right;
+			//raise y zero point to middle of plot
+			int32_t ly = yoffset;
+			for (int32_t i = m_data.size() - 1; i > 0 and posx > spacing; --i) {
+				int32_t const curx = static_cast<int32_t>(posx);
+				int32_t       cury = yoffset;
+				if (int32_t value = m_data[i]) {
+					const float length_y =
+						yline_length
+						/
+						(static_cast<float>(highest_scale) / static_cast<float>(value))
+						//highest_scale represent the space between zero line and top.
+						//-> half of the whole differential plot area
+						/ static_cast<float>(2);
+					cury -= static_cast<int32_t>(length_y);
+				}
+				dst.draw_line(lx, ly, curx, cury, color);
+
+				posx -= sub;
+
+				lx = curx;
+				ly = cury;
+			}
+		}
+}
+
+/**
+ * Register a new negative plot data stream. This stream is
+ * used as subtrahend for calculating the plot data.
+ */
+void DifferentialPlot_Area::register_negative_plot_data
+	(uint32_t const id, std::vector<uint32_t> const * const data) {
+
+	if (id >= m_negative_plotdata.size())
+		m_negative_plotdata.resize(id + 1);
+
+	m_negative_plotdata[id].dataset   = data;
+}

=== added file 'src/wui/differential_plot_area.h'
--- src/wui/differential_plot_area.h	1970-01-01 00:00:00 +0000
+++ src/wui/differential_plot_area.h	2011-11-06 17:03:27 +0000
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 by the Widelands Development Team
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WUI_DIFFERENTIAL_PLOT_AREA_H
+#define WUI_DIFFERENTIAL_PLOT_AREA_H
+
+#include "plot_area.h"
+
+/**
+ * A Plot Area is a simple 2D Plot, with the
+ * X Axis as time (actually Minus Time)
+ * and the Y Axis as the difference between two data vectors
+ */
+struct DifferentialPlot_Area : public WUIPlot_Area {
+public:
+	DifferentialPlot_Area
+		(UI::Panel * parent, int32_t x, int32_t y, int32_t w, int32_t h);
+
+	virtual void draw(RenderTarget &);
+
+	void register_negative_plot_data
+		(uint32_t id, const std::vector<uint32_t> * data);
+
+private:
+	/**
+	 * for the negative plotdata only the values matter.
+	 * The color and visibillity is determined by the normal
+	 * plotdata
+	 */
+	struct __reduced_plotdata {
+		const std::vector<uint32_t> * dataset;
+	};
+	std::vector<__reduced_plotdata>  m_negative_plotdata;
+};
+
+#endif

=== modified file 'src/wui/plot_area.cc'
--- src/wui/plot_area.cc	2011-11-06 10:23:32 +0000
+++ src/wui/plot_area.cc	2011-11-06 17:03:27 +0000
@@ -36,7 +36,7 @@
 static const uint32_t hours = 60 * 60 * 1000;
 static const uint32_t days = 24 * 60 * 60 * 1000;
 
-static const uint32_t time_in_ms[] = {
+const uint32_t WUIPlot_Area::time_in_ms[] = {
 	15 * minutes,
 	30 * minutes,
 	1  * hours,
@@ -46,8 +46,6 @@
 	30 * hours
 };
 
-#define NR_SAMPLES 30   // How many samples per diagramm when relative plotting
-
 #define BG_PIC "pics/plot_area_bg.png"
 #define LINE_COLOR RGBColor(0, 0, 0)
 
@@ -57,6 +55,9 @@
 	 int32_t const x, int32_t const y, int32_t const w, int32_t const h)
 :
 UI::Panel (parent, x, y, w, h),
+spacing(5),
+space_at_bottom(15),
+space_at_right(5),
 m_time    (TIME_GAME),
 m_plotmode(PLOTMODE_ABSOLUTE),
 m_game_time_id(0)
@@ -154,7 +155,129 @@
  * Draw this. This is the main function
  */
 void WUIPlot_Area::draw(RenderTarget & dst) {
+
+	uint32_t time_in_ms_;
+
+	float const xline_length = get_inner_w() - space_at_right  - spacing;
+	float const yline_length = get_inner_h() - space_at_bottom - spacing;
+
+	time_in_ms_ = draw_diagram(dst, xline_length, yline_length);
+
+	uint32_t max = 0;
+	//  Find the maximum value.
+	if (m_plotmode == PLOTMODE_ABSOLUTE)  {
+		for (uint32_t i = 0; i < m_plotdata.size(); ++i)
+			if (m_plotdata[i].showplot) {
+				for (uint32_t l = 0; l < m_plotdata[i].dataset->size(); ++l)
+					if (max < (*m_plotdata[i].dataset)[l])
+						max = (*m_plotdata[i].dataset)[l];
+			}
+	} else {
+		for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
+			if (m_plotdata[plot].showplot) {
+
+				std::vector<uint32_t> const & dataset = *m_plotdata[plot].dataset;
+
+				// How many do we take together
+				int32_t const how_many =
+					static_cast<int32_t>
+					((static_cast<float>(time_in_ms_)
+					  /
+					  static_cast<float>(nr_samples))
+					 /
+					 static_cast<float>(m_sample_rate));
+
+				uint32_t add = 0;
+				//  Relative data, first entry is always zero.
+				for (uint32_t i = 0; i < dataset.size(); ++i) {
+					add += dataset[i];
+					if (0 == ((i + 1) % how_many)) {
+						if (max < add)
+							max = add;
+						add = 0;
+					}
+				}
+			}
+	}
+
+	//  print the maximal value into the top right corner
+	char buffer[200];
+	sprintf(buffer, "%u", max);
+
+	draw_value
+		(dst, buffer, RGBColor(60, 125, 0),
+		 Point(get_inner_w() - space_at_right - 2, spacing + 2));
+
+	//  plot the pixels
+	float sub =
+		xline_length
+		/
+		(static_cast<float>(time_in_ms_)
+		 /
+		 static_cast<float>(m_sample_rate));
+	for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
+		if (m_plotdata[plot].showplot) {
+
+			RGBColor color = m_plotdata[plot].plotcolor;
+			std::vector<uint32_t> const * dataset = m_plotdata[plot].dataset;
+
+			std::vector<uint32_t> m_data;
+			if (m_plotmode == PLOTMODE_RELATIVE) {
+				//  How many do we take together.
+				const int32_t how_many = static_cast<int32_t>
+				((static_cast<float>(time_in_ms_)
+				  /
+				  static_cast<float>(nr_samples))
+				 /
+				 static_cast<float>(m_sample_rate));
+
+				uint32_t add = 0;
+				// Relative data, first entry is always zero
+				m_data.push_back(0);
+				for (uint32_t i = 0; i < dataset->size(); ++i) {
+					add += (*dataset)[i];
+					if (0 == ((i + 1) % how_many)) {
+						m_data.push_back(add);
+						add = 0;
+					}
+				}
+
+				dataset = &m_data;
+				sub = xline_length / static_cast<float>(nr_samples);
+			}
+
+			float posx = get_inner_w() - space_at_right;
+
+			int32_t lx = get_inner_w() - space_at_right;
+			int32_t ly = get_inner_h() - space_at_bottom;
+			for (int32_t i = dataset->size() - 1; i > 0 and posx > spacing; --i) {
+				int32_t const curx = static_cast<int32_t>(posx);
+				int32_t       cury = get_inner_h() - space_at_bottom;
+				if (int32_t value = (*dataset)[i]) {
+					const float length_y =
+						yline_length
+						/
+						(static_cast<float>(max) / static_cast<float>(value));
+					cury -= static_cast<int32_t>(length_y);
+				}
+				dst.draw_line(lx, ly, curx, cury, color);
+
+				posx -= sub;
+
+				lx = curx;
+				ly = cury;
+			}
+		}
+}
+
+/**
+ * draw the background and the axis of the diagram
+ */
+uint32_t WUIPlot_Area::draw_diagram
+	(RenderTarget & dst, float const xline_length, float const yline_length) {
+
 	uint32_t time_in_ms_, how_many_ticks, max_x;
+	char buffer[200];
 
 	time_in_ms_ = get_plot_time();
 	UNIT unit = get_suggested_unit(time_in_ms_);
@@ -188,13 +311,6 @@
 		(Rect(Point(0, 0), get_inner_w(), get_inner_h()),
 		 g_gr->get_picture(PicMod_Game, BG_PIC), Point(0, 0));
 
-	int32_t const spacing         =  5;
-	int32_t const space_at_bottom = 15;
-	int32_t const space_at_right  =  5;
-
-	float const xline_length = get_inner_w() - space_at_right  - spacing;
-	float const yline_length = get_inner_h() - space_at_bottom - spacing;
-
 	// Draw coordinate system
 	// X Axis
 	dst.draw_line
@@ -224,7 +340,7 @@
 
 	float sub = xline_length / how_many_ticks;
 	float posx = get_inner_w() - space_at_right;
-	char buffer[200];
+
 	for (uint32_t i = 0; i <= how_many_ticks; ++i) {
 		dst.draw_line
 			(static_cast<int32_t>(posx), get_inner_h() - space_at_bottom,
@@ -257,119 +373,29 @@
 		 spacing + ((get_inner_h() - space_at_bottom) - spacing) / 2,
 		 LINE_COLOR);
 
-	uint32_t max = 0;
-	//  Find the maximum value.
-	if (m_plotmode == PLOTMODE_ABSOLUTE)  {
-		for (uint32_t i = 0; i < m_plotdata.size(); ++i)
-			if (m_plotdata[i].showplot) {
-				for (uint32_t l = 0; l < m_plotdata[i].dataset->size(); ++l)
-					if (max < (*m_plotdata[i].dataset)[l])
-						max = (*m_plotdata[i].dataset)[l];
-			}
-	} else {
-		for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
-			if (m_plotdata[plot].showplot) {
-
-				std::vector<uint32_t> const & dataset = *m_plotdata[plot].dataset;
-
-				// How many do we take together
-				int32_t const how_many =
-					static_cast<int32_t>
-					((static_cast<float>(time_in_ms_)
-					  /
-					  static_cast<float>(NR_SAMPLES))
-					 /
-					 static_cast<float>(m_sample_rate));
-
-				uint32_t add = 0;
-				//  Relative data, first entry is always zero.
-				for (uint32_t i = 0; i < dataset.size(); ++i) {
-					add += dataset[i];
-					if (0 == ((i + 1) % how_many)) {
-						if (max < add)
-							max = add;
-						add = 0;
-					}
-				}
-			}
-	}
-
-	//  print the maximal value
-	sprintf(buffer, "%u", max);
-	UI::TextStyle ymarkstyle(UI::TextStyle::ui_small());
-	ymarkstyle.fg = RGBColor(60, 125, 0);
-
-	UI::g_fh->draw_text
-		(dst, ymarkstyle,
-		 Point(get_inner_w() - space_at_right - 2, spacing + 2),
-		 buffer, UI::Align_CenterRight);
-
 	//  print the used unit
 	UI::g_fh->draw_text
 		(dst, xtickstyle,
 		 Point(2, spacing + 2),
 		 get_unit_name(unit), UI::Align_CenterLeft);
 
-	//  plot the pixels
-	sub =
-		xline_length
-		/
-		(static_cast<float>(time_in_ms_)
-		 /
-		 static_cast<float>(m_sample_rate));
-	for (uint32_t plot = 0; plot < m_plotdata.size(); ++plot)
-		if (m_plotdata[plot].showplot) {
-
-			RGBColor color = m_plotdata[plot].plotcolor;
-			std::vector<uint32_t> const * dataset = m_plotdata[plot].dataset;
-
-			std::vector<uint32_t> m_data;
-			if (m_plotmode == PLOTMODE_RELATIVE) {
-				//  How many do we take together.
-				const int32_t how_many = static_cast<int32_t>
-				((static_cast<float>(time_in_ms_)
-				  /
-				  static_cast<float>(NR_SAMPLES))
-				 /
-				 static_cast<float>(m_sample_rate));
-
-				uint32_t add = 0;
-				// Relative data, first entry is always zero
-				m_data.push_back(0);
-				for (uint32_t i = 0; i < dataset->size(); ++i) {
-					add += (*dataset)[i];
-					if (0 == ((i + 1) % how_many)) {
-						m_data.push_back(add);
-						add = 0;
-					}
-				}
-
-				dataset = &m_data;
-				sub = xline_length / static_cast<float>(NR_SAMPLES);
-			}
-
-			posx = get_inner_w() - space_at_right;
-
-			int32_t lx = get_inner_w() - space_at_right;
-			int32_t ly = get_inner_h() - space_at_bottom;
-			for (int32_t i = dataset->size() - 1; i > 0 and posx > spacing; --i) {
-				int32_t const curx = static_cast<int32_t>(posx);
-				int32_t       cury = get_inner_h() - space_at_bottom;
-				if (int32_t value = (*dataset)[i]) {
-					const float length_y =
-						yline_length
-						/
-						(static_cast<float>(max) / static_cast<float>(value));
-					cury -= static_cast<int32_t>(length_y);
-				}
-				dst.draw_line(lx, ly, curx, cury, color);
-
-				posx -= sub;
-
-				lx = curx;
-				ly = cury;
-			}
-		}
+	return time_in_ms_;
+}
+
+/**
+ * print the string into the RenderTarget.
+ */
+void WUIPlot_Area::draw_value
+	(RenderTarget & dst, const char * value, RGBColor color, Point pos)
+{
+
+	UI::TextStyle ymarkstyle(UI::TextStyle::ui_small());
+	ymarkstyle.fg = color;
+
+	UI::g_fh->draw_text
+		(dst, ymarkstyle,
+		 pos,
+		 value, UI::Align_CenterRight);
 }
 
 /*

=== modified file 'src/wui/plot_area.h'
--- src/wui/plot_area.h	2011-11-06 10:23:32 +0000
+++ src/wui/plot_area.h	2011-11-06 17:03:27 +0000
@@ -83,13 +83,18 @@
 
 	std::vector<std::string> get_labels();
 
-private:
-	uint32_t get_game_time();
-	uint32_t get_plot_time();
-	void calc_game_time_id();
-	UNIT get_suggested_unit(uint32_t game_time);
-	std::string get_unit_name(UNIT unit);
-	uint32_t ms_to_unit(UNIT unit, uint32_t ms);
+protected:
+	uint32_t draw_diagram
+		(RenderTarget & dst, float const xline_length, float const yline_length);
+	void draw_value
+		(RenderTarget & dst, const char * value, RGBColor color, Point pos);
+
+	int32_t const spacing;
+	int32_t const space_at_bottom;
+	int32_t const space_at_right;
+
+	static const uint32_t time_in_ms[];
+	static const uint32_t nr_samples = 30;   // How many samples per diagramm when relative plotting
 
 	struct __plotdata {
 		const std::vector<uint32_t> * dataset;
@@ -97,9 +102,18 @@
 		RGBColor                      plotcolor;
 	};
 	std::vector<__plotdata> m_plotdata;
+
 	TIME                    m_time;  // How much do you want to list
 	int32_t                 m_sample_rate;
 	int32_t                 m_plotmode;
+
+private:
+	uint32_t get_game_time();
+	uint32_t get_plot_time();
+	void calc_game_time_id();
+	UNIT get_suggested_unit(uint32_t game_time);
+	std::string get_unit_name(UNIT unit);
+	uint32_t ms_to_unit(UNIT unit, uint32_t ms);
 	int32_t                 m_game_time_id; // what label is used for TIME_GAME
 };
 

=== modified file 'src/wui/ware_statistics_menu.cc'
--- src/wui/ware_statistics_menu.cc	2011-11-06 12:38:07 +0000
+++ src/wui/ware_statistics_menu.cc	2011-11-06 17:03:27 +0000
@@ -27,22 +27,26 @@
 #include "logic/tribe.h"
 #include "logic/warelist.h"
 #include "plot_area.h"
+#include "differential_plot_area.h"
 #include "waresdisplay.h"
 
 #include "ui_basic/button.h"
 #include "ui_basic/checkbox.h"
 #include "ui_basic/textarea.h"
+#include "ui_basic/wsm_checkbox.h"
+#include "ui_basic/tabpanel.h"
 #include "ui_basic/slider.h"
 
-#define WARES_DISPLAY_BG "pics/ware_list_bg.png"
 
 #define MIN_WARES_PER_LINE 7
 #define MAX_WARES_PER_LINE 11
 
-
 #define PLOT_HEIGHT 100
 
-#define COLOR_BOX_HEIGHT 7
+//TODO place holder, need to be changed
+static const char pic_tab_production[] = "pics/menu_tab_wares.png";
+static const char pic_tab_consumption[] = "pics/menu_tab_wares.png";
+static const char pic_tab_economy[] = "pics/menu_tab_wares.png";
 
 static const RGBColor colors[] = {
 	RGBColor  (0, 210, 254),
@@ -316,7 +320,6 @@
 	}
 };
 
-
 Ware_Statistics_Menu::Ware_Statistics_Menu
 	(Interactive_Player & parent, UI::UniqueWindow::Registry & registry)
 :
@@ -326,24 +329,87 @@
 {
 	set_cache(false);
 
+	//  First, we must decide about the size.
 	UI::Box * box = new UI::Box(this, 0, 0, UI::Box::Vertical, 0, 0, 5);
 	box->set_border(5, 5, 5, 5);
 	set_center_panel(box);
 
-	m_plot = new WUIPlot_Area
-			(box, 0, 0, 100, PLOT_HEIGHT);
-	m_plot->set_sample_rate(STATISTICS_SAMPLE_TIME);
-	m_plot->set_plotmode(WUIPlot_Area::PLOTMODE_RELATIVE);
-
-	box->add(m_plot, UI::Box::AlignLeft, true);
-
 	uint8_t const nr_wares = parent.get_player()->tribe().get_nrwares().value();
+
+	//setup plot widgets
+	//create a tabbed environment for the different plots
+	uint8_t const tab_offset = 30;
+	uint8_t const spacing = 5;
+	uint8_t const plot_width = get_inner_w() - 2 * spacing;
+	uint8_t const plot_height = PLOT_HEIGHT + tab_offset + spacing;
+
+	UI::Tab_Panel * tabs =
+		 new UI::Tab_Panel
+			 (box, spacing, 0, g_gr->get_picture(PicMod_UI, "pics/but1.png"));
+
+
+	m_plot_production =
+		new WUIPlot_Area
+			(tabs,
+			 0, 0, plot_width, plot_height);
+	m_plot_production->set_sample_rate(STATISTICS_SAMPLE_TIME);
+	m_plot_production->set_plotmode(WUIPlot_Area::PLOTMODE_RELATIVE);
+
+	tabs->add
+		("production", g_gr->get_picture(PicMod_UI, pic_tab_production),
+			m_plot_production, _("Production"));
+
+	m_plot_consumption =
+		new WUIPlot_Area
+			(tabs,
+			 0, 0, plot_width, plot_height);
+	m_plot_consumption->set_sample_rate(STATISTICS_SAMPLE_TIME);
+	m_plot_consumption->set_plotmode(WUIPlot_Area::PLOTMODE_RELATIVE);
+
+	tabs->add
+		("consumption", g_gr->get_picture(PicMod_UI, pic_tab_consumption),
+			m_plot_consumption, _("Consumption"));
+
+	m_plot_economy =
+		new DifferentialPlot_Area
+			(tabs,
+			 0, 0, plot_width, plot_height);
+	m_plot_economy->set_sample_rate(STATISTICS_SAMPLE_TIME);
+	m_plot_economy->set_plotmode(WUIPlot_Area::PLOTMODE_RELATIVE);
+
+	tabs->add
+		("economy_health", g_gr->get_picture(PicMod_UI, pic_tab_production),
+			m_plot_economy, _("Economy Health"));
+
+	tabs->activate(0);
+
+	//add tabbed environment to box
+	box->add(tabs, UI::Box::AlignLeft, true);
+
+	//register statistics data
 	for (Widelands::Ware_Index::value_t cur_ware = 0; cur_ware < nr_wares; ++cur_ware) {
-		m_plot->register_plot_data
-			(cur_ware,
-			 parent.get_player()->get_ware_production_statistics
-				(Widelands::Ware_Index(cur_ware)),
-			 colors[cur_ware]);
+		m_plot_production->register_plot_data
+			(cur_ware,
+				parent.get_player()->get_ware_production_statistics
+				(Widelands::Ware_Index(cur_ware)),
+				colors[cur_ware]);
+
+		m_plot_consumption->register_plot_data
+			(cur_ware,
+				parent.get_player()->get_ware_consumption_statistics
+				(Widelands::Ware_Index(cur_ware)),
+				colors[cur_ware]);
+
+		m_plot_economy->register_plot_data
+			(cur_ware,
+				parent.get_player()->get_ware_production_statistics
+				(Widelands::Ware_Index(cur_ware)),
+				colors[cur_ware]);
+
+		m_plot_economy->register_negative_plot_data
+			(cur_ware,
+				parent.get_player()->get_ware_consumption_statistics
+				(Widelands::Ware_Index(cur_ware)));
 	}
 
 	box->add
@@ -352,25 +418,32 @@
 			 boost::bind(&Ware_Statistics_Menu::cb_changed_to, boost::ref(*this), _1, _2)),
 		 UI::Box::AlignLeft, true);
 
-
 	box->add
-		(new WUIPlot_Area_Slider
-			(box, *m_plot, 0, 0, 100, 45,
-			 g_gr->get_picture(PicMod_UI, "pics/but1.png")),
-		UI::Box::AlignLeft, true);
+		(new WUIPlot_Generic_Area_Slider
+			(this, *m_plot_production, this,
+			0, 0, 100, 45,
+			g_gr->get_picture(PicMod_UI, "pics/but1.png")),
+		 UI::Box::AlignLeft, true);
+
 }
 
-
 /**
- * Called when the ok button has been clicked
- * \todo Implement help
-*/
-void Ware_Statistics_Menu::clicked_help() {}
-
-/*
- * Cb has been changed to this state
+ * Callback for the ware buttons. Change the state of all ware statistics
+ * simultaneously.
  */
 void Ware_Statistics_Menu::cb_changed_to(Widelands::Ware_Index id, bool what) {
-	m_plot->show_plot(static_cast<size_t>(id), what);
+	m_plot_production->show_plot(static_cast<size_t>(id), what);
+	m_plot_consumption->show_plot(static_cast<size_t>(id), what);
+	m_plot_economy->show_plot(static_cast<size_t>(id), what);
+}
+
+/**
+ * Callback for the time buttons. Change the time axis of all ware
+ * statistics simultaneously.
+ */
+void Ware_Statistics_Menu::set_time(int32_t timescale) {
+	m_plot_production->set_time_id(timescale);
+	m_plot_consumption->set_time_id(timescale);
+	m_plot_economy->set_time_id(timescale);
 }
 

=== modified file 'src/wui/ware_statistics_menu.h'
--- src/wui/ware_statistics_menu.h	2011-11-05 18:25:27 +0000
+++ src/wui/ware_statistics_menu.h	2011-11-06 17:03:27 +0000
@@ -21,19 +21,56 @@
 #define WARE_STATISTICS_MENU_H
 
 #include "ui_basic/unique_window.h"
+#include "plot_area.h"
+#include "differential_plot_area.h"
 
 struct Interactive_Player;
 struct WUIPlot_Area;
 
 struct Ware_Statistics_Menu : public UI::UniqueWindow {
+public:
 	Ware_Statistics_Menu(Interactive_Player &, UI::UniqueWindow::Registry &);
+	void set_time(int32_t);
 
 private:
 	Interactive_Player * m_parent;
-	WUIPlot_Area       * m_plot;
+	WUIPlot_Area       * m_plot_production;
+	WUIPlot_Area       * m_plot_consumption;
+	DifferentialPlot_Area       * m_plot_economy;
 
 	void clicked_help();
 	void cb_changed_to(Widelands::Ware_Index, bool);
 };
 
+
+
+/**
+ * A discrete slider with plot time steps preconfigured, automatic signal
+ * setup and the set_time callback function from Ware_Statistics_Menu.
+ *
+ */
+struct WUIPlot_Generic_Area_Slider : public UI::DiscreteSlider {
+	WUIPlot_Generic_Area_Slider
+		(Panel * const parent,
+		 WUIPlot_Area & plot_area,
+		 Ware_Statistics_Menu * signal_listener,
+		 const int32_t x, const int32_t y, const uint32_t w, const uint32_t h,
+		 const PictureID background_picture_id,
+		 const std::string & tooltip_text = std::string(),
+		 const uint32_t cursor_size = 20,
+		 const bool enabled = true)
+	: DiscreteSlider
+		(parent,
+		 x, y, w, h,
+		 plot_area.get_labels(),
+		 plot_area.get_time(),
+		 background_picture_id,
+		 tooltip_text,
+		 cursor_size,
+		 enabled)
+	{
+		changedto->set(signal_listener, &Ware_Statistics_Menu::set_time);
+	}
+};
+
 #endif


Follow ups