Motion.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. namespace Terminal.Gui.TextEffects;
  2. public class Coord
  3. {
  4. public int Column { get; set; }
  5. public int Row { get; set; }
  6. public Coord (int column, int row)
  7. {
  8. Column = column;
  9. Row = row;
  10. }
  11. public override string ToString () => $"({Column}, {Row})";
  12. }
  13. public class Waypoint
  14. {
  15. public string WaypointId { get; set; }
  16. public Coord Coord { get; set; }
  17. public List<Coord> BezierControl { get; set; }
  18. public Waypoint (string waypointId, Coord coord, List<Coord> bezierControl = null)
  19. {
  20. WaypointId = waypointId;
  21. Coord = coord;
  22. BezierControl = bezierControl ?? new List<Coord> ();
  23. }
  24. }
  25. public class Segment
  26. {
  27. public Waypoint Start { get; private set; }
  28. public Waypoint End { get; private set; }
  29. public double Distance { get; private set; }
  30. public bool EnterEventTriggered { get; set; }
  31. public bool ExitEventTriggered { get; set; }
  32. public Segment (Waypoint start, Waypoint end)
  33. {
  34. Start = start;
  35. End = end;
  36. Distance = CalculateDistance (start, end);
  37. }
  38. private double CalculateDistance (Waypoint start, Waypoint end)
  39. {
  40. // Add bezier control point distance calculation if needed
  41. return Math.Sqrt (Math.Pow (end.Coord.Column - start.Coord.Column, 2) + Math.Pow (end.Coord.Row - start.Coord.Row, 2));
  42. }
  43. public Coord GetCoordOnSegment (double distanceFactor)
  44. {
  45. int column = (int)(Start.Coord.Column + (End.Coord.Column - Start.Coord.Column) * distanceFactor);
  46. int row = (int)(Start.Coord.Row + (End.Coord.Row - Start.Coord.Row) * distanceFactor);
  47. return new Coord (column, row);
  48. }
  49. }
  50. public class Path
  51. {
  52. public string PathId { get; private set; }
  53. public double Speed { get; set; }
  54. public Func<double, double> EaseFunction { get; set; }
  55. public int Layer { get; set; }
  56. public int HoldTime { get; set; }
  57. public bool Loop { get; set; }
  58. public List<Segment> Segments { get; private set; } = new List<Segment> ();
  59. public int CurrentStep { get; set; }
  60. public double TotalDistance { get; set; }
  61. public double LastDistanceReached { get; set; }
  62. public int MaxSteps => (int)Math.Ceiling (TotalDistance / Speed); // Calculates max steps based on total distance and speed
  63. public Path (string pathId, double speed, Func<double, double> easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false)
  64. {
  65. PathId = pathId;
  66. Speed = speed;
  67. EaseFunction = easeFunction;
  68. Layer = layer;
  69. HoldTime = holdTime;
  70. Loop = loop;
  71. }
  72. public void AddWaypoint (Waypoint waypoint)
  73. {
  74. if (Segments.Count > 0)
  75. {
  76. var lastSegment = Segments.Last ();
  77. var newSegment = new Segment (lastSegment.End, waypoint);
  78. Segments.Add (newSegment);
  79. TotalDistance += newSegment.Distance;
  80. }
  81. else
  82. {
  83. var originWaypoint = new Waypoint ("origin", new Coord (0, 0)); // Assuming the path starts at origin
  84. var initialSegment = new Segment (originWaypoint, waypoint);
  85. Segments.Add (initialSegment);
  86. TotalDistance = initialSegment.Distance;
  87. }
  88. }
  89. public Coord Step ()
  90. {
  91. if (CurrentStep <= MaxSteps)
  92. {
  93. double progress = EaseFunction?.Invoke ((double)CurrentStep / TotalDistance) ?? (double)CurrentStep / TotalDistance;
  94. double distanceTravelled = TotalDistance * progress;
  95. LastDistanceReached = distanceTravelled;
  96. foreach (var segment in Segments)
  97. {
  98. if (distanceTravelled <= segment.Distance)
  99. {
  100. double segmentProgress = distanceTravelled / segment.Distance;
  101. return segment.GetCoordOnSegment (segmentProgress);
  102. }
  103. distanceTravelled -= segment.Distance;
  104. }
  105. }
  106. return Segments.Last ().End.Coord; // Return the end of the last segment if out of bounds
  107. }
  108. }
  109. public class Motion
  110. {
  111. public Dictionary<string, Path> Paths { get; private set; } = new Dictionary<string, Path> ();
  112. public Path ActivePath { get; private set; }
  113. public Coord CurrentCoord { get; set; }
  114. public Coord PreviousCoord { get; set; }
  115. public EffectCharacter Character { get; private set; } // Assuming EffectCharacter is similar to base_character.EffectCharacter
  116. public Motion (EffectCharacter character)
  117. {
  118. Character = character;
  119. CurrentCoord = new Coord (character.InputCoord.Column, character.InputCoord.Row); // Assuming similar properties
  120. PreviousCoord = new Coord (-1, -1);
  121. }
  122. public void SetCoordinate (Coord coord)
  123. {
  124. CurrentCoord = coord;
  125. }
  126. public Path CreatePath (string pathId, double speed, Func<double, double> easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false)
  127. {
  128. if (Paths.ContainsKey (pathId))
  129. throw new ArgumentException ($"A path with ID {pathId} already exists.");
  130. var path = new Path (pathId, speed, easeFunction, layer, holdTime, loop);
  131. Paths [pathId] = path;
  132. return path;
  133. }
  134. public Path QueryPath (string pathId)
  135. {
  136. if (!Paths.TryGetValue (pathId, out var path))
  137. throw new KeyNotFoundException ($"No path found with ID {pathId}.");
  138. return path;
  139. }
  140. public bool MovementIsComplete ()
  141. {
  142. return ActivePath == null || ActivePath.CurrentStep >= ActivePath.TotalDistance;
  143. }
  144. public void ActivatePath (Path path)
  145. {
  146. if (path == null)
  147. throw new ArgumentNullException (nameof (path), "Path cannot be null when activating.");
  148. ActivePath = path;
  149. ActivePath.CurrentStep = 0; // Reset the path's progress
  150. }
  151. /// <summary>
  152. /// Set the active path to None if the active path is the given path.
  153. /// </summary>
  154. public void DeactivatePath (Path p)
  155. {
  156. if (p == ActivePath)
  157. {
  158. ActivePath = null;
  159. }
  160. }
  161. public void DeactivatePath ()
  162. {
  163. ActivePath = null;
  164. }
  165. public void Move ()
  166. {
  167. if (ActivePath != null)
  168. {
  169. PreviousCoord = CurrentCoord;
  170. CurrentCoord = ActivePath.Step ();
  171. ActivePath.CurrentStep++;
  172. if (ActivePath.CurrentStep >= ActivePath.TotalDistance)
  173. {
  174. if (ActivePath.Loop)
  175. ActivePath.CurrentStep = 0; // Reset the path for looping
  176. else
  177. DeactivatePath (); // Deactivate the path if it is not set to loop
  178. }
  179. }
  180. }
  181. public void ChainPaths (IEnumerable<Path> paths, bool loop = false)
  182. {
  183. var pathList = paths.ToList ();
  184. for (int i = 0; i < pathList.Count; i++)
  185. {
  186. var currentPath = pathList [i];
  187. var nextPath = i + 1 < pathList.Count ? pathList [i + 1] : pathList.FirstOrDefault ();
  188. // Here we could define an event system to trigger path activation when another completes
  189. // For example, you could listen for a "path complete" event and then activate the next path
  190. if (loop && nextPath != null)
  191. {
  192. // Implementation depends on your event system
  193. }
  194. }
  195. }
  196. }