Refactor/cleanup of SyncTrack and added serialization to/from json.
This commit is contained in:
		
							parent
							
								
									3b5537fc9d
								
							
						
					
					
						commit
						36c7f7a11e
					
				
							
								
								
									
										177
									
								
								src/SyncTrack.h
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								src/SyncTrack.h
									
									
									
									
									
								
							| @ -6,57 +6,62 @@ | ||||
| #define ANIMTESTBED_SYNCTRACK_H | ||||
| 
 | ||||
| #include <cassert> | ||||
| #include <cmath> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "3rdparty/json/json.hpp" | ||||
| 
 | ||||
| constexpr int cSyncTrackMaxIntervals = 8; | ||||
| 
 | ||||
| /// Metadata used for synced animation blending.
 | ||||
| //
 | ||||
| // A SyncTrack consists of multiple SyncInterval that are adjacent to each
 | ||||
| // other.
 | ||||
| //
 | ||||
| // Important definitions:
 | ||||
| //
 | ||||
| // - Absolute Time: time within an animation duration in seconds.
 | ||||
| // - Ratio: time relative to the animations duration, e.g. 0.5 corresponds to
 | ||||
| //   50% of the duration.
 | ||||
| // - SyncTime is a floating point value where the integer parts defines the
 | ||||
| //   SyncInterval and the fractional part the fraction within the interval. I.e.
 | ||||
| //   a SyncTime of 5.332 means it is ~33% through interval 5.
 | ||||
| //
 | ||||
| // A SyncInterval is defined by a ratio of the starting point and the ratio of
 | ||||
| // the interval's duration.
 | ||||
