source: trunk/Track.pas

Last change on this file was 143, checked in by chronos, 11 months ago

Removed U prefix from all units.

File size: 14.2 KB
Line 
1unit Track;
2
3interface
4
5uses
6 Classes, SysUtils, Math, Generics.Collections, Geometric, DOM, XML, Items;
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 procedure LoadFromXmlNode(Node: TDOMNode; Points: TTrackPoints);
24 procedure SaveToXmlNode(Node: TDOMNode);
25 end;
26
27 { TTrackPoint }
28
29 TTrackPoint = class(TItem)
30 OwnerPoint: TObject;
31 Position: TPoint;
32 PositionDesigned: TPoint;
33 Pending: Boolean;
34 Track: TTrack;
35 NeighPoints: TTrackPoints;
36 NeighLinks: TTrackLinks;
37 LinkUp: TTrackLink;
38 LinkDown: TTrackLink;
39 procedure Connect(TrackPoint: TTrackPoint);
40 procedure Disconnect(TrackPoint: TTrackPoint);
41 function GetDown: TTrackPoint;
42 function GetUp: TTrackPoint;
43 function GetNeighDown: TTrackPoint;
44 function GetNeighUp: TTrackPoint;
45 function GetLinkDown: TTrackLink;
46 function GetLinkUp: TTrackLink;
47 function GetTrackPosition: Integer;
48
49 // Move to TTrackLink later
50 function GetDistance: Integer;
51
52 class function GetClassSysName: string; override;
53 procedure LoadFromXmlNode(Node: TDOMNode); override;
54 procedure SaveToXmlNode(Node: TDOMNode); override;
55 constructor Create;
56 destructor Destroy; override;
57 end;
58
59 { TTrackPoints }
60
61 TTrackPoints = class(TItems<TTrackPoint>)
62 Track: TTrack;
63 class function GetClassSysName: string; override;
64 function CreateItem: TTrackPoint; override;
65 procedure ResetToDesignatedPosition;
66 end;
67
68 { TTrackLink }
69
70 TTrackLink = class(TItem)
71 Points: TTrackPoints;
72 Shift: TPoint;
73 class function GetClassSysName: string; override;
74 procedure LoadFromXmlNode(Node: TDOMNode); override;
75 procedure SaveToXmlNode(Node: TDOMNode); override;
76 constructor Create;
77 destructor Destroy; override;
78 end;
79
80 { TTrackLinks }
81
82 TTrackLinks = class(TItems<TTrackLink>)
83 class function GetClassSysName: string; override;
84 function SearchPoints(Point1, Point2: TTrackPoint): TTrackLink;
85 end;
86
87 { TTrack }
88
89 TTrack = class(TItem)
90 Points: TTrackPoints;
91 Links: TTrackLinks;
92 Owner: TObject;
93 function GetLength: Integer;
94 procedure RouteTrack(TP1, TP2: TTrackPoint);
95 procedure RemoveTrackBetween(TP1, TP2: TTrackPoint);
96 constructor Create;
97 destructor Destroy; override;
98 class function GetClassSysName: string; override;
99 procedure LoadFromXmlNode(Node: TDOMNode); override;
100 procedure SaveToXmlNode(Node: TDOMNode); override;
101 end;
102
103 { TTrackPointsAngle }
104
105 TTrackPointsAngle = class
106 Angle: Double;
107 TrackLinks: TTrackLinks;
108 constructor Create;
109 destructor Destroy; override;
110 end;
111
112 { TTrackPointsAngleGroup }
113
114 TTrackPointsAngleGroup = class(TObjectList<TTrackPointsAngle>)
115 function SearchAngle(Angle: Double): TTrackPointsAngle;
116 end;
117
118
119implementation
120
121resourcestring
122 SAlreadyConnectedTrackPoint = 'Trying to connect already connected track point';
123 SAlreadyDisconnectedTrackPoint = 'Trying to disconnect not connected track point';
124 STrackPointNotFound = 'Track point %d not found';
125
126{ TTrackPosition }
127
128function TTrackPosition.GetTrackPosition: Integer;
129begin
130 Result := Round(RelPos);
131 if Assigned(BaseTrackPoint) then
132 Result := Result + BaseTrackPoint.GetTrackPosition;
133end;
134
135function TTrackPosition.GetVector: TVector;
136var
137 D: Integer;
138 UpPoint: TTrackPoint;
139begin
140 Result.Position := Point(0, 0);
141 if Assigned(BaseTrackPoint) then
142 with BaseTrackPoint do begin
143 UpPoint := BaseTrackPoint.GetNeighUp;
144 if Assigned(UpPoint) then begin
145 Result.Direction := SubPoint(UpPoint.Position, Position);
146 D := Distance(UpPoint.Position, Position);
147 if D > 0 then begin
148 Result.Position := Point(Trunc(Position.X + Result.Direction.X * RelPos / D),
149 Trunc(Position.Y + Result.Direction.Y * RelPos / D));
150 end else begin
151 Result.Position := Position;
152 end;
153 end;
154 end;
155end;
156
157procedure TTrackPosition.Move(Distance: Double);
158var
159 Direction: Integer;
160begin
161 Direction := Sign(Distance);
162 Distance := Abs(Distance);
163 while Distance > 0 do begin
164 if Direction > 0 then begin
165 if RelPos + Distance < BaseTrackPoint.GetDistance then begin
166 RelPos := RelPos + Distance;
167 Distance := 0;
168 end else begin
169 if Assigned(BaseTrackPoint.GetNeighUp) then begin
170 Distance := Distance - (BaseTrackPoint.GetDistance - RelPos);
171 BaseTrackPoint := BaseTrackPoint.GetNeighUp;
172 RelPos := 0;
173 end else
174 // Reverse direction at the end of track
175 Direction := -Direction;
176 end;
177 end else
178 if Direction < 0 then begin
179 if RelPos - Distance >= 0 then begin
180 RelPos := RelPos - Distance;
181 Distance := 0;
182 end else begin
183 if Assigned(BaseTrackPoint.GetNeighDown) then begin
184 Distance := Distance - RelPos;
185 BaseTrackPoint := BaseTrackPoint.GetNeighDown;
186 RelPos := BaseTrackPoint.GetDistance;
187 end else
188 // Reverse direction at the end of track
189 Direction := -Direction;
190 end;
191 end;
192 end;
193end;
194
195procedure TTrackPosition.LoadFromXmlNode(Node: TDOMNode; Points: TTrackPoints);
196begin
197 RelPos := ReadDouble(Node, 'RelPos', RelPos);
198 BaseTrackPoint := Points.FindById(ReadInteger(Node, 'BaseTrackPoint', 0));
199end;
200
201procedure TTrackPosition.SaveToXmlNode(Node: TDOMNode);
202begin
203 WriteDouble(Node, 'RelPos', RelPos);
204 if Assigned(BaseTrackPoint) then
205 WriteInteger(Node, 'BaseTrackPoint', BaseTrackPoint.Id)
206 else WriteInteger(Node, 'BaseTrackPoint', 0)
207end;
208
209{ TTrackLinks }
210
211class function TTrackLinks.GetClassSysName: string;
212begin
213 Result := 'Links';
214end;
215
216function TTrackLinks.SearchPoints(Point1, Point2: TTrackPoint): TTrackLink;
217var
218 I: Integer;
219begin
220 I := 0;
221 while (I < 0) and
222 ((Items[I].Points.First <> Point1) or (Items[I].Points.Last <> Point2)) and
223 ((Items[I].Points.First <> Point2) or (Items[I].Points.Last <> Point1)) do
224 Inc(I);
225 if I < 0 then Result := Items[I]
226 else Result := nil;
227end;
228
229{ TTrackPoints }
230
231class function TTrackPoints.GetClassSysName: string;
232begin
233 Result := 'Points';
234end;
235
236function TTrackPoints.CreateItem: TTrackPoint;
237begin
238 Result := inherited;
239 Result.Track := Track;
240end;
241
242procedure TTrackPoints.ResetToDesignatedPosition;
243var
244 I: Integer;
245begin
246 for I := 0 to Count - 1 do
247 Items[I].Position := Items[I].PositionDesigned;
248end;
249
250{ TTrack }
251
252constructor TTrack.Create;
253begin
254 Points := TTrackPoints.Create;
255 Points.Track := Self;
256 Links := TTrackLinks.Create;
257end;
258
259destructor TTrack.Destroy;
260begin
261 FreeAndNil(Points);
262 FreeAndNil(Links);
263 inherited;
264end;
265
266class function TTrack.GetClassSysName: string;
267begin
268 Result := 'Track';
269end;
270
271procedure TTrack.LoadFromXmlNode(Node: TDOMNode);
272var
273 NewNode: TDOMNode;
274begin
275 NewNode := Node.FindNode(DOMString(TTrackPoints.GetClassSysName));
276 if Assigned(NewNode) then
277 Points.LoadFromXmlNode(NewNode);
278
279 NewNode := Node.FindNode(DOMString(TTrackLinks.GetClassSysName));
280 if Assigned(NewNode) then
281 Links.LoadFromXmlNode(NewNode);
282end;
283
284procedure TTrack.SaveToXmlNode(Node: TDOMNode);
285var
286 NewNode: TDOMNode;
287begin
288 Points.RebuildItemsId;
289 Links.RebuildItemsId;
290
291 NewNode := Node.OwnerDocument.CreateElement(DOMString(TTrackPoints.GetClassSysName));
292 Node.AppendChild(NewNode);
293 Points.SaveToXmlNode(NewNode);
294
295 NewNode := Node.OwnerDocument.CreateElement(DOMString(TTrackLinks.GetClassSysName));
296 Node.AppendChild(NewNode);
297 Links.SaveToXmlNode(NewNode);
298end;
299
300function TTrack.GetLength: Integer;
301var
302 I: Integer;
303begin
304 Result := 0;
305 for I := 0 to Points.Count - 1 do
306 if I > 0 then
307 Result := Result + Distance(Points[I].Position, Points[I - 1].Position);
308end;
309
310procedure TTrack.RouteTrack(TP1, TP2: TTrackPoint);
311var
312 NewTrackPoint: TTrackPoint;
313 Delta: TPoint;
314 P1, P2: TPoint;
315 Index1, Index2: Integer;
316begin
317 RemoveTrackBetween(TP1, TP2);
318 Index1 := Points.IndexOf(TP1);
319 Index2 := Points.IndexOf(TP2);
320 P1 := Points[Index1].PositionDesigned;
321 P2 := Points[Index2].PositionDesigned;
322 NewTrackPoint := Points.CreateItem;
323 Delta := Point(P2.X - P1.X, P2.Y - P1.Y);
324 if Abs(Delta.X) > Abs(Delta.Y) then begin
325 NewTrackPoint.PositionDesigned := Point(P2.X - Sign(Delta.X) * Abs(Delta.Y), P1.Y);
326 NewTrackPoint.Position := NewTrackPoint.PositionDesigned;
327 end else begin
328 NewTrackPoint.PositionDesigned := Point(P1.X, P2.Y - Sign(Delta.Y) * Abs(Delta.X));
329 NewTrackPoint.Position := NewTrackPoint.PositionDesigned;
330 end;
331 Points.Insert(Index1 + 1, NewTrackPoint);
332end;
333
334procedure TTrack.RemoveTrackBetween(TP1, TP2: TTrackPoint);
335var
336 Index1, Index2: Integer;
337 Temp: Integer;
338 I: Integer;
339begin
340 Index1 := Points.IndexOf(TP1);
341 Index2 := Points.IndexOf(TP2);
342 if (Index1 = -1) then
343 raise Exception.Create(Format(STrackPointNotFound, [1]));
344 if (Index2 = -1) then
345 raise Exception.Create(Format(STrackPointNotFound, [2]));
346 if Index1 > Index2 then begin
347 Temp := Index1;
348 Index1 := Index2;
349 Index2 := Temp;
350 end;
351 for I := 1 to Index2 - Index1 - 1 do
352 Points.Delete(Index1 + 1);
353end;
354
355{ TTrackLink }
356
357class function TTrackLink.GetClassSysName: string;
358begin
359 Result := 'Link';
360end;
361
362procedure TTrackLink.LoadFromXmlNode(Node: TDOMNode);
363begin
364 inherited;
365end;
366
367procedure TTrackLink.SaveToXmlNode(Node: TDOMNode);
368begin
369 inherited;
370end;
371
372constructor TTrackLink.Create;
373begin
374 Points := TTrackPoints.Create;
375 Points.OwnsObjects := False;
376end;
377
378destructor TTrackLink.Destroy;
379begin
380 FreeAndNil(Points);
381 inherited;
382end;
383
384{ TTrackPointsAngleGroup }
385
386function TTrackPointsAngleGroup.SearchAngle(Angle: Double): TTrackPointsAngle;
387var
388 I: Integer;
389begin
390 I := 0;
391 while (I < Count) and (Items[I].Angle <> Angle) do Inc(I);
392 if I < Count then Result := Items[I]
393 else Result := nil;
394end;
395
396{ TTrackPointsAngle }
397
398constructor TTrackPointsAngle.Create;
399begin
400 TrackLinks := TTrackLinks.Create;
401 TrackLinks.OwnsObjects := False;
402end;
403
404destructor TTrackPointsAngle.Destroy;
405begin
406 FreeAndNil(TrackLinks);
407 inherited;
408end;
409
410{ TTrackPoint }
411
412procedure TTrackPoint.Connect(TrackPoint: TTrackPoint);
413var
414 NewLink: TTrackLink;
415begin
416 if NeighPoints.IndexOf(TrackPoint) = -1 then begin
417 NeighPoints.Add(TrackPoint);
418 TrackPoint.NeighPoints.Add(Self);
419
420 // Add new link to both self and connected track point
421 NewLink := Track.Links.CreateItem;
422 NewLink.Points.Add(TrackPoint);
423 NewLink.Points.Add(Self);
424 NeighLinks.Add(NewLink);
425 TrackPoint.NeighLinks.Add(NewLink);
426 Track.Links.Add(NewLink);
427 end else raise Exception.Create(SAlreadyConnectedTrackPoint);
428end;
429
430procedure TTrackPoint.Disconnect(TrackPoint: TTrackPoint);
431var
432 Index: Integer;
433 Link: TTrackLink;
434begin
435 Index := NeighPoints.IndexOf(TrackPoint);
436 if NeighPoints.IndexOf(TrackPoint) <> -1 then begin
437 NeighPoints.Delete(Index);
438 TrackPoint.NeighPoints.Remove(Self);
439
440 // Remove link from both track points
441 Link := NeighLinks.SearchPoints(Self, TrackPoint);
442 NeighLinks.Remove(Link);
443 TrackPoint.NeighLinks.Remove(Link);
444 Track.Links.Remove(Link);
445 end else raise Exception.Create(SAlreadyDisconnectedTrackPoint);
446end;
447
448function TTrackPoint.GetDown: TTrackPoint;
449var
450 I: Integer;
451begin
452 I := Track.Points.IndexOf(Self) - 1;
453 while (I >= 0) and not Assigned(Track.Points[I].OwnerPoint) do
454 Dec(I);
455 if I >= 0 then Result := Track.Points[I]
456 else Result := nil;
457end;
458
459function TTrackPoint.GetUp: TTrackPoint;
460var
461 I: Integer;
462begin
463 I := Track.Points.IndexOf(Self) + 1;
464 while (I < Track.Points.Count) and not Assigned(Track.Points[I].OwnerPoint) do
465 Inc(I);
466 if I < Track.Points.Count then Result := Track.Points[I]
467 else Result := nil;
468end;
469
470function TTrackPoint.GetNeighDown: TTrackPoint;
471var
472 NewIndex: Integer;
473begin
474 Result := nil;
475 NewIndex := Track.Points.IndexOf(Self) - 1;
476 if NewIndex >= 0 then Result := Track.Points[NewIndex];
477end;
478
479function TTrackPoint.GetNeighUp: TTrackPoint;
480var
481 NewIndex: Integer;
482begin
483 Result := nil;
484 if Assigned(Track) then begin
485 NewIndex := Track.Points.IndexOf(Self) + 1;
486 if NewIndex < Track.Points.Count then Result := Track.Points[NewIndex];
487 end;
488end;
489
490function TTrackPoint.GetLinkDown: TTrackLink;
491begin
492 if Assigned(LinkDown) then Result := LinkDown
493 else begin
494 LinkDown := TTrackLink.Create;
495 LinkDown.Points.Add(GetNeighDown);
496 LinkDown.Points.Add(Self);
497 Result := LinkDown;
498 GetNeighDown.LinkUp := LinkDown;
499 end;
500end;
501
502function TTrackPoint.GetLinkUp: TTrackLink;
503begin
504 if Assigned(LinkUp) then Result := LinkUp
505 else begin
506 LinkUp := TTrackLink.Create;
507 LinkUp.Points.Add(Self);
508 LinkUp.Points.Add(GetNeighUp);
509 Result := LinkUp;
510 GetNeighUp.LinkDown := LinkUp;
511 end;
512end;
513
514function TTrackPoint.GetTrackPosition: Integer;
515var
516 Index: Integer;
517 I: Integer;
518begin
519 Result := 0;
520 Index := Track.Points.IndexOf(Self);
521 for I := 0 to Index - 1 do
522 Result := Result + Track.Points[I].GetDistance;
523end;
524
525function TTrackPoint.GetDistance: Integer;
526var
527 Index: Integer;
528begin
529 Index := Track.Points.IndexOf(Self);
530 if Index + 1 < Track.Points.Count then begin
531 Result := Distance(Track.Points[Index + 1].Position, Track.Points[Index].Position);
532 end else Result := 0;
533end;
534
535class function TTrackPoint.GetClassSysName: string;
536begin
537 Result := 'Point';
538end;
539
540procedure TTrackPoint.LoadFromXmlNode(Node: TDOMNode);
541begin
542 inherited;
543 Position.X := ReadInteger(Node, 'PositionX', Position.X);
544 Position.Y := ReadInteger(Node, 'PositionY', Position.Y);
545 PositionDesigned.X := ReadInteger(Node, 'PositionDesignedX', PositionDesigned.X);
546 PositionDesigned.Y := ReadInteger(Node, 'PositionDesignedY', PositionDesigned.Y);
547end;
548
549procedure TTrackPoint.SaveToXmlNode(Node: TDOMNode);
550begin
551 inherited;
552 WriteInteger(Node, 'PositionX', Position.X);
553 WriteInteger(Node, 'PositionY', Position.Y);
554 WriteInteger(Node, 'PositionDesignedX', PositionDesigned.X);
555 WriteInteger(Node, 'PositionDesignedY', PositionDesigned.Y);
556end;
557
558constructor TTrackPoint.Create;
559begin
560 NeighPoints := TTrackPoints.Create;
561 NeighPoints.OwnsObjects := False;
562 NeighLinks := TTrackLinks.Create;
563 NeighLinks.OwnsObjects := False;
564end;
565
566destructor TTrackPoint.Destroy;
567begin
568 FreeAndNil(NeighLinks);
569 FreeAndNil(NeighPoints);
570 Track := nil;
571 OwnerPoint := nil;
572 inherited;
573end;
574
575end.
576
Note: See TracBrowser for help on using the repository browser.