You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
8.3KB

  1. /* Copyright 2013-2019 Matt Tytel
  2. *
  3. * vital is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * vital is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with vital. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "line_generator.h"
  17. #include "poly_utils.h"
  18. #include "futils.h"
  19. LineGenerator::LineGenerator(int resolution) : points_(), powers_(), num_points_(2), resolution_(resolution),
  20. loop_(false), smooth_(false), linear_(true), render_count_(0) {
  21. buffer_ = std::make_unique<vital::mono_float[]>(resolution + kExtraValues);
  22. initLinear();
  23. }
  24. void LineGenerator::initLinear() {
  25. points_[0] = { 0.0f, 1.0f };
  26. points_[1] = { 1.0f, 0.0f };
  27. powers_[0] = 0.0f;
  28. powers_[1] = 0.0f;
  29. num_points_ = 2;
  30. linear_ = true;
  31. name_ = "Linear";
  32. smooth_ = false;
  33. render();
  34. }
  35. void LineGenerator::initTriangle() {
  36. points_[0] = { 0.0f, 1.0f };
  37. points_[1] = { 0.5f, 0.0f };
  38. points_[2] = { 1.0f, 1.0f };
  39. powers_[0] = 0.0f;
  40. powers_[1] = 0.0f;
  41. powers_[2] = 0.0f;
  42. num_points_ = 3;
  43. linear_ = false;
  44. name_ = "Triangle";
  45. smooth_ = false;
  46. render();
  47. }
  48. void LineGenerator::initSquare() {
  49. num_points_ = 5;
  50. points_[0] = { 0.0f, 1.0f };
  51. points_[1] = { 0.0f, 0.0f };
  52. points_[2] = { 0.5f, 0.0f };
  53. points_[3] = { 0.5f, 1.0f };
  54. points_[4] = { 1.0f, 1.0f };
  55. for (int i = 0; i < num_points_; ++i)
  56. powers_[i] = 0.0f;
  57. linear_ = false;
  58. name_ = "Square";
  59. smooth_ = false;
  60. render();
  61. }
  62. void LineGenerator::initSin() {
  63. points_[0] = { 0.0f, 1.0f };
  64. points_[1] = { 0.5f, 0.0f };
  65. points_[2] = { 1.0f, 1.0f };
  66. powers_[0] = 0.0f;
  67. powers_[1] = 0.0f;
  68. powers_[2] = 0.0f;
  69. num_points_ = 3;
  70. linear_ = false;
  71. name_ = "Sin";
  72. smooth_ = true;
  73. render();
  74. }
  75. void LineGenerator::initSawUp() {
  76. num_points_ = 3;
  77. points_[0] = { 0.0f, 1.0f };
  78. points_[1] = { 1.0f, 0.0f };
  79. points_[2] = { 1.0f, 1.0f };
  80. powers_[0] = 0.0f;
  81. powers_[1] = 0.0f;
  82. powers_[2] = 0.0f;
  83. linear_ = false;
  84. name_ = "Saw Up";
  85. smooth_ = false;
  86. render();
  87. }
  88. void LineGenerator::initSawDown() {
  89. num_points_ = 3;
  90. points_[0] = { 0.0f, 0.0f };
  91. points_[1] = { 1.0f, 1.0f };
  92. points_[2] = { 1.0f, 0.0f };
  93. powers_[0] = 0.0f;
  94. powers_[1] = 0.0f;
  95. powers_[2] = 0.0f;
  96. linear_ = false;
  97. name_ = "Saw Down";
  98. smooth_ = false;
  99. render();
  100. }
  101. json LineGenerator::stateToJson() {
  102. json point_data;
  103. json power_data;
  104. for (int i = 0; i < num_points_; ++i) {
  105. std::pair<float, float> point = points_[i];
  106. point_data.push_back(point.first);
  107. point_data.push_back(point.second);
  108. power_data.push_back(powers_[i]);
  109. }
  110. json data;
  111. data["num_points"] = num_points_;
  112. data["points"] = point_data;
  113. data["powers"] = power_data;
  114. data["name"] = name_;
  115. data["smooth"] = smooth_;
  116. return data;
  117. }
  118. bool LineGenerator::isValidJson(json data) {
  119. if (!data.count("num_points") || !data.count("points") || !data.count("powers"))
  120. return false;
  121. json point_data = data["points"];
  122. json power_data = data["powers"];
  123. return point_data.is_array() && power_data.is_array();
  124. }
  125. void LineGenerator::jsonToState(json data) {
  126. num_points_ = data["num_points"];
  127. json point_data = data["points"];
  128. json power_data = data["powers"];
  129. name_ = "";
  130. if (data.count("name"))
  131. name_ = data["name"].get<std::string>();
  132. smooth_ = false;
  133. if (data.count("smooth"))
  134. smooth_ = data["smooth"];
  135. int num_read = kMaxPoints;
  136. num_read = std::min(num_read, static_cast<int>(power_data.size()));
  137. for (int i = 0; i < num_read; ++i) {
  138. points_[i] = std::pair<float, float>(point_data[2 * i], point_data[2 * i + 1]);
  139. powers_[i] = power_data[i];
  140. }
  141. checkLineIsLinear();
  142. render();
  143. }
  144. void LineGenerator::render() {
  145. render_count_++;
  146. int point_index = 0;
  147. std::pair<float, float> last_point = points_[point_index];
  148. float current_power = 0.0f;
  149. std::pair<float, float> current_point = points_[point_index];
  150. if (loop_) {
  151. last_point = points_[num_points_ - 1];
  152. last_point.first -= 1.0f;
  153. current_power = powers_[num_points_ - 1];
  154. }
  155. for (int i = 0; i < resolution_; ++i) {
  156. float x = i / (resolution_ - 1.0f);
  157. float t = 1.0f;
  158. if (current_point.first > last_point.first)
  159. t = (x - last_point.first) / (current_point.first - last_point.first);
  160. if (smooth_)
  161. t = smoothTransition(t);
  162. t = vital::utils::clamp(vital::futils::powerScale(t, current_power), 0.0f, 1.0f);
  163. float y = last_point.second + t * (current_point.second - last_point.second);
  164. buffer_[i + 1] = 1.0f - y;
  165. while (x > current_point.first && point_index < num_points_) {
  166. current_power = powers_[point_index % num_points_];
  167. point_index++;
  168. last_point = current_point;
  169. current_point = points_[point_index % num_points_];
  170. if (point_index >= num_points_) {
  171. current_point.first += 1.0f;
  172. break;
  173. }
  174. }
  175. }
  176. if (loop_) {
  177. buffer_[0] = buffer_[resolution_];
  178. buffer_[resolution_ + 1] = buffer_[1];
  179. buffer_[resolution_ + 2] = buffer_[2];
  180. }
  181. else {
  182. buffer_[0] = buffer_[1];
  183. buffer_[resolution_ + 1] = buffer_[resolution_];
  184. buffer_[resolution_ + 2] = buffer_[resolution_];
  185. }
  186. }
  187. float LineGenerator::valueAtPhase(float phase) {
  188. float scaled_phase = vital::utils::clamp(phase, 0.0f, 1.0f) * resolution_;
  189. int index = scaled_phase;
  190. return vital::utils::interpolate(buffer_[index + 1], buffer_[index + 2], scaled_phase - index);
  191. }
  192. void LineGenerator::checkLineIsLinear() {
  193. linear_ = !smooth_ && num_points_ == 2 && powers_[0] == 0.0f &&
  194. points_[0] == std::pair<float, float>(0.0f, 1.0f) &&
  195. points_[1] == std::pair<float, float>(1.0f, 0.0f);
  196. }
  197. float LineGenerator::getValueBetweenPoints(float x, int index_from, int index_to) {
  198. VITAL_ASSERT(index_from >= 0 && index_to < num_points_);
  199. std::pair<float, float> first = points_[index_from];
  200. std::pair<float, float> second = points_[index_to];
  201. float power = powers_[index_from];
  202. float width = second.first - first.first;
  203. if (width <= 0.0f)
  204. return second.second;
  205. float t = (x - first.first) / width;
  206. if (smooth_)
  207. t = smoothTransition(t);
  208. t = vital::utils::clamp(vital::futils::powerScale(t, power), 0.0f, 1.0f);
  209. return t * (second.second - first.second) + first.second;
  210. }
  211. float LineGenerator::getValueAtPhase(float phase) {
  212. for (int i = 0; i < num_points_ - 1; ++i) {
  213. if (points_[i].first <= phase && points_[i + 1].first >= phase)
  214. return getValueBetweenPoints(phase, i, i + 1);
  215. }
  216. return lastPoint().second;
  217. }
  218. void LineGenerator::addPoint(int index, std::pair<float, float> position) {
  219. for (int i = num_points_; i > index; --i) {
  220. points_[i] = points_[i - 1];
  221. powers_[i] = powers_[i - 1];
  222. }
  223. num_points_++;
  224. points_[index] = position;
  225. powers_[index] = 0.0f;
  226. checkLineIsLinear();
  227. }
  228. void LineGenerator::addMiddlePoint(int index) {
  229. VITAL_ASSERT(index > 0);
  230. float x = (points_[index - 1].first + points_[index].first) * 0.5f;
  231. float y = getValueBetweenPoints(x, index - 1, index);
  232. addPoint(index, { x, y });
  233. }
  234. void LineGenerator::removePoint(int index) {
  235. num_points_--;
  236. for (int i = index; i < num_points_; ++i) {
  237. points_[i] = points_[i + 1];
  238. powers_[i] = powers_[i + 1];
  239. }
  240. checkLineIsLinear();
  241. }
  242. void LineGenerator::flipHorizontal() {
  243. for (int i = 0; i < (num_points_ + 1) / 2; ++i) {
  244. float tmp_x = 1.0f - points_[i].first;
  245. float tmp_y = points_[i].second;
  246. points_[i].first = 1.0f - points_[num_points_ - i - 1].first;
  247. points_[i].second = points_[num_points_ - i - 1].second;
  248. points_[num_points_ - i - 1].first = tmp_x;
  249. points_[num_points_ - i - 1].second = tmp_y;
  250. }
  251. for (int i = 0; i < num_points_ / 2; ++i) {
  252. float tmp_power = powers_[i];
  253. powers_[i] = -powers_[num_points_ - i - 2];
  254. powers_[num_points_ - i - 2] = -tmp_power;
  255. }
  256. render();
  257. checkLineIsLinear();
  258. }
  259. void LineGenerator::flipVertical() {
  260. for (int i = 0; i < num_points_; ++i)
  261. points_[i].second = 1.0f - points_[i].second;
  262. render();
  263. checkLineIsLinear();
  264. }