Mercurial > silverbladetech
comparison SilverlightGlimpse/SilverFlow.Controls/Controllers/InertiaController.cs @ 63:536498832a79
Latest version before changing bindings to Listbox
author | Steven Hollidge <stevenhollidge@hotmail.com> |
---|---|
date | Sun, 22 Apr 2012 13:33:42 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
62:810116cd6b8e | 63:536498832a79 |
---|---|
1 using System; | |
2 using System.Collections.Generic; | |
3 using System.Windows; | |
4 using System.Windows.Media.Animation; | |
5 using SilverFlow.Controls.Extensions; | |
6 using SilverFlow.Geometry; | |
7 | |
8 namespace SilverFlow.Controls.Controllers | |
9 { | |
10 /// <summary> | |
11 /// Calculates inertial motion of a dragged element. | |
12 /// </summary> | |
13 public class InertiaController | |
14 { | |
15 private const double SCREEN_DPI = 96; | |
16 private const double INCH = 0.0254; | |
17 private const double GRAVITATIONAL_ACCELERATION = 9.81; | |
18 private const double COEFFICIENT_OF_SLIDING_FRICTION = 0.015; | |
19 private const double MIN_DELTA = 0.001; | |
20 private const double DELAY_BEFORE_MOUSE_UP_IN_MILLISECONDS = 100; | |
21 private const int MAX_LAST_POINTS_TO_COUNT = 4; | |
22 | |
23 private double pixelsPerMeter = SCREEN_DPI / INCH; | |
24 | |
25 private Queue<Trail> mouseTrails = new Queue<Trail>(); | |
26 | |
27 /// <summary> | |
28 /// An element starts its motion. | |
29 /// </summary> | |
30 /// <param name="point">The starting point of the motion.</param> | |
31 public void StartMotion(Point point) | |
32 { | |
33 mouseTrails.Clear(); | |
34 mouseTrails.Enqueue(new Trail(point, DateTime.Now)); | |
35 } | |
36 | |
37 /// <summary> | |
38 /// Saves the last position of the moved element. | |
39 /// </summary> | |
40 /// <param name="point">Current position.</param> | |
41 public void MoveToPoint(Point point) | |
42 { | |
43 while (mouseTrails.Count >= MAX_LAST_POINTS_TO_COUNT) | |
44 { | |
45 // Remove all points except the last ones | |
46 mouseTrails.Dequeue(); | |
47 } | |
48 | |
49 mouseTrails.Enqueue(new Trail(point, DateTime.Now)); | |
50 } | |
51 | |
52 /// <summary> | |
53 /// Gets inertial motion parameters: distance and duration. | |
54 /// </summary> | |
55 /// <param name="hostBounds">The host bounds.</param> | |
56 /// <param name="windowBounds">The window bounds.</param> | |
57 /// <returns> | |
58 /// The inertial motion parameters: distance and duration. | |
59 /// </returns> | |
60 public InertialMotion GetInertialMotionParameters(Rect hostBounds, Rect windowBounds) | |
61 { | |
62 if (mouseTrails.Count < 2) return null; | |
63 | |
64 var mouseTrailsArray = mouseTrails.ToArray(); | |
65 | |
66 Point startPosition = mouseTrailsArray[0].Position; | |
67 DateTime startTime = mouseTrailsArray[0].Timestamp; | |
68 Point endPosition = mouseTrailsArray[mouseTrails.Count - 1].Position; | |
69 DateTime endTime = mouseTrailsArray[mouseTrails.Count - 1].Timestamp; | |
70 | |
71 double timeBetweenNowAndLastMove = (DateTime.Now - endTime).TotalMilliseconds; | |
72 Vector2 vector = new Vector2(startPosition, endPosition); | |
73 | |
74 if (timeBetweenNowAndLastMove < DELAY_BEFORE_MOUSE_UP_IN_MILLISECONDS && !vector.IsZero) | |
75 { | |
76 double time = (endTime - startTime).TotalSeconds; | |
77 time = (time == 0) ? 0.001 : time; | |
78 | |
79 double distance = vector.Length / pixelsPerMeter; | |
80 | |
81 double intialVelocity = distance / time; | |
82 | |
83 double expectedDistance = ((intialVelocity * intialVelocity) / (2 * COEFFICIENT_OF_SLIDING_FRICTION * GRAVITATIONAL_ACCELERATION)); | |
84 double expectedTime = (2 * expectedDistance) / intialVelocity; | |
85 | |
86 double shiftX = Math.Round(vector.LengthX * expectedDistance / distance); | |
87 double shiftY = Math.Round(vector.LengthY * expectedDistance / distance); | |
88 | |
89 // New Inertial Motion Vector | |
90 Vector2 imVector = new Vector2(endPosition, shiftX, shiftY).Round(); | |
91 double expectedLength = imVector.Length; | |
92 | |
93 Rect bounds = hostBounds.Add(-windowBounds.Width, -windowBounds.Height); | |
94 | |
95 if (bounds.Contains(endPosition)) | |
96 { | |
97 imVector = EnsureEndPointInBounds(imVector, bounds).Round(); | |
98 } | |
99 else if (hostBounds.Contains(endPosition)) | |
100 { | |
101 imVector = EnsureEndPointInBounds(imVector, hostBounds).Round(); | |
102 } | |
103 | |
104 // Reduce expected time if the Inertial Motion Vector was truncated by the bounds | |
105 double realTime = (expectedLength == 0) ? 0 : (expectedTime * imVector.Length / expectedLength); | |
106 | |
107 var motion = new InertialMotion() | |
108 { | |
109 Seconds = realTime, | |
110 EndPosition = imVector.End, | |
111 EasingFunction = new CubicEase() | |
112 { | |
113 EasingMode = EasingMode.EaseOut | |
114 } | |
115 }; | |
116 | |
117 return motion; | |
118 } | |
119 | |
120 return null; | |
121 } | |
122 | |
123 /// <summary> | |
124 /// Ensures the end point is in bounds. | |
125 /// </summary> | |
126 /// <param name="vector">The vector to check its ending point.</param> | |
127 /// <param name="bounds">The bounds.</param> | |
128 /// <returns>A vector with the ending point within specified bounds.</returns> | |
129 private Vector2 EnsureEndPointInBounds(Vector2 vector, Rect bounds) | |
130 { | |
131 if (!vector.IsZero && !bounds.Contains(vector.End)) | |
132 { | |
133 if (vector.IsVertical) | |
134 { | |
135 vector.End = vector.End.EnsureInVerticalBounds(bounds); | |
136 } | |
137 else if (vector.IsHorizontal) | |
138 { | |
139 vector.End = vector.End.EnsureInHorizontalBounds(bounds); | |
140 } | |
141 else | |
142 { | |
143 double k = (vector.LengthY) / (vector.LengthX); | |
144 Point point = vector.End; | |
145 | |
146 if (vector.End.X < bounds.Left || vector.End.X > bounds.Right) | |
147 { | |
148 point = point.EnsureInHorizontalBounds(bounds); | |
149 point.Y = (k * (point.X - vector.Start.X)) + vector.Start.Y; | |
150 } | |
151 | |
152 if (point.Y < bounds.Top || point.Y > bounds.Bottom) | |
153 { | |
154 point = point.EnsureInVerticalBounds(bounds); | |
155 point.X = ((point.Y - vector.Start.Y) / k) + vector.Start.X; | |
156 } | |
157 | |
158 vector.End = point.EnsureInBounds(bounds); | |
159 } | |
160 } | |
161 | |
162 return vector; | |
163 } | |
164 } | |
165 } |