public Interpolator(STFReader stf) { List <float> list = new List <float>(); stf.MustMatch("("); while (!stf.EndOfBlock()) { list.Add(stf.ReadFloat(STFReader.UNITS.Any, null)); } if (list.Count % 2 == 1) { STFException.TraceWarning(stf, "Ignoring extra odd value in Interpolator list."); } int n = list.Count / 2; if (n < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two value pairs."); } X = new float[n]; Y = new float[n]; Size = n; for (int i = 0; i < n; i++) { X[i] = list[2 * i]; Y[i] = list[2 * i + 1]; if (i > 0 && X[i - 1] >= X[i]) { STFException.TraceWarning(stf, "Interpolator x values must be increasing."); } } }
public Interpolator2D(STFReader stf) { List <float> xlist = new List <float>(); List <Interpolator> ilist = new List <Interpolator>(); stf.MustMatch("("); while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.UNITS.Any, null)); ilist.Add(new Interpolator(stf)); } stf.SkipRestOfBlock(); int n = xlist.Count; if (n < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two x values."); } X = new float[n]; Y = new Interpolator[n]; Size = n; for (int i = 0; i < n; i++) { X[i] = xlist[i]; Y[i] = ilist[i]; if (i > 0 && X[i - 1] >= X[i]) { STFException.TraceWarning(stf, " Interpolator x values must be increasing."); } } }
public static Interpolator CreateInterpolator(this STFReader reader) { List <double> list = new List <double>(); reader.MustMatchBlockStart(); while (!reader.EndOfBlock()) { list.Add(reader.ReadFloat(STFReader.Units.Any, null)); } if (list.Count % 2 == 1) { STFException.TraceWarning(reader, "Ignoring extra odd value in Interpolator list."); } int n = list.Count / 2; if (n < 2) { STFException.TraceWarning(reader, "Interpolator must have at least two value pairs."); } double[] xArray = new double[n]; double[] yArray = new double[n]; for (int i = 0; i < n; i++) { xArray[i] = list[2 * i]; yArray[i] = list[2 * i + 1]; if (i > 0 && xArray[i - 1] >= xArray[i]) { STFException.TraceWarning(reader, "Interpolator x values must be increasing."); } } return(new Interpolator(xArray, yArray)); }
public static Interpolator2D CreateInterpolator2D(this STFReader stf) { List <double> xlist = new List <double>(); List <Interpolator> ilist = new List <Interpolator>(); stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.Units.Any, null)); ilist.Add(stf.CreateInterpolator()); } stf.SkipRestOfBlock(); int n = xlist.Count; if (n < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two x values."); } double[] xArray = new double[n]; Interpolator[] yArray = new Interpolator[n]; for (int i = 0; i < n; i++) { xArray[i] = xlist[i]; yArray[i] = ilist[i]; if (i > 0 && xArray[i - 1] >= xArray[i]) { STFException.TraceWarning(stf, " Interpolator x values must be increasing."); } } return(new Interpolator2D(xArray, yArray)); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalLight(STFReader stf) { stf.MustMatchBlockStart(); Index = stf.ReadInt(null); Name = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("radius", () => { Radius = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("position", () => { stf.MustMatchBlockStart(); position = new Vector3(stf.ReadFloat(null), stf.ReadFloat(null), stf.ReadFloat(null)); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "semaphore_change": SemaphoreChange = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalLight flag " + stf.ReadString()); break; } } }), }); }
// SigSubJnLinkIf is not supported /// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalSubObj(STFReader stf) { SignalSubType = -1; // not (yet) specified stf.MustMatch("("); Index = stf.ReadInt(null); MatrixName = stf.ReadString().ToUpper(); Description = stf.ReadString(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("sigsubtype", () => { SignalSubType = SignalSubTypes.IndexOf(stf.ReadStringBlock(null).ToUpper()); }), new STFReader.TokenProcessor("sigsubstype", () => { SignalSubSignalType = stf.ReadStringBlock(null).ToLowerInvariant(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "optional": Optional = true; break; case "default": Default = true; break; case "back_facing": BackFacing = true; break; case "jn_link": JunctionLink = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalSubObj flag " + stf.ReadString()); break; } } }), }); }
public Initial_Trigger(STFReader f) { f.MustMatch("("); while (!f.EndOfBlock()) { ParsePlayCommand(f, f.ReadString().ToLower()); } }
public Discrete_Trigger(STFReader f) { f.MustMatch("("); TriggerID = f.ReadInt(null); while (!f.EndOfBlock()) { ParsePlayCommand(f, f.ReadString().ToLower()); } }
public Variable_Trigger(STFReader f) { f.MustMatch("("); string eventString = f.ReadString(); Threshold = f.ReadFloat(STFReader.UNITS.None, null); switch (eventString.ToLower()) { case "speed_inc_past": Event = Events.Speed_Inc_Past; break; case "speed_dec_past": Event = Events.Speed_Dec_Past; break; case "distance_inc_past": { Event = Events.Distance_Inc_Past; Threshold = Threshold * Threshold; break; } case "distance_dec_past": { Event = Events.Distance_Dec_Past; Threshold = Threshold * Threshold; break; } case "variable1_inc_past": Event = Events.Variable1_Inc_Past; break; case "variable1_dec_past": Event = Events.Variable1_Dec_Past; break; case "variable2_inc_past": Event = Events.Variable2_Inc_Past; break; case "variable2_dec_past": Event = Events.Variable2_Dec_Past; break; case "variable3_inc_past": Event = Events.Variable3_Inc_Past; break; case "variable3_dec_past": Event = Events.Variable3_Dec_Past; break; case "brakecyl_inc_past": Event = Events.BrakeCyl_Inc_Past; break; case "brakecyl_dec_past": Event = Events.BrakeCyl_Dec_Past; break; case "curveforce_inc_past": Event = Events.CurveForce_Inc_Past; break; case "curveforce_dec_past": Event = Events.CurveForce_Dec_Past; break; } while (!f.EndOfBlock()) { ParsePlayCommand(f, f.ReadString().ToLower()); } }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> /// <param name="ORTSMode">Process SignalType for ORTS mode (always set NumClearAhead_ORTS only)</param> public SignalType(STFReader stf, bool ORTSMode) : this() { stf.MustMatch("("); Name = stf.ReadString().ToLowerInvariant(); int numClearAhead = -2; int numdefs = 0; stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalfntype", () => { FnType = ReadFnType(stf); }), //[Rob Roeterdink] value was not passed new STFReader.TokenProcessor("signallighttex", () => { LightTextureName = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signallights", () => { Lights = ReadLights(stf); }), new STFReader.TokenProcessor("signaldrawstates", () => { DrawStates = ReadDrawStates(stf); }), new STFReader.TokenProcessor("signalaspects", () => { Aspects = ReadAspects(stf); }), new STFReader.TokenProcessor("approachcontrolsettings", () => { ApproachControlDetails = ReadApproachControlDetails(stf); }), new STFReader.TokenProcessor("signalnumclearahead", () => { numClearAhead = numClearAhead >= -1 ? numClearAhead : stf.ReadIntBlock(null); numdefs++; }), new STFReader.TokenProcessor("semaphoreinfo", () => { SemaphoreInfo = stf.ReadFloatBlock(STFReader.UNITS.None, null); }), new STFReader.TokenProcessor("sigflashduration", () => { stf.MustMatch("("); FlashTimeOn = stf.ReadFloat(STFReader.UNITS.None, null); FlashTimeOff = stf.ReadFloat(STFReader.UNITS.None, null); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "abs": Abs = true; break; case "no_gantry": NoGantry = true; break; case "semaphore": Semaphore = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalType flag " + stf.ReadString()); break; } } }), }); if (ORTSMode) { // In ORTS mode : always set value for NumClearAhead_ORTS NumClearAhead_MSTS = -2; NumClearAhead_ORTS = numClearAhead; } else { // In MSTS mode : if one line for SignalNumClearAhead defined, set value for NumClearAhead_MSTS, otherwise set value for NumClearAhead_ORTS NumClearAhead_MSTS = numdefs == 1 ? numClearAhead : -2; NumClearAhead_ORTS = numdefs == 2 ? numClearAhead : -2; } }
protected void ParsePosition(STFReader stf) { stf.MustMatchBlockStart(); bounds = new Rectangle(stf.ReadInt(null), stf.ReadInt(null), stf.ReadInt(null), stf.ReadInt(null)); // skipping additional values in between while (!stf.EndOfBlock()) { STFException.TraceWarning(stf, "Ignored additional positional parameters"); bounds.Width = bounds.Height; bounds.Height = stf.ReadInt(null); } }
protected virtual (Color[] Colors, float TriggerValue) ParseControlColors(STFReader stf) { float trigger = 0f; List <Color> colors = new List <Color>(stf.ReadInt(0)); if (!stf.EndOfBlock()) { stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { colors.Add(ParseControlColor(stf)); }), new STFReader.TokenProcessor("switchval", () => { trigger = ParseSwitchVal(stf); }) }); } return(colors.ToArray(), trigger); }
public Random_Trigger(STFReader f) { f.MustMatch("("); while (!f.EndOfBlock()) { string lowtok = f.ReadString().ToLower(); switch (lowtok) { case "delay_min_max": f.MustMatch("("); Delay_Min = f.ReadFloat(STFReader.UNITS.None, null); Delay_Max = f.ReadFloat(STFReader.UNITS.None, null); f.SkipRestOfBlock(); break; case "volume_min_max": f.MustMatch("("); Volume_Min = f.ReadFloat(STFReader.UNITS.None, null); Volume_Max = f.ReadFloat(STFReader.UNITS.None, null); f.SkipRestOfBlock(); break; default: ParsePlayCommand(f, lowtok); break; } } }
protected void ParsePosition(STFReader stf) { stf.MustMatch("("); PositionX = stf.ReadDouble(null); PositionY = stf.ReadDouble(null); Width = stf.ReadDouble(null); Height = stf.ReadDouble(null); // Handling middle values while (!stf.EndOfBlock()) { STFException.TraceWarning(stf, "Ignored additional positional parameters"); Width = Height; Height = stf.ReadInt(null); } }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalAspect(STFReader stf) { SpeedLimit = -1; stf.MustMatchBlockStart(); string aspectName = stf.ReadString(); if (!EnumExtension.GetValue(aspectName, out SignalAspectState aspect)) { STFException.TraceInformation(stf, "Skipped unknown signal aspect " + aspectName); Aspect = SignalAspectState.Unknown; } else { Aspect = aspect; } DrawStateName = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("speedmph", () => { SpeedLimit = (float)Speed.MeterPerSecond.FromMpH(stf.ReadFloatBlock(STFReader.Units.None, 0)); }), new STFReader.TokenProcessor("speedkph", () => { SpeedLimit = (float)Speed.MeterPerSecond.FromKpH(stf.ReadFloatBlock(STFReader.Units.None, 0)); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "asap": Asap = true; break; case "or_speedreset": Reset = true; break; case "or_nospeedreduction": NoSpeedReduction = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
public PlayOneShot(STFReader f) { f.MustMatch("("); int count = f.ReadInt(null); Files = new string[count]; int iFile = 0; while (!f.EndOfBlock()) { switch (f.ReadString().ToLower()) { case "file": if (iFile < count) { f.MustMatch("("); Files[iFile++] = f.ReadString(); f.ReadInt(null); f.SkipRestOfBlock(); } else // MSTS skips extra files { STFException.TraceWarning(f, "Skipped extra File"); f.SkipBlock(); } break; case "selectionmethod": f.MustMatch("("); string s = f.ReadString(); switch (s.ToLower()) { case "randomselection": SelectionMethod = SelectionMethods.RandomSelection; break; case "sequentialselection": SelectionMethod = SelectionMethods.SequentialSelection; break; default: STFException.TraceWarning(f, "Skipped unknown selection method " + s); break; } f.SkipRestOfBlock(); break; case "(": f.SkipRestOfBlock(); break; } } }
// SigSubJnLinkIf is not supported /// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> internal SignalSubObject(STFReader stf) { SignalSubType = SignalSubType.None; stf.MustMatchBlockStart(); Index = stf.ReadInt(null); MatrixName = stf.ReadString().ToUpperInvariant(); Description = stf.ReadString(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("sigsubtype", () => { if (EnumExtension.GetValue(stf.ReadStringBlock(null), out SignalSubType subType)) { SignalSubType = subType; } }), new STFReader.TokenProcessor("sigsubstype", () => { SignalSubSignalType = stf.ReadStringBlock(null); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToUpperInvariant()) { case "OPTIONAL": Optional = true; break; case "DEFAULT": Default = true; break; case "BACK_FACING": BackFacing = true; break; case "JN_LINK": JunctionLink = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalSubObj flag " + stf.ReadString()); break; } } }), });
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalDrawLight(STFReader stf) { stf.MustMatch("("); LightIndex = stf.ReadUInt(null); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "flashing": Flashing = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
public CabViewGaugeControl(STFReader stf, string basePath) { stf.MustMatchBlockStart(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("type", () => { ParseType(stf); }), new STFReader.TokenProcessor("position", () => { ParsePosition(stf); }), new STFReader.TokenProcessor("scalerange", () => { ParseScaleRange(stf); }), new STFReader.TokenProcessor("graphic", () => { ParseGraphic(stf, basePath); }), new STFReader.TokenProcessor("style", () => { ParseStyle(stf); }), new STFReader.TokenProcessor("units", () => { ParseUnits(stf); }), new STFReader.TokenProcessor("zeropos", () => { ZeroPos = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("orientation", () => { Orientation = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("dirincrease", () => { Direction = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("area", () => { stf.MustMatchBlockStart(); area = new Rectangle(stf.ReadInt(null), stf.ReadInt(null), stf.ReadInt(null), stf.ReadInt(null)); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("positivecolour", () => { stf.MustMatchBlockStart(); (PositiveColors, PositiveTrigger) = ParseControlColors(stf); }), new STFReader.TokenProcessor("negativecolour", () => { stf.MustMatchBlockStart(); (NegativeColors, NegativeTrigger) = ParseControlColors(stf); }), new STFReader.TokenProcessor("decreasecolour", () => { stf.MustMatchBlockStart(); stf.ReadInt(0); if (!stf.EndOfBlock()) { stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { DecreaseColor = ParseControlColor(stf); }) }); } }) });
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalAspect(STFReader stf) { SpeedMpS = -1; stf.MustMatch("("); string aspectName = stf.ReadString(); try { Aspect = (MstsSignalAspect)Enum.Parse(typeof(MstsSignalAspect), aspectName, true); } catch (ArgumentException) { STFException.TraceInformation(stf, "Skipped unknown signal aspect " + aspectName); Aspect = MstsSignalAspect.UNKNOWN; } DrawStateName = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("speedmph", () => { SpeedMpS = MpS.FromMpH(stf.ReadFloatBlock(STFReader.UNITS.None, 0)); }), new STFReader.TokenProcessor("speedkph", () => { SpeedMpS = MpS.FromKpH(stf.ReadFloatBlock(STFReader.UNITS.None, 0)); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "asap": Asap = true; break; case "or_speedreset": Reset = true; break; case "or_nospeedreduction": NoSpeedReduction = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> internal SignalDrawLight(STFReader stf) { stf.MustMatchBlockStart(); Index = stf.ReadInt(null); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToUpperInvariant()) { case "FLASHING": Flashing = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> /// <param name="orMode">Process SignalType for ORTS mode (always set NumClearAhead_ORTS only)</param> public SignalType(STFReader stf, bool orMode) : this() { stf.MustMatchBlockStart(); Name = stf.ReadString().ToLowerInvariant(); int numClearAhead = -2; int numdefs = 0; string ortsFunctionType = string.Empty; string ortsNormalSubType = string.Empty; stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("ortsscript", () => { Script = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signalfntype", () => { if (orMode) { ortsFunctionType = ReadOrtsFunctionType(stf); } else { FunctionType = ReadFunctionType(stf); } }), new STFReader.TokenProcessor("signallighttex", () => { LightTextureName = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signallights", () => { Lights = ReadLights(stf); }), new STFReader.TokenProcessor("signaldrawstates", () => { DrawStates = ReadDrawStates(stf); }), new STFReader.TokenProcessor("signalaspects", () => { Aspects = ReadAspects(stf); }), new STFReader.TokenProcessor("approachcontrolsettings", () => { ApproachControlDetails = ReadApproachControlDetails(stf); }), new STFReader.TokenProcessor("signalnumclearahead", () => { numClearAhead = numClearAhead >= -1 ? numClearAhead : stf.ReadIntBlock(null); numdefs++; }), new STFReader.TokenProcessor("semaphoreinfo", () => { SemaphoreInfo = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsdayglow", () => { DayGlow = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsnightglow", () => { NightGlow = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsdaylight", () => { DayLight = stf.ReadBoolBlock(true); }), new STFReader.TokenProcessor("ortsnormalsubtype", () => { ortsNormalSubType = ReadOrtsNormalSubType(stf); }), new STFReader.TokenProcessor("ortsonofftime", () => { TransitionTime = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("sigflashduration", () => { stf.MustMatchBlockStart(); FlashTimeOn = stf.ReadFloat(STFReader.Units.None, null); FlashTimeOff = stf.ReadFloat(STFReader.Units.None, null); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "abs": Abs = true; break; case "no_gantry": NoGantry = true; break; case "semaphore": Semaphore = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalType flag " + stf.ReadString()); break; } } }), }); if (orMode) { // set related MSTS function type OrtsFunctionTypeIndex = OrSignalTypes.Instance.FunctionTypes.FindIndex(i => StringComparer.OrdinalIgnoreCase.Equals(i, ortsFunctionType)); if (!EnumExtension.GetValue(ortsFunctionType, out SignalFunction functionType)) { FunctionType = SignalFunction.Info; } else { FunctionType = functionType; } // set index for Normal Subtype OrtsNormalSubTypeIndex = OrSignalTypes.Instance.NormalSubTypes.FindIndex(i => StringComparer.OrdinalIgnoreCase.Equals(i, ortsNormalSubType)); // set SNCA NumClearAhead_MSTS = -2; NumClearAhead_ORTS = numClearAhead; } else { // set defaulted OR function type OrtsFunctionTypeIndex = (int)FunctionType; // set SNCA NumClearAhead_MSTS = numdefs == 1 ? numClearAhead : -2; NumClearAhead_ORTS = numdefs == 2 ? numClearAhead : -2; } }
public static Interpolator2D CreateInterpolator2D(this STFReader stf, bool tab) { List <double> xlist = new List <double>(); List <Interpolator> ilist = new List <Interpolator>(); bool errorFound = false; if (tab) { stf.MustMatchBlockStart(); int numOfRows = stf.ReadInt(0); if (numOfRows < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two rows."); errorFound = true; } int numOfColumns = stf.ReadInt(0); string header = stf.ReadString().ToLower(); if (header == "throttle") { stf.MustMatchBlockStart(); int numOfThrottleValues = 0; while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.Units.None, 0f)); ilist.Add(new Interpolator(numOfRows)); numOfThrottleValues++; } if (numOfThrottleValues != (numOfColumns - 1)) { STFException.TraceWarning(stf, "Interpolator throttle vs. num of columns mismatch."); errorFound = true; } if (numOfColumns < 3) { STFException.TraceWarning(stf, "Interpolator must have at least three columns."); errorFound = true; } int numofData = 0; string tableLabel = stf.ReadString().ToLower(); if (tableLabel == "table") { stf.MustMatchBlockStart(); for (int i = 0; i < numOfRows; i++) { float x = stf.ReadFloat(STFReader.Units.SpeedDefaultMPH, 0); numofData++; for (int j = 0; j < numOfColumns - 1; j++) { if (j >= ilist.Count) { STFException.TraceWarning(stf, "Interpolator throttle vs. num of columns mismatch. (missing some throttle values)"); errorFound = true; } ilist[j][x] = stf.ReadFloat(STFReader.Units.Force, 0); numofData++; } } stf.SkipRestOfBlock(); } else { STFException.TraceWarning(stf, "Interpolator didn't find a table to load."); errorFound = true; } //check the table for inconsistencies foreach (Interpolator checkMe in ilist) { if (checkMe.Size != numOfRows) { STFException.TraceWarning(stf, "Interpolator has found a mismatch between num of rows declared and num of rows given."); errorFound = true; } double dx = (checkMe.MaxX() - checkMe.MinX()) * 0.1f; if (dx <= 0f) { STFException.TraceWarning(stf, "Interpolator has found X data error - x values must be increasing. (Possible row number mismatch)"); errorFound = true; } else { for (double x = checkMe.MinX(); x <= checkMe.MaxX(); x += dx) { if (double.IsNaN(checkMe[x])) { STFException.TraceWarning(stf, "Interpolator has found X data error - x values must be increasing. (Possible row number mismatch)"); errorFound = true; break; } } } } if (numofData != (numOfRows * numOfColumns)) { STFException.TraceWarning(stf, "Interpolator has found a mismatch: num of data doesn't fit the header information."); errorFound = true; } } else { STFException.TraceWarning(stf, "Interpolator must have a 'throttle' header row."); errorFound = true; } stf.SkipRestOfBlock(); } else { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.Units.Any, null)); ilist.Add(stf.CreateInterpolator()); } } int n = xlist.Count; if (n < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two x values."); errorFound = true; } double[] xArray = new double[n]; Interpolator[] yArray = new Interpolator[n]; for (int i = 0; i < n; i++) { xArray[i] = xlist[i]; yArray[i] = ilist[i]; if (i > 0 && xArray[i - 1] >= xArray[i]) { STFException.TraceWarning(stf, "Interpolator x values must be increasing."); } } if (errorFound) { STFException.TraceWarning(stf, "Errors found in the Interpolator definition!!! The Interpolator will not work correctly!"); } Interpolator2D result = new Interpolator2D(xArray, yArray); return(result); }
public InterpolatorDiesel2D(STFReader stf, bool tab) { // <CSComment> TODO: probably there is some other stf.SkipRestOfBlock() that should be removed </CSComment> List <float> xlist = new List <float>(); List <Interpolator> ilist = new List <Interpolator>(); bool errorFound = false; if (tab) { stf.MustMatch("("); int numOfRows = stf.ReadInt(0); if (numOfRows < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two rows."); errorFound = true; } int numOfColumns = stf.ReadInt(0); string header = stf.ReadString().ToLower(); if (header == "throttle") { stf.MustMatch("("); int numOfThrottleValues = 0; while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.UNITS.None, 0f)); ilist.Add(new Interpolator(numOfRows)); numOfThrottleValues++; } if (numOfThrottleValues != (numOfColumns - 1)) { STFException.TraceWarning(stf, "Interpolator throttle vs. num of columns mismatch."); errorFound = true; } if (numOfColumns < 3) { STFException.TraceWarning(stf, "Interpolator must have at least three columns."); errorFound = true; } int numofData = 0; string tableLabel = stf.ReadString().ToLower(); if (tableLabel == "table") { stf.MustMatch("("); for (int i = 0; i < numOfRows; i++) { float x = stf.ReadFloat(STFReader.UNITS.SpeedDefaultMPH, 0); numofData++; for (int j = 0; j < numOfColumns - 1; j++) { if (j >= ilist.Count) { STFException.TraceWarning(stf, "Interpolator throttle vs. num of columns mismatch. (missing some throttle values)"); errorFound = true; } ilist[j][x] = stf.ReadFloat(STFReader.UNITS.Force, 0); numofData++; } } stf.SkipRestOfBlock(); } else { STFException.TraceWarning(stf, "Interpolator didn't find a table to load."); errorFound = true; } //check the table for inconsistencies foreach (Interpolator checkMe in ilist) { if (checkMe.GetSize() != numOfRows) { STFException.TraceWarning(stf, "Interpolator has found a mismatch between num of rows declared and num of rows given."); errorFound = true; } float dx = (checkMe.MaxX() - checkMe.MinX()) * 0.1f; if (dx <= 0f) { STFException.TraceWarning(stf, "Interpolator has found X data error - x values must be increasing. (Possible row number mismatch)"); errorFound = true; } else { for (float x = checkMe.MinX(); x <= checkMe.MaxX(); x += dx) { if ((checkMe[x] == float.NaN)) { STFException.TraceWarning(stf, "Interpolator has found X data error - x values must be increasing. (Possible row number mismatch)"); errorFound = true; break; } } } } if (numofData != (numOfRows * numOfColumns)) { STFException.TraceWarning(stf, "Interpolator has found a mismatch: num of data doesn't fit the header information."); errorFound = true; } } else { STFException.TraceWarning(stf, "Interpolator must have a 'throttle' header row."); errorFound = true; } stf.SkipRestOfBlock(); } else { stf.MustMatch("("); while (!stf.EndOfBlock()) { xlist.Add(stf.ReadFloat(STFReader.UNITS.Any, null)); ilist.Add(new Interpolator(stf)); } } int n = xlist.Count; if (n < 2) { STFException.TraceWarning(stf, "Interpolator must have at least two x values."); errorFound = true; } X = new float[n]; Y = new Interpolator[n]; Size = n; for (int i = 0; i < n; i++) { X[i] = xlist[i]; Y[i] = ilist[i]; if (i > 0 && X[i - 1] >= X[i]) { STFException.TraceWarning(stf, "Interpolator x values must be increasing."); } } //stf.SkipRestOfBlock(); if (errorFound) { STFException.TraceWarning(stf, "Errors found in the Interpolator definition!!! The Interpolator will not work correctly!"); } }
public CVCGauge(STFReader stf, string basepath) { stf.MustMatch("("); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("type", () => { ParseType(stf); }), new STFReader.TokenProcessor("position", () => { ParsePosition(stf); }), new STFReader.TokenProcessor("scalerange", () => { ParseScaleRange(stf); }), new STFReader.TokenProcessor("graphic", () => { ParseGraphic(stf, basepath); }), new STFReader.TokenProcessor("style", () => { ParseStyle(stf); }), new STFReader.TokenProcessor("units", () => { ParseUnits(stf); }), new STFReader.TokenProcessor("zeropos", () => { ZeroPos = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("orientation", () => { Orientation = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("dirincrease", () => { Direction = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("area", () => { stf.MustMatch("("); int x = stf.ReadInt(null); int y = stf.ReadInt(null); int width = stf.ReadInt(null); int height = stf.ReadInt(null); Area = new Rectangle(x, y, width, height); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("positivecolour", () => { stf.MustMatch("("); NumPositiveColors = stf.ReadInt(0); if ((stf.EndOfBlock() == false)) { List <color> Colorset = new List <color>(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { Colorset.Add(ParseControlColor(stf)); }), new STFReader.TokenProcessor("switchval", () => { PositiveSwitchVal = ParseSwitchVal(stf); }) }); PositiveColor = Colorset [0]; if ((NumPositiveColors >= 2) && (Colorset.Count >= 2)) { SecondPositiveColor = Colorset [1]; } } }), new STFReader.TokenProcessor("negativecolour", () => { stf.MustMatch("("); NumNegativeColors = stf.ReadInt(0); if ((stf.EndOfBlock() == false)) { List <color> Colorset = new List <color>(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { Colorset.Add(ParseControlColor(stf)); }), new STFReader.TokenProcessor("switchval", () => { NegativeSwitchVal = ParseSwitchVal(stf); }) }); NegativeColor = Colorset[0]; if ((NumNegativeColors >= 2) && (Colorset.Count >= 2)) { SecondNegativeColor = Colorset[1]; } } }), new STFReader.TokenProcessor("decreasecolour", () => { stf.MustMatch("("); stf.ReadInt(0); if (stf.EndOfBlock() == false) { stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { DecreaseColor = ParseControlColor(stf); }) }); } }) }); }
public CVCDiscrete(STFReader stf, string basepath) { // try { stf.MustMatch("("); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("type", () => { ParseType(stf); }), new STFReader.TokenProcessor("position", () => { ParsePosition(stf); }), new STFReader.TokenProcessor("scalerange", () => { ParseScaleRange(stf); }), new STFReader.TokenProcessor("graphic", () => { ParseGraphic(stf, basepath); }), new STFReader.TokenProcessor("style", () => { ParseStyle(stf); }), new STFReader.TokenProcessor("units", () => { ParseUnits(stf); }), new STFReader.TokenProcessor("mousecontrol", () => { MouseControl = stf.ReadBoolBlock(false); }), new STFReader.TokenProcessor("orientation", () => { Orientation = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("dirincrease", () => { Direction = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("numframes", () => { stf.MustMatch("("); FramesCount = stf.ReadInt(null); FramesX = stf.ReadInt(null); FramesY = stf.ReadInt(null); stf.SkipRestOfBlock(); }), // <CJComment> Would like to revise this, as it is difficult to follow and debug. // Can't do that until interaction of ScaleRange, NumFrames, NumPositions and NumValues is more fully specified. // What is needed is samples of data that must be accommodated. // Some decisions appear unwise but they might be a pragmatic solution to a real problem. </CJComment> // // Code accommodates: // - NumValues before NumPositions or the other way round. // - More NumValues than NumPositions and the other way round - perhaps unwise. // - The count of NumFrames, NumValues and NumPositions is ignored - perhaps unwise. // - Abbreviated definitions so that values at intermediate unspecified positions can be omitted. // Strangely, these values are set to 0 and worked out later when drawing. // Max and min NumValues which don't match the ScaleRange are ignored - perhaps unwise. new STFReader.TokenProcessor("numpositions", () => { stf.MustMatch("("); // If Positions are not filled before by Values bool shouldFill = (Positions.Count == 0); numPositions = stf.ReadInt(null); // Number of Positions var minPosition = 0; var positionsRead = 0; while (!stf.EndOfBlock()) { int p = stf.ReadInt(null); minPosition = positionsRead == 0 ? p : Math.Min(minPosition, p); // used to get correct offset positionsRead++; // If Positions are not filled before by Values if (shouldFill) { Positions.Add(p); } } // If positions do not start at 0, add offset to shift them all so they do. // An example of this is RENFE 400 (from http://www.trensim.com/lib/msts/index.php?act=view&id=186) // which has a COMBINED_CONTROL with: // NumPositions ( 21 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ) // Also handles definitions with position in reverse order, e.g. // NumPositions ( 5 8 7 2 1 0 ) positionsRead++; if (minPosition < 0) { for (int iPos = 0; iPos <= Positions.Count - 1; iPos++) { Positions[iPos] -= minPosition; } } // This is a hack for SLI locomotives which have the positions listed as "1056964608 0 0 0 ...". if (Positions.Any(p => p > 0xFFFF)) { STFException.TraceInformation(stf, "Renumbering cab control positions from zero due to value > 0xFFFF"); for (var i = 0; i < Positions.Count; i++) { Positions[i] = i; } } // Check if eligible for filling if (Positions.Count > 1 && Positions[0] != 0) { canFill = false; } else { for (var iPos = 1; iPos <= Positions.Count - 1; iPos++) { if (Positions[iPos] > Positions[iPos - 1]) { continue; } canFill = false; break; } } // This is a protection against GP40 locomotives that erroneously have positions pointing beyond frame count limit. if (Positions.Count > 1 && canFill && Positions.Count < FramesCount && Positions[Positions.Count - 1] >= FramesCount && Positions[0] == 0) { STFException.TraceInformation(stf, "Some NumPositions entries refer to non-exisiting frames, trying to renumber"); Positions[Positions.Count - 1] = FramesCount - 1; for (var iPos = Positions.Count - 2; iPos >= 1; iPos--) { if ((Positions[iPos] >= FramesCount || Positions[iPos] >= Positions[iPos + 1])) { Positions[iPos] = Positions[iPos + 1] - 1; } else { break; } } } }), new STFReader.TokenProcessor("numvalues", () => { stf.MustMatch("("); var numValues = stf.ReadDouble(null); // Number of Values while (!stf.EndOfBlock()) { double v = stf.ReadDouble(null); // If the Positions are less than expected add new Position(s) while (Positions.Count <= _ValuesRead) { Positions.Add(_ValuesRead); } // Avoid later repositioning, put every value to its Position // But before resize Values if needed if (numValues != numPositions) { while (Values.Count <= Positions[_ValuesRead]) { Values.Add(0); } // Avoid later repositioning, put every value to its Position Values[Positions[_ValuesRead]] = v; } Values.Add(v); _ValuesRead++; } }), }); // If no ACE, just don't need any fixup // Because Values are tied to the image Frame to be shown if (string.IsNullOrEmpty(ACEFile)) { return; } // Now, we have an ACE. // If read any Values, or the control requires Values to control // The twostate, tristate, signal displays are not in these // Need check the Values collection for validity if (_ValuesRead > 0 || ControlStyle == CABViewControlStyles.SPRUNG || ControlStyle == CABViewControlStyles.NOT_SPRUNG || FramesCount > 0 || (FramesX > 0 && FramesY > 0)) { // Check max number of Frames if (FramesCount == 0) { // Check valid Frame information if (FramesX == 0 || FramesY == 0) { // Give up, it won't work // Because later we won't know how to display frames from that Trace.TraceWarning("Invalid Frames information given for ACE {0} in {1}", ACEFile, stf.FileName); ACEFile = ""; return; } // Valid frames info, set FramesCount FramesCount = FramesX * FramesY; } // Now we have an ACE and Frames for it. // Only shuffle data in following cases if (Values.Count != Positions.Count || (Values.Count < FramesCount & canFill) || (Values.Count > 0 && Values[0] == Values[Values.Count - 1] && Values[0] == 0)) { // Fixup Positions and Values collections first // If the read Positions and Values are not match // Or we didn't read Values but have Frames to draw // Do not test if FramesCount equals Values count, we trust in the creator - // maybe did not want to display all Frames // (If there are more Values than Frames it will checked at draw time) // Need to fix the whole Values if (Positions.Count != _ValuesRead || (FramesCount > 0 && (Values.Count == 0 || Values.Count == 1))) { //This if clause covers among others following cases: // Case 1 (e.g. engine brake lever of Dash 9): //NumFrames ( 22 11 2 ) //NumPositions ( 1 0 ) //NumValues ( 1 0 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Case 2 (e.g. throttle lever of Acela): //NumFrames ( 25 5 5 ) //NumPositions ( 0 ) //NumValues ( 0 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Clear existing Positions.Clear(); Values.Clear(); // Add the two sure positions, the two ends Positions.Add(0); // We will need the FramesCount later! // We use Positions only here Positions.Add(FramesCount); // Fill empty Values for (int i = 0; i < FramesCount; i++) { Values.Add(0); } Values[0] = MinValue; Values.Add(MaxValue); } else if (Values.Count == 2 && Values[0] == 0 && Values[1] < MaxValue && Positions[0] == 0 && Positions[1] == 1 && Values.Count < FramesCount) { //This if clause covers among others following cases: // Case 1 (e.g. engine brake lever of gp38): //NumFrames ( 18 2 9 ) //NumPositions ( 2 0 1 ) //NumValues ( 2 0 0.3 ) //Orientation ( 0 ) //DirIncrease ( 0 ) //ScaleRange ( 0 1 ) Positions.Add(FramesCount); // Fill empty Values for (int i = Values.Count; i < FramesCount; i++) { Values.Add(Values[1]); } Values.Add(MaxValue); } else { //This if clause covers among others following cases: // Case 1 (e.g. train brake lever of Acela): //NumFrames ( 12 4 3 ) //NumPositions ( 5 0 1 9 10 11 ) //NumValues ( 5 0 0.2 0.85 0.9 0.95 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Fill empty Values int iValues = 1; for (int i = 1; i < FramesCount && i <= Positions.Count - 1 && Values.Count < FramesCount; i++) { var deltaPos = Positions[i] - Positions[i - 1]; while (deltaPos > 1 && Values.Count < FramesCount) { Values.Insert(iValues, 0); iValues++; deltaPos--; } iValues++; } // Add the maximums to the end, the Value will be removed // We use Positions only here if (Values.Count > 0 && Values[0] <= Values[Values.Count - 1]) { Values.Add(MaxValue); } else if (Values.Count > 0 && Values[0] > Values[Values.Count - 1]) { Values.Add(MinValue); } } // OK, we have a valid size of Positions and Values // Now it is the time for checking holes in the given data if ((Positions.Count < FramesCount - 1 && Values[0] <= Values[Values.Count - 1]) || (Values.Count > 1 && Values[0] == Values[Values.Count - 2] && Values[0] == 0)) { int j = 1; int p = 0; // Skip the 0 element, that is the default MinValue for (int i = 1; i < Positions.Count; i++) { // Found a hole if (Positions[i] != p + 1) { // Iterate to the next valid data and fill the hole for (j = p + 1; j < Positions[i]; j++) { // Extrapolate into the hole Values[j] = MathHelper.Lerp((float)Values[p], (float)Values[Positions[i]], (float)j / (float)Positions[i]); } } p = Positions[i]; } } // Don't need the MaxValue added before, remove it Values.RemoveAt(Values.Count - 1); } } // MSTS ignores/overrides various settings by the following exceptional cases: if (ControlType == CABViewControlTypes.CP_HANDLE) { ControlStyle = CABViewControlStyles.NOT_SPRUNG; } if (ControlType == CABViewControlTypes.PANTOGRAPH || ControlType == CABViewControlTypes.PANTOGRAPH2) { ControlStyle = CABViewControlStyles.ONOFF; } if (ControlType == CABViewControlTypes.HORN || ControlType == CABViewControlTypes.SANDERS || ControlType == CABViewControlTypes.BELL || ControlType == CABViewControlTypes.RESET) { ControlStyle = CABViewControlStyles.WHILE_PRESSED; } if (ControlType == CABViewControlTypes.DIRECTION && Orientation == 0) { Direction = 1 - Direction; } } // catch (Exception error) // { // if (error is STFException) // Parsing error, so pass it on // throw; // else // Unexpected error, so provide a hint // throw new STFException(stf, "Problem with NumPositions/NumValues/NumFrames/ScaleRange"); // } // End of Need check the Values collection for validity } // End of Constructor
public CVCDigital(STFReader stf, string basepath) { // Set white as the default positive colour for digital displays color white = new color(); white.R = 255f; white.G = 255f; white.B = 255f; PositiveColor = white; FontSize = 10; FontStyle = 0; FontFamily = "Courier New"; stf.MustMatch("("); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("type", () => { ParseType(stf); }), new STFReader.TokenProcessor("position", () => { ParsePosition(stf); }), new STFReader.TokenProcessor("scalerange", () => { ParseScaleRange(stf); }), new STFReader.TokenProcessor("graphic", () => { ParseGraphic(stf, basepath); }), new STFReader.TokenProcessor("style", () => { ParseStyle(stf); }), new STFReader.TokenProcessor("units", () => { ParseUnits(stf); }), new STFReader.TokenProcessor("leadingzeros", () => { ParseLeadingZeros(stf); }), new STFReader.TokenProcessor("accuracy", () => { ParseAccuracy(stf); }), new STFReader.TokenProcessor("accuracyswitch", () => { ParseAccuracySwitch(stf); }), new STFReader.TokenProcessor("justification", () => { ParseJustification(stf); }), new STFReader.TokenProcessor("positivecolour", () => { stf.MustMatch("("); NumPositiveColors = stf.ReadInt(0); if ((stf.EndOfBlock() == false)) { List <color> Colorset = new List <color>(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { Colorset.Add(ParseControlColor(stf)); }), new STFReader.TokenProcessor("switchval", () => { PositiveSwitchVal = ParseSwitchVal(stf); }) }); PositiveColor = Colorset [0]; if ((NumPositiveColors >= 2) && (Colorset.Count >= 2)) { SecondPositiveColor = Colorset [1]; } } }), new STFReader.TokenProcessor("negativecolour", () => { stf.MustMatch("("); NumNegativeColors = stf.ReadInt(0); if ((stf.EndOfBlock() == false)) { List <color> Colorset = new List <color>(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { Colorset.Add(ParseControlColor(stf)); }), new STFReader.TokenProcessor("switchval", () => { NegativeSwitchVal = ParseSwitchVal(stf); }) }); NegativeColor = Colorset[0]; if ((NumNegativeColors >= 2) && (Colorset.Count >= 2)) { SecondNegativeColor = Colorset[1]; } } }), new STFReader.TokenProcessor("decreasecolour", () => { stf.MustMatch("("); stf.ReadInt(0); if (stf.EndOfBlock() == false) { stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("controlcolour", () => { DecreaseColor = ParseControlColor(stf); }) }); } }), new STFReader.TokenProcessor("ortsfont", () => { ParseFont(stf); }) }); }