From fc69946ceb5059323d90887ff366eaa39eeda61a Mon Sep 17 00:00:00 2001
From: namark <namark@disroot.org>
Date: Wed, 26 Aug 2020 02:07:24 +0400
Subject: [PATCH] Initial version of symphony.

---
 source/simple/motion/algorithm.hpp | 23 ++++++++
 source/simple/motion/common.hpp    |  9 ++++
 source/simple/motion/melody.hpp    | 63 ++++++----------------
 source/simple/motion/symphony.hpp  | 84 +++++++++++++++++++++++++++++-
 4 files changed, 130 insertions(+), 49 deletions(-)

diff --git a/source/simple/motion/algorithm.hpp b/source/simple/motion/algorithm.hpp
index d605bcd..8e15229 100644
--- a/source/simple/motion/algorithm.hpp
+++ b/source/simple/motion/algorithm.hpp
@@ -1,6 +1,7 @@
 #ifndef SIMPLE_MOTION_ALGORITHM_HPP
 #define SIMPLE_MOTION_ALGORITHM_HPP
 #include <utility>
+#include "common.hpp"
 
 namespace simple::motion
 {
@@ -25,6 +26,28 @@ int loop(Target&& target, Motion& motion, typename Motion::duration delta)
 	return count;
 }
 
+template <typename It, typename Duration, typename Function>
+constexpr
+multi_advance_result<Duration, It> sequence(It begin, It end, Duration delta, Function&& advance)
+{
+	support::range<It> updated{begin, begin};
+	for(; begin != end; ++begin)
+	{
+		auto result = std::apply(
+			std::forward<Function>(advance),
+			std::forward_as_tuple(begin, delta)
+		);
+		++updated.upper();
+
+		delta = result.remaining;
+
+		if(delta <= Duration{})
+			return {true, delta, updated};
+	}
+
+	return {false, delta, updated};
+}
+
 } // namespace simple::motion
 
 #endif /* end of include guard */
diff --git a/source/simple/motion/common.hpp b/source/simple/motion/common.hpp
index 7dc1ffe..add029b 100644
--- a/source/simple/motion/common.hpp
+++ b/source/simple/motion/common.hpp
@@ -1,5 +1,6 @@
 #ifndef SIMPLE_MOTION_COMMON_HPP
 #define SIMPLE_MOTION_COMMON_HPP
+#include "simple/support/range.hpp"
 
 namespace simple::motion
 {
@@ -12,6 +13,14 @@ struct advance_result
 	explicit operator bool() { return success; }
 };
 
+template <typename Duration, typename It = size_t>
+struct multi_advance_result : public advance_result<Duration>
+{
+	support::range<It> updated {};
+	using advance_result<Duration>::operator bool;
+};
+
+
 } // namespace simple::motion
 
 #endif /* end of include guard */
diff --git a/source/simple/motion/melody.hpp b/source/simple/motion/melody.hpp
index cd11e1a..29e2f59 100644
--- a/source/simple/motion/melody.hpp
+++ b/source/simple/motion/melody.hpp
@@ -1,21 +1,14 @@
 #ifndef SIMPLE_MOTION_MELODY_HPP
 #define SIMPLE_MOTION_MELODY_HPP
-#include "simple/support/range.hpp"
 #include "simple/support/tuple_utils.hpp"
 #include "common.hpp"
