using System;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.Linq;
using Prism.Mvvm;
using TrainEditor2.Extensions;
namespace TrainEditor2.Models.Trains
{
///
/// The representation of the train.dat.
///
internal class Train : BindableBase, ICloneable
{
private Handle handle;
private Cab cab;
private Device device;
internal Handle Handle
{
get
{
return handle;
}
set
{
SetProperty(ref handle, value);
}
}
internal Cab Cab
{
get
{
return cab;
}
set
{
SetProperty(ref cab, value);
}
}
internal Device Device
{
get
{
return device;
}
set
{
SetProperty(ref device, value);
}
}
internal ObservableCollection Cars;
internal ObservableCollection Couplers;
internal Train()
{
Handle = new Handle();
Cab = new Cab();
Device = new Device();
Cars = new ObservableCollection();
Couplers = new ObservableCollection();
}
public object Clone()
{
return new Train
{
Handle = (Handle)Handle.Clone(),
Cab = (Cab)Cab.Clone(),
Device = (Device)Device.Clone(),
Cars = new ObservableCollection(Cars.Select(c => (Car)c.Clone())),
Couplers = new ObservableCollection(Couplers.Select(c => (Coupler)c.Clone()))
};
}
#region Handle
internal void ApplyPowerNotchesToCar()
{
foreach (Car car in Cars)
{
for (int i = car.Delay.DelayPower.Count; i < Handle.PowerNotches; i++)
{
car.Delay.DelayPower.Add(new Delay.Entry());
}
for (int i = car.Delay.DelayPower.Count - 1; i >= Handle.PowerNotches; i--)
{
car.Delay.DelayPower.RemoveAt(i);
}
}
foreach (MotorCar car in Cars.OfType().ToArray())
{
for (int i = car.Acceleration.Entries.Count; i < Handle.PowerNotches; i++)
{
car.Acceleration.Entries.Add(new Acceleration.Entry());
}
for (int i = car.Acceleration.Entries.Count - 1; i >= Handle.PowerNotches; i--)
{
car.Acceleration.Entries.RemoveAt(i);
}
}
}
internal void ApplyBrakeNotchesToCar()
{
foreach (Car car in Cars)
{
for (int i = car.Delay.DelayBrake.Count; i < Handle.BrakeNotches; i++)
{
car.Delay.DelayBrake.Add(new Delay.Entry());
}
for (int i = car.Delay.DelayBrake.Count - 1; i >= Handle.BrakeNotches; i--)
{
car.Delay.DelayBrake.RemoveAt(i);
}
}
}
internal void ApplyLocoBrakeNotchesToCar()
{
foreach (Car car in Cars)
{
for (int i = car.Delay.DelayLocoBrake.Count; i < Handle.LocoBrakeNotches; i++)
{
car.Delay.DelayLocoBrake.Add(new Delay.Entry());
}
for (int i = car.Delay.DelayLocoBrake.Count - 1; i >= Handle.LocoBrakeNotches; i--)
{
car.Delay.DelayLocoBrake.RemoveAt(i);
}
}
}
#endregion
#region Acceleration
private double GetDeceleration(MotorCar car, double velocity)
{
const double AccelerationDueToGravity = 9.80665;
const double AirDensity = 1.22497705587732;
velocity /= 3.6;
double mass = car.Mass * 1000.0;
double frontalArea = Cars.IndexOf(car) == 0 ? car.ExposedFrontalArea : car.UnexposedFrontalArea;
double f = frontalArea * car.Performance.AerodynamicDragCoefficient * AirDensity / (2.0 * mass);
double a = AccelerationDueToGravity * car.Performance.CoefficientOfRollingResistance + f * Math.Pow(velocity, 2.0);
return a * 3.6;
}
private void DrawAccelerationCurve(System.Drawing.Graphics g, MotorCar car, Acceleration.Entry entry, bool selected)
{
// curve
Point[] points = new Point[car.Acceleration.ImageWidth];
for (int x = 0; x < car.Acceleration.ImageWidth; x++)
{
double velocity = car.Acceleration.XtoVelocity(x);
double acceleration;
if (car.Acceleration.Resistance)
{
acceleration = Math.Max(car.Acceleration.GetAcceleration(entry, velocity) - GetDeceleration(car, velocity), 0.0);
}
else
{
acceleration = car.Acceleration.GetAcceleration(entry, velocity);
}
int y = (int)Math.Round(car.Acceleration.AccelerationToY(acceleration));
points[x] = new Point(x, y);
}
double hue;
if (car.Acceleration.Entries.Count <= 1)
{
hue = 1.0;
}
else
{
hue = 0.5 * car.Acceleration.Entries.IndexOf(entry) / (car.Acceleration.Entries.Count - 1);
}
Color color = Utilities.GetColor(hue, selected);
g.DrawLines(new Pen(color), points);
// points
{
double v1 = entry.V1;
double a1 = entry.A1;
if (car.Acceleration.Resistance)
{
a1 -= GetDeceleration(car, v1);
}
int x1 = (int)Math.Round(car.Acceleration.VelocityToX(v1));
int y1 = (int)Math.Round(car.Acceleration.AccelerationToY(a1));
g.FillEllipse(new SolidBrush(color), new Rectangle(x1 - 2, y1 - 2, 5, 5));
double v2 = entry.V2;
double a2 = car.Acceleration.GetAcceleration(entry, v2);
if (car.Acceleration.Resistance)
{
a2 -= GetDeceleration(car, v2);
}
int x2 = (int)Math.Round(car.Acceleration.VelocityToX(v2));
int y2 = (int)Math.Round(car.Acceleration.AccelerationToY(a2));
g.FillEllipse(new SolidBrush(color), new Rectangle(x2 - 2, y2 - 2, 5, 5));
}
}
private void DrawDecelerationCurve(System.Drawing.Graphics g, MotorCar car)
{
if (!car.Acceleration.Resistance)
{
// curve
Point[] points = new Point[car.Acceleration.ImageWidth];
for (int x = 0; x < car.Acceleration.ImageWidth; x++)
{
double velocity = car.Acceleration.XtoVelocity(x);
double acceleration = GetDeceleration(car, velocity);
int y = (int)Math.Round(car.Acceleration.AccelerationToY(acceleration));
points[x] = new Point(x, y);
}
g.DrawLines(Pens.DimGray, points);
}
}
internal void DrawAccelerationImage(MotorCar car)
{
CultureInfo culture = CultureInfo.InvariantCulture;
Bitmap image = new Bitmap(car.Acceleration.ImageWidth, car.Acceleration.ImageHeight);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(image);
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.High;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.Clear(Color.Black);
Font font = new Font("MS UI Gothic", 7.0f);
Pen grayPen = new Pen(Color.DimGray);
Brush grayBrush = Brushes.DimGray;
// vertical grid
for (double v = 0.0; v < car.Acceleration.MaxVelocity; v += 10.0)
{
float x = (float)car.Acceleration.VelocityToX(v);
g.DrawLine(grayPen, new PointF(x, 0.0f), new PointF(x, car.Acceleration.ImageHeight));
g.DrawString(v.ToString("0", culture), font, grayBrush, new PointF(x, 1.0f));
}
// horizontal grid
for (double a = 0.0; a < car.Acceleration.MaxAcceleration; a += 1.0)
{
float y = (float)car.Acceleration.AccelerationToY(a);
g.DrawLine(grayPen, new PointF(0.0f, y), new PointF(car.Acceleration.ImageWidth, y));
g.DrawString(a.ToString("0", culture), font, grayBrush, new PointF(1.0f, y));
}
DrawDecelerationCurve(g, car);
foreach (Acceleration.Entry entry in car.Acceleration.Entries)
{
if (entry != car.Acceleration.SelectedEntry)
{
DrawAccelerationCurve(g, car, entry, false);
}
}
DrawAccelerationCurve(g, car, car.Acceleration.SelectedEntry, true);
car.Acceleration.Image = image;
}
#endregion
}
}