widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #09223
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
SirVer has proposed merging lp:~widelands-dev/widelands/animation_manager into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/animation_manager/+merge/313879
Request for testing help!!!
This code provides a new way to "jump" to events in the map. This is not yet done - there are some NOCOMs in the code. But I am kinda tired with tweaking and wanted some input on the current parameter set.
For testing:
1) Tweak constants at the top of src/wui/map_view.cc and compile.
2) Load a game that spans a big map.
3) Open the message window and jump to the event location. Try various locations.
Please let me know how this "feels". I can elaborate on the choices I made and the things I tried out if that is desired.
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/animation_manager into lp:widelands.
=== modified file 'data/scripting/ui.lua'
--- data/scripting/ui.lua 2016-01-28 05:24:34 +0000
+++ data/scripting/ui.lua 2016-12-26 21:45:30 +0000
@@ -67,6 +67,7 @@
-- PUBLIC FUNCTIONS
-- =======================================================================
+-- NOCOM(#sirver): replace everything in this file through engine functions.
-- RST
-- .. function:: timed_scroll(pts[, dt = 20])
--
=== modified file 'src/base/math.h'
--- src/base/math.h 2016-11-03 07:20:57 +0000
+++ src/base/math.h 2016-12-26 21:45:30 +0000
@@ -40,6 +40,11 @@
return val;
}
+// A simple square function.
+template <typename T> T sqr(const T& a) {
+ return a * a;
+}
+
} // namespace math
#endif // end of include guard: WL_BASE_MATH_H
=== modified file 'src/game_io/game_preload_packet.cc'
--- src/game_io/game_preload_packet.cc 2016-12-10 09:34:41 +0000
+++ src/game_io/game_preload_packet.cc 2016-12-26 21:45:30 +0000
@@ -126,7 +126,7 @@
std::unique_ptr<Texture> texture;
if (ipl != nullptr) { // Player
texture = draw_minimap(
- game, &ipl->player(), ipl->get_view_area(), MiniMapType::kStaticViewWindow, layers);
+ game, &ipl->player(), ipl->view_area(), MiniMapType::kStaticViewWindow, layers);
} else { // Observer
texture = draw_minimap(game, nullptr, Rectf(), MiniMapType::kStaticMap, layers);
}
=== modified file 'src/logic/cmd_luacoroutine.cc'
--- src/logic/cmd_luacoroutine.cc 2016-08-04 15:49:05 +0000
+++ src/logic/cmd_luacoroutine.cc 2016-12-26 21:45:30 +0000
@@ -34,16 +34,16 @@
namespace Widelands {
+CmdLuaCoroutine::~CmdLuaCoroutine() {}
+
void CmdLuaCoroutine::execute(Game& game) {
try {
int rv = cr_->resume();
const uint32_t sleeptime = cr_->pop_uint32();
if (rv == LuaCoroutine::YIELDED) {
- game.enqueue_command(new Widelands::CmdLuaCoroutine(sleeptime, cr_));
- cr_ = nullptr; // Remove our ownership so we don't delete.
+ game.enqueue_command(new Widelands::CmdLuaCoroutine(sleeptime, std::move(cr_)));
} else if (rv == LuaCoroutine::DONE) {
- delete cr_;
- cr_ = nullptr;
+ cr_.reset();
}
} catch (LuaError& e) {
log("Error in Lua Coroutine\n");
@@ -90,6 +90,6 @@
upcast(LuaGameInterface, lgi, &egbase.lua());
assert(lgi); // If this is not true, this is not a game.
- lgi->write_coroutine(fw, cr_);
+ lgi->write_coroutine(fw, cr_.get());
}
}
=== modified file 'src/logic/cmd_luacoroutine.h'
--- src/logic/cmd_luacoroutine.h 2016-08-04 15:49:05 +0000
+++ src/logic/cmd_luacoroutine.h 2016-12-26 21:45:30 +0000
@@ -20,6 +20,7 @@
#ifndef WL_LOGIC_CMD_LUACOROUTINE_H
#define WL_LOGIC_CMD_LUACOROUTINE_H
+#include <memory>
#include <string>
#include "logic/cmd_queue.h"
@@ -28,15 +29,13 @@
namespace Widelands {
struct CmdLuaCoroutine : public GameLogicCommand {
- CmdLuaCoroutine() : GameLogicCommand(0), cr_(nullptr) {
+ CmdLuaCoroutine() : GameLogicCommand(0) {
} // For savegame loading
- CmdLuaCoroutine(uint32_t const init_duetime, LuaCoroutine* const cr)
- : GameLogicCommand(init_duetime), cr_(cr) {
+ CmdLuaCoroutine(uint32_t const init_duetime, std::unique_ptr<LuaCoroutine> cr)
+ : GameLogicCommand(init_duetime), cr_(std::move(cr)) {
}
- ~CmdLuaCoroutine() {
- delete cr_;
- }
+ ~CmdLuaCoroutine();
// Write these commands to a file (for savegames)
void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
@@ -49,7 +48,7 @@
void execute(Game&) override;
private:
- LuaCoroutine* cr_;
+ std::unique_ptr<LuaCoroutine> cr_;
};
}
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2016-10-15 16:36:12 +0000
+++ src/logic/game.cc 2016-12-26 21:45:30 +0000
@@ -306,7 +306,7 @@
table->do_not_warn_about_unaccessed_keys();
win_condition_displayname_ = table->get_string("name");
std::unique_ptr<LuaCoroutine> cr = table->get_coroutine("func");
- enqueue_command(new CmdLuaCoroutine(get_gametime() + 100, cr.release()));
+ enqueue_command(new CmdLuaCoroutine(get_gametime() + 100, std::move(cr)));
} else {
win_condition_displayname_ = "Scenario";
}
=== modified file 'src/logic/player.cc'
--- src/logic/player.cc 2016-12-18 17:02:44 +0000
+++ src/logic/player.cc 2016-12-26 21:45:30 +0000
@@ -200,7 +200,7 @@
table->do_not_warn_about_unaccessed_keys();
std::unique_ptr<LuaCoroutine> cr = table->get_coroutine("func");
cr->push_arg(this);
- game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), cr.release()));
+ game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), std::move(cr)));
// Check if other starting positions are shared in and initialize them as well
for (uint8_t n = 0; n < further_shared_in_player_.size(); ++n) {
@@ -213,7 +213,7 @@
->get_coroutine("func");
ncr->push_arg(this);
ncr->push_arg(further_pos);
- game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), ncr.release()));
+ game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), std::move(ncr)));
}
} else
throw WLWarning(_("Missing starting position"),
=== modified file 'src/scripting/logic.cc'
--- src/scripting/logic.cc 2016-08-04 15:49:05 +0000
+++ src/scripting/logic.cc 2016-12-26 21:45:30 +0000
@@ -146,8 +146,8 @@
LuaGameInterface::~LuaGameInterface() {
}
-LuaCoroutine* LuaGameInterface::read_coroutine(FileRead& fr) {
- LuaCoroutine* rv = new LuaCoroutine(nullptr);
+std::unique_ptr<LuaCoroutine> LuaGameInterface::read_coroutine(FileRead& fr) {
+ std::unique_ptr<LuaCoroutine> rv(new LuaCoroutine(nullptr));
rv->read(lua_state_, fr);
return rv;
}
=== modified file 'src/scripting/logic.h'
--- src/scripting/logic.h 2016-08-04 15:49:05 +0000
+++ src/scripting/logic.h 2016-12-26 21:45:30 +0000
@@ -51,7 +51,7 @@
std::unique_ptr<LuaTable> run_script(const std::string& script) override;
// Input/output for coroutines.
- LuaCoroutine* read_coroutine(FileRead&);
+ std::unique_ptr<LuaCoroutine> read_coroutine(FileRead&);
void write_coroutine(FileWrite&, LuaCoroutine*);
// Input output for the global game state.
=== modified file 'src/scripting/lua_root.cc'
--- src/scripting/lua_root.cc 2016-11-02 05:48:00 +0000
+++ src/scripting/lua_root.cc 2016-12-26 21:45:30 +0000
@@ -19,6 +19,8 @@
#include "scripting/lua_root.h"
+#include <memory>
+
#include <boost/format.hpp>
#include "logic/cmd_luacoroutine.h"
@@ -195,16 +197,17 @@
int nargs = lua_gettop(L);
uint32_t runtime = get_game(L).get_gametime();
if (nargs < 2)
- report_error(L, "Too little arguments!");
+ report_error(L, "Too few arguments!");
if (nargs == 3) {
runtime = luaL_checkuint32(L, 3);
lua_pop(L, 1);
}
- LuaCoroutine* cr = new LuaCoroutine(luaL_checkthread(L, 2));
+ std::unique_ptr<LuaCoroutine> cr(new LuaCoroutine(luaL_checkthread(L, 2)));
lua_pop(L, 2); // Remove coroutine and Game object from stack
- get_game(L).enqueue_command(new Widelands::CmdLuaCoroutine(runtime, cr));
+ get_game(L).enqueue_command(
+ new Widelands::CmdLuaCoroutine(runtime, std::move(cr)));
return 0;
}
=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc 2016-11-23 08:31:25 +0000
+++ src/wui/interactive_base.cc 2016-12-26 21:45:30 +0000
@@ -378,7 +378,7 @@
void InteractiveBase::mainview_move() {
if (m->minimap.window) {
- m->mm->set_view(get_view_area());
+ m->mm->set_view(view_area());
}
}
=== modified file 'src/wui/mapview.cc'
--- src/wui/mapview.cc 2016-11-16 10:01:52 +0000
+++ src/wui/mapview.cc 2016-12-26 21:45:30 +0000
@@ -19,6 +19,8 @@
#include "wui/mapview.h"
+#include <SDL.h>
+
#include "base/macros.h"
#include "base/math.h"
#include "graphic/game_renderer.h"
@@ -34,6 +36,27 @@
namespace {
+// NOCOM(#sirver): maybe replace set_zoom through set_view.
+// NOCOM(#sirver): how to 'reverse' a plan?
+
+// Number of keyframes to generate for a plan. The more points, the smoother
+// the animation (though we also lineraly interpolate between keyframes) and
+// the more work.
+constexpr int kNumKeyFrames = 102;
+
+// The maximum zoom to use in moving animations.
+constexpr float kMaxAnimationZoom = 8.f;
+
+// The time used for paning only automated map movement.
+constexpr float kPanOnlyAnimationTimeMs = 500.f;
+
+// The time used for zooming and paning automated map movement.
+constexpr float kPanAndZoomAnimationTimeMs = 1500.f;
+
+// If the difference between the current zoom and the target zoom in an
+// animation plan is smaller than this value, we will do a pan-only movement.
+constexpr float kPanOnlyZoomThreshold = 0.25f;
+
// Given 'p' on a torus of dimension ('h', 'h') and 'r' that contains this
// point, change 'p' so that r.x < p.x < r.x + r.w and similar for y.
// Containing is defined as such that the shortest distance between the center
@@ -55,6 +78,194 @@
return p;
}
+// Returns the view area, i.e. the currently visible rectangle in map pixel
+// space for the given 'view'.
+Rectf get_view_area(const MapView::View& view, const int width, const int height) {
+ return Rectf(view.viewpoint, width * view.zoom, height * view.zoom);
+}
+
+template <typename T>
+struct KeyFrame {
+ float time_ms;
+ T value;
+};
+
+constexpr float pow2(float t) {
+ return t * t;
+}
+constexpr float pow3(float t) {
+ return t * t * t;
+}
+
+// NOCOM(#sirver): Explain interpolator properties, i.e number of required keypoints
+template <typename T>
+class Interpolator {
+public:
+ Interpolator(std::vector<KeyFrame<T>> keyframes)
+ : keyframes_(std::move(keyframes)) {
+#ifndef NDEBUG
+ assert(keyframes_.size() >= 4);
+ for (size_t i = 1; i < keyframes_.size(); ++i) {
+ assert(keyframes_[i-1].time_ms < keyframes_[i].time_ms);
+ }
+#endif
+ }
+
+ T value(const float time_ms) {
+ if (time_ms <= keyframes_.front().time_ms) {
+ return keyframes_.front().value;
+ }
+ if (time_ms >= keyframes_.back().time_ms) {
+ return keyframes_.back().value;
+ }
+
+ size_t i = 0;
+ while (i < keyframes_.size() && keyframes_[i].time_ms <= time_ms) {
+ ++i;
+ }
+
+ // NOCOM(#sirver): are these assumptions right?
+ // The interpolated point must be in the second interval or later.
+ assert(i > 1);
+ // The interpolated point must not in the last interval.
+ assert(i <= keyframes_.size() - 1);
+ assert(keyframes_[i-1].time_ms <= time_ms);
+ assert(time_ms <= keyframes_[i].time_ms);
+
+ // const float ts0 = keyframes_[i-2].time_ms;
+ const float ts1 = keyframes_[i-1].time_ms;
+ const float ts2 = keyframes_[i].time_ms;
+ // const float ts3 = keyframes_[i+1].time_ms;
+
+ const T& p0 = keyframes_[i-2].value;
+ const T& p1 = keyframes_[i-1].value;
+ const T& p2 = keyframes_[i].value;
+ const T& p3 = keyframes_[i+1].value;
+
+ float u = (time_ms - ts1) / (ts2 - ts1);
+
+ const Vector2f c0 = p1;
+ const Vector2f c1 = -p0 / 2.0f + p2 / 2.0f;
+ const Vector2f c2 = p0 - p1 * (5.0f / 2.0f) + p2 * 2 - p3 / 2.0f;
+ const Vector2f c3 = -p0 / 2.0f + p1 * (3.0f / 2.0f) - p2 * (3.0f / 2.0f) + p3 / 2.0f;
+
+ return c0 + c1 * u + c2 * pow2(u) + c3 * pow3(u);
+ }
+
+private:
+ std::vector<KeyFrame<T>> keyframes_;
+
+ DISALLOW_COPY_AND_ASSIGN(Interpolator);
+};
+
+
+template <typename T>
+T mix(float t, const T& a, const T& b) {
+ return a * (1.f - t) + b * t;
+}
+
+// https://en.wikipedia.org/wiki/Smoothstep
+template <typename T>
+class SmoothstepInterpolator {
+public:
+ SmoothstepInterpolator(const T& start, const T& end, float dt)
+ : start_(start), end_(end), dt_(dt) {
+ }
+
+ T value(const float time_ms) {
+ const float t = math::clamp(time_ms / dt_, 0.f, 1.f);
+ return mix(pow2(t) * (3.f - 2.f * t), start_, end_);
+ // NOCOM(#sirver): Smootherstep - maybe accelerations too slowly, but definitvely smoother.
+ // return mix(pow3(t) * (t * (t * 6.f - 15.f) + 10.f), start_, end_);
+ }
+
+private:
+ T start_, end_;
+ float dt_;
+
+ DISALLOW_COPY_AND_ASSIGN(SmoothstepInterpolator);
+};
+
+template <typename T, typename P>
+class SymmetricInterpolator {
+public:
+ SymmetricInterpolator(const T& start, const T& end, float dt)
+ : inner_(start, end, dt / 2.f), dt_(dt) {
+ }
+
+ T value(const float time_ms) {
+ const float t = math::clamp(time_ms / dt_, 0.f, 1.f);
+ if (t < 0.5f) {
+ return inner_.value(t * dt_);
+ } else {
+ return inner_.value((1.f - t) * dt_);
+ }
+ }
+
+private:
+ P inner_;
+ float dt_;
+
+ DISALLOW_COPY_AND_ASSIGN(SymmetricInterpolator);
+};
+
+
+
+// Calculates a animation plan from 'start' to 'end_viewpoint' - both at 'zoom',
+// taking 'duraction_ms'. The animation is assumed to start at the
+// time of calling the function
+// NOCOM(#sirver): do everything in floats, only convert at the end.
+std::vector<MapView::TimestampedView> plan_animation(const Widelands::Map& map,
+ const Vector2f& start,
+ const Vector2f& end,
+ const float start_zoom,
+ const int width,
+ const int height) {
+ const Vector2f start_center =
+ get_view_area(MapView::View{start, start_zoom}, width, height).center();
+ const Vector2f end_center =
+ get_view_area(MapView::View{end, start_zoom}, width, height).center();
+ const Vector2f center_point_change =
+ MapviewPixelFunctions::calc_pix_difference(map, end_center, start_center);
+
+ // Heuristic: How many screens is the target point away from the current
+ // viewpoint? We use it to decide the zoom out factor and scroll speed.
+ float num_screens = std::max(std::abs(center_point_change.x) / (width * start_zoom),
+ std::abs(center_point_change.y) / (height * start_zoom));
+
+ // If the target is 4 screens away, we zoom out to x4. If we would not zoom
+ // out, we do not interpolate the zoom at all. This avoids rounding errors.
+ const float target_zoom = math::clamp(num_screens, start_zoom, kMaxAnimationZoom);
+ const float delta_zoom = target_zoom - start_zoom;
+ const bool pan_and_zoom_animation = delta_zoom > kPanOnlyZoomThreshold;
+ const float duration_ms =
+ pan_and_zoom_animation ? kPanAndZoomAnimationTimeMs : kPanOnlyAnimationTimeMs;
+
+ SymmetricInterpolator<float, SmoothstepInterpolator<float>> zoom_t(
+ start_zoom, target_zoom, static_cast<float>(duration_ms));
+
+ SmoothstepInterpolator<Vector2f> center_point_t(
+ start_center, start_center + center_point_change, duration_ms);
+ const uint32_t start_time = SDL_GetTicks();
+ std::vector<MapView::TimestampedView> plan;
+ plan.push_back(MapView::TimestampedView{start_time, MapView::View{start, start_zoom}});
+ for (int i = 1; i < kNumKeyFrames - 2; i++) {
+ float dt = (duration_ms / kNumKeyFrames) * i;
+ const float zoom = pan_and_zoom_animation ? zoom_t.value(dt) : start_zoom;
+ const Vector2f center_point = center_point_t.value(dt);
+ const Vector2f viewpoint = center_point - Vector2f(width * zoom / 2.f, height * zoom / 2.f);
+ plan.push_back(MapView::TimestampedView{
+ static_cast<uint32_t>(std::lround(start_time + dt)), MapView::View{viewpoint, zoom}});
+ }
+ // Correct numeric instabilities. We want to land precisely at 'end'.
+ const Vector2f end_viewpoint = (start_center + center_point_change) -
+ Vector2f(width * start_zoom / 2.f, height * start_zoom / 2.f);
+ plan.push_back(
+ MapView::TimestampedView{static_cast<uint32_t>(std::lround(start_time + duration_ms)),
+ MapView::View{end_viewpoint, start_zoom}});
+ return plan;
+}
+
} // namespace
MapView::MapView(
@@ -62,8 +273,7 @@
: UI::Panel(parent, x, y, w, h),
renderer_(new GameRenderer()),
intbase_(player),
- viewpoint_(0.f, 0.f),
- zoom_(1.f),
+ view_{Vector2f(0.f, 0.f), 1.f},
dragging_(false) {
}
@@ -71,15 +281,15 @@
}
Vector2f MapView::get_viewpoint() const {
- return viewpoint_;
+ return view_.viewpoint;
}
Vector2f MapView::to_panel(const Vector2f& map_pixel) const {
- return MapviewPixelFunctions::map_to_panel(viewpoint_, zoom_, map_pixel);
+ return MapviewPixelFunctions::map_to_panel(view_.viewpoint, view_.zoom, map_pixel);
}
Vector2f MapView::to_map(const Vector2f& panel_pixel) const {
- return MapviewPixelFunctions::panel_to_map(viewpoint_, zoom_, panel_pixel);
+ return MapviewPixelFunctions::panel_to_map(view_.viewpoint, view_.zoom, panel_pixel);
}
/// Moves the mouse cursor so that it is directly above the given node
@@ -105,18 +315,18 @@
const Widelands::Map& map = intbase().egbase().map();
const Vector2f map_pixel = MapviewPixelFunctions::to_map_pixel_with_normalization(map, c);
- const Rectf view_area = get_view_area();
+ const Rectf area = view_area();
- const Vector2f view_center = view_area.center();
- const int w = MapviewPixelFunctions::get_map_end_screen_x(map);
- const int h = MapviewPixelFunctions::get_map_end_screen_y(map);
+ const Vector2f view_center = area.center();
const Vector2f dist = MapviewPixelFunctions::calc_pix_difference(map, view_center, map_pixel);
// Check if the point is visible on screen.
- if (dist.x > view_area.w / 2.f || dist.y > view_area.h / 2.f) {
+ if (std::abs(dist.x) > area.w / 2.f || std::abs(dist.y) > area.h / 2.f) {
return;
}
- const Vector2f in_panel = to_panel(move_inside(map_pixel, view_area, w, h));
+ const Vector2f in_panel =
+ to_panel(move_inside(map_pixel, area, MapviewPixelFunctions::get_map_end_screen_x(map),
+ MapviewPixelFunctions::get_map_end_screen_y(map)));
set_mouse_pos(round(in_panel));
track_sel(in_panel);
}
@@ -124,6 +334,28 @@
void MapView::draw(RenderTarget& dst) {
Widelands::EditorGameBase& egbase = intbase().egbase();
+ if (!current_plan_.empty()) {
+ uint32_t now = SDL_GetTicks();
+ size_t i = 0;
+ while (i < current_plan_.size() && current_plan_[i].t < now) {
+ ++i;
+ }
+ if (i == current_plan_.size()) {
+ current_plan_.clear();
+ } else if (i > 0) {
+ // Linearly interpolate between the next and the last.
+ float t = (now - current_plan_[i - 1].t) /
+ static_cast<float>(current_plan_[i].t - current_plan_[i - 1].t);
+ const float zoom =
+ mix(t, current_plan_[i - 1].view.zoom, current_plan_[i].view.zoom);
+ set_zoom(zoom);
+ const Vector2f viewpoint = mix(
+ t, current_plan_[i - 1].view.viewpoint, current_plan_[i].view.viewpoint);
+ set_viewpoint(viewpoint, false);
+ // log("#sirver %d,%.4f,%.4f,%.4f\n", now, viewpoint.x, viewpoint.y, zoom);
+ }
+ }
+
if (upcast(Widelands::Game, game, &egbase)) {
// Bail out if the game isn't actually loaded.
// This fixes a crash with displaying an error dialog during loading.
@@ -142,18 +374,18 @@
if (upcast(InteractivePlayer const, interactive_player, &intbase())) {
renderer_->rendermap(
- egbase, viewpoint_, zoom_, interactive_player->player(), draw_text, &dst);
+ egbase, view_.viewpoint, view_.zoom, interactive_player->player(), draw_text, &dst);
} else {
- renderer_->rendermap(egbase, viewpoint_, zoom_, static_cast<TextToDraw>(draw_text), &dst);
+ renderer_->rendermap(egbase, view_.viewpoint, view_.zoom, static_cast<TextToDraw>(draw_text), &dst);
}
}
float MapView::get_zoom() const {
- return zoom_;
+ return view_.zoom;
}
void MapView::set_zoom(const float zoom) {
- zoom_ = zoom;
+ view_.zoom = zoom;
}
/*
@@ -162,9 +394,9 @@
===============
*/
void MapView::set_viewpoint(const Vector2f& viewpoint, bool jump) {
- viewpoint_ = viewpoint;
+ view_.viewpoint = viewpoint;
const Widelands::Map& map = intbase().egbase().map();
- MapviewPixelFunctions::normalize_pix(map, &viewpoint_);
+ MapviewPixelFunctions::normalize_pix(map, &view_.viewpoint);
changeview(jump);
}
@@ -180,16 +412,19 @@
}
void MapView::center_view_on_map_pixel(const Vector2f& pos) {
- const Rectf view_area = get_view_area();
- set_viewpoint(pos - Vector2f(view_area.w / 2.f, view_area.h / 2.f), true);
+ const Rectf area = view_area();
+ const Vector2f target_view = pos - Vector2f(area.w / 2.f, area.h / 2.f);
+ const Widelands::Map& map = intbase().egbase().map();
+ current_plan_ =
+ plan_animation(map, view_.viewpoint, target_view, view_.zoom, get_w(), get_h());
}
-Rectf MapView::get_view_area() const {
- return Rectf(viewpoint_, get_w() * zoom_, get_h() * zoom_);
+Rectf MapView::view_area() const {
+ return get_view_area(view_, get_w(), get_h());
}
void MapView::pan_by(Vector2i delta_pixels) {
- set_viewpoint(get_viewpoint() + delta_pixels.cast<float>() * zoom_, false);
+ set_viewpoint(get_viewpoint() + delta_pixels.cast<float>() * view_.zoom, false);
}
void MapView::stop_dragging() {
@@ -248,7 +483,7 @@
}
constexpr float kPercentPerMouseWheelTick = 0.02f;
- float zoom = zoom_ * static_cast<float>(
+ float zoom = view_.zoom * static_cast<float>(
std::pow(1.f - math::sign(y) * kPercentPerMouseWheelTick, std::abs(y)));
zoom_around(zoom, last_mouse_pos_.cast<float>());
return true;
@@ -263,9 +498,9 @@
// Zoom around the current mouse position. See
// http://stackoverflow.com/questions/2916081/zoom-in-on-a-point-using-scale-and-translate
// for a good explanation of this math.
- const Vector2f offset = -panel_pixel * (new_zoom - zoom_);
- zoom_ = new_zoom;
- set_viewpoint(viewpoint_ + offset, false);
+ const Vector2f offset = -panel_pixel * (new_zoom - view_.zoom);
+ view_.zoom = new_zoom;
+ set_viewpoint(view_.viewpoint + offset, false);
}
/*
@@ -293,10 +528,10 @@
constexpr float kPercentPerKeyPress = 0.10f;
switch (code.sym) {
case SDLK_PLUS:
- zoom_around(zoom_ - kPercentPerKeyPress, Vector2f(get_w() / 2.f, get_h() / 2.f));
+ zoom_around(view_.zoom - kPercentPerKeyPress, Vector2f(get_w() / 2.f, get_h() / 2.f));
return true;
case SDLK_MINUS:
- zoom_around(zoom_ + kPercentPerKeyPress, Vector2f(get_w() / 2.f, get_h() / 2.f));
+ zoom_around(view_.zoom + kPercentPerKeyPress, Vector2f(get_w() / 2.f, get_h() / 2.f));
return true;
case SDLK_0:
zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f));
=== modified file 'src/wui/mapview.h'
--- src/wui/mapview.h 2016-11-03 07:20:57 +0000
+++ src/wui/mapview.h 2016-12-26 21:45:30 +0000
@@ -37,6 +37,22 @@
* Implements a view of a map. It is used to render a valid map on the screen.
*/
struct MapView : public UI::Panel {
+ struct View {
+ // Mappixel of top-left pixel of this MapView.
+ Vector2f viewpoint;
+
+ // Current zoom value.
+ float zoom;
+ };
+
+ struct TimestampedView {
+ // Time in milliseconds since the game was launched. Animations always
+ // happen in real-time, not in gametime. Therefore they are also not
+ // affected by pause.
+ uint32_t t;
+ View view;
+ };
+
MapView(UI::Panel* const parent,
const int32_t x,
const int32_t y,
@@ -60,7 +76,7 @@
void center_view_on_map_pixel(const Vector2f& pos);
Vector2f get_viewpoint() const;
- Rectf get_view_area() const;
+ Rectf view_area() const;
float get_zoom() const;
// Set the zoom to the new value without changing view_point. For the user
@@ -104,10 +120,12 @@
std::unique_ptr<GameRenderer> renderer_;
InteractiveBase& intbase_;
- Vector2f viewpoint_;
- float zoom_;
+ View view_;
Vector2i last_mouse_pos_;
bool dragging_;
+
+ // If not empty(), the MapView is currently following this animation plan.
+ std::vector<TimestampedView> current_plan_;
};
#endif // end of include guard: WL_WUI_MAPVIEW_H
=== modified file 'src/wui/quicknavigation.cc'
--- src/wui/quicknavigation.cc 2016-11-23 08:31:25 +0000
+++ src/wui/quicknavigation.cc 2016-12-26 21:45:30 +0000
@@ -41,7 +41,7 @@
}
void QuickNavigation::view_changed(bool jump) {
- const Rectf view_area = map_view_->get_view_area();
+ const Rectf view_area = map_view_->view_area();
if (havefirst_ && update_) {
if (!jump) {
// Check if the anchor is moved outside the screen. If that is the
=== modified file 'src/wui/watchwindow.cc'
--- src/wui/watchwindow.cc 2016-10-24 14:07:28 +0000
+++ src/wui/watchwindow.cc 2016-12-26 21:45:30 +0000
@@ -295,7 +295,7 @@
* Cause the main mapview_ to jump to our current position.
*/
void WatchWindow::do_goto() {
- warp_mainview(mapview_.get_view_area().center());
+ warp_mainview(mapview_.view_area().center());
}
/**
Follow ups
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: GunChleoc, 2017-01-11
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: noreply, 2017-01-08
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-08
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: GunChleoc, 2017-01-06
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-05
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2017-01-05
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2017-01-05
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: TiborB, 2017-01-04
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: kaputtnik, 2017-01-04
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2017-01-04
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: TiborB, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: kaputtnik, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: TiborB, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: kaputtnik, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-03
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: TiborB, 2017-01-03
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2017-01-02
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: Klaus Halfmann, 2017-01-02
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-02
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: SirVer, 2017-01-02
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-31
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-31
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-29
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-29
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-29
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-28
-
Re: [Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: TiborB, 2016-12-27
-
[Merge] lp:~widelands-dev/widelands/animation_manager into lp:widelands
From: bunnybot, 2016-12-27