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