+#include "algorithm.hpp"
 
 //TODO: might want to separate the iterator type (elapsed, current_index) from the structure type (total, start, target)
 
 namespace simple::motion
 {
 
-template <typename Duration>
-struct multi_advance_result : public advance_result<Duration>
-{
-	support::range<size_t> updated {0,0};
-	using advance_result<Duration>::operator bool;
-};
-
 template <typename... Motions>
 class melody
 {
@@ -39,22 +32,21 @@ class melody
 	// WIP: TODO: need complete restructure can't nest melodies
 	multi_advance_result<duration> advance(duration delta)
 	{
-		support::range<size_t> updated{current_index, current_index};
-		for(; current_index < sizeof...(Motions); ++current_index)
-		{
-			auto result = support::apply_for(current_index, [&delta](auto&& movement)
+		return sequence
+		(
+			current_index, sizeof...(Motions),
+			delta,
+			[this](auto current, auto delta)
 			{
-				return movement.advance(delta);
-			}, movements);
-			++updated.upper();
-
-			delta = result.remaining;
-
-			if(delta <= duration{})
-				return {true, delta, updated};
-		}
-
-		return {false, delta, updated};
+				return support::apply_for(current,
+					[&delta](auto&& move)
+					{
+						return move.advance(delta);
+					},
+					movements
+				);
+			}
+		);
 	}
 
 	template <size_t... I>
@@ -124,31 +116,6 @@ class melody
 			for_all<F, I-1>(std::forward<F>(f));
 	}
 
-	multi_advance_result<duration> advance(duration delta, support::range<size_t> updated)
-	{
-		auto [success, remaining] = support::apply_for(current_index, [&delta](auto&& movement)
-		{
-			return movement.advance(delta);
-		}, movements);
-
-		if(!success)
-		{
-			if(current_index == sizeof...(Motions) - 1)
-				return {success, remaining, updated};
-
-			++current_index;
-
-			if(remaining > duration{})
-				return advance(remaining, {updated.lower(), updated.upper()+1});
-			else
-				return {true, remaining, updated};
-		}
-		else
-		{
-			return {success, remaining, updated};
-		}
-	}
-
 };
 
 } // namespace simple::motion
diff --git a/source/simple/motion/symphony.hpp b/source/simple/motion/symphony.hpp
index 650e1ed..f1f2262 100644
--- a/source/simple/motion/symphony.hpp
+++ b/source/simple/motion/symphony.hpp
@@ -1,10 +1,92 @@
 #ifndef SIMPLE_MOTION_SYMPHONY_HPP
 #define SIMPLE_MOTION_SYMPHONY_HPP
+#include <vector>
+#include <variant>
+#include "simple/support/algorithm.hpp"
+#include "common.hpp"
+#include "algorithm.hpp"
 
 namespace simple::motion
 {
 
-class symphony; // TODO: dynamic melody with all strings attached
+template <typename... Motions>
+struct variant : public std::variant<Motions...>
+{
+	using duration = std::common_type_t<typename Motions::duration...>;
+
+	using std::variant<Motions...>::variant;
+
+	decltype(auto) advance(duration delta)
+	{
+		return std::visit([&delta](auto&& motion)
+		{
+			return motion.advance(delta);
+		}, *this);
+	}
+
+	decltype(auto) value()
+	{
+		return std::visit([](auto&& motion)
+		{
+			return motion.value();
+		}, *this);
+	}
+
+};
+
+template <typename Motion,
+	template<typename T> typename Range =
+		std::vector>
+class symphony
+{
+	public:
+	using duration = typename Motion::duration;
+	using iterator = typename Range<Motion>::iterator;
+
+	explicit symphony(Range<Motion> motions) :
+		motions(std::move(motions)),
+		current(std::begin(this->motions))
+	{
+	}
+
+	multi_advance_result<duration, iterator>
+	advance(duration delta)
+	{
+		return sequence
+		(
+			current, motions.end(),
+			delta,
+			[this](auto current, auto delta)
+			{
+				return (*current).advance(delta);
+			}
+		);
+	}
+
+	template <typename TargetRange>
+	advance_result<duration> move(TargetRange&& target, duration delta)
+	{
+		using std::begin;
+		using std::end;
+		using support::map_range;
+		using support::reverse;
+
+		auto r = advance(delta);
+
+		auto to_update = reverse(map_range(
+			r.updated, target, begin(motions) ));
+		auto updated = begin(reverse(r.updated));
+
+		for(auto&& i : to_update)
+			i = (*updated++).value();
+		return r;
+	}
+
+	private:
+	Range<Motion> motions;
+	iterator current;
+
+};
 
 } // namespace simple::motion
 
-- 
GitLab