source: tags/1.3.0/UTrack.pas

Last change on this file was 107, checked in by chronos, 20 months ago
  • Fixed: Initial view resize on new game.
  • Fixed: Wrong train drawing position if track points distance close to zero.
File size: 11.0 KB
Line 
1unit UTrack;
2
3interface
4
5uses
6 Classes, SysUtils, Math, Generics.Collections, UGeometric;
7
8type
9 TTrack = class;
10 TTrackPoint = class;
11 TTrackPoints = class;
12 TTrackLink = class;
13 TTrackLinks = class;
14
15 { TTrackPosition }
16
17 TTrackPosition = record
18 BaseTrackPoint: TTrackPoint;
19 RelPos: Double;
20 function GetTrackPosition: Integer;
21 function GetVector: TVector;
22 procedure Move(Distance: Double);
23 end;
24
25 { TTrackPoint }
26
27 TTrackPoint = class
28 OwnerPoint: TObject;
29 Position: TPoint;
30 //PositionShift: TPoint;
31 PositionDesigned: TPoint;
32 Pending: Boolean;
33 Track: TTrack;
34 NeighPoints: TTrackPoints;
35 NeighLinks: TTrackLinks;
36 LinkUp: TTrackLink;
37 LinkDown: TTrackLink;
38 procedure Connect(TrackPoint: TTrackPoint);
39 procedure Disconnect(TrackPoint: TTrackPoint);
40 function GetDown: TTrackPoint;
41 function GetUp: TTrackPoint;
42 function GetNeighDown: TTrackPoint;
43 function GetNeighUp: TTrackPoint;
44 function GetLinkDown: TTrackLink;
45 function GetLinkUp: TTrackLink;
46 function GetTrackPosition: Integer;
47
48 // Move to TTrackLink later
49 function GetDistance: Integer;
50 constructor Create;
51 destructor Destroy; override;
52 end;
53
54 { TTrackPoints }
55
56 TTrackPoints = class(TObjectList<TTrackPoint>)
57 Track: TTrack;
58 function AddNew: TTrackPoint;
59 end;
60
61 { TTrackLink }
62
63 TTrackLink = class
64 Points: TTrackPoints;
65 Shift: TPoint;
66 constructor Create;
67 destructor Destroy; override;
68 end;
69
70 { TTrackLinks }
71
72 TTrackLinks = class(TObjectList<TTrackLink>)
73 function SearchPoints(Point1, Point2: TTrackPoint): TTrackLink;
74 function AddNew: TTrackLink;
75 end;
76
77 { TTrack }
78
79 TTrack = class
80 Points: TTrackPoints;
81 Links: TTrackLinks;
82 Owner: TObject;
83 function GetLength: Integer;
84 procedure RouteTrack(TP1, TP2: TTrackPoint);
85 procedure RemoveTrackBetween(TP1, TP2: TTrackPoint);
86 constructor Create;
87 destructor Destroy; override;
88 end;
89
90 { TTrackPointsAngle }
91
92 TTrackPointsAngle = class
93 Angle: Double;
94 TrackLinks: TTrackLinks;
95 constructor Create;
96 destructor Destroy; override;
97 end;
98
99 { TTrackPointsAngleGroup }
100
101 TTrackPointsAngleGroup = class(TObjectList<TTrackPointsAngle>)
102 function SearchAngle(Angle: Double): TTrackPointsAngle;
103 end;
104
105
106implementation
107
108resourcestring
109 SAlreadyConnectedTrackPoint = 'Trying to connect already connected track point';
110 SAlreadyDisconnectedTrackPoint = 'Trying to disconnect not connected track point';
111 STrackPointNotFound = 'Track point %d not found';
112
113{ TTrackPosition }
114
115function TTrackPosition.GetTrackPosition: Integer;
116begin
117 Result := Round(RelPos);
118 if Assigned(BaseTrackPoint) then
119 Result := Result + BaseTrackPoint.GetTrackPosition;
120end;
121
122function TTrackPosition.GetVector: TVector;
123var
124 D: Integer;
125 UpPoint: TTrackPoint;
126begin
127 Result.Position := Point(0, 0);
128 if Assigned(BaseTrackPoint) then
129 with BaseTrackPoint do begin
130 UpPoint := BaseTrackPoint.GetNeighUp;
131 if Assigned(UpPoint) then begin
132 Result.Direction := SubPoint(UpPoint.Position, Position);
133 D := Distance(UpPoint.Position, Position);
134 if D > 0 then begin
135 Result.Position := Point(Trunc(Position.X + Result.Direction.X * RelPos / D),
136 Trunc(Position.Y + Result.Direction.Y * RelPos / D));
137 end else begin
138 Result.Position := Position;
139 end;
140 end;
141 end;
142end;
143
144procedure TTrackPosition.Move(Distance: Double);
145var
146 Direction: Integer;
147begin
148 Direction := Sign(Distance);
149 Distance := Abs(Distance);
150 while Distance > 0 do begin
151 if Direction > 0 then begin
152 if RelPos + Distance < BaseTrackPoint.GetDistance then begin
153 RelPos := RelPos + Distance;
154 Distance := 0;
155 end else begin
156 if Assigned(BaseTrackPoint.GetNeighUp) then begin
157 Distance := Distance - (BaseTrackPoint.GetDistance - RelPos);
158 BaseTrackPoint := BaseTrackPoint.GetNeighUp;
159 RelPos := 0;
160 end else
161 // Reverse direction at the end of track
162 Direction := -Direction;
163 end;
164 end else
165 if Direction < 0 then begin
166 if RelPos - Distance >= 0 then begin
167 RelPos := RelPos - Distance;
168 Distance := 0;
169 end else begin
170 if Assigned(BaseTrackPoint.GetNeighDown) then begin
171 Distance := Distance - RelPos;
172 BaseTrackPoint := BaseTrackPoint.GetNeighDown;
173 RelPos := BaseTrackPoint.GetDistance;
174 end else
175 // Reverse direction at the end of track
176 Direction := -Direction;
177 end;
178 end;
179 end;
180end;
181
182{ TTrackLinks }
183
184function TTrackLinks.SearchPoints(Point1, Point2: TTrackPoint): TTrackLink;
185var
186 I: Integer;
187begin
188 I := 0;
189 while (I < 0) and
190 ((Items[I].Points.First <> Point1) or (Items[I].Points.Last <> Point2)) and
191 ((Items[I].Points.First <> Point2) or (Items[I].Points.Last <> Point1)) do
192 Inc(I);
193 if I < 0 then Result := Items[I]
194 else Result := nil;
195end;
196
197function TTrackLinks.AddNew: TTrackLink;
198begin
199 Result := TTrackLink.Create;
200end;
201
202{ TTrackPoints }
203
204function TTrackPoints.AddNew: TTrackPoint;
205begin
206 Result := TTrackPoint.Create;
207 Result.Track := Track;
208end;
209
210{ TTrack }
211
212constructor TTrack.Create;
213begin
214 Points := TTrackPoints.Create;
215 Points.Track := Self;
216 Links := TTrackLinks.Create;
217end;
218
219destructor TTrack.Destroy;
220begin
221 FreeAndNil(Points);
222 FreeAndNil(Links);
223 inherited;
224end;
225
226function TTrack.GetLength: Integer;
227var
228 I: Integer;
229begin
230 Result := 0;
231 for I := 0 to Points.Count - 1 do
232 if I > 0 then
233 Result := Result + Distance(Points[I].Position, Points[I - 1].Position);
234end;
235
236procedure TTrack.RouteTrack(TP1, TP2: TTrackPoint);
237var
238 NewTrackPoint: TTrackPoint;
239 Delta: TPoint;
240 P1, P2: TPoint;
241 Index1, Index2: Integer;
242begin
243 RemoveTrackBetween(TP1, TP2);
244 Index1 := Points.IndexOf(TP1);
245 Index2 := Points.IndexOf(TP2);
246 P1 := Points[Index1].PositionDesigned;
247 P2 := Points[Index2].PositionDesigned;
248 NewTrackPoint := Points.AddNew;
249 Delta := Point(P2.X - P1.X, P2.Y - P1.Y);
250 if Abs(Delta.X) > Abs(Delta.Y) then begin
251 NewTrackPoint.PositionDesigned := Point(P2.X - Sign(Delta.X) * Abs(Delta.Y), P1.Y);
252 NewTrackPoint.Position := NewTrackPoint.PositionDesigned;
253 end else begin
254 NewTrackPoint.PositionDesigned := Point(P1.X, P2.Y - Sign(Delta.Y) * Abs(Delta.X));
255 NewTrackPoint.Position := NewTrackPoint.PositionDesigned;
256 end;
257 Points.Insert(Index1 + 1, NewTrackPoint);
258end;
259
260procedure TTrack.RemoveTrackBetween(TP1, TP2: TTrackPoint);
261var
262 Index1, Index2: Integer;
263 Temp: Integer;
264 I: Integer;
265begin
266 Index1 := Points.IndexOf(TP1);
267 Index2 := Points.IndexOf(TP2);
268 if (Index1 = -1) then
269 raise Exception.Create(Format(STrackPointNotFound, [1]));
270 if (Index2 = -1) then
271 raise Exception.Create(Format(STrackPointNotFound, [2]));
272 if Index1 > Index2 then begin
273 Temp := Index1;
274 Index1 := Index2;
275 Index2 := Temp;
276 end;
277 for I := 1 to Index2 - Index1 - 1 do
278 Points.Delete(Index1 + 1);
279end;
280
281{ TTrackLink }
282
283constructor TTrackLink.Create;
284begin
285 Points := TTrackPoints.Create;
286 Points.OwnsObjects := False;
287end;
288
289destructor TTrackLink.Destroy;
290begin
291 FreeAndNil(Points);
292 inherited;
293end;
294
295{ TTrackPointsAngleGroup }
296
297function TTrackPointsAngleGroup.SearchAngle(Angle: Double): TTrackPointsAngle;
298var
299 I: Integer;
300begin
301 I := 0;
302 while (I < Count) and (Items[I].Angle <> Angle) do Inc(I);
303 if I < Count then Result := Items[I]
304 else Result := nil;
305end;
306
307{ TTrackPointsAngle }
308
309constructor TTrackPointsAngle.Create;
310begin
311 TrackLinks := TTrackLinks.Create;
312 TrackLinks.OwnsObjects := False;
313end;
314
315destructor TTrackPointsAngle.Destroy;
316begin
317 FreeAndNil(TrackLinks);
318 inherited;
319end;
320
321{ TTrackPoint }
322
323procedure TTrackPoint.Connect(TrackPoint: TTrackPoint);
324var
325 NewLink: TTrackLink;
326begin
327 if NeighPoints.IndexOf(TrackPoint) = -1 then begin
328 NeighPoints.Add(TrackPoint);
329 TrackPoint.NeighPoints.Add(Self);
330
331 // Add new link to both self and connected track point
332 NewLink := Track.Links.AddNew;
333 NewLink.Points.Add(TrackPoint);
334 NewLink.Points.Add(Self);
335 NeighLinks.Add(NewLink);
336 TrackPoint.NeighLinks.Add(NewLink);
337 Track.Links.Add(NewLink);
338 end else raise Exception.Create(SAlreadyConnectedTrackPoint);
339end;
340
341procedure TTrackPoint.Disconnect(TrackPoint: TTrackPoint);
342var
343 Index: Integer;
344 Link: TTrackLink;
345begin
346 Index := NeighPoints.IndexOf(TrackPoint);
347 if NeighPoints.IndexOf(TrackPoint) <> -1 then begin
348 NeighPoints.Delete(Index);
349 TrackPoint.NeighPoints.Remove(Self);
350
351 // Remove link from both track points
352 Link := NeighLinks.SearchPoints(Self, TrackPoint);
353 NeighLinks.Remove(Link);
354 TrackPoint.NeighLinks.Remove(Link);
355 Track.Links.Remove(Link);
356 end else raise Exception.Create(SAlreadyDisconnectedTrackPoint);
357end;
358
359function TTrackPoint.GetDown: TTrackPoint;
360var
361 I: Integer;
362begin
363 I := Track.Points.IndexOf(Self) - 1;
364 while (I >= 0) and not Assigned(Track.Points[I].OwnerPoint) do
365 Dec(I);
366 if I >= 0 then Result := Track.Points[I]
367 else Result := nil;
368end;
369
370function TTrackPoint.GetUp: TTrackPoint;
371var
372 I: Integer;
373begin
374 I := Track.Points.IndexOf(Self) + 1;
375 while (I < Track.Points.Count) and not Assigned(Track.Points[I].OwnerPoint) do
376 Inc(I);
377 if I < Track.Points.Count then Result := Track.Points[I]
378 else Result := nil;
379end;
380
381function TTrackPoint.GetNeighDown: TTrackPoint;
382var
383 NewIndex: Integer;
384begin
385 Result := nil;
386 NewIndex := Track.Points.IndexOf(Self) - 1;
387 if NewIndex >= 0 then Result := Track.Points[NewIndex];
388end;
389
390function TTrackPoint.GetNeighUp: TTrackPoint;
391var
392 NewIndex: Integer;
393begin
394 Result := nil;
395 if Assigned(Track) then begin
396 NewIndex := Track.Points.IndexOf(Self) + 1;
397 if NewIndex < Track.Points.Count then Result := Track.Points[NewIndex];
398 end;
399end;
400
401function TTrackPoint.GetLinkDown: TTrackLink;
402begin
403 if Assigned(LinkDown) then Result := LinkDown
404 else begin
405 LinkDown := TTrackLink.Create;
406 LinkDown.Points.Add(GetNeighDown);
407 LinkDown.Points.Add(Self);
408 Result := LinkDown;
409 GetNeighDown.LinkUp := LinkDown;
410 end;
411end;
412
413function TTrackPoint.GetLinkUp: TTrackLink;
414begin
415 if Assigned(LinkUp) then Result := LinkUp
416 else begin
417 LinkUp := TTrackLink.Create;
418 LinkUp.Points.Add(Self);
419 LinkUp.Points.Add(GetNeighUp);
420 Result := LinkUp;
421 GetNeighUp.LinkDown := LinkUp;
422 end;
423end;
424
425function TTrackPoint.GetTrackPosition: Integer;
426var
427 Index: Integer;
428 I: Integer;
429begin
430 Result := 0;
431 Index := Track.Points.IndexOf(Self);
432 for I := 0 to Index - 1 do
433 Result := Result + Track.Points[I].GetDistance;
434end;
435
436function TTrackPoint.GetDistance: Integer;
437var
438 Index: Integer;
439begin
440 Index := Track.Points.IndexOf(Self);
441 if Index + 1 < Track.Points.Count then begin
442 Result := Distance(Track.Points[Index + 1].Position, Track.Points[Index].Position);
443 end else Result := 0;
444end;
445
446constructor TTrackPoint.Create;
447begin
448 NeighPoints := TTrackPoints.Create;
449 NeighPoints.OwnsObjects := False;
450 NeighLinks := TTrackLinks.Create;
451 NeighLinks.OwnsObjects := False;
452end;
453
454destructor TTrackPoint.Destroy;
455begin
456 FreeAndNil(NeighLinks);
457 FreeAndNil(NeighPoints);
458 inherited;
459end;
460
461end.
462
Note: See TracBrowser for help on using the repository browser.