| struct SyncTrack { | ||||
|   SyncTrack() : m_duration(0.f), m_num_intervals(0) { | ||||
|   SyncTrack() : m_duration(0.f), m_num_intervals(1) { | ||||
|     for (int i = 0; i < cSyncTrackMaxIntervals; i++) { | ||||
|       m_sync_markers[i] = 0.f; | ||||
|       m_interval_ratio[i] = 0.f; | ||||
|       m_interval_ratio[i] = 0.f; | ||||
|       m_interval_start_ratio[i] = 0.f; | ||||
|       m_interval_duration_ratio[i] = 0.f; | ||||
|     } | ||||
| 
 | ||||
|     m_interval_duration_ratio[0] = 1.0f; | ||||
|   } | ||||
| 
 | ||||
|   float m_duration; | ||||
|   int m_num_intervals; | ||||
|   float m_sync_markers[cSyncTrackMaxIntervals]; | ||||
|   float m_interval_start[cSyncTrackMaxIntervals]; | ||||
|   float m_interval_ratio[cSyncTrackMaxIntervals]; | ||||
| 
 | ||||
|   void CalcIntervals() { | ||||
|     if (m_num_intervals == 0) { | ||||
|       m_num_intervals = 1; | ||||
|       m_sync_markers[0] = 0.f; | ||||
|     } | ||||
|     for (int i = 0; i < m_num_intervals; i++) { | ||||
|       assert(m_sync_markers[i] >= 0.f && m_sync_markers[i] <= 1.0f); | ||||
| 
 | ||||
|       int end_index = i < m_num_intervals - 1 ? i + 1 : 0; | ||||
| 
 | ||||
|       m_interval_start[i] = m_sync_markers[i]; | ||||
|       float interval_end = m_sync_markers[end_index]; | ||||
| 
 | ||||
|       if (interval_end < m_interval_start[i]) { | ||||
|         interval_end += 1.0f; | ||||
|       } | ||||
| 
 | ||||
|       m_interval_ratio[i] = interval_end - m_interval_start[i]; | ||||
|     } | ||||
|   } | ||||
|   float m_interval_start_ratio | ||||
|       [cSyncTrackMaxIntervals];  //< Starting time of interval in absolute time.
 | ||||
|   float m_interval_duration_ratio[cSyncTrackMaxIntervals];  //<
 | ||||
| 
 | ||||
|   float CalcSyncFromAbsTime(float abs_time) { | ||||
|     float sync_time = fmodf(abs_time, m_duration) / m_duration; | ||||
|     for (int i = 0; i < m_num_intervals; i++) { | ||||
|       float query_abs_time = abs_time; | ||||
|       float interval_start = m_interval_start_ratio[i] * m_duration; | ||||
|       float interval_end = | ||||
|           interval_start + m_interval_duration_ratio[i] * m_duration; | ||||
| 
 | ||||
|     int interval_index = 0; | ||||
|     while (sync_time >= m_interval_ratio[interval_index]) { | ||||
|       sync_time -= m_interval_ratio[interval_index]; | ||||
|       interval_index++; | ||||
|       if (query_abs_time < interval_start) { | ||||
|         query_abs_time += m_duration; | ||||
|       } | ||||
|       if (query_abs_time >= interval_start && query_abs_time < interval_end) { | ||||
|         return float(i) | ||||
|                + (query_abs_time - interval_start) | ||||
|                      / (interval_end - interval_start); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return float(interval_index) + sync_time / m_interval_ratio[interval_index]; | ||||
|     assert(false && "Invalid absolute time"); | ||||
|     return -1.f; | ||||
|   } | ||||
| 
 | ||||
|   float CalcRatioFromSyncTime(float sync_time) { | ||||
| @ -64,8 +69,8 @@ struct SyncTrack { | ||||
|     int interval = int(sync_time - interval_ratio); | ||||
| 
 | ||||
|     return fmodf( | ||||
|         m_interval_start[interval] | ||||
|             + m_interval_ratio[interval] * interval_ratio, | ||||
|         m_interval_start_ratio[interval] | ||||
|             + m_interval_duration_ratio[interval] * interval_ratio, | ||||
|         1.0f); | ||||
|   } | ||||
| 
 | ||||
| @ -78,8 +83,11 @@ struct SyncTrack { | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < m_num_intervals; i++) { | ||||
|       if ((fabsf(m_interval_start[i] - other.m_interval_start[i]) > 1.0e-5) | ||||
|           || (fabsf(m_interval_ratio[i] - other.m_interval_ratio[i]) | ||||
|       if ((fabsf(m_interval_start_ratio[i] - other.m_interval_start_ratio[i]) | ||||
|            > 1.0e-5) | ||||
|           || (fabsf( | ||||
|                   m_interval_duration_ratio[i] | ||||
|                   - other.m_interval_duration_ratio[i]) | ||||
|               > 1.0e-5)) { | ||||
|         return false; | ||||
|       } | ||||
| @ -88,18 +96,40 @@ struct SyncTrack { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   /** Constructs SyncTrack from markers.
 | ||||
|    * | ||||
|    * Markers are specified in absolute time and must be >= 0 and <= duration. | ||||
|    * They define the start of the interval. The last marker is implicitly the | ||||
|    * (possibly looped) first marker. | ||||
|    */ | ||||
|   static SyncTrack CreateFromMarkers( | ||||
|       float duration, | ||||
|       int n_markers, | ||||
|       float markers[cSyncTrackMaxIntervals]) { | ||||
|       const std::vector<float>& markers) { | ||||
|     assert(markers.size() > 0); | ||||
|     assert(markers.size() < cSyncTrackMaxIntervals); | ||||
| 
 | ||||
|     SyncTrack result; | ||||
|     result.m_duration = duration; | ||||
|     result.m_num_intervals = n_markers; | ||||
|     for (int i = 0; i < n_markers; i++) { | ||||
|       result.m_sync_markers[i] = markers[i]; | ||||
|     } | ||||
|     result.m_num_intervals = markers.size(); | ||||
| 
 | ||||
|     result.CalcIntervals(); | ||||
|     for (int i = 0; i < markers.size(); i++) { | ||||
|       assert(markers[i] >= 0.f && markers[i] <= duration); | ||||
| 
 | ||||
|       int end_index = i == (markers.size() - 1) ? 0 : i + 1; | ||||
| 
 | ||||
|       float interval_start = markers[i]; | ||||
|       float interval_end = markers[end_index]; | ||||
| 
 | ||||
|       if (interval_end == interval_start) { | ||||
|         interval_end = interval_start + duration; | ||||
|       } else if (interval_end < interval_start) { | ||||
|         interval_end += duration; | ||||
|       } | ||||
| 
 | ||||
|       result.m_interval_start_ratio[i] = interval_start / duration; | ||||
|       result.m_interval_duration_ratio[i] = | ||||
|           (interval_end - interval_start) / duration; | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
|   } | ||||
| @ -115,41 +145,62 @@ struct SyncTrack { | ||||
|         (1.0f - weight) * track_A.m_duration + weight * track_B.m_duration; | ||||
| 
 | ||||
|     float interval_0_offset = | ||||
|         track_B.m_interval_start[0] - track_A.m_interval_start[0]; | ||||
|         track_B.m_interval_start_ratio[0] - track_A.m_interval_start_ratio[0]; | ||||
|     if (interval_0_offset > 0.5f) { | ||||
|       interval_0_offset = -fmodf(1.f - interval_0_offset, 1.0f); | ||||
|     } else if (interval_0_offset < -0.5) { | ||||
|       interval_0_offset = fmodf(1.f + interval_0_offset, 1.0f); | ||||
|     } | ||||
| 
 | ||||
|     result.m_interval_start[0] = fmodf( | ||||
|         1.0 + (1.0f - weight) * track_A.m_interval_start[0] | ||||
|             + weight * (track_A.m_interval_start[0] + interval_0_offset), | ||||
|     result.m_interval_start_ratio[0] = fmodf( | ||||
|         1.0 + (1.0f - weight) * track_A.m_interval_start_ratio[0] | ||||
|             + weight * (track_A.m_interval_start_ratio[0] + interval_0_offset), | ||||
|         1.0f); | ||||
|     result.m_sync_markers[0] = result.m_interval_start[0]; | ||||
| 
 | ||||
|     for (int i = 0; i < result.m_num_intervals; i++) { | ||||
|       float interval_duration_A = track_A.m_interval_ratio[i]; | ||||
|       float interval_duration_B = track_B.m_interval_ratio[i]; | ||||
|       result.m_interval_ratio[i] = | ||||
|       float interval_duration_A = track_A.m_interval_duration_ratio[i]; | ||||
|       float interval_duration_B = track_B.m_interval_duration_ratio[i]; | ||||
|       result.m_interval_duration_ratio[i] = | ||||
|           (1.0f - weight) * interval_duration_A + weight * interval_duration_B; | ||||
| 
 | ||||
|       if (i < cSyncTrackMaxIntervals) { | ||||
|         result.m_interval_start[i + 1] = | ||||
|             result.m_interval_start[i] + result.m_interval_ratio[i]; | ||||
|         if (result.m_interval_start[i + 1] > 1.0f) { | ||||
|           result.m_interval_start[i + 1] = | ||||
|               fmodf(result.m_interval_start[i + 1], 1.0f); | ||||
|         result.m_interval_start_ratio[i + 1] = | ||||
|             result.m_interval_start_ratio[i] | ||||
|             + result.m_interval_duration_ratio[i]; | ||||
|         if (result.m_interval_start_ratio[i + 1] > 1.0f) { | ||||
|           result.m_interval_start_ratio[i + 1] = | ||||
|               fmodf(result.m_interval_start_ratio[i + 1], 1.0f); | ||||
|         } | ||||
| 
 | ||||
|         result.m_sync_markers[i + 1] = result.m_interval_start[i + 1]; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     assert (result.m_num_intervals < cSyncTrackMaxIntervals); | ||||
|     assert(result.m_num_intervals < cSyncTrackMaxIntervals); | ||||
| 
 | ||||
|     return result; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| inline void to_json(nlohmann::json& j, const SyncTrack& sync_track) { | ||||
|   j["type"] = "SyncTrack"; | ||||
|   j["duration"] = sync_track.m_duration; | ||||
|   for (int i = 0; i < sync_track.m_num_intervals; i++) { | ||||
|     j["interval"][i]["start"] = sync_track.m_interval_start_ratio[i]; | ||||
|     j["interval"][i]["ratio"] = sync_track.m_interval_duration_ratio[i]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| inline void from_json(const nlohmann::json& j, SyncTrack& sync_track) { | ||||
|   assert(j["type"] == "SyncTrack"); | ||||
| 
 | ||||
|   sync_track.m_duration = j["duration"]; | ||||
| 
 | ||||
|   sync_track.m_num_intervals = j["interval"].size(); | ||||
|   assert(sync_track.m_num_intervals < cSyncTrackMaxIntervals); | ||||
| 
 | ||||
|   for (int i = 0; i < sync_track.m_num_intervals; i++) { | ||||
|     sync_track.m_interval_start_ratio[i] = j["interval"][i]["start"]; | ||||
|     sync_track.m_interval_duration_ratio[i] = j["interval"][i]["ratio"]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_SYNCTRACK_H
 | ||||
|  | ||||
| @ -6,197 +6,228 @@ | ||||
| #include "catch.hpp" | ||||
| 
 | ||||
| TEST_CASE("Basic", "[SyncTrack]") { | ||||
|   SyncTrack track_A; | ||||
|   track_A.m_num_intervals = 2; | ||||
|   track_A.m_duration = 2.0; | ||||
|   track_A.m_interval_start[0] = 0.f; | ||||
|   track_A.m_interval_ratio[0] = 0.7; | ||||
|   track_A.m_interval_start[1] = 0.7f; | ||||
|   track_A.m_interval_ratio[1] = 0.3; | ||||
|   SyncTrack track_a; | ||||
|   track_a.m_num_intervals = 2; | ||||
|   track_a.m_duration = 2.0; | ||||
|   track_a.m_interval_start_ratio[0] = 0.f; | ||||
|   track_a.m_interval_duration_ratio[0] = 0.7; | ||||
|   track_a.m_interval_start_ratio[1] = 0.7f; | ||||
|   track_a.m_interval_duration_ratio[1] = 0.3; | ||||
| 
 | ||||
|   SyncTrack track_B; | ||||
|   track_B.m_num_intervals = 2; | ||||
|   track_B.m_duration = 1.5; | ||||
|   track_B.m_interval_start[0] = 0.0f; | ||||
|   track_B.m_interval_ratio[0] = 0.6; | ||||
|   track_B.m_interval_start[1] = 0.6f; | ||||
|   track_B.m_interval_ratio[1] = 0.4; | ||||
|   SyncTrack track_b; | ||||
|   track_b.m_num_intervals = 2; | ||||
|   track_b.m_duration = 1.5; | ||||
|   track_b.m_interval_start_ratio[0] = 0.0f; | ||||
|   track_b.m_interval_duration_ratio[0] = 0.6; | ||||
|   track_b.m_interval_start_ratio[1] = 0.6f; | ||||
|   track_b.m_interval_duration_ratio[1] = 0.4; | ||||
| 
 | ||||
|   WHEN("Calculating sync time of track_B at 0.5 duration") { | ||||
|     float sync_time_at_0_75 = | ||||
|         track_B.CalcSyncFromAbsTime(0.5 * track_B.m_duration); | ||||
|         track_b.CalcSyncFromAbsTime(0.5 * track_b.m_duration); | ||||
|     REQUIRE(sync_time_at_0_75 == Catch::Detail::Approx(0.83333)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating sync time of track_B at 0.6 duration") { | ||||
|     float sync_time_at_0_6 = | ||||
|         track_B.CalcSyncFromAbsTime(0.6 * track_B.m_duration); | ||||
|         track_b.CalcSyncFromAbsTime(0.6 * track_b.m_duration); | ||||
|     REQUIRE(sync_time_at_0_6 == Catch::Detail::Approx(1.0)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating sync time of track_B at 0.7 duration") { | ||||
|     float sync_time_at_0_7 = | ||||
|         track_B.CalcSyncFromAbsTime(0.7 * track_B.m_duration); | ||||
|         track_b.CalcSyncFromAbsTime(0.7 * track_b.m_duration); | ||||
|     REQUIRE(sync_time_at_0_7 == Catch::Detail::Approx(1.25)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating sync time of track_B at 0.0 duration") { | ||||
|     float sync_time_at_1_0 = | ||||
|         track_B.CalcSyncFromAbsTime(0.0 * track_B.m_duration); | ||||
|         track_b.CalcSyncFromAbsTime(0.0 * track_b.m_duration); | ||||
|     REQUIRE(sync_time_at_1_0 == Catch::Detail::Approx(0.0)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating sync time of track_B at 1.0 duration") { | ||||
|     float sync_time_at_1_0 = | ||||
|         track_B.CalcSyncFromAbsTime(0.9999 * track_B.m_duration); | ||||
|         track_b.CalcSyncFromAbsTime(0.9999 * track_b.m_duration); | ||||
|     REQUIRE(sync_time_at_1_0 == Catch::Detail::Approx(2.0).epsilon(0.001f)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating ratio from sync time on track_A at 0.83333") { | ||||
|     float ratio = track_A.CalcRatioFromSyncTime(0.83333333); | ||||
|     float ratio = track_a.CalcRatioFromSyncTime(0.83333333); | ||||
|     REQUIRE(ratio == Catch::Detail::Approx(0.5833333)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Calculating ratio from sync time on track_A at 0.83333") { | ||||
|     float ratio = track_A.CalcRatioFromSyncTime(1.25); | ||||
|     float ratio = track_a.CalcRatioFromSyncTime(1.25); | ||||
|     REQUIRE(ratio == Catch::Detail::Approx(0.775)); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Blending two synctracks with weight 0.") { | ||||
|     SyncTrack blended = SyncTrack::Blend(0.f, track_A, track_B); | ||||
|     SyncTrack blended = SyncTrack::Blend(0.f, track_a, track_b); | ||||
| 
 | ||||
|     THEN("Result must equal track_A") { REQUIRE(track_A == blended); } | ||||
|     THEN("Result must equal track_A") { REQUIRE(track_a == blended); } | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Blending two synctracks with weight 1.") { | ||||
|     SyncTrack blended = SyncTrack::Blend(1.f, track_A, track_B); | ||||
|     SyncTrack blended = SyncTrack::Blend(1.f, track_a, track_b); | ||||
| 
 | ||||
|     THEN("Result must equal track_B") { REQUIRE(track_B == blended); } | ||||
|     THEN("Result must equal track_B") { REQUIRE(track_b == blended); } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Sync Marker Interval Calculation", "[SyncTrack]") { | ||||
|   SyncTrack track_A; | ||||
|   track_A.m_num_intervals = 2; | ||||
|   track_A.m_duration = 2.0; | ||||
|   track_A.m_sync_markers[0] = 0.9; | ||||
|   track_A.m_sync_markers[1] = 0.2; | ||||
| TEST_CASE("Sync Track From Marker", "[SyncTrack]") { | ||||
|   SyncTrack track = SyncTrack::CreateFromMarkers(2.0f, {0.9f, 0.2f}); | ||||
| 
 | ||||
|   WHEN("Calculating intervals") { | ||||
|     track_A.CalcIntervals(); | ||||
|   WHEN("Querying Ratios") { | ||||
|     CHECK(track.m_interval_start_ratio[0] == Catch::Detail::Approx(0.45f)); | ||||
|     CHECK(track.m_interval_duration_ratio[0] == Catch::Detail::Approx(0.65f)); | ||||
| 
 | ||||
|     CHECK(track_A.m_interval_start[0] == Catch::Detail::Approx(0.9f)); | ||||
|     CHECK(track_A.m_interval_ratio[0] == Catch::Detail::Approx(0.3f)); | ||||
|     CHECK(track.m_interval_start_ratio[1] == Catch::Detail::Approx(0.1f)); | ||||
|     CHECK(track.m_interval_duration_ratio[1] == Catch::Detail::Approx(0.35f)); | ||||
| 
 | ||||
|     CHECK(track_A.m_interval_start[1] == Catch::Detail::Approx(0.2f)); | ||||
|     CHECK(track_A.m_interval_ratio[1] == Catch::Detail::Approx(0.7f)); | ||||
|     WHEN("Querying ratio at sync time at 0.001") { | ||||
|       float ratio = track.CalcRatioFromSyncTime(0.0001f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.45).epsilon(0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Querying ratio at sync time at 1.001") { | ||||
|       float ratio = track_A.CalcRatioFromSyncTime(1.0001f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.2).epsilon(0.001)); | ||||
|     WHEN("Querying ratio at sync time at 0.9999") { | ||||
|       float ratio = track.CalcRatioFromSyncTime(0.9999f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.1).epsilon(0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Querying ratio at sync time at 1.001") { | ||||
|       float ratio = track_A.CalcRatioFromSyncTime(0.0001f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.9).epsilon(0.001)); | ||||
|       float ratio = track.CalcRatioFromSyncTime(1.0001f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.1).epsilon(0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Querying ratio at sync time at 1.9999") { | ||||
|       float ratio = track_A.CalcRatioFromSyncTime(0.9999f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.2).epsilon(0.001)); | ||||
|       float ratio = track.CalcRatioFromSyncTime(1.9999f); | ||||
|       CHECK(ratio == Catch::Detail::Approx(0.45).epsilon(0.001)); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Blending sync track with 3 events") { | ||||
|     track_A.m_num_intervals = 3; | ||||
|     track_A.m_duration = 2.0; | ||||
|     track_A.m_sync_markers[0] = 0.; | ||||
|     track_A.m_sync_markers[1] = 0.3; | ||||
|     track_A.m_sync_markers[2] = 0.9; | ||||
|     track_A.CalcIntervals(); | ||||
| 
 | ||||
|     SyncTrack track_B; | ||||
|     track_B.m_num_intervals = 3; | ||||
|     track_B.m_duration = 1.5; | ||||
|     track_B.m_sync_markers[0] = 0.7; | ||||
|     track_B.m_sync_markers[1] = 0.9; | ||||
|     track_B.m_sync_markers[2] = 0.2; | ||||
|     track_B.CalcIntervals(); | ||||
| 
 | ||||
|     WHEN("Calculating A's durations") { | ||||
|       CHECK(track_A.m_interval_ratio[0] == Catch::Detail::Approx(0.3)); | ||||
|       CHECK(track_A.m_interval_ratio[1] == Catch::Detail::Approx(0.6)); | ||||
|       CHECK(track_A.m_interval_ratio[2] == Catch::Detail::Approx(0.1)); | ||||
|   WHEN("Querying SyncTime from Absolute Time") { | ||||
|     WHEN("Querying absolute time at 0.9001s") { | ||||
|       float sync_time = track.CalcSyncFromAbsTime(0.9001f); | ||||
|       CHECK_THAT(sync_time, Catch::WithinAbs(0.0, 0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Calculating B's durations") { | ||||
|       CHECK(track_B.m_interval_ratio[0] == Catch::Detail::Approx(0.2)); | ||||
|       CHECK(track_B.m_interval_ratio[1] == Catch::Detail::Approx(0.3)); | ||||
|       CHECK(track_B.m_interval_ratio[2] == Catch::Detail::Approx(0.5)); | ||||
|     WHEN("Querying absolute time at 0.2001s") { | ||||
|       float sync_time = track.CalcSyncFromAbsTime(0.2001f); | ||||
|       CHECK_THAT(sync_time, Catch::WithinAbs(1.0, 0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Blending two synctracks with weight 0.") { | ||||
|       SyncTrack blended = SyncTrack::Blend(0.f, track_A, track_B); | ||||
| 
 | ||||
|       THEN("Result must equal track_A") { REQUIRE(track_A == blended); } | ||||
|     WHEN("Querying absolute time at 0.8999s") { | ||||
|       float sync_time = track.CalcSyncFromAbsTime(0.8999f); | ||||
|       CHECK_THAT(sync_time, Catch::WithinAbs(1.999, 0.001)); | ||||
|     } | ||||
| 
 | ||||
|     WHEN("Blending two synctracks with weight 1.") { | ||||
|       SyncTrack blended = SyncTrack::Blend(1.f, track_A, track_B); | ||||
| 
 | ||||
|       THEN("Result must equal track_B") { REQUIRE(track_B == blended); } | ||||
|     WHEN("Querying absolute time at 1.9999s") { | ||||
|       float sync_time = track.CalcSyncFromAbsTime(1.9999f); | ||||
|       CHECK_THAT(sync_time, Catch::WithinAbs(0.84615384, 0.001)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|     WHEN("Blending with weight 0.2") { | ||||
|       float weight = 0.2f; | ||||
|       SyncTrack blended = SyncTrack::Blend(weight, track_A, track_B); | ||||
| TEST_CASE("Sync Track Blending", "[SyncTrack]") { | ||||
|   SyncTrack track_a = SyncTrack::CreateFromMarkers(2.0, {0., 0.6, 1.8}); | ||||
|   SyncTrack track_b = SyncTrack::CreateFromMarkers(1.5f, {1.05, 1.35, 0.3}); | ||||
| 
 | ||||
|       REQUIRE( | ||||
|           blended.m_duration | ||||
|           == (1.0f - weight) * track_A.m_duration | ||||
|                  + weight * track_B.m_duration); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_start[0] | ||||
|           == fmodf( | ||||
|               (1.0f - weight) * (track_A.m_interval_start[0] + 1.0f) | ||||
|                   + weight * (track_B.m_interval_start[0]), | ||||
|               1.0f)); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_ratio[1] | ||||
|           == (1.0f - weight) * (track_A.m_interval_ratio[1]) | ||||
|                   + weight * (track_B.m_interval_ratio[1]) | ||||
|               ); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_ratio[2] | ||||
|           == (1.0f - weight) * (track_A.m_interval_ratio[2]) | ||||
|                  + weight * (track_B.m_interval_ratio[2]) | ||||
|       ); | ||||
|     } | ||||
|   WHEN("Calculating A's durations") { | ||||
|     CHECK(track_a.m_interval_duration_ratio[0] == Catch::Detail::Approx(0.3)); | ||||
|     CHECK(track_a.m_interval_duration_ratio[1] == Catch::Detail::Approx(0.6)); | ||||
|     CHECK(track_a.m_interval_duration_ratio[2] == Catch::Detail::Approx(0.1)); | ||||
|   } | ||||
| 
 | ||||
|     WHEN("Inverted blending with weight 0.2") { | ||||
|       float weight = 0.2f; | ||||
|       SyncTrack blended = SyncTrack::Blend(weight, track_B, track_A); | ||||
|   WHEN("Calculating B's durations") { | ||||
|     CHECK(track_b.m_interval_duration_ratio[0] == Catch::Detail::Approx(0.2)); | ||||
|     CHECK(track_b.m_interval_duration_ratio[1] == Catch::Detail::Approx(0.3)); | ||||
|     CHECK(track_b.m_interval_duration_ratio[2] == Catch::Detail::Approx(0.5)); | ||||
|   } | ||||
| 
 | ||||
|       REQUIRE( | ||||
|           blended.m_duration | ||||
|           == (1.0f - weight) * track_B.m_duration | ||||
|                  + weight * track_A.m_duration); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_start[0] | ||||
|           == fmodf( | ||||
|               (1.0f - weight) * (track_B.m_interval_start[0]) | ||||
|                   + weight * (track_A.m_interval_start[0] + 1.0f), | ||||
|               1.0f)); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_ratio[1] | ||||
|           == (1.0f - weight) * (track_B.m_interval_ratio[1]) | ||||
|                  + weight * (track_A.m_interval_ratio[1]) | ||||
|       ); | ||||
|       REQUIRE( | ||||
|           blended.m_interval_ratio[2] | ||||
|           == (1.0f - weight) * (track_B.m_interval_ratio[2]) | ||||
|                  + weight * (track_A.m_interval_ratio[2]) | ||||
|       ); | ||||
|     } | ||||
|   WHEN("Blending two synctracks with weight 0.") { | ||||
|     SyncTrack blended = SyncTrack::Blend(0.f, track_a, track_b); | ||||
| 
 | ||||
|     THEN("Result must equal track_A") { REQUIRE(track_a == blended); } | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Blending two synctracks with weight 1.") { | ||||
|     SyncTrack blended = SyncTrack::Blend(1.f, track_a, track_b); | ||||
| 
 | ||||
|     THEN("Result must equal track_B") { REQUIRE(track_b == blended); } | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Blending with weight 0.2") { | ||||
|     float weight = 0.2f; | ||||
|     SyncTrack blended = SyncTrack::Blend(weight, track_a, track_b); | ||||
| 
 | ||||
|     REQUIRE( | ||||
|         blended.m_duration | ||||
|         == (1.0f - weight) * track_a.m_duration + weight * track_b.m_duration); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_start_ratio[0] | ||||
|         == fmodf( | ||||
|             (1.0f - weight) * (track_a.m_interval_start_ratio[0] + 1.0f) | ||||
|                 + weight * (track_b.m_interval_start_ratio[0]), | ||||
|             1.0f)); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_duration_ratio[1] | ||||
|         == (1.0f - weight) * (track_a.m_interval_duration_ratio[1]) | ||||
|                + weight * (track_b.m_interval_duration_ratio[1])); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_duration_ratio[2] | ||||
|         == (1.0f - weight) * (track_a.m_interval_duration_ratio[2]) | ||||
|                + weight * (track_b.m_interval_duration_ratio[2])); | ||||
|   } | ||||
| 
 | ||||
|   WHEN("Inverted blending with weight 0.2") { | ||||
|     float weight = 0.2f; | ||||
|     SyncTrack blended = SyncTrack::Blend(weight, track_b, track_a); | ||||
| 
 | ||||
|     REQUIRE( | ||||
|         blended.m_duration | ||||
|         == (1.0f - weight) * track_b.m_duration + weight * track_a.m_duration); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_start_ratio[0] | ||||
|         == fmodf( | ||||
|             (1.0f - weight) * (track_b.m_interval_start_ratio[0]) | ||||
|                 + weight * (track_a.m_interval_start_ratio[0] + 1.0f), | ||||
|             1.0f)); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_duration_ratio[1] | ||||
|         == (1.0f - weight) * (track_b.m_interval_duration_ratio[1]) | ||||
|                + weight * (track_a.m_interval_duration_ratio[1])); | ||||
|     REQUIRE( | ||||
|         blended.m_interval_duration_ratio[2] | ||||
|         == (1.0f - weight) * (track_b.m_interval_duration_ratio[2]) | ||||
|                + weight * (track_a.m_interval_duration_ratio[2])); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Serialization", "[SyncTrack]") { | ||||
|   SyncTrack track; | ||||
|   track.m_num_intervals = 3; | ||||
|   track.m_duration = 2.0; | ||||
|   track.m_interval_start_ratio[0] = 0.f; | ||||
|   track.m_interval_duration_ratio[0] = 0.7; | ||||
|   track.m_interval_start_ratio[1] = 0.7f; | ||||
|   track.m_interval_duration_ratio[1] = 0.3; | ||||
|   track.m_interval_start_ratio[2] = 0.7f; | ||||
|   track.m_interval_duration_ratio[2] = 0.3; | ||||
| 
 | ||||
|   nlohmann::json synctrack_json = track; | ||||
| 
 | ||||
|   const SyncTrack synctrack_deserialized = synctrack_json; | ||||
| 
 | ||||
|   CHECK(synctrack_deserialized.m_duration == track.m_duration); | ||||
|   CHECK(synctrack_deserialized.m_num_intervals == track.m_num_intervals); | ||||
| 
 | ||||
|   for (int i = 0; i < track.m_num_intervals; i++) { | ||||
|     CHECK( | ||||
|         synctrack_deserialized.m_interval_start_ratio[i] | ||||
|         == track.m_interval_start_ratio[i]); | ||||
|     CHECK( | ||||
|         synctrack_deserialized.m_interval_duration_ratio[i] | ||||
|         == track.m_interval_duration_ratio[i]); | ||||
|   } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Martin Felis
						Martin Felis