4
/// <summary>Provides structures to represent the track.</summary>
5
public static class Track {
8
// TODO: This is a stub. Expand as necessary.
11
// --- structures and enumerations ---
13
/// <summary>Represents the endpoint of a track segment.</summary>
14
public enum SegmentEndpoint {
15
/// <summary>An invalid endpoint.</summary>
17
/// <summary>The beginning of the track segment.</summary>
19
/// <summary>The end of the track segment.</summary>
21
/// <summary>A segment-specific special endpoint.</summary>
25
/// <summary>Represents a connection to a track segment.</summary>
26
public struct SegmentConnection {
28
/// <summary>The segment this connection points to, or a null reference.</summary>
29
public Segment Segment;
30
/// <summary>The endpoint of the segment this connection points to.</summary>
31
public SegmentEndpoint Endpoint;
33
/// <summary>Creates a new instance of this structure.</summary>
34
/// <param name="segment">The segment this connection points to, or a null reference.</param>
35
/// <param name="endpoint">The endpoint of the segment this connection points to, or SegmentEndpoint.Invalid.</param>
36
public SegmentConnection(Segment segment, SegmentEndpoint endpoint) {
37
this.Segment = segment;
38
this.Endpoint = endpoint;
41
/// <summary>Represents a connection that does not point anywhere. Can be used at the end of the track, or to represent an uninitialized connection.</summary>
42
public static readonly SegmentConnection Empty = new SegmentConnection(null, SegmentEndpoint.Invalid);
45
/// <summary>Represents a point on a track segment.</summary>
46
public struct SegmentPoint {
48
/// <summary>The physical track segment the point lies on.</summary>
49
public PhysicalSegment Segment;
50
/// <summary>The position at the point.</summary>
51
public Math.Vector3 Position;
52
/// <summary>The orientation at the point without factoring in the Roll parameter.</summary>
53
/// <summary>If the OrientationWithoutRoll is rotated around its z-axis by the Roll parameter, it is equivalent to OrientationWithRoll.</summary>
54
public Math.Orientation3 OrientationWithoutRoll;
55
/// <summary>The orientation at the point with factoring in the Roll parameter.</summary>
56
/// <summary>If the OrientationWithoutRoll is rotated around its z-axis by the Roll parameter, it is equivalent to OrientationWithRoll.</summary>
57
public Math.Orientation3 OrientationWithRoll;
58
/// <summary>The roll expressed as an angle.</summary>
59
/// <summary>If the OrientationWithoutRoll is rotated around its z-axis by the Roll parameter, it is equivalent to OrientationWithRoll.</summary>
62
/// <summary>Creates a new instance of this structure.</summary>
63
/// <param name="segment">The physical track segment the point lies on.</param>
64
/// <param name="position">The position.</param>
65
/// <param name="orientation">The orientation, either with or without factoring in the Roll parameter.</param>
66
/// <param name="orientationIncludesRoll">Whether the Orientation has the Roll parameter factored in.</param>
67
/// <param name="roll">The roll expressed as an angle.</param>
68
public SegmentPoint(PhysicalSegment segment, Math.Vector3 position, Math.Orientation3 orientation, bool orientationIncludesRoll, double roll) {
69
this.Segment = segment;
70
this.Position = position;
71
if (orientationIncludesRoll) {
72
this.OrientationWithRoll = orientation;
73
this.OrientationWithoutRoll = Math.Orientation3.RotateAroundZAxis(orientation, System.Math.Cos(roll), -System.Math.Sin(roll));
75
this.OrientationWithoutRoll = orientation;
76
this.OrientationWithRoll = Math.Orientation3.RotateAroundZAxis(orientation, System.Math.Cos(roll), System.Math.Sin(roll));
81
/// <summary>Represents an invalid or uninitialized point.</summary>
82
public static readonly SegmentPoint Invalid = new SegmentPoint(null, Math.Vector3.Null, Math.Orientation3.Null, false, 0.0);
86
// --- abstract segment ---
88
/// <summary>Represents a track segment.</summary>
89
public abstract class Segment { }
92
// --- physical segments ---
94
/// <summary>Represents a physical track segment, i.e. a track segment of non-zero length.</summary>
95
/// <remarks>Physical track segments include straight and curved pieces of track.</remarks>
96
public abstract class PhysicalSegment : Segment {
98
/// <summary>The connection pointing away from the beginning of this track segment.</summary>
99
public SegmentConnection Previous;
100
/// <summary>The connection pointing away from the end of this track segment.</summary>
101
public SegmentConnection Next;
102
/// <summary>The positive length of this track segment.</summary>
103
/// <remarks>The length is measured on the track, i.e. for curves, this is the arc length on the curve.</remarks>
104
public double Length;
105
/// <summary>The roll at the beginning of this track segment, expressed as an angle in radians.</summary>
106
/// <remarks>This angle can be used to represent cant or an otherwise banked track.</remarks>
107
public double StartingRoll;
108
/// <summary>The roll at the end of this track segment, expressed as an angle in radians.</summary>
109
/// <remarks>This angle can be used to represent cant or an otherwise banked track.</remarks>
110
public double EndingRoll;
111
// instance functions
112
/// <summary>Takes an offset relative to the beginning of this track segment and returns the point on the track corresponding to this offset in an output parameter.</summary>
113
/// <param name="offset">The offset relative to the beginning of this track segment. A value of zero corresponds to the beginning of this track segment, while a value of the underlying Length field corresponds to the end of this track segment.</param>
114
/// <param name="point">Receives point on the track, including its position, orientation and roll.</param>
115
/// <returns>The success of the operation. This operation fails if the specified offset points to a point outside of the available track.</returns>
116
public bool GetPoint(double offset, out SegmentPoint point) {
119
* The offset is negative and thus outside the bounds of this segment.
120
* We need to continue processing with the previous segment.
122
if (this.Previous.Segment is PhysicalSegment) {
123
PhysicalSegment previous = (PhysicalSegment)this.Previous.Segment;
124
SegmentEndpoint endpoint = this.Previous.Endpoint;
125
if (endpoint == SegmentEndpoint.Beginning) {
126
return GetPoint(-offset, out point);
127
} else if (endpoint == SegmentEndpoint.End) {
128
return GetPoint(previous.Length + offset, out point);
130
throw new InvalidOperationException();
132
} else if (this.Previous.Segment is VirtualSegment) {
133
// TODO: Virtual segments can have various endpoints.
134
// Each virtual segment needs to be handled specially.
135
throw new NotImplementedException();
136
} else if (this.Previous.Segment == null) {
137
point = SegmentPoint.Invalid;
140
throw new InvalidOperationException();
142
} else if (offset > this.Length) {
144
* The offset exceeds the length of this segment and is thus outside the bounds of this segment.
145
* We need to continue processing with the next segment.
147
if (this.Next.Segment is PhysicalSegment) {
148
PhysicalSegment next = (PhysicalSegment)this.Next.Segment;
149
SegmentEndpoint endpoint = this.Next.Endpoint;
150
if (endpoint == SegmentEndpoint.Beginning) {
151
return GetPoint(offset - this.Length, out point);
152
} else if (endpoint == SegmentEndpoint.End) {
153
return GetPoint(this.Length + next.Length - offset, out point);
155
throw new InvalidOperationException();
157
} else if (this.Next.Segment is VirtualSegment) {
158
// TODO: Virtual segments can have various endpoints.
159
// Each virtual segment needs to be handled specially.
160
throw new NotImplementedException();
161
} else if (this.Next.Segment == null) {
162
point = SegmentPoint.Invalid;
165
throw new InvalidOperationException();
169
* The offset is within the bounds of this segment.
170
* We can calculate the track point data now.
172
if (this is StraightSegment) {
173
StraightSegment straight = (StraightSegment)this;
174
Math.Vector3 position = straight.Position + offset * straight.Orientation.Z;
175
double rollFactor = offset / straight.Length;
176
double roll = (1.0 - rollFactor) * straight.StartingRoll + rollFactor * straight.EndingRoll;
177
point = new SegmentPoint(this, position, straight.Orientation, false, roll);
179
} else if (this is CurvedSegment) {
180
CurvedSegment curve = (CurvedSegment)this;
181
double angle = offset / curve.Radius;
182
double cosineOfAngle = System.Math.Cos(angle);
183
double sineOfAngle = System.Math.Sin(angle);
184
double radiusX = curve.Radius * cosineOfAngle;
185
double radiusZ = curve.Radius * sineOfAngle;
186
Math.Vector3 position = curve.Center - radiusX * curve.Orientation.X + radiusZ * curve.Orientation.Z;
187
Math.Orientation3 orientation = Math.Orientation3.RotateAroundYAxis(curve.Orientation, cosineOfAngle, sineOfAngle);
188
double rollFactor = offset / curve.Length;
189
double roll = (1.0 - rollFactor) * curve.StartingRoll + rollFactor * curve.EndingRoll;
190
point = new SegmentPoint(this, position, orientation, false, roll);
193
throw new InvalidOperationException();
199
/// <summary>Represents a straight track segment.</summary>
200
public class StraightSegment : PhysicalSegment {
201
/// <summary>The position at the beginning of this track segment.</summary>
202
public Math.Vector3 Position;
203
/// <summary>The orientation at the beginning of this track segment without factoring in Roll, i.e. as if the track was unbanked.</summary>
204
/// <remarks>The X-component points right, the Y-component up. The Z-component points from the beginning of this track piece to the end.</remarks>
205
public Math.Orientation3 Orientation;
208
/// <summary>Represents a curved track segment based on a circular arc.</summary>
209
public class CurvedSegment : PhysicalSegment {
210
/// <summary>The position of the center of the circle.</summary>
211
public Math.Vector3 Center;
212
/// <summary>The orientation at the beginning of this track segment without factoring in Roll, i.e. as if the track was unbanked.</summary>
213
/// <remarks>The X-component points to the center of the circle. The Z-component points tangentially from the beginning of this track piece into the direction of travel.</remarks>
214
public Math.Orientation3 Orientation;
215
/// <summary>The positive curve radius.</summary>
216
public double Radius;
220
// --- virtual segments ---
222
/// <summary>Represents a virtual track segment, i.e. a track segment of zero length.</summary>
223
/// <remarks>Virtual track segments are used to convey point-based information in-between physical track segments.</remarks>
224
public abstract class VirtualSegment : Segment { }
226
/// <summary>Represents an event occuring between physical track segments.</summary>
227
public class EventSegment : VirtualSegment {
228
/// <summary>The connection pointing away from the beginning of this track segment. The corresponding endpoint is SegmentEndpoint.Beginning.</summary>
229
public SegmentConnection Previous;
230
/// <summary>The connection pointing away from the end of this track segment. The corresponding endpoint is SegmentEndpoint.End.</summary>
231
public SegmentConnection Next;
232
/// <summary>The event occuring at this point.</summary>
236
/// <summary>Represents a switch.</summary>
237
public class SwitchSegment : VirtualSegment {
238
/// <summary>The connection pointing away from the beginning of this track segment. The corresponding endpoint is SegmentEndpoint.Beginning.</summary>
239
public SegmentConnection Previous;
240
/// <summary>The first connection pointing away from the end of this track segment. The corresponding endpoint is SegmentEndpoint.End.</summary>
241
public SegmentConnection Next;
242
/// <summary>The second connection pointing away from the end of this track segment. The corresponding endpoint is SegmentEndpoint.Special.</summary>
243
public SegmentConnection Branch;
249
/// <summary>Represents an event occuring in-between physical track segments.</summary>
250
public abstract class Event { }
258
// TODO: Helper functions
260
/// <summary>Creates a straight track segment from two specified points.</summary>
261
/// <param name="pointA">The first point.</param>
262
/// <param name="pointB">The second point.</param>
263
/// <param name="orientation">The orientation at the first point without factoring in roll. The Z component of this parameter must point from A toward B.</param>
264
/// <param name="rollA">The roll at the first point.</param>
265
/// <param name="rollB">The roll at the second point.</param>
266
/// <param name="segment">Receives the segment on success. The start and end of the segment may be connected to other track segments.</param>
267
/// <returns>The success of this operation. This operation fails if the two specified points coincide.</returns>
268
public static bool CreateStraightSegmentFromPoints(Math.Vector3 pointA, Math.Vector3 pointB, Math.Orientation3 orientation, double rollA, double rollB, out PhysicalSegment segment) {
269
if (pointA == pointB) {
273
double length = OpenBveApi.Math.Vector3.Norm(pointB - pointA);
274
StraightSegment straight = new StraightSegment();
275
straight.Previous = SegmentConnection.Empty;
276
straight.Next = SegmentConnection.Empty;
277
straight.Length = length;
278
straight.StartingRoll = rollA;
279
straight.EndingRoll = rollB;
280
straight.Position = pointA;
281
straight.Orientation = orientation;
287
/// <summary>Creates a track segment from two specified points.</summary>
288
/// <param name="pointA">The first point.</param>
289
/// <param name="pointB">The second point.</param>
290
/// <param name="orientationA">The orientation at the first point.</param>
291
/// <param name="orientationB">Receives the orientation at the second point.</param>
292
/// <param name="rollA">The roll at the first point.</param>
293
/// <param name="rollB">The roll at the second point.</param>
294
/// <param name="segment">Receives the segment on success. The start and end of the segment may be connected to other track segments.</param>
295
/// <returns>The success of this operation. This operation fails if the two specified points coincide.</returns>
296
public static bool CreateCurvedSegmentFromPoints(Math.Vector3 pointA, Math.Vector3 pointB, Math.Orientation3 orientationA, out Math.Orientation3 orientationB, double rollA, double rollB, out PhysicalSegment segment) {
297
throw new NotImplementedException();
300
/// <summary>Creates track segments from two specified points.</summary>
301
/// <param name="pointA">The first point.</param>
302
/// <param name="pointB">The second point.</param>
303
/// <param name="orientationA">The orientation at the first point.</param>
304
/// <param name="orientationB">The orientation at the second point.</param>
305
/// <param name="rollA">The roll at the first point.</param>
306
/// <param name="rollB">The roll at the second point.</param>
307
/// <param name="segments">Receives the segments on success. The start of the first segment and end of the last segment may be connected to other track segments.</param>
308
/// <returns>The success of this operation. This operation fails if the two specified points coincide.</returns>
309
/// <remarks>This method usually creates two circular segments of equal radius but opposing direction in order to connect the two points while respecting their specified orientations. In special cases, this method will create a single curved segment or a single straight segment.</remarks>
310
public static bool CreateCurvedSegmentFromPoints(Math.Vector3 pointA, Math.Vector3 pointB, Math.Orientation3 orientationA, Math.Orientation3 orientationB, double rollA, double rollB, out PhysicalSegment[] segments) {
311
throw new NotImplementedException();
b'\\ No newline at end of file'