View Javadoc

1   /*
2    * @(#)VehiclePhysical.java Jul 4, 2004
3    * 
4    * Copyright (c) 2003, 2004 Delft University of Technology Jaffalaan 5, 2628 BX
5    * Delft, the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
9    */
10  
11  package nl.tudelft.simulation.traffic.vehicle;
12  
13  import java.rmi.RemoteException;
14  import java.util.ArrayList;
15  import java.util.Iterator;
16  import java.util.List;
17  import javax.media.j3d.Bounds;
18  import javax.media.j3d.Transform3D;
19  import javax.vecmath.Point3d;
20  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
21  import nl.tudelft.simulation.dsol.simulators.DEVDESSSimulatorInterface;
22  import nl.tudelft.simulation.dsol.statistics.charts.XYChart;
23  import nl.tudelft.simulation.event.EventInterface;
24  import nl.tudelft.simulation.event.EventProducer;
25  import nl.tudelft.simulation.event.EventProducerInterface;
26  import nl.tudelft.simulation.event.EventType;
27  import nl.tudelft.simulation.event.TimedEvent;
28  import nl.tudelft.simulation.language.d3.BoundingBox;
29  import nl.tudelft.simulation.language.d3.DirectedPoint;
30  import nl.tudelft.simulation.logger.Logger;
31  import nl.tudelft.simulation.traffic.animation.VehicleAnimation;
32  import nl.tudelft.simulation.traffic.controlpoint.ControlPointInterface;
33  import nl.tudelft.simulation.traffic.controlpoint.real.Changeable;
34  import nl.tudelft.simulation.traffic.controlpoint.real.SpeedLimitInterface;
35  import nl.tudelft.simulation.traffic.controlpoint.real.StopSignInterface;
36  import nl.tudelft.simulation.traffic.controlpoint.util.ControlPointsList;
37  import nl.tudelft.simulation.traffic.track.Track;
38  import nl.tudelft.simulation.traffic.track.TrackInterface;
39  import nl.tudelft.simulation.traffic.track.TrackLinkInterface;
40  import nl.tudelft.simulation.traffic.track.util.TrackProgression;
41  
42  /***
43   * <br>
44   * (c) copyright 2003-2004 <a href="http://www.simulation.tudelft.nl">Delft
45   * University of Technology </a>, the Netherlands. <br>
46   * See for project information <a href="http://www.simulation.tudelft.nl">
47   * www.simulation.tudelft.nl </a> <br>
48   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
49   * License (GPL) </a>, no warranty <br>
50   * 
51   * @version Jul 4, 2004 <br>
52   * @author <a
53   * href="http://www.tbm.tudelft.nl/webstaf/alexandv/index.htm">Alexander
54   * Verbraeck </a>
55   */
56  public class VehiclePhysical extends EventProducer
57          implements
58              VehiclePhysicalInterface
59  {
60      /*** the simulator to schedule on */
61      private DEVDESSSimulatorInterface simulator;
62  
63      /*** the vehicle type with all relevant information */
64      private VehicleType vehicleType;
65  
66      /*** vehicle control of this vehicle */
67      private VehicleControlInterface vehicleControl;
68  
69      /*** current track to travel on */
70      private List currentTracks = new ArrayList();
71  
72      /*** current progression on the current track */
73      private double progression;
74  
75      /*** current speed */
76      private double currentSpeed = 0.0;
77  
78      /*** total distance traveled since creation */
79      private double distanceTraveled;
80  
81      /*** are we allowed to drive? */
82      private boolean driving = false;
83  
84      /*** the differential equations to make the vehicle move */
85      private VehiclePositioner vehiclePositioner;
86  
87      /*** current maximum speed */
88      private double currentMaximumSpeed;
89  
90      /*** acceleration profile */
91      private AccelerationProfile accelerationProfile;
92  
93      /*** deceleration profile */
94      private DecelerationProfile decelerationProfile;
95  
96      /*** the progression horizon to look for control points */
97      private double horizon;
98  
99      /*** the control points between the current position and the horizon */
100     private List speedControlPoints = new ArrayList();
101 
102     /*** the array of control points on the current track */
103     private List trackControlPointsList = new ArrayList();
104 
105     /*** the unique name */
106     private String name;
107 
108     /*** the chart to display the speed vs time graph */
109     private XYChart tvChart;
110 
111     /*** the chart to display the speed vs progression graph */
112     private XYChart xvChart;
113 
114     /*** the t-v event type to use for the xyChart */
115     public EventType TIME_SPEED_1SECOND_EVENT = new EventType("tv");
116 
117     /*** the x-v event type to use for the xyChart */
118     public EventType DISTANCE_SPEED_1SECOND_EVENT = new EventType("xv");
119 
120     /*** last second in the chart */
121     private double sampleSecond = -10.0;
122 
123     /*** helper string */
124     private String lastString;
125 
126     /*** the maximum braking distance */
127     private double maxBrakeDistance;
128 
129     /***
130      * Create a physical vehicle
131      * 
132      * @param vehicleType
133      * @param vehicleControl
134      * @param track
135      * @param progression
136      * @param maxSpeedStart
137      * @param simulator
138      */
139     public VehiclePhysical(final VehicleType vehicleType,
140             final VehicleControlInterface vehicleControl,
141             final TrackInterface track, final double progression,
142             final double maxSpeedStart,
143             final DEVDESSSimulatorInterface simulator)
144     {
145         try
146         {
147             this.simulator = simulator;
148             this.distanceTraveled = 0;
149             this.name = vehicleType.generateUniqueName();
150             this.vehicleType = vehicleType;
151             this.accelerationProfile = vehicleType.getAccelerationProfile();
152             this.decelerationProfile = vehicleType.getDecelerationProfile();
153             this.maxBrakeDistance = this.decelerationProfile.getBrakeDistance(
154                     vehicleType.getVehicleMaximumSpeed(), 0.0);
155             this.horizon = this.maxBrakeDistance * 1.5;
156             System.out.println("Horizon for vehicle " + this.name + " is "
157                     + this.horizon);
158             this.vehicleControl = vehicleControl;
159             moveToFirstTrack(track, progression);
160             this.currentMaximumSpeed = maxSpeedStart;
161             if (simulator instanceof AnimatorInterface)
162             {
163                 ArrayList segments = this.vehicleType.getSegments();
164                 if (segments.isEmpty())
165                 {
166                     new VehicleAnimation(this, simulator, this.vehicleType
167                             .getColor());
168                 } else
169                 {
170                     for (int i = 0; i < segments.size(); i++)
171                     {
172                         VehicleType.Segment segment = (VehicleType.Segment) segments
173                                 .get(i);
174                         new VehicleSegment(this, simulator, vehicleType
175                                 .getColor(), segment.getFront(), segment
176                                 .getAxle1(), segment.getAxle2(), segment
177                                 .getLength());
178                     }
179                 }
180                 // make a t/v graph for this vehicle
181                 this.tvChart = new XYChart(simulator, "t-v vehicle "
182                         + this.name);
183                 this.tvChart.getChart().getXYPlot().getDomainAxis()
184                         .setAutoRange(true);
185                 this.tvChart.getChart().getXYPlot().getDomainAxis().setLabel(
186                         "time (s)");
187                 this.tvChart.getChart().getXYPlot().getRangeAxis()
188                         .setAutoRange(false);
189                 this.tvChart.getChart().getXYPlot().getRangeAxis()
190                         .setLowerBound(0.0);
191                 this.tvChart.getChart().getXYPlot().getRangeAxis()
192                         .setUpperBound(
193                                 this.vehicleType.getVehicleMaximumSpeed());
194                 this.tvChart.getChart().getXYPlot().getRangeAxis().setLabel(
195                         "speed (m/s)");
196                 this.TIME_SPEED_1SECOND_EVENT = new EventType("t-v vehicle "
197                         + this.name);
198                 this.tvChart.add("t-v vehicle " + this.name, this,
199                         this.TIME_SPEED_1SECOND_EVENT);
200                 // make an x/v graph for this vehicle
201                 this.xvChart = new XYChart(simulator, "x-v vehicle "
202                         + this.name);
203                 this.xvChart.getChart().getXYPlot().getDomainAxis()
204                         .setAutoRange(true);
205                 this.xvChart.getChart().getXYPlot().getDomainAxis().setLabel(
206                         "progression (m)");
207                 this.xvChart.getChart().getXYPlot().getRangeAxis()
208                         .setAutoRange(false);
209                 this.xvChart.getChart().getXYPlot().getRangeAxis()
210                         .setLowerBound(0.0);
211                 this.xvChart.getChart().getXYPlot().getRangeAxis()
212                         .setUpperBound(
213                                 this.vehicleType.getVehicleMaximumSpeed());
214                 this.xvChart.getChart().getXYPlot().getRangeAxis().setLabel(
215                         "speed (m/s)");
216                 this.DISTANCE_SPEED_1SECOND_EVENT = new EventType(
217                         "x-v vehicle " + this.name);
218                 this.xvChart.add("x-v vehicle " + this.name, this,
219                         this.DISTANCE_SPEED_1SECOND_EVENT);
220             }
221             this.vehiclePositioner = new VehiclePositioner(this, simulator);
222             this.vehiclePositioner.setValue(0.0, progression);
223         } catch (Exception exception)
224         {
225             exception.printStackTrace();
226         }
227     }
228 
229     /***
230      * @see nl.tudelft.simulation.event.EventProducerInterface#getEventTypes()
231      */
232     public synchronized EventType[] getEventTypes()
233     {
234         return new EventType[]{};
235     }
236 
237     /***
238      * rebuilds the list of control points between the current location and the
239      * horizon. Always take at least two tracks, where the second track should
240      * be longer than the max brake distance.
241      */
242     private void readControlPointsWithinHorizon()
243     {
244         Logger.fine(this, "readControlPointsWithinHorizon", this.name
245                 + ": Making new horizon list for track "
246                 + this.getCurrentTrack());
247         this.speedControlPoints.clear();
248         // loop through the tracks, and store all control points we encounter
249         TrackInterface track = this.getCurrentTrack();
250         double progression = 0.0;
251         while (progression < this.horizon + this.getCurrentTrack().getLength())
252         {
253             if (track == null)
254                 return;
255             // take whole tracks
256             ControlPointsList cpList = track.getControlPoints();
257             Iterator it = cpList.iterator();
258             while (it.hasNext())
259             {
260                 ControlPointInterface cp = (ControlPointInterface) it.next();
261                 // subscribe to the changable ones
262                 if (cp instanceof Changeable)
263                 {
264                     try
265                     {
266                         ((EventProducerInterface) cp).addListener(this,
267                                 Changeable.CHANGE_STATUS_EVENT);
268                     } catch (Exception e)
269                     {
270                         Logger
271                                 .severe(this, "readControlPointsWithinHorizon",
272                                         e);
273                     }
274                 }
275                 // look at the ones that (might) limit speed or are changable
276                 // and add these to the list, with the right progression
277                 // from the start of the first track in the list
278                 if ((cp instanceof Changeable)
279                         || (cp instanceof SpeedLimitInterface)
280                         || (cp instanceof StopSignInterface))
281                 {
282                     ControlPointProgression cpp = new ControlPointProgression(
283                             cp, progression + cp.getProgression());
284                     this.speedControlPoints.add(cpp);
285                     Logger.fine(this, "readControlPointsWithinHorizon",
286                             "   ..." + cp + ", distance = " + progression
287                                     + cp.getProgression());
288                 }
289             }
290             progression += track.getLength();
291             TrackLinkInterface link = track.getEndLink();
292             track = link.getActiveSuccessor(getCurrentTrack());
293         }
294     }
295 
296     /***
297      * indicate that the vehicle can start driving. As long as this variable is
298      * false, the acceleration remains zero.
299      * 
300      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#startDriving()
301      */
302     public void startDriving()
303     {
304         this.driving = true;
305     }
306 
307     /***
308      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getCurrentAcceleration(double,
309      * double)
310      */
311     public double getCurrentAcceleration(final double speed,
312             final double progression)
313     {
314         try
315         {
316             double time = this.simulator.getSimulatorTime();
317             // sample max 1 times per second
318             if (time - this.sampleSecond > 1.0)
319             {
320                 this.fireEvent(new TimedEvent(this.TIME_SPEED_1SECOND_EVENT,
321                         this, new Double(speed), time));
322                 this.fireEvent(new TimedEvent(
323                         this.DISTANCE_SPEED_1SECOND_EVENT, this, new Double(
324                                 speed), this.distanceTraveled));
325                 this.sampleSecond = time;
326             }
327         } catch (RemoteException e)
328         {
329             e.printStackTrace();
330         }
331         // this is the method that determines, based on the restrictions,
332         // whether to decelerate, accelerate, or drive constant. It should
333         // be efficient, as it is called in each timestep for each vehicle.
334         if (!this.driving)
335         {
336             return 0.0;
337         }
338         // look if we can still accelerate
339         double accdec = 0.0;
340         if (speed < this.currentMaximumSpeed)
341         {
342             accdec = this.accelerationProfile.getAcceleration(speed);
343         }
344         // look if we need to brake; look at the current brake distance and
345         // see if there are any speed limits within the interval, that need
346         // our attention
347         // cheat a little bit: brake 1 centimeters before stop point.
348         // add one meter for the moment, because the deceleration is one
349         // meter off -- TODO: check the brake distance
350         double brakeDistance0 = this.decelerationProfile.getBrakeDistance(
351                 speed, 0) + 1.01;
352         // Look if we are too close to a predecessor. If so, brake.
353         double predecessorDistance = calculatePredecessorDistance(progression,
354                 brakeDistance0);
355         if (predecessorDistance < brakeDistance0)
356         {
357             if (speed > 0)
358                 return this.decelerationProfile.getDeceleration(speed);
359             else
360                 return 0.0;
361         }
362         // See if there are speedpoints
363         Iterator it = this.speedControlPoints.iterator();
364         boolean finished = false;
365         while (it.hasNext() && !finished)
366         {
367             ControlPointProgression cpp = (ControlPointProgression) it.next();
368             ControlPointInterface cp = cpp.getControlPoint();
369             double cpDistance = cpp.getDistance() - progression;
370             if (cpDistance > brakeDistance0)
371                 finished = true;
372             // if < 0, we already passed the point, but the list was
373             // regenerated for the WHOLE track -- e.g. after a Changeable
374             // event, or another disruption.
375             if (cpDistance > 0.0)
376             {
377                 // look if we need to brake for this point
378                 if (cp instanceof SpeedLimitInterface)
379                 {
380                     if (cpDistance <= this.decelerationProfile
381                             .getBrakeDistance(speed, ((SpeedLimitInterface) cp)
382                                     .getSpeedLimit()))
383                     {
384                         if (!cp.toString().equals(this.lastString))
385                         {
386                             this.lastString = cp.toString();
387                             Logger
388                                     .fine(
389                                             this,
390                                             "getCurrentAcceleration",
391                                             this.name
392                                                     + " braking for speedlimit cp "
393                                                     + this.lastString
394                                                     + ", cpDistance="
395                                                     + cpDistance
396                                                     + "\n   brakeDistance="
397                                                     + this.decelerationProfile
398                                                             .getBrakeDistance(
399                                                                     speed,
400                                                                     ((SpeedLimitInterface) cp)
401                                                                             .getSpeedLimit())
402                                                     + ", progression="
403                                                     + this.progression
404                                                     + "\n   track="
405                                                     + this.getCurrentTrack()
406                                                     + ", length="
407                                                     + this.getCurrentTrack()
408                                                             .getLength());
409                         }
410                         accdec = this.decelerationProfile
411                                 .getDeceleration(speed);
412                         finished = true;
413                     }
414                 }
415                 // look if we need to stop for this point
416                 else if (cp instanceof StopSignInterface)
417                 {
418                     if (((StopSignInterface) cp).getStatus().equals(
419                             StopSignInterface.STOP)
420                             && (cpDistance <= brakeDistance0))
421                     {
422                         if (!cp.toString().equals(this.lastString))
423                         {
424                             this.lastString = cp.toString();
425                             Logger.fine(this, "getCurrentAcceleration",
426                                     this.name
427                                             + " braking for trafficlight cp "
428                                             + this.lastString
429                                             + ", cpDistance="
430                                             + cpDistance
431                                             + "\n   brakeDistance="
432                                             + brakeDistance0
433                                             + ", progression="
434                                             + this.progression
435                                             + "\n   track="
436                                             + this.getCurrentTrack()
437                                             + ", length="
438                                             + this.getCurrentTrack()
439                                                     .getLength());
440                         }
441                         accdec = this.decelerationProfile
442                                 .getDeceleration(speed);
443                         finished = true;
444                     }
445                     // look if we are 'close' to the traffic light;
446                     // if it is red, we also stop.
447                     if (((StopSignInterface) cp).getStatus().equals(
448                             StopSignInterface.STOP)
449                             && (cpDistance <= 1.01))
450                     {
451                         if (speed > 0)
452                             accdec = this.decelerationProfile
453                                     .getDeceleration(speed);
454                         else
455                             accdec = 0.0;
456                     }
457                 }
458             }
459         }
460         return accdec;
461     }
462 
463     /***
464      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#setSpeedAndProgression(double,
465      * double)
466      */
467     public double[] setSpeedAndProgression(final double speed,
468             final double progression)
469     {
470         this.currentSpeed = speed;
471         // the progression is increased with the new progression minus
472         // the previous progression
473         this.distanceTraveled += progression - this.progression;
474         TrackInterface track = this.getCurrentTrack();
475         // check if we hit any control points. Work on the basis of just the
476         // next control point, but check if there are multiple control points
477         // on or near the same location
478         if (progression < track.getLength())
479         {
480             driveOverControlPoints(this.progression, progression);
481             this.progression = progression;
482         } else
483         {
484             // we moved off the track; handle the control points of both this
485             // track and the next.
486             driveOverControlPoints(this.progression, track.getLength());
487             double restProgression = Math.max(0.0, track.getLength()
488                     - this.progression);
489             moveToNextTrack();
490             track = this.getCurrentTrack();
491             driveOverControlPoints(0.0, restProgression);
492             this.vehiclePositioner.setValue(this.currentSpeed, restProgression);
493             this.progression = restProgression;
494         }
495         return new double[]{this.currentSpeed, this.progression};
496     }
497 
498     /***
499      * The front of the vehicle passes the track limit. Add a new track to the
500      * current track list. The progression on the new track will be 0. The
501      * 'update' method is responsible for deleting trailing tracks that are no
502      * longer needed.
503      * 
504      * Note: the vehicle is only registered on the track of the front of the
505      * vehicle.
506      */
507     protected void moveToNextTrack()
508     {
509         getCurrentTrack().removeVehicle(this);
510         TrackLinkInterface link = getCurrentTrack().getEndLink();
511         TrackInterface newCurrentTrack = link
512                 .getActiveSuccessor(getCurrentTrack());
513         this.progression = 0;
514         this.currentTracks.add(newCurrentTrack);
515         Logger.fine(this, "moveToNextTrack", this.name
516                 + " moved to next track " + this.getCurrentTrack());
517         readControlPointsWithinHorizon();
518         makeControlPointsArray(newCurrentTrack);
519         newCurrentTrack.addVehicle(this);
520     }
521 
522     /***
523      * @param track
524      * @param progression
525      */
526     protected void moveToFirstTrack(final TrackInterface track,
527             final double progression)
528     {
529         this.currentTracks.add(track);
530         this.progression = progression;
531         this.currentTracks.add(track);
532         readControlPointsWithinHorizon();
533         makeControlPointsArray(track);
534         track.addVehicle(this);
535     }
536 
537     /***
538      * @param progression
539      * @param maxDistance
540      * @return
541      */
542     private double calculatePredecessorDistance(final double progression,
543             final double maxDistance)
544     {
545         double rest = maxDistance;
546         TrackInterface track = getCurrentTrack();
547         double length = track.getLength() - progression;
548         while (rest > 0)
549         {
550             List vehicleList = track.getVehiclesOnTrack();
551             for (int i = 0; i < vehicleList.size(); i++)
552             {
553                 VehiclePhysicalInterface vehicle = (VehiclePhysicalInterface) vehicleList
554                         .get(i);
555                 if (vehicle != this)
556                 {
557                     double distance = Track.calculateDistanceFrontBack(this,
558                             vehicle);
559                     if (distance > 0)
560                         return distance;
561                 }
562             }
563             rest -= length;
564             track = track.getEndLink().getActiveSuccessor(track);
565             if (track == null)
566                 return (maxDistance + 1.0);
567             length = track.getLength();
568         }
569         return (maxDistance + 1.0);
570     }
571 
572     /***
573      * @param track
574      */
575     private void makeControlPointsArray(final TrackInterface track)
576     {
577         this.trackControlPointsList.clear();
578         ControlPointsList cpl = track.getControlPoints();
579         Iterator it = cpl.iterator();
580         while (it.hasNext())
581         {
582             ControlPointInterface cp = (ControlPointInterface) it.next();
583             this.trackControlPointsList.add(cp);
584         }
585     }
586 
587     /***
588      * @param from
589      * @param to
590      */
591     protected void driveOverControlPoints(final double from, final double to)
592     {
593         boolean finished = false;
594         while (!finished)
595         {
596             if (this.trackControlPointsList.size() == 0)
597                 finished = true;
598             else
599             {
600                 ControlPointInterface cp = (ControlPointInterface) this.trackControlPointsList
601                         .get(0);
602                 if (cp.getProgression() >= from - 0.001
603                         && cp.getProgression() <= to + 0.001)
604                 {
605                     System.out.println(this.name + " drove over cp " + cp);
606                     this.trackControlPointsList.remove(0);
607                     // see if we can also remove it from the speedControlPoints
608                     Iterator it = this.speedControlPoints.iterator();
609                     while (it.hasNext())
610                     {
611                         ControlPointProgression cpp = (ControlPointProgression) it
612                                 .next();
613                         if (cpp.getControlPoint().equals(cp))
614                         {
615                             System.out
616                                     .println("also removed from speedControlPoints: "
617                                             + cp);
618                             it.remove();
619                         }
620                     }
621                     cp.pass(this);
622                 } else
623                 {
624                     finished = true;
625                 }
626             }
627         }
628     }
629 
630     /***
631      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getCurrentSpeed()
632      */
633     public double getCurrentSpeed()
634     {
635         return this.currentSpeed;
636     }
637 
638     /***
639      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#setMaximumSpeed(double)
640      */
641     public void setMaximumSpeed(final double maxSpeed)
642     {
643         this.currentMaximumSpeed = maxSpeed;
644     }
645 
646     /***
647      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getProgression()
648      */
649     public double getProgression()
650     {
651         return this.progression;
652     }
653 
654     /***
655      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getCurrentTrack()
656      */
657     public TrackInterface getCurrentTrack()
658     {
659         return (TrackInterface) this.currentTracks.get(this.currentTracks
660                 .size() - 1);
661     }
662 
663     /***
664      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getVehicleType()
665      */
666     public VehicleType getVehicleType()
667     {
668         return this.vehicleType;
669     }
670 
671     /***
672      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getVehicleControl()
673      */
674     public VehicleControlInterface getVehicleControl()
675     {
676         return this.vehicleControl;
677     }
678 
679     /***
680      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#setVehicleControl(nl.tudelft.simulation.traffic.vehicle.VehicleControlInterface)
681      */
682     public void setVehicleControl(VehicleControlInterface vehicleControl)
683     {
684         this.vehicleControl = vehicleControl;
685     }
686 
687     /***
688      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#getDistanceTraveled()
689      */
690     public double getDistanceTraveled()
691     {
692         return this.distanceTraveled;
693     }
694 
695     /***
696      * @see nl.tudelft.simulation.traffic.vehicle.VehiclePhysicalInterface#removeVehicle()
697      */
698     public void removeVehicle() throws RemoteException
699     {
700         // the vehicle is at this moment only registered on the front track
701         getCurrentTrack().removeVehicle(this);
702         this.vehicleControl.removeVehicle();
703         this.fireEvent(VehiclePhysicalInterface.REMOVE_EVENT, true);
704         removeAllListeners();
705         this.vehiclePositioner.stopIntegration();
706         this.driving = false;
707     }
708 
709     /***
710      * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
711      */
712     public void notify(EventInterface event) throws RemoteException
713     {
714         if (event.getType().equals(Changeable.CHANGE_STATUS_EVENT))
715         {
716             if (!this.driving)
717             {
718                 ((EventProducerInterface) event.getSource()).removeListener(
719                         this, Changeable.CHANGE_STATUS_EVENT);
720             }
721             this.readControlPointsWithinHorizon();
722         } else
723             System.err.println("Vehicle " + this.name + ": unknown event: "
724                     + event);
725     }
726 
727     /***
728      * @return the total length of the current track list
729      */
730     protected double getCurrentTracksLength()
731     {
732         double totalLength = 0.0;
733         for (int i = 0; i < this.currentTracks.size(); i++)
734         {
735             totalLength += ((TrackInterface) this.currentTracks.get(i))
736                     .getLength();
737         }
738         return totalLength;
739     }
740 
741     /***
742      * @see nl.tudelft.simulation.dsol.animation.LocatableInterface#getLocation()
743      */
744     public DirectedPoint getLocation()
745     {
746         // for this moment: determine direction on basis of total length
747         // measure along the path
748         DirectedPoint front = getCurrentTrack().getLocationOfProgression(
749                 this.progression);
750         TrackProgression tp = getCurrentTrack()
751                 .calculateTrackProgressionListActive(
752                         this.progression - this.vehicleType.getLength());
753         if (tp == null)
754         {
755             tp = getCurrentTrack().calculateTrackProgressionListActive(
756                     this.progression - 0.1);
757         }
758         if (tp == null)
759             return new DirectedPoint(front.x, front.y, 0.1, 0, 0, 0);
760         DirectedPoint back = tp.getTrack().getLocationOfProgression(
761                 tp.getProgression());
762         double theta = Math.atan2(back.y - front.y, back.x - front.x);
763         return new DirectedPoint(front.x, front.y, front.z + 0.05, 0, 0, theta);
764     }
765 
766     /***
767      * get a location along the track to the back
768      * 
769      * @param distance
770      * @return the location
771      */
772     public Point3d getBackwardLocation(final double distance)
773     {
774         TrackProgression tp = getCurrentTrack()
775                 .calculateTrackProgressionListActive(
776                         this.progression - distance);
777         if (tp == null)
778         {
779             System.err.println("ERROR " + this.name
780                     + ": cannot calculate backwardLocation from "
781                     + getCurrentTrack() + ", progression=" + this.progression
782                     + ", back distance=" + distance);
783             tp = getCurrentTrack().calculateTrackProgressionListActive(
784                     this.progression);
785             if (tp == null)
786                 return new DirectedPoint(0, 0, 0);
787         }
788         DirectedPoint back = tp.getTrack().getLocationOfProgression(
789                 tp.getProgression());
790         return new Point3d(back.x, back.y, back.z);
791     }
792 
793     /***
794      * @see nl.tudelft.simulation.dsol.animation.LocatableInterface#getBounds()
795      */
796     public Bounds getBounds()
797     {
798         DirectedPoint location = getLocation();
799         double width = this.vehicleType.getWidth();
800         Bounds box = new BoundingBox(new Point3d(0.0, -width / 2.0, 0.0),
801                 new Point3d(this.vehicleType.getLength(), width / 2.0,
802                         0.0));
803         Transform3D rot = new Transform3D();
804         rot.rotZ(location.getRotZ());
805         box.transform(rot);
806         return box;
807     }
808 
809     /***
810      * @see java.lang.Object#toString()
811      */
812     public String toString()
813     {
814         return this.name;
815     }
816 
817     /***
818      * 
819      * <br>
820      * (c) copyright 2003-2004 <a href="http://www.simulation.tudelft.nl">Delft
821      * University of Technology </a>, the Netherlands. <br>
822      * See for project information <a href="http://www.simulation.tudelft.nl">
823      * www.simulation.tudelft.nl </a> <br>
824      * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General
825      * Public License (GPL) </a>, no warranty <br>
826      * 
827      * @version Jul 7, 2004 <br>
828      * @author <a
829      * href="http://www.tbm.tudelft.nl/webstaf/alexandv/index.htm">Alexander
830      * Verbraeck </a>
831      */
832     private class ControlPointProgression
833     {
834         /*** the control point */
835         private ControlPointInterface controlPoint;
836 
837         /*** the overall progression from the start of the first track */
838         private double distance;
839 
840         /***
841          * @param controlPoint
842          * @param distance
843          */
844         public ControlPointProgression(
845                 final ControlPointInterface controlPoint, final double distance)
846         {
847             this.controlPoint = controlPoint;
848             this.distance = distance;
849         }
850 
851         /***
852          * @return Returns the controlPoint.
853          */
854         public ControlPointInterface getControlPoint()
855         {
856             return this.controlPoint;
857         }
858 
859         /***
860          * @return Returns the distance.
861          */
862         public double getDistance()
863         {
864             return this.distance;
865         }
866 
867         /***
868          * @see java.lang.Object#toString()
869          */
870         public String toString()
871         {
872             return "CPP:" + this.controlPoint + ", distance:" + this.distance;
873         }
874     }
875 }