source: tags/1.4.0/UTrack.pas

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