The Battle for Wesnoth  1.15.12+dev
frame_private.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include "lexical_cast.hpp"
19 
20 #include <vector>
21 
22 namespace image { class locator; }
23 
24 template<typename T, typename D>
26 {
27 public:
28  using data_t = std::vector<std::pair<D, int>>;
29 
30  progressive_base(const std::string& input)
31  : data_()
32  , input_(input)
33  {}
34 
35  virtual const T get_current_element(int current_time, T default_val) const = 0;
36 
37  virtual bool does_not_change() const
38  {
39  return data_.size() <= 1;
40  }
41 
42  int duration() const
43  {
44  int total = 0;
45  for(const auto& entry : data_) {
46  total += entry.second;
47  }
48 
49  return total;
50  }
51 
52  std::string get_original() const
53  {
54  return input_;
55  }
56 
58  {
59  return data_;
60  }
61 
62  const data_t& data() const
63  {
64  return data_;
65  }
66 
67  virtual ~progressive_base() {}
68 
69 private:
71  std::string input_;
72 };
73 
74 template<typename T>
75 class progressive_pair : public progressive_base<T, std::pair<T, T>>
76 {
77 public:
78  progressive_pair(const std::string& input = "", int duration = 0)
79  : progressive_base<T, std::pair<T, T>>(input)
80  {
81  auto& base_data = progressive_pair_base_type::data();
82 
83  const int split_flag = utils::REMOVE_EMPTY; // useless to strip spaces
84 
85  const std::vector<std::string> comma_split = utils::split(input, ',', split_flag);
86  const int time_chunk = std::max<int>(1, duration / std::max<int>(comma_split.size(), 1));
87 
88  for(const auto& entry : comma_split) {
89  std::vector<std::string> colon_split = utils::split(entry, ':', split_flag);
90  int time = 0;
91 
92  try {
93  time = (colon_split.size() > 1) ? std::stoi(colon_split[1]) : time_chunk;
94  } catch(const std::invalid_argument&) {
95  //ERR_NG << "Invalid time in unit animation: " << colon_split[1] << "\n";
96  }
97 
98  try {
99  std::vector<std::string> range = utils::split(colon_split[0],'~',split_flag);
100  T range0 = lexical_cast<T>(range[0]);
101  T range1 = (range.size() > 1) ? lexical_cast<T>(range[1]) : range0;
102 
103  base_data.push_back({{range0, range1}, time});
104  } catch(const bad_lexical_cast&) {}
105  }
106  }
107 
108  virtual const T get_current_element(int current_time, T default_val = T()) const override
109  {
110  const auto& base_data = progressive_pair_base_type::data();
111  const int& base_duration = progressive_pair_base_type::duration();
112 
113  if(base_data.empty()) {
114  return default_val;
115  }
116 
117  int time = 0;
118  unsigned int i = 0;
119  const int searched_time = std::clamp(current_time, 0, base_duration);
120 
121  while(time < searched_time && i < base_data.size()) {
122  time += base_data[i].second;
123  ++i;
124  }
125 
126  if(i != 0) {
127  i--;
128  time -= base_data[i].second;
129  }
130 
131  const T first = base_data[i].first.first;
132  const T second = base_data[i].first.second;
133 
134  return T((
135  static_cast<double>(searched_time - time) /
136  static_cast<double>(base_data[i].second)
137  ) * (second - first) + first);
138  }
139 
140  bool does_not_change() const override
141  {
142  const auto& base_data = progressive_pair_base_type::data();
143  return base_data.empty() || (base_data.size() == 1 && base_data[0].first.first == base_data[0].first.second);
144  }
145 
146 private:
148 };
149 
150 template<typename T>
152 {
153 public:
154  progressive_single(const std::string& input = "", int duration = 0)
155  : progressive_base<T, T>(input)
156  {
157  auto& base_data = progressive_single_base_type::data();
158 
159  const std::vector<std::string> first_pass = utils::square_parenthetical_split(input);
160  int time_chunk = std::max<int>(duration, 1);
161 
162  if(duration > 1 && !first_pass.empty()) {
163  // If duration specified, divide evenly the time for items with unspecified times
164  int total_specified_time = 0;
165 
166  for(const std::string& fp_string : first_pass) {
167  std::vector<std::string> second_pass = utils::split(fp_string, ':');
168  if(second_pass.size() > 1) {
169  try {
170  total_specified_time += std::stoi(second_pass[1]);
171  } catch(const std::invalid_argument&) {
172  //ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
173  }
174  }
175  }
176 
177  time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
178  }
179 
180  for(const std::string& fp_string : first_pass) {
181  std::vector<std::string> second_pass = utils::split(fp_string, ':');
182  if(second_pass.size() > 1) {
183  try {
184  base_data.push_back({std::move(second_pass[0]), std::stoi(second_pass[1])});
185  } catch(const std::invalid_argument&) {
186  //ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
187  }
188  } else {
189  base_data.push_back({std::move(second_pass[0]) ,time_chunk});
190  }
191  }
192  }
193 
194  virtual const T get_current_element(int current_time, T default_val = T()) const override
195  {
196  const auto& base_data = progressive_single_base_type::data();
197 
198  if(base_data.empty()) {
199  return default_val;
200  }
201 
202  int time = 0;
203  unsigned int i = 0;
204 
205  while(time < current_time && i < base_data.size()) {
206  time += base_data[i].second;
207  ++i;
208  }
209 
210  // TODO: what is this for?
211  if(i) {
212  i--;
213  }
214 
215  return base_data[i].first;
216  }
217 
218 private:
220 };
221 
222 // Common types used by the unit frame code.
225 
virtual bool does_not_change() const
virtual const T get_current_element(int current_time, T default_val=T()) const override
progressive_base(const std::string &input)
New lexcical_cast header.
std::string get_original() const
STL namespace.
To lexical_cast(From value)
Lexical cast converts one type to another.
bool does_not_change() const override
virtual const T get_current_element(int current_time, T default_val=T()) const override
int duration() const
std::vector< std::pair< std::string, int > > data_t
virtual ~progressive_base()
progressive_pair(const std::string &input="", int duration=0)
std::string input_
progressive_single(const std::string &input="", int duration=0)
std::size_t i
Definition: function.cpp:940
std::vector< std::string > split(const config_attribute_value &val)
Functions to load and save images from/to disk.
Thrown when a lexical_cast fails.
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
const data_t & data() const