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 }