← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1573057-graph-relative-plotmode into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1573057-graph-relative-plotmode into lp:widelands.

Commit message:
Fixed relative plot mode and flickering graphs on button click. Only draw active statistics tab. Some refactoring.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1566167 in widelands: "Ware statistic: Clicking the first time on an icon the graph flickers"
  https://bugs.launchpad.net/widelands/+bug/1566167
  Bug #1573057 in widelands: "ware graphs not properly scaled"
  https://bugs.launchpad.net/widelands/+bug/1573057

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1573057-graph-relative-plotmode/+merge/295198
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1573057-graph-relative-plotmode into lp:widelands.
=== modified file 'src/logic/cmd_calculate_statistics.cc'
--- src/logic/cmd_calculate_statistics.cc	2015-10-24 15:42:37 +0000
+++ src/logic/cmd_calculate_statistics.cc	2016-05-19 11:15:41 +0000
@@ -31,7 +31,7 @@
 	game.sample_statistics();
 	game.enqueue_command
 		(new CmdCalculateStatistics
-		 (game.get_gametime() + STATISTICS_SAMPLE_TIME));
+		 (game.get_gametime() + kStatisticsSampleTime));
 }
 
 constexpr uint16_t kCurrentPacketVersion = 1;

=== modified file 'src/logic/constants.h'
--- src/logic/constants.h	2015-03-01 09:21:20 +0000
+++ src/logic/constants.h	2016-05-19 11:15:41 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2015 by the Widelands Development Team
+ * Copyright (C) 2006-2016 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
@@ -20,6 +20,8 @@
 #ifndef WL_LOGIC_CONSTANTS_H
 #define WL_LOGIC_CONSTANTS_H
 
+#include <cstdint>
+
 /// Maximum numbers of players in a game. The game logic code reserves 5 bits
 /// for player numbers, so it can keep track of 32 different player numbers, of
 /// which the value 0 means neutral and the values 1 .. 31 can be used as the
@@ -27,6 +29,6 @@
 #define MAX_PLAYERS 8
 
 /// How often are statistics to be sampled.
-#define STATISTICS_SAMPLE_TIME 30000
+constexpr uint32_t kStatisticsSampleTime = 30000;
 
 #endif  // end of include guard: WL_LOGIC_CONSTANTS_H

=== modified file 'src/wui/general_statistics_menu.cc'
--- src/wui/general_statistics_menu.cc	2016-01-29 08:37:22 +0000
+++ src/wui/general_statistics_menu.cc	2016-05-19 11:15:41 +0000
@@ -67,7 +67,9 @@
 	 440, 400, _("General Statistics")),
          my_registry_   (&registry),
          box_           (this, 0, 0, UI::Box::Vertical, 0, 0, 5),
-         plot_          (&box_, 0, 0, 430, PLOT_HEIGHT),
+			plot_          (&box_, 0, 0, 430, PLOT_HEIGHT,
+								 kStatisticsSampleTime,
+								 WuiPlotArea::Plotmode::kAbsolute),
          selected_information_(0)
 {
 	assert (my_registry_);
@@ -78,8 +80,6 @@
 	box_.set_border(5, 5, 5, 5);
 
 	// Setup plot data
-	plot_.set_sample_rate(STATISTICS_SAMPLE_TIME);
-	plot_.set_plotmode(WuiPlotArea::PLOTMODE_ABSOLUTE);
 	Game & game = *parent.get_game();
 	const Game::GeneralStatsVector & genstats =
 		game.get_general_statistics();

=== modified file 'src/wui/plot_area.cc'
--- src/wui/plot_area.cc	2016-03-29 17:28:15 +0000
+++ src/wui/plot_area.cc	2016-05-19 11:15:41 +0000
@@ -39,24 +39,24 @@
 
 constexpr int32_t kUpdateTimeInGametimeMs = 1000;  //  1 second, gametime
 
-const uint32_t minutes = 60 * 1000;
-const uint32_t hours = 60 * 60 * 1000;
-const uint32_t days = 24 * 60 * 60 * 1000;
+constexpr uint32_t kMinutes = 60 * 1000;
+constexpr uint32_t kHours = 60 * 60 * 1000;
+constexpr uint32_t kDays = 24 * 60 * 60 * 1000;
 
-const int32_t spacing = 5;
-const int32_t space_at_bottom = 20;
-const int32_t space_at_right = 10;
-const int32_t space_left_of_label = 15;
-const uint32_t nr_samples = 30;   // How many samples per diagramm when relative plotting
+constexpr int32_t kSpacing = 5;
+constexpr int32_t kSpaceBottom = 20;
+constexpr int32_t kSpaceRight = 10;
+constexpr int32_t kSpaceLeftOfLabel = 15;
+constexpr uint32_t KNoSamples = 30;   // How many samples per diagramm when relative plotting
 
 const uint32_t time_in_ms[] = {
-	15 * minutes,
-	30 * minutes,
-	1  * hours,
-	2  * hours,
-	5  * hours,
-	10 * hours,
-	30 * hours
+	15 * kMinutes,
+	30 * kMinutes,
+	1  * kHours,
+	2  * kHours,
+	5  * kHours,
+	10 * kHours,
+	30 * kHours
 };
 
 const char BG_PIC[] = "images/wui/plot_area_bg.png";
@@ -99,18 +99,18 @@
 Units get_suggested_unit(uint32_t game_time, bool is_generic = false) {
 	// Find a nice unit for max_x
 	if (is_generic) {
-		if (game_time > 4 * days) {
+		if (game_time > 4 * kDays) {
 			return Units::kDayGeneric;
-		} else if (game_time > 4 * hours) {
+		} else if (game_time > 4 * kHours) {
 			return Units::kHourGeneric;
 		} else {
 			return Units::kMinutesGeneric;
 		}
 	}
 	else {
-		if (game_time > 4 * days) {
+		if (game_time > 4 * kDays) {
 			return Units::kDayNarrow;
-		} else if (game_time > 4 * hours) {
+		} else if (game_time > 4 * kHours) {
 			return Units::kHourNarrow;
 		} else {
 			return Units::kMinutesNarrow;
@@ -153,13 +153,13 @@
 	switch (unit) {
 	case Units::kDayGeneric:
 	case Units::kDayNarrow:
-		return ms / days;
+		return ms / kDays;
 	case Units::kHourGeneric:
 	case Units::kHourNarrow:
-		return ms / hours;
+		return ms / kHours;
 	case Units::kMinutesGeneric:
 	case Units::kMinutesNarrow:
-		return ms / minutes;
+		return ms / kMinutes;
 	}
 	NEVER_HERE();
 }
@@ -167,11 +167,11 @@
 /**
  * calculate how many values are taken together when plot mode is relative
  */
-int32_t calc_how_many(uint32_t time_ms, int32_t sample_rate) {
+int32_t calc_how_many(uint32_t time_ms, uint32_t sample_rate) {
 	int32_t how_many = static_cast<int32_t>
 			((static_cast<float>(time_ms)
 				/
-				static_cast<float>(nr_samples))
+				static_cast<float>(KNoSamples))
 				/
 				static_cast<float>(sample_rate));
 
@@ -230,28 +230,28 @@
 	// Draw coordinate system
 	// X Axis
 	dst.draw_line_strip({
-		FloatPoint(spacing, inner_h - space_at_bottom),
-		FloatPoint(inner_w - space_at_right, inner_h - space_at_bottom)},
+		FloatPoint(kSpacing, inner_h - kSpaceBottom),
+		FloatPoint(inner_w - kSpaceRight, inner_h - kSpaceBottom)},
 		kAxisLineColor, kAxisLinesWidth);
 	// Arrow
 	dst.draw_line_strip({
-		FloatPoint(spacing + 5, inner_h - space_at_bottom - 3),
-		FloatPoint(spacing, inner_h - space_at_bottom),
-		FloatPoint(spacing + 5, inner_h - space_at_bottom + 3),
+		FloatPoint(kSpacing + 5, inner_h - kSpaceBottom - 3),
+		FloatPoint(kSpacing, inner_h - kSpaceBottom),
+		FloatPoint(kSpacing + 5, inner_h - kSpaceBottom + 3),
 		}, kAxisLineColor, kAxisLinesWidth);
 
 	//  Y Axis
-	dst.draw_line_strip({FloatPoint(inner_w - space_at_right, spacing),
-	                     FloatPoint(inner_w - space_at_right, inner_h - space_at_bottom)},
+	dst.draw_line_strip({FloatPoint(inner_w - kSpaceRight, kSpacing),
+	                     FloatPoint(inner_w - kSpaceRight, inner_h - kSpaceBottom)},
 							  kAxisLineColor, kAxisLinesWidth);
 	//  No Arrow here, since this doesn't continue.
 
-	float sub = (xline_length - space_left_of_label) / how_many_ticks;
-	float posx = inner_w - space_at_right;
+	float sub = (xline_length - kSpaceLeftOfLabel) / how_many_ticks;
+	float posx = inner_w - kSpaceRight;
 
 	for (uint32_t i = 0; i <= how_many_ticks; ++i) {
-		dst.draw_line_strip({FloatPoint(static_cast<int32_t>(posx), inner_h - space_at_bottom),
-		                     FloatPoint(static_cast<int32_t>(posx), inner_h - space_at_bottom + 3)},
+		dst.draw_line_strip({FloatPoint(static_cast<int32_t>(posx), inner_h - kSpaceBottom),
+		                     FloatPoint(static_cast<int32_t>(posx), inner_h - kSpaceBottom + 3)},
 								  kAxisLineColor, kAxisLinesWidth);
 
 		// The space at the end is intentional to have the tick centered
@@ -259,7 +259,7 @@
 		const Image* xtick = UI::g_fh1->render
 			(xtick_text_style((boost::format("-%u ") % (max_x / how_many_ticks * i)).str()));
 		dst.blit
-			(Point(static_cast<int32_t>(posx), inner_h - space_at_bottom + 10),
+			(Point(static_cast<int32_t>(posx), inner_h - kSpaceBottom + 10),
 			 xtick, BlendMode::UseAlpha, UI::Align::kCenter);
 
 		posx -= sub;
@@ -267,29 +267,32 @@
 
 	//  draw yticks, one at full, one at half
 	dst.draw_line_strip({
-		FloatPoint(inner_w - space_at_right, spacing), FloatPoint(inner_w - space_at_right - 3, spacing)},
+		FloatPoint(inner_w - kSpaceRight, kSpacing), FloatPoint(inner_w - kSpaceRight - 3, kSpacing)},
 		kAxisLineColor, kAxisLinesWidth);
 	dst.draw_line_strip({
-		FloatPoint(inner_w - space_at_right, spacing + ((inner_h - space_at_bottom) - spacing) / 2),
-		FloatPoint(inner_w - space_at_right - 3, spacing + ((inner_h - space_at_bottom) - spacing) / 2)},
+		FloatPoint(inner_w - kSpaceRight, kSpacing + ((inner_h - kSpaceBottom) - kSpacing) / 2),
+		FloatPoint(inner_w - kSpaceRight - 3, kSpacing + ((inner_h - kSpaceBottom) - kSpacing) / 2)},
 		kAxisLineColor, kAxisLinesWidth);
 
 	//  print the used unit
 	const Image* xtick = UI::g_fh1->render(xtick_text_style(get_generic_unit_name(unit)));
-	dst.blit(Point(2, spacing + 2), xtick, BlendMode::UseAlpha, UI::Align::kCenterLeft);
+	dst.blit(Point(2, kSpacing + 2), xtick, BlendMode::UseAlpha, UI::Align::kCenterLeft);
 }
 
 }  // namespace
 
 WuiPlotArea::WuiPlotArea
 	(UI::Panel * const parent,
-	 int32_t const x, int32_t const y, int32_t const w, int32_t const h)
+	 int32_t const x, int32_t const y, int32_t const w, int32_t const h,
+	 uint32_t sample_rate, Plotmode plotmode)
 :
 UI::Panel (parent, x, y, w, h),
-plotmode_(PLOTMODE_ABSOLUTE),
-sample_rate_(0),
+plotmode_(plotmode),
+sample_rate_(sample_rate),
 needs_update_(true),
 lastupdate_(0),
+xline_length_(get_inner_w() - kSpaceRight  - kSpacing),
+yline_length_(get_inner_h() - kSpaceBottom - kSpacing),
 time_ms_(0),
 highest_scale_(0),
 sub_(0.0f),
@@ -300,19 +303,19 @@
 }
 
 
-uint32_t WuiPlotArea::get_game_time() {
+uint32_t WuiPlotArea::get_game_time() const {
 	uint32_t game_time = 0;
 
 	// Find running time of the game, based on the plot data
 	for (uint32_t plot = 0; plot < plotdata_.size(); ++plot)
-		if (game_time < plotdata_[plot].dataset->size() * sample_rate_)
-			game_time = plotdata_[plot].dataset->size() * sample_rate_;
+		if (game_time < plotdata_[plot].absolute_data->size() * sample_rate_)
+			game_time = plotdata_[plot].absolute_data->size() * sample_rate_;
 	return game_time;
 }
 
-std::vector<std::string> WuiPlotArea::get_labels() {
+std::vector<std::string> WuiPlotArea::get_labels() const {
 	std::vector<std::string> labels;
-	for (int32_t i = 0; i < game_time_id_; i++) {
+	for (uint32_t i = 0; i < game_time_id_; i++) {
 		Units unit = get_suggested_unit(time_in_ms[i], false);
 		labels.push_back(get_value_with_unit(unit, ms_to_unit(unit, time_in_ms[i])));
 	}
@@ -320,7 +323,7 @@
 	return labels;
 }
 
-uint32_t WuiPlotArea::get_plot_time() {
+uint32_t WuiPlotArea::get_plot_time() const {
 	if (time_ == TIME_GAME) {
 		// Start with the game time
 		uint32_t time_ms = get_game_time();
@@ -331,14 +334,14 @@
 		// or a multiple of 2h
 		// or a multiple of 20h
 		// or a multiple of 4 days
-		if (time_ms > 8 * days) {
-			time_ms += - (time_ms % (4 * days)) + 4 * days;
-		} else if (time_ms > 40 * hours) {
-			time_ms += - (time_ms % (20 * hours)) + 20 * hours;
-		} else if (time_ms > 4 * hours) {
-			time_ms += - (time_ms % (2 * hours)) + 2 * hours;
+		if (time_ms > 8 * kDays) {
+			time_ms += - (time_ms % (4 * kDays)) + 4 * kDays;
+		} else if (time_ms > 40 * kHours) {
+			time_ms += - (time_ms % (20 * kHours)) + 20 * kHours;
+		} else if (time_ms > 4 * kHours) {
+			time_ms += - (time_ms % (2 * kHours)) + 2 * kHours;
 		} else {
-			time_ms += - (time_ms % (15 * minutes)) + 15 * minutes;
+			time_ms += - (time_ms % (15 * kMinutes)) + 15 * kMinutes;
 		}
 		return time_ms;
 	} else {
@@ -356,7 +359,7 @@
  * We start to search with i=1 to ensure that at least one option besides
  * "game" will be offered to the user.
  */
-int32_t WuiPlotArea::get_game_time_id() {
+uint32_t WuiPlotArea::get_game_time_id() {
 	uint32_t game_time = get_game_time();
 	uint32_t i;
 	for (i = 1; i < 7 && time_in_ms[i] <= game_time; i++) {
@@ -376,29 +379,47 @@
 	needs_update_ = false;
 }
 
+int32_t WuiPlotArea::initialize_update() {
+	// Initialize
+	for (uint32_t i = 0; i < plotdata_.size(); ++i) {
+		plotdata_[i].relative_data->clear();
+	}
+	// Get range
+	time_ms_ = get_plot_time();
+	if (plotmode_ == Plotmode::kAbsolute)  {
+		sub_ =
+			(xline_length_ - kSpaceLeftOfLabel)
+			/
+			(static_cast<float>(time_ms_)
+			 /
+			 static_cast<float>(sample_rate_));
+	} else {
+		sub_ = (xline_length_ - kSpaceLeftOfLabel) / static_cast<float>(KNoSamples);
+	}
+
+	// How many do we aggregate when relative plotting
+	return calc_how_many(time_ms_, sample_rate_);
+}
 
 //  Find the maximum value.
 void WuiPlotArea::update() {
-	float const xline_length = get_inner_w() - space_at_right  - spacing;
-
-	time_ms_ = get_plot_time();
-
-	// How many do we take together when relative ploting
-	const int32_t how_many = calc_how_many(time_ms_, sample_rate_);
+	const int32_t how_many = initialize_update();
+
+	// Calculate highest scale
 	highest_scale_ = 0;
-	if (plotmode_ == PLOTMODE_ABSOLUTE)  {
+	if (plotmode_ == Plotmode::kAbsolute)  {
 		for (uint32_t i = 0; i < plotdata_.size(); ++i)
 			if (plotdata_[i].showplot) {
-				for (uint32_t l = 0; l < plotdata_[i].dataset->size(); ++l)
-					if (highest_scale_ < (*plotdata_[i].dataset)[l])
-						highest_scale_ = (*plotdata_[i].dataset)[l];
+				for (uint32_t l = 0; l < plotdata_[i].absolute_data->size(); ++l) {
+					if (highest_scale_ < (*plotdata_[i].absolute_data)[l]) {
+						highest_scale_ = (*plotdata_[i].absolute_data)[l];
+					}
+				}
 			}
 	} else {
 		for (uint32_t plot = 0; plot < plotdata_.size(); ++plot) {
 			if (plotdata_[plot].showplot) {
-
-				const std::vector<uint32_t> & dataset = *plotdata_[plot].dataset;
-
+				const std::vector<uint32_t> & dataset = *plotdata_[plot].absolute_data;
 				uint32_t add = 0;
 				//  Relative data, first entry is always zero.
 				for (uint32_t i = 0; i < dataset.size(); ++i) {
@@ -413,31 +434,21 @@
 		}
 	}
 
-	//  Update the datasets
-	sub_ =
-		(xline_length - space_left_of_label)
-		/
-		(static_cast<float>(time_ms_)
-		 /
-		 static_cast<float>(sample_rate_));
-	for (uint32_t plot = 0; plot < plotdata_.size(); ++plot) {
-		if (plotdata_[plot].showplot) {
-			std::vector<uint32_t> const * dataset = plotdata_[plot].dataset;
-
-			std::vector<uint32_t> data;
-			if (plotmode_ == PLOTMODE_RELATIVE) {
+	//  Calculate plot data
+	if (plotmode_ == Plotmode::kRelative) {
+		for (uint32_t plot = 0; plot < plotdata_.size(); ++plot) {
+			if (plotdata_[plot].showplot) {
+				std::vector<uint32_t> const * dataset = plotdata_[plot].absolute_data;
 				uint32_t add = 0;
 				// Relative data, first entry is always zero
-				data.push_back(0);
+				plotdata_[plot].relative_data->push_back(0);
 				for (uint32_t i = 0; i < dataset->size(); ++i) {
 					add += (*dataset)[i];
 					if (0 == ((i + 1) % how_many)) {
-						data.push_back(add);
+						plotdata_[plot].relative_data->push_back(add);
 						add = 0;
 					}
 				}
-				dataset = &data;
-				sub_ = (xline_length - space_left_of_label) / static_cast<float>(nr_samples);
 			}
 		}
 	}
@@ -449,23 +460,22 @@
  * Draw this. This is the main function
  */
 void WuiPlotArea::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;
+	draw_plot(dst, get_inner_h() - kSpaceBottom, std::to_string(highest_scale_), highest_scale_);
+}
 
-	draw_diagram(time_ms_, get_inner_w(), get_inner_h(), xline_length, dst);
+void WuiPlotArea::draw_plot(RenderTarget& dst, float const yoffset, const std::string& yscale_label, uint32_t highest_scale) {
+	draw_diagram(time_ms_, get_inner_w(), get_inner_h(), xline_length_, dst);
 
 	//  print the maximal value into the top right corner
 	draw_value
-		(std::to_string(highest_scale_), RGBColor(60, 125, 0),
-		 Point(get_inner_w() - space_at_right - 2, spacing + 2), dst);
+		(yscale_label, RGBColor(60, 125, 0),
+		 Point(get_inner_w() - kSpaceRight - 2, kSpacing + 2), dst);
 
 	//  plot the pixels
 	for (uint32_t plot = 0; plot < plotdata_.size(); ++plot) {
 		if (plotdata_[plot].showplot) {
-			RGBColor color = plotdata_[plot].plotcolor;
-			std::vector<uint32_t> const * dataset = plotdata_[plot].dataset;
 			draw_plot_line
-				(dst, dataset, yline_length, highest_scale_, sub_, color, get_inner_h() - space_at_bottom);
+				(dst, (plotmode_ == Plotmode::kRelative) ? plotdata_[plot].relative_data : plotdata_[plot].absolute_data, highest_scale, sub_, plotdata_[plot].plotcolor, yoffset);
 		}
 	}
 }
@@ -476,34 +486,37 @@
  * \param sub horizontal difference between 2 y values
  */
 void WuiPlotArea::draw_plot_line
-		(RenderTarget & dst, std::vector<uint32_t> const * dataset, float const yline_length,
+		(RenderTarget & dst, std::vector<uint32_t> const * dataset,
 		 uint32_t const highest_scale, float const sub, RGBColor const color, int32_t const yoffset)
 {
-	float posx = get_inner_w() - space_at_right;
-	const int lx = get_inner_w() - space_at_right;
-	int ly = yoffset;
-	// Init start point of the plot line with the first data value.
-	// This prevents that the plot starts always at zero
-	if (int value = (*dataset)[dataset->size() - 1]) {
-		ly -= static_cast<int32_t>(scale_value(yline_length, highest_scale, value));
-	}
-
-	std::vector<FloatPoint> points;
-	points.emplace_back(lx, ly);
-
-	for (int32_t i = dataset->size() - 1; i > 0 && posx > spacing; --i) {
-		int32_t const curx = static_cast<int32_t>(posx);
-		int32_t       cury = yoffset;
-
-		// Scale the line to the available space
-		if (int32_t value = (*dataset)[i]) {
-			const float length_y = scale_value(yline_length, highest_scale, value);
-			cury -= static_cast<int32_t>(length_y);
-		}
-		points.emplace_back(curx, cury);
-		posx -= sub;
-	}
-	dst.draw_line_strip(points, color, kPlotLinesWidth);
+	if (!dataset->empty()) {
+		float posx = get_inner_w() - kSpaceRight;
+		const int lx = get_inner_w() - kSpaceRight;
+		int ly = yoffset;
+		// Init start point of the plot line with the first data value.
+		// This prevents that the plot starts always at zero
+
+		if (int value = (*dataset)[dataset->size() - 1]) {
+			ly -= static_cast<int32_t>(scale_value(yline_length_, highest_scale, value));
+		}
+
+		std::vector<FloatPoint> points;
+		points.emplace_back(lx, ly);
+
+		for (int32_t i = dataset->size() - 1; i > 0 && posx > kSpacing; --i) {
+			int32_t const curx = static_cast<int32_t>(posx);
+			int32_t       cury = yoffset;
+
+			// Scale the line to the available space
+			if (int32_t value = (*dataset)[i]) {
+				const float length_y = scale_value(yline_length_, highest_scale, value);
+				cury -= static_cast<int32_t>(length_y);
+			}
+			points.emplace_back(curx, cury);
+			posx -= sub;
+		}
+		dst.draw_line_strip(points, color, kPlotLinesWidth);
+	}
 }
 
 /*
@@ -517,7 +530,8 @@
 	if (id >= plotdata_.size())
 		plotdata_.resize(id + 1);
 
-	plotdata_[id].dataset   = data;
+	plotdata_[id].absolute_data = data;
+	plotdata_[id].relative_data = new std::vector<uint32_t>(); // Will be filled in the update() function.
 	plotdata_[id].showplot  = false;
 	plotdata_[id].plotcolor = color;
 
@@ -544,17 +558,9 @@
 	needs_update_ = true;
 }
 
-/*
- * Set sample rate the data uses
- */
-void WuiPlotArea::set_sample_rate(uint32_t const id) {
-	sample_rate_ = id;
-	needs_update_ = true;
-}
-
 
 void WuiPlotAreaSlider::draw(RenderTarget & dst) {
-	int32_t new_game_time_id = plot_area_.get_game_time_id();
+	uint32_t new_game_time_id = plot_area_.get_game_time_id();
 	if (new_game_time_id != last_game_time_id_) {
 		last_game_time_id_ = new_game_time_id;
 		set_labels(plot_area_.get_labels());
@@ -565,31 +571,27 @@
 
 DifferentialPlotArea::DifferentialPlotArea
 		(UI::Panel * const parent,
-		 int32_t const x, int32_t const y, int32_t const w, int32_t const h)
+		 int32_t const x, int32_t const y, int32_t const w, int32_t const h,
+		 uint32_t sample_rate, Plotmode plotmode)
 :
-WuiPlotArea (parent, x, y, w, h)
+WuiPlotArea (parent, x, y, w, h, sample_rate, plotmode)
 {
 	update();
 }
 
 void DifferentialPlotArea::update() {
-	float const xline_length = get_inner_w() - space_at_right  - spacing;
-
-	time_ms_ = get_plot_time();
-
-	// How many do we take together when relative ploting
-	const int32_t how_many = calc_how_many(time_ms_, sample_rate_);
-
-	// Find max and min value
+	const int32_t how_many = initialize_update();
+
+	// Calculate highest scale
 	int32_t max = 0;
 	int32_t min = 0;
 
-	if (plotmode_ == PLOTMODE_ABSOLUTE)  {
+	if (plotmode_ == Plotmode::kAbsolute)  {
 		for (uint32_t i = 0; i < plotdata_.size(); ++i)
 			if (plotdata_[i].showplot) {
-				for (uint32_t l = 0; l < plotdata_[i].dataset->size(); ++l) {
-					int32_t temp = (*plotdata_[i].dataset)[l] -
-									(*negative_plotdata_[i].dataset)[l];
+				for (uint32_t l = 0; l < plotdata_[i].absolute_data->size(); ++l) {
+					int32_t temp = (*plotdata_[i].absolute_data)[l] -
+									(*negative_plotdata_[i].absolute_data)[l];
 					if (max < temp) max = temp;
 					if (min > temp) min = temp;
 				}
@@ -598,8 +600,8 @@
 		for (uint32_t plot = 0; plot < plotdata_.size(); ++plot)
 			if (plotdata_[plot].showplot) {
 
-				const std::vector<uint32_t> & dataset = *plotdata_[plot].dataset;
-				const std::vector<uint32_t> & ndataset = *negative_plotdata_[plot].dataset;
+				const std::vector<uint32_t> & dataset = *plotdata_[plot].absolute_data;
+				const std::vector<uint32_t> & ndataset = *negative_plotdata_[plot].absolute_data;
 
 				int32_t add = 0;
 				//  Relative data, first entry is always zero.
@@ -616,77 +618,43 @@
 
 	// Use equal positive and negative range
 	min = abs(min);
-	highest_scale_ = 0;
-	if (min > max) {
-		highest_scale_ = min;
-	} else {
-		highest_scale_ = max;
-	}
-
-	//  plot the pixels
-	sub_ =
-		xline_length
-		/
-		(static_cast<float>(time_ms_)
-		 /
-		 static_cast<float>(sample_rate_));
-	for (uint32_t plot = 0; plot < plotdata_.size(); ++plot)
-		if (plotdata_[plot].showplot) {
-			std::vector<uint32_t> const * dataset = plotdata_[plot].dataset;
-			std::vector<uint32_t> const * ndataset = negative_plotdata_[plot].dataset;
-
-			std::vector<uint32_t> data_;
-			if (plotmode_ == PLOTMODE_RELATIVE) {
-				int32_t add = 0;
+	highest_scale_ =  (min > max) ? min : max;
+
+	//  Calculate plot data
+	if (plotmode_ == Plotmode::kRelative) {
+		for (uint32_t plot = 0; plot < plotdata_.size(); ++plot) {
+			if (plotdata_[plot].showplot) {
+				std::vector<uint32_t> const * dataset = plotdata_[plot].absolute_data;
+				std::vector<uint32_t> const * ndataset = negative_plotdata_[plot].absolute_data;
+				uint32_t add = 0;
 				// Relative data, first entry is always zero
-				data_.push_back(0);
+				plotdata_[plot].relative_data->push_back(0);
 				for (uint32_t i = 0; i < dataset->size(); ++i) {
 					add += (*dataset)[i] - (*ndataset)[i];
 					if (0 == ((i + 1) % how_many)) {
-						data_.push_back(add);
+						plotdata_[plot].relative_data->push_back(add);
 						add = 0;
 					}
 				}
-				dataset = &data_;
-				sub_ = xline_length / static_cast<float>(nr_samples);
 			}
 		}
+	}
 }
 
 void DifferentialPlotArea::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;
+	float const yoffset = kSpacing + ((get_inner_h() - kSpaceBottom) - kSpacing) / 2;
+	draw_plot(dst, yoffset, std::to_string(highest_scale_), 2 * highest_scale_);
 
-	time_ms_ = get_plot_time();
-	draw_diagram(time_ms_, get_inner_w(), get_inner_h(), xline_length, dst);
+	// Print the min value
+	draw_value
+		((boost::format("-%u") % (highest_scale_)).str(), RGBColor(125, 0, 0),
+		 Point(get_inner_w() - kSpaceRight - 2, get_inner_h() - kSpacing - 15), dst);
 
 	// draw zero line
-	dst.draw_line_strip({FloatPoint(get_inner_w() - space_at_right, yoffset),
-	                     FloatPoint(get_inner_w() - space_at_right - xline_length, yoffset)},
+	dst.draw_line_strip({FloatPoint(get_inner_w() - kSpaceRight, yoffset),
+	                     FloatPoint(get_inner_w() - kSpaceRight - xline_length_, yoffset)},
 							  kZeroLineColor, kPlotLinesWidth);
-
-	// Print the min and max values
-	draw_value
-		(std::to_string(highest_scale_), RGBColor(60, 125, 0),
-		 Point(get_inner_w() - space_at_right - 2, spacing + 2), dst);
-
-	draw_value
-		((boost::format("-%u") % highest_scale_).str(), RGBColor(125, 0, 0),
-		 Point(get_inner_w() - space_at_right - 2, get_inner_h() - spacing - 15), dst);
-
-	//  plot the pixels
-	for (uint32_t plot = 0; plot < plotdata_.size(); ++plot)
-		if (plotdata_[plot].showplot) {
-
-			RGBColor color = plotdata_[plot].plotcolor;
-			std::vector<uint32_t> const * dataset = plotdata_[plot].dataset;
-
-			// Highest_scale represent the space between zero line and top.
-			// -> half of the whole differential plot area
-			draw_plot_line(dst, dataset, yline_length, highest_scale_ * 2, sub_, color, yoffset);
-		}
 }
 
 /**
@@ -696,9 +664,10 @@
 void DifferentialPlotArea::register_negative_plot_data
 	(uint32_t const id, std::vector<uint32_t> const * const data) {
 
-	if (id >= negative_plotdata_.size())
+	if (id >= negative_plotdata_.size()) {
 		negative_plotdata_.resize(id + 1);
+	}
 
-	negative_plotdata_[id].dataset   = data;
+	negative_plotdata_[id].absolute_data = data;
 	needs_update_ = true;
 }

=== modified file 'src/wui/plot_area.h'
--- src/wui/plot_area.h	2016-03-19 17:30:37 +0000
+++ src/wui/plot_area.h	2016-05-19 11:15:41 +0000
@@ -44,16 +44,17 @@
 		TIME_16_HOURS,
 		TIME_GAME,
 	};
-	enum PLOTMODE {
-		//  Always take the samples of some times together, so that the graph is
-		//  not completely zigg-zagged.
-		PLOTMODE_RELATIVE,
-
-		PLOTMODE_ABSOLUTE
+	enum class Plotmode {
+		//  Always aggregate the samples of some time periods, so that the graph is
+		//  not completely zig-zagged.
+		kRelative,
+		kAbsolute
 	};
 
+	// sample_rate is in in milliseconds
 	WuiPlotArea
-		(UI::Panel * parent, int32_t x, int32_t y, int32_t w, int32_t h);
+		(UI::Panel * parent, int32_t x, int32_t y, int32_t w, int32_t h,
+		 uint32_t sample_rate, Plotmode plotmode);
 
 	/// Calls update() if needed
 	void think() override;
@@ -65,51 +66,53 @@
 		needs_update_ = true;
 	}
 
-	void set_time_id(int32_t time) {
+	void set_time_id(uint32_t time) {
 		if (time == game_time_id_)
 			set_time(TIME_GAME);
 		else
 			set_time(static_cast<TIME>(time));
 		needs_update_ = true;
 	}
-	TIME get_time() {return static_cast<TIME>(time_); }
-	int32_t get_time_id() {
+	TIME get_time() const {return static_cast<TIME>(time_); }
+	int32_t get_time_id() const {
 		if (time_ == TIME_GAME)
 			return game_time_id_;
 		else
 			return time_;
 	}
-	void set_sample_rate(uint32_t id); // in milliseconds
 
-	int32_t get_game_time_id();
+	uint32_t get_game_time_id();
 
 	void register_plot_data
 		(uint32_t id, const std::vector<uint32_t> * data, RGBColor);
 	void show_plot(uint32_t id, bool t);
 
-	void set_plotmode(int32_t id) {plotmode_ = id;}
-
 	void set_plotcolor(uint32_t id, RGBColor color);
 
-	std::vector<std::string> get_labels();
+	std::vector<std::string> get_labels() const;
 
 protected:
+	void draw_plot(RenderTarget& dst, float const yoffset, const std::string& yscale_label, uint32_t highest_scale);
 	void draw_plot_line
-		(RenderTarget & dst, std::vector<uint32_t> const * dataset, float const yline_length,
+		(RenderTarget & dst, std::vector<uint32_t> const * dataset,
 		 uint32_t const highest_scale, float const sub, RGBColor const color, int32_t yoffset);
-	uint32_t get_plot_time();
+	uint32_t get_plot_time() const;
 	/// Recalculates the data
 	virtual void update();
+	// Initializes relative_dataset and time scaling.
+	// Returns how many values will be aggregated when relative plotting
+	int32_t initialize_update();
 
 	struct PlotData {
-		const std::vector<uint32_t> * dataset;
+		const std::vector<uint32_t> * absolute_data; // The absolute dataset
+		std::vector<uint32_t> * relative_data; // The relative dataset
 		bool                          showplot;
 		RGBColor                      plotcolor;
 	};
 	std::vector<PlotData> plotdata_;
 
-	int32_t                 plotmode_;
-	int32_t                 sample_rate_;
+	Plotmode                plotmode_;
+	uint32_t                sample_rate_;
 
 	/// Whether there has ben a data update since the last time that think() was executed
 	bool needs_update_;
@@ -117,15 +120,17 @@
 	uint32_t lastupdate_;
 
 	/// For first updating and then plotting the data
+	float const xline_length_;
+	float const yline_length_;
 	uint32_t time_ms_;
 	uint32_t highest_scale_;
 	float sub_;
 
 private:
-	uint32_t get_game_time();
+	uint32_t get_game_time() const;
 
 	TIME                    time_;  // How much do you want to list
-	int32_t                 game_time_id_; // what label is used for TIME_GAME
+	uint32_t                game_time_id_; // what label is used for TIME_GAME
 };
 
 /**
@@ -161,7 +166,7 @@
 
 private:
 	WuiPlotArea & plot_area_;
-	int32_t last_game_time_id_;
+	uint32_t last_game_time_id_;
 };
 
 /**
@@ -172,7 +177,8 @@
 struct DifferentialPlotArea : public WuiPlotArea {
 public:
 	DifferentialPlotArea
-		(UI::Panel * parent, int32_t x, int32_t y, int32_t w, int32_t h);
+		(UI::Panel * parent, int32_t x, int32_t y, int32_t w, int32_t h,
+		 uint32_t sample_rate, Plotmode plotmode);
 
 	void draw(RenderTarget &) override;
 
@@ -184,13 +190,14 @@
 	void update() override;
 
 private:
-
 	/**
-	 * for the negative plotdata only the values matter. The color and
+	 * For the negative plotdata, only the values matter. The color and
 	 * visibility is determined by the normal plotdata.
+	 * We don't need relative data to fill - this is also done in the
+	 * normal plotdata
 	 */
 	struct ReducedPlotData {
-		const std::vector<uint32_t> * dataset;
+		const std::vector<uint32_t> * absolute_data;
 	};
 	std::vector<ReducedPlotData>  negative_plotdata_;
 };

=== modified file 'src/wui/ware_statistics_menu.cc'
--- src/wui/ware_statistics_menu.cc	2016-03-30 07:20:05 +0000
+++ src/wui/ware_statistics_menu.cc	2016-05-19 11:15:41 +0000
@@ -164,9 +164,9 @@
 	plot_production_ =
 		new WuiPlotArea
 			(tabs,
-			 0, 0, kPlotWidth, kPlotHeight + kSpacing);
-	plot_production_->set_sample_rate(STATISTICS_SAMPLE_TIME);
-	plot_production_->set_plotmode(WuiPlotArea::PLOTMODE_RELATIVE);
+			 0, 0, kPlotWidth, kPlotHeight + kSpacing,
+			 kStatisticsSampleTime,
+			 WuiPlotArea::Plotmode::kRelative);
 
 	tabs->add
 		("production", g_gr->images().get(pic_tab_production),
@@ -175,9 +175,9 @@
 	plot_consumption_ =
 		new WuiPlotArea
 			(tabs,
-			 0, 0, kPlotWidth, kPlotHeight + kSpacing);
-	plot_consumption_->set_sample_rate(STATISTICS_SAMPLE_TIME);
-	plot_consumption_->set_plotmode(WuiPlotArea::PLOTMODE_RELATIVE);
+			 0, 0, kPlotWidth, kPlotHeight + kSpacing,
+			 kStatisticsSampleTime,
+			 WuiPlotArea::Plotmode::kRelative);
 
 	tabs->add
 		("consumption", g_gr->images().get(pic_tab_consumption),
@@ -186,9 +186,9 @@
 	plot_economy_ =
 		new DifferentialPlotArea
 			(tabs,
-			 0, 0, kPlotWidth, kPlotHeight + kSpacing);
-	plot_economy_->set_sample_rate(STATISTICS_SAMPLE_TIME);
-	plot_economy_->set_plotmode(WuiPlotArea::PLOTMODE_RELATIVE);
+			 0, 0, kPlotWidth, kPlotHeight + kSpacing,
+			 kStatisticsSampleTime,
+			 WuiPlotArea::Plotmode::kRelative);
 
 	tabs->add
 		("economy_health", g_gr->images().get(pic_tab_economy),
@@ -196,9 +196,9 @@
 
 	plot_stock_ = new WuiPlotArea
 			(tabs,
-			 0, 0, kPlotWidth, kPlotHeight + kSpacing);
-	plot_stock_->set_sample_rate(STATISTICS_SAMPLE_TIME);
-	plot_stock_->set_plotmode(WuiPlotArea::PLOTMODE_ABSOLUTE);
+			 0, 0, kPlotWidth, kPlotHeight + kSpacing,
+			 kStatisticsSampleTime,
+			 WuiPlotArea::Plotmode::kAbsolute);
 
 	tabs->add
 		("stock", g_gr->images().get(pic_tab_stock),


Follow ups