1 using System;
2 using System.Collections.ObjectModel;
3 using System.Drawing;
4 using System.Drawing.Drawing2D;
5 using System.Drawing.Text;
6 using System.Globalization;
7 using System.Linq;
8 using Prism.Mvvm;
9 using TrainEditor2.Extensions;
10 
11 namespace TrainEditor2.Models.Trains
12 {
13 	/// <summary>
14 	/// The representation of the train.dat.
15 	/// </summary>
16 	internal class Train : BindableBase, ICloneable
17 	{
18 		private Handle handle;
19 		private Cab cab;
20 		private Device device;
21 
22 		internal Handle Handle
23 		{
24 			get
25 			{
26 				return handle;
27 			}
28 			set
29 			{
30 				SetProperty(ref handle, value);
31 			}
32 		}
33 
34 		internal Cab Cab
35 		{
36 			get
37 			{
38 				return cab;
39 			}
40 			set
41 			{
42 				SetProperty(ref cab, value);
43 			}
44 		}
45 
46 		internal Device Device
47 		{
48 			get
49 			{
50 				return device;
51 			}
52 			set
53 			{
54 				SetProperty(ref device, value);
55 			}
56 		}
57 
58 		internal ObservableCollection<Car> Cars;
59 		internal ObservableCollection<Coupler> Couplers;
60 
Train()61 		internal Train()
62 		{
63 			Handle = new Handle();
64 			Cab = new Cab();
65 			Device = new Device();
66 			Cars = new ObservableCollection<Car>();
67 			Couplers = new ObservableCollection<Coupler>();
68 		}
69 
Clone()70 		public object Clone()
71 		{
72 			return new Train
73 			{
74 				Handle = (Handle)Handle.Clone(),
75 				Cab = (Cab)Cab.Clone(),
76 				Device = (Device)Device.Clone(),
77 				Cars = new ObservableCollection<Car>(Cars.Select(c => (Car)c.Clone())),
78 				Couplers = new ObservableCollection<Coupler>(Couplers.Select(c => (Coupler)c.Clone()))
79 			};
80 		}
81 
82 		#region Handle
83 
ApplyPowerNotchesToCar()84 		internal void ApplyPowerNotchesToCar()
85 		{
86 			foreach (Car car in Cars)
87 			{
88 				for (int i = car.Delay.DelayPower.Count; i < Handle.PowerNotches; i++)
89 				{
90 					car.Delay.DelayPower.Add(new Delay.Entry());
91 				}
92 
93 				for (int i = car.Delay.DelayPower.Count - 1; i >= Handle.PowerNotches; i--)
94 				{
95 					car.Delay.DelayPower.RemoveAt(i);
96 				}
97 			}
98 
99 			foreach (MotorCar car in Cars.OfType<MotorCar>().ToArray())
100 			{
101 				for (int i = car.Acceleration.Entries.Count; i < Handle.PowerNotches; i++)
102 				{
103 					car.Acceleration.Entries.Add(new Acceleration.Entry());
104 				}
105 
106 				for (int i = car.Acceleration.Entries.Count - 1; i >= Handle.PowerNotches; i--)
107 				{
108 					car.Acceleration.Entries.RemoveAt(i);
109 				}
110 			}
111 		}
112 
ApplyBrakeNotchesToCar()113 		internal void ApplyBrakeNotchesToCar()
114 		{
115 			foreach (Car car in Cars)
116 			{
117 				for (int i = car.Delay.DelayBrake.Count; i < Handle.BrakeNotches; i++)
118 				{
119 					car.Delay.DelayBrake.Add(new Delay.Entry());
120 				}
121 
122 				for (int i = car.Delay.DelayBrake.Count - 1; i >= Handle.BrakeNotches; i--)
123 				{
124 					car.Delay.DelayBrake.RemoveAt(i);
125 				}
126 			}
127 		}
128 
ApplyLocoBrakeNotchesToCar()129 		internal void ApplyLocoBrakeNotchesToCar()
130 		{
131 			foreach (Car car in Cars)
132 			{
133 				for (int i = car.Delay.DelayLocoBrake.Count; i < Handle.LocoBrakeNotches; i++)
134 				{
135 					car.Delay.DelayLocoBrake.Add(new Delay.Entry());
136 				}
137 
138 				for (int i = car.Delay.DelayLocoBrake.Count - 1; i >= Handle.LocoBrakeNotches; i--)
139 				{
140 					car.Delay.DelayLocoBrake.RemoveAt(i);
141 				}
142 			}
143 		}
144 
145 		#endregion
146 
147 		#region Acceleration
148 
GetDeceleration(MotorCar car, double velocity)149 		private double GetDeceleration(MotorCar car, double velocity)
150 		{
151 			const double AccelerationDueToGravity = 9.80665;
152 			const double AirDensity = 1.22497705587732;
153 
154 			velocity /= 3.6;
155 			double mass = car.Mass * 1000.0;
156 			double frontalArea = Cars.IndexOf(car) == 0 ? car.ExposedFrontalArea : car.UnexposedFrontalArea;
157 
158 			double f = frontalArea * car.Performance.AerodynamicDragCoefficient * AirDensity / (2.0 * mass);
159 			double a = AccelerationDueToGravity * car.Performance.CoefficientOfRollingResistance + f * Math.Pow(velocity, 2.0);
160 
161 			return a * 3.6;
162 		}
163 
DrawAccelerationCurve(System.Drawing.Graphics g, MotorCar car, Acceleration.Entry entry, bool selected)164 		private void DrawAccelerationCurve(System.Drawing.Graphics g, MotorCar car, Acceleration.Entry entry, bool selected)
165 		{
166 			// curve
167 			Point[] points = new Point[car.Acceleration.ImageWidth];
168 
169 			for (int x = 0; x < car.Acceleration.ImageWidth; x++)
170 			{
171 				double velocity = car.Acceleration.XtoVelocity(x);
172 				double acceleration;
173 
174 				if (car.Acceleration.Resistance)
175 				{
176 					acceleration = Math.Max(car.Acceleration.GetAcceleration(entry, velocity) - GetDeceleration(car, velocity), 0.0);
177 				}
178 				else
179 				{
180 					acceleration = car.Acceleration.GetAcceleration(entry, velocity);
181 				}
182 
183 				int y = (int)Math.Round(car.Acceleration.AccelerationToY(acceleration));
184 
185 				points[x] = new Point(x, y);
186 			}
187 
188 			double hue;
189 
190 			if (car.Acceleration.Entries.Count <= 1)
191 			{
192 				hue = 1.0;
193 			}
194 			else
195 			{
196 				hue = 0.5 * car.Acceleration.Entries.IndexOf(entry) / (car.Acceleration.Entries.Count - 1);
197 			}
198 
199 			Color color = Utilities.GetColor(hue, selected);
200 
201 			g.DrawLines(new Pen(color), points);
202 
203 			// points
204 			{
205 				double v1 = entry.V1;
206 				double a1 = entry.A1;
207 
208 				if (car.Acceleration.Resistance)
209 				{
210 					a1 -= GetDeceleration(car, v1);
211 				}
212 
213 				int x1 = (int)Math.Round(car.Acceleration.VelocityToX(v1));
214 				int y1 = (int)Math.Round(car.Acceleration.AccelerationToY(a1));
215 
216 				g.FillEllipse(new SolidBrush(color), new Rectangle(x1 - 2, y1 - 2, 5, 5));
217 
218 				double v2 = entry.V2;
219 				double a2 = car.Acceleration.GetAcceleration(entry, v2);
220 
221 				if (car.Acceleration.Resistance)
222 				{
223 					a2 -= GetDeceleration(car, v2);
224 				}
225 
226 				int x2 = (int)Math.Round(car.Acceleration.VelocityToX(v2));
227 				int y2 = (int)Math.Round(car.Acceleration.AccelerationToY(a2));
228 
229 				g.FillEllipse(new SolidBrush(color), new Rectangle(x2 - 2, y2 - 2, 5, 5));
230 			}
231 		}
232 
DrawDecelerationCurve(System.Drawing.Graphics g, MotorCar car)233 		private void DrawDecelerationCurve(System.Drawing.Graphics g, MotorCar car)
234 		{
235 			if (!car.Acceleration.Resistance)
236 			{
237 				// curve
238 				Point[] points = new Point[car.Acceleration.ImageWidth];
239 
240 				for (int x = 0; x < car.Acceleration.ImageWidth; x++)
241 				{
242 					double velocity = car.Acceleration.XtoVelocity(x);
243 					double acceleration = GetDeceleration(car, velocity);
244 
245 					int y = (int)Math.Round(car.Acceleration.AccelerationToY(acceleration));
246 
247 					points[x] = new Point(x, y);
248 				}
249 
250 				g.DrawLines(Pens.DimGray, points);
251 			}
252 		}
253 
DrawAccelerationImage(MotorCar car)254 		internal void DrawAccelerationImage(MotorCar car)
255 		{
256 			CultureInfo culture = CultureInfo.InvariantCulture;
257 
258 			Bitmap image = new Bitmap(car.Acceleration.ImageWidth, car.Acceleration.ImageHeight);
259 
260 			System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(image);
261 
262 			g.CompositingQuality = CompositingQuality.HighQuality;
263 			g.InterpolationMode = InterpolationMode.High;
264 			g.SmoothingMode = SmoothingMode.AntiAlias;
265 			g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
266 			g.Clear(Color.Black);
267 
268 			Font font = new Font("MS UI Gothic", 7.0f);
269 			Pen grayPen = new Pen(Color.DimGray);
270 			Brush grayBrush = Brushes.DimGray;
271 
272 			// vertical grid
273 			for (double v = 0.0; v < car.Acceleration.MaxVelocity; v += 10.0)
274 			{
275 				float x = (float)car.Acceleration.VelocityToX(v);
276 				g.DrawLine(grayPen, new PointF(x, 0.0f), new PointF(x, car.Acceleration.ImageHeight));
277 				g.DrawString(v.ToString("0", culture), font, grayBrush, new PointF(x, 1.0f));
278 			}
279 
280 			// horizontal grid
281 			for (double a = 0.0; a < car.Acceleration.MaxAcceleration; a += 1.0)
282 			{
283 				float y = (float)car.Acceleration.AccelerationToY(a);
284 				g.DrawLine(grayPen, new PointF(0.0f, y), new PointF(car.Acceleration.ImageWidth, y));
285 				g.DrawString(a.ToString("0", culture), font, grayBrush, new PointF(1.0f, y));
286 			}
287 
288 			DrawDecelerationCurve(g, car);
289 
290 			foreach (Acceleration.Entry entry in car.Acceleration.Entries)
291 			{
292 				if (entry != car.Acceleration.SelectedEntry)
293 				{
294 					DrawAccelerationCurve(g, car, entry, false);
295 				}
296 			}
297 
298 			DrawAccelerationCurve(g, car, car.Acceleration.SelectedEntry, true);
299 
300 			car.Acceleration.Image = image;
301 		}
302 
303 		#endregion
304 	}
305 }
306