View Javadoc

1   package Steerings;
2   
3   import SteeringStuff.SteeringManager;
4   import SteeringStuff.SteeringTools;
5   import SteeringStuff.RefBoolean;
6   import SteeringStuff.ISteering;
7   
8   import java.util.List;
9   
10  import javax.vecmath.Tuple3d;
11  import javax.vecmath.Vector2d;
12  import javax.vecmath.Vector3d;
13  
14  import SteeringProperties.PathFollowingProperties;
15  import SteeringProperties.SteeringProperties;
16  import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
17  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
18  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
19  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
20  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange;
21  import cz.cuni.amis.utils.future.FutureStatus;
22  
23  /**
24   * A class for providing pathFuture following steering to bots.
25   *
26   * @author Marki
27   */
28  public class PathFollowingSteer implements ISteering {
29  
30      /** The bot moved by the PathFollowingSteer1; */
31      private UT2004Bot botself;
32  
33      /**Steering properties: the magnitude of the repulsive force, to repulse agent from the side of the corridor.
34       * Reasonable values are 0 - 1000, the default value is 200.*/
35      private int repulsiveForce;
36      /**Steering properties:  distance from the pathFuture.*/
37      int distanceFromThePath;
38      /**Steering properties:  pathFuture - list of pathFuture elemnts (ilocated).*/
39      IPathFuture<ILocated> pathFuture;
40      /**Steering properties:  path - list of pathFuture elemnts (ilocated).*/
41      List<ILocated> path;
42      /**Steering properties:  regulating force - helps the bot to keep the direction of the path.*/
43      double regulatingForce;
44      /**Steering properties: */
45      private int projection;
46  
47      private static int NEARLY_THERE_DISTANCE = 150;
48      
49      /** previousLocation location. */
50      Location previousLocation;
51      /** nextLocation location. */
52      Location nextLocation;
53      /** Index of the actual ILocated in the pathFuture.*/
54      int actualIndex;
55      // Distance from the nextLocation in last logic.
56      double lastDistanceFromNextLocation;
57      /**When we get new pathFuture, the variable is true. When we set this pathFuture, we also set newPath false*/
58      boolean newPath;
59  
60      private static boolean ZERO_DISTANCE = false;
61      //private int nextLocationIndex;
62  
63      /**
64       * 
65       * @param bot
66       */
67      public PathFollowingSteer(UT2004Bot bot) {
68          botself = bot;
69          actualIndex = 0;
70      }
71  
72      /**
73       * Moves the bot around the given pathFuture.
74       */
75      public Vector3d run(Vector3d scaledActualVelocity, RefBoolean wantsToGoFaster, RefBoolean wantsToStop, Location focus) {
76          Vector3d nextVelocity;
77          Vector3d actualVelocity = botself.getVelocity().getVector3d();
78          Location pointHeading;
79          Vector3d pointHeadingToFoot;
80          double distanceFromNextLocation;
81  
82          nextVelocity = new Vector3d(0,0,0);
83  
84          if (pathFuture == null) {
85              if (SteeringManager.DEBUG) System.out.println("path null");
86              return new Vector3d(0,0,0);
87          } 
88  
89          if (!newPath && pathFuture.getStatus() != FutureStatus.FUTURE_IS_READY) {
90              if (SteeringManager.DEBUG) System.out.println("path not ready");
91              return new Vector3d(0,0,0);
92          }
93  
94          if (newPath && pathFuture.getStatus() == FutureStatus.FUTURE_IS_READY) {
95              setComputedPath();
96          }
97  
98          if (path == null) {
99              setComputedPath();
100         }
101 
102         //----------------------------------------------------Path inited----------------------------------------------------//
103         
104         boolean shifted = shiftNextLocation();
105         distanceFromNextLocation = botself.getLocation().getDistance(nextLocation);
106 
107         //----------------------------------------------------Zero distance (no steering, just following exactly the path)----------------------------------------------------//
108         if (ZERO_DISTANCE) {
109             if (shifted) {
110                 /* Jestliže jsme dosáhli dalšího vrcholu, musíme zkontrolovat, zda již nejsme nakonci.*/
111                 if (actualIndex + 1 >= path.size()) {
112                     /* Jestliže jsme dosáhli posledního bodu, zastavíme se.*/
113                     if (distanceFromNextLocation <= NEARLY_THERE_DISTANCE) {
114                         wantsToStop.setValue(true);
115                         if (SteeringManager.DEBUG) System.out.println("We reached the target point of the path.");
116                     }
117                     return nextVelocity;
118                 }
119             }
120             Location botsLoc = botself.getLocation();
121             Vector3d toNextLoc = new Vector3d(nextLocation.x - botsLoc.x, nextLocation.y - botsLoc.y, nextLocation.z - botsLoc.z);
122             toNextLoc.normalize();
123             toNextLoc.scale(repulsiveForce);
124             nextVelocity.add(toNextLoc);
125             return nextVelocity;
126         }
127         //----------------------------------------------------Classical Path Following----------------------------------------------------//
128 
129         if (!shifted) {
130             if (lastDistanceFromNextLocation < distanceFromNextLocation) {
131                 lastDistanceFromNextLocation = distanceFromNextLocation;
132                 if (SteeringManager.DEBUG) System.out.println("Jdeme na spatnou stranu!");
133                 /* Jestliže se odchýlil od správného směru, tak na něj začne působit síla ve směru aktuálního úseku cesty.
134                  * Její velikost je přímo úměrná tomu, jak moc se odchýlil od správného směru.*/
135                 Vector3d rightDirectionForce = new Vector3d(nextLocation.x - previousLocation.x, nextLocation.y - previousLocation.y, 0);
136                 rightDirectionForce.normalize();
137                 if (SteeringManager.DEBUG) System.out.println("Od správného směru jsme se odchýlili o "+rightDirectionForce.angle(botself.getVelocity().getVector3d()));
138                 double scale = rightDirectionForce.angle(botself.getVelocity().getVector3d())/Math.PI;
139                 rightDirectionForce.scale(repulsiveForce*scale);
140                 nextVelocity.add((Tuple3d) rightDirectionForce);
141                 wantsToGoFaster.setValue(false);
142                 return nextVelocity;
143             } else {
144                 lastDistanceFromNextLocation = distanceFromNextLocation;
145             }
146         } else {
147             lastDistanceFromNextLocation = distanceFromNextLocation;
148             if (actualIndex + 1 >= path.size()) {
149                 /* Jestliže jsme dosáhli posledního bodu, zastavíme se.*/
150                 if (distanceFromNextLocation <= NEARLY_THERE_DISTANCE) {
151                     wantsToStop.setValue(true);
152                     //focus.setTo(nextLocation);    //Natočíme se k poslednímu bodu.
153                     if (SteeringManager.DEBUG) System.out.println("We reached the target point of the path.");
154                 } else {
155                     Vector3d botToNextLocation = new Vector3d(nextLocation.x - botself.getLocation().x, nextLocation.y - botself.getLocation().y, nextLocation.z - botself.getLocation().z);
156                     botToNextLocation.scale(actualVelocity.length() / botToNextLocation.length());
157                     nextVelocity.add((Tuple3d) botToNextLocation);
158                     if (SteeringManager.DEBUG) System.out.println("We must go to the target point.");
159                 }
160                 return nextVelocity;
161             }
162         }
163 
164         /* Jestliže se vůdce pohybuje, tak si vypočítáme svou pozici a na tu se budeme snažit dostat.*/
165         ConfigChange cc = botself.getWorldView().getSingle(ConfigChange.class);
166         double oneTick = cc.getVisionTime();    //Doba trvání jednoho tiku. Potřebuje ke spočítání lokace vůdce za jeden tik.
167 
168         double projectionTime = projection * oneTick;
169 
170         /*Vektor leadersShiftToNextTick představuje vektor posunutí vůdce do příštího tiku, avšak místo jeho aktuální rychlosti bereme v případě paměti tu průměrnou.*/
171         Vector3d shift = new Vector3d(projectionTime * actualVelocity.x, projectionTime * actualVelocity.y, projectionTime * actualVelocity.z);
172 
173         //The bot's projection - where he will be in short time. It's bot's location + his velocity vector.
174         pointHeading = new Location(botself.getLocation().x + shift.x, botself.getLocation().y + shift.y, botself.getLocation().z + shift.z);
175 
176         Vector2d start = new Vector2d(previousLocation.x, previousLocation.y);
177         Vector2d end = new Vector2d(nextLocation.x, nextLocation.y);
178         Vector2d pointP = new Vector2d(pointHeading.x, pointHeading.y);
179 
180         Vector2d foot = SteeringTools.getNearestPoint(start, end, pointP, false);
181         
182         //A vector from the point of heading to the crossing of -bx+ay+d=d. Its length is a distance of the bot's projection to the pathFuture.
183         pointHeadingToFoot = new Vector3d(foot.x - pointHeading.getX(), foot.y - pointHeading.getY(), 0);
184 
185         //If the bot's projection wanders off the pathFuture, the vector footToPoint is added to his velocity - it should be sufficient, typically.
186         if (pointHeadingToFoot.length() > distanceFromThePath) {
187             double distOut = pointHeadingToFoot.length() - distanceFromThePath;
188             //The further the projection is, the more powerful the steering force is.
189 
190             /* Co všechno má na sílu přitahující agenta dovnitř koridoru vliv?
191              * Tak jednak to, jak moc se vzdálí od středu koridoru. Tedy pointHeadingToFoot.length().
192              * Druhak šíře koridoru, tedy pak spíše pointHeadingToFoot.length() - distanceFromThePath.
193              * Dále to, za jak dlouho se to stane. Tedy si nějak spočítat, kdy by měl překročit hranice koridoru.
194              * Nakonec velikost síly repulsiveForce. Touto silou by to mělo působit v nějakém průměrném případě.
195              */
196 
197             //Jak daleko od cesty se za danou dobu dostaneme. Pokud dvakrát vzdálenost od cesty či více, koefA=1; čím méně, tím je koefA menší.
198             double koefA = distOut / distanceFromThePath;   
199             if (koefA > 2) {
200                 koefA = 2;
201             }
202             nextVelocity.add(pointHeadingToFoot);
203             nextVelocity.normalize();
204             nextVelocity.scale(koefA * repulsiveForce + 30);    //Přičítáme 30, aby i těsně na hranici působila síla, která něco zmůže.
205             if (SteeringManager.DEBUG) System.out.println("STAY IN CORRIDOR!");
206             wantsToGoFaster.setValue(false);
207         } else { //If the bot is on his pathFuture, he may continue at full throttle.
208             wantsToGoFaster.setValue(true);
209         }       
210 
211         Vector3d regulatingVector = new Vector3d(nextLocation.x - previousLocation.x,nextLocation.y - previousLocation.y,0);
212         regulatingVector.normalize();
213         regulatingVector.scale(regulatingForce);
214         nextVelocity.add((Tuple3d) regulatingVector);
215 
216         return nextVelocity;
217     }
218 
219     private boolean shiftNextLocation() {
220         if (path == null) return false;
221         if (SteeringManager.DEBUG) System.out.println("vzdalenost od nextLocation "+botself.getLocation().getDistance(nextLocation));
222         
223         if (botself.getLocation().getDistance(nextLocation) <= distanceFromThePath || getBehindBorder()) {  //Tady může být problém, že v jednom tiku se lze posunout nejvýše o jeden bod na cestě.
224             if (actualIndex + 1 < path.size()) {
225                 if (SteeringManager.DEBUG) {
226                     System.out.println("Novy navpoint; size " + path.size() + " actual index " + actualIndex);
227                 }
228                 ILocated help = path.get(actualIndex + 1);
229                 if (help == null) {
230                     if (SteeringManager.DEBUG) {
231                         System.out.println("NULL!!! " + pathFuture.getStatus() + pathFuture.isDone());
232                     }
233                 } else {
234                     actualIndex++;
235                     previousLocation = nextLocation;
236                     nextLocation = help.getLocation();
237                 }
238             }
239             return true;
240         } else {
241             return false;
242         }
243     }
244 
245     /**Returns true, if the agent overstepped the border of the nextLocation.*/
246     private boolean getBehindBorder() {
247         /* Vektory startA a endA (agent) určují úsečku "A" od previousLocation k lokaci agenta.*/
248         Vector2d startA = new Vector2d(previousLocation.x, previousLocation.y);
249         Vector2d endA = new Vector2d(botself.getLocation().x, botself.getLocation().y);
250         Vector2d directionA = new Vector2d(endA.x - startA.x, endA.y - startA.y);
251         
252         /* Vektory startB a endB (border) určují přímku "B" prochízející nextLocation mající směr kolmý na spojnici nextLocation a previousLocation. Je to tedy jakási hranice
253          * a my zkoumáme, zda se agent dostal za tuto hranici.*/
254         Vector2d startB = new Vector2d(nextLocation.x, nextLocation.y);
255         Vector2d normalDirectionB = new Vector2d(nextLocation.x - previousLocation.x, nextLocation.y - previousLocation.y);
256         Vector2d directionB = new Vector2d(-normalDirectionB.y, normalDirectionB.x);
257         //Vector2d endB = new Vector2d(startB.x + directionB.x, startB.y + directionB.y);
258 
259         Vector2d intersection = SteeringTools.getIntersection(startA, directionA, startB, directionB, SteeringTools.LineType.ABSCISSA, SteeringTools.LineType.STRAIGHT_LINE);
260         /*Pokud úsečka "A" protíná přímku "B" (intersection není null), agent se dostal za hranici a je třeba posunout následující bod na cestě.*/
261         if (SteeringManager.DEBUG) System.out.println("Jsme za hranicí "+(intersection != null)+" průsečík: "+intersection);
262         return (intersection != null);
263     }
264 
265     /**V tuto chvíli víme, že je pathFuture done a chceme nastavit path. Pokud se to nepovede, vrací false, jinak true.*/
266     private boolean setComputedPath() {
267         List<ILocated> myNewPath = this.pathFuture.get();
268         if (myNewPath == null) {
269             if (SteeringManager.DEBUG) System.out.println("null cesta ");
270             return false;
271         } else if (myNewPath.size() < 1) {
272             if (SteeringManager.DEBUG) System.out.println("kratka cesta " + myNewPath.size());
273             return false;
274         }
275         if (SteeringManager.DEBUG) System.out.println("path received " + myNewPath.size());
276         if (differentNewPath(myNewPath)) {  //Jestliže se nová cesta neliší od staré, nemá smysl nic měnit. Jestliže se liší, pak si vybereme bod na této cestě nejbližší naší lokaci - a ten si nastavíme jako nextLocation.
277             path = myNewPath;
278             actualIndex = getIndexOfNearestILocated();
279             nextLocation = path.get(actualIndex).getLocation();
280             previousLocation = botself.getLocation();
281             lastDistanceFromNextLocation = botself.getLocation().getDistance(nextLocation);
282         }
283         newPath = false;    //Nová cesta byla nastavena, tedy není třeba řešit žádnou newPath.
284         return true;
285     }
286 
287     private boolean differentNewPath(List<ILocated> myNewPath) {
288         if (path == null) {
289             return true;    //Je-li první cesta null, má smysl zkoušet druhou.
290         } else if (myNewPath == null) {
291             return false;   //V takovém případě chceme nechat původní cestu.
292         } else if (path.size() != myNewPath.size()) {
293             return true;    //Jsou-li jinak dlouhé, pak jsou různé.
294         } else {
295             for(int i = 0; i < path.size(); i++) {
296                 ILocated a = path.get(i);
297                 ILocated b = myNewPath.get(i);
298                 if (!a.equals(b)) {
299                     return true;    //Jestliže se alespoň jedna dvojice bodů liší, jsou cesty různé.
300                 }
301             }
302             return false;   //Žádná dvojice se neliší.
303         }
304     }
305 
306     /**Vrátí index na cestě path, který je nejblíže aktuální lokaci agenta.*/
307     private int getIndexOfNearestILocated() {
308         int result = 0;
309         Location botsLocation = botself.getLocation();
310         double dist = botsLocation.getDistance(path.get(0).getLocation());
311         for(int index = 0; index < path.size(); index++) {
312             ILocated il = path.get(index);
313             if (botsLocation.getDistance(il.getLocation()) < dist) {
314                 result = index;
315                 dist = botsLocation.getDistance(il.getLocation());
316             }
317         }
318         return result;
319     }
320 
321     public void setProperties(SteeringProperties newProperties) {
322         this.repulsiveForce = ((PathFollowingProperties)newProperties).getRepulsiveForce();
323         this.distanceFromThePath = ((PathFollowingProperties)newProperties).getDistanceFromThePath();
324         this.pathFuture = ((PathFollowingProperties)newProperties).getPath();
325         this.regulatingForce = ((PathFollowingProperties)newProperties).getRegulatingForce();
326         this.projection = ((PathFollowingProperties)newProperties).getProjection();
327         if (path != null) { //Jestliže jsme už v průběhu výpočtů nějaké cesty, chceme pokračovat s ní, dokud nebude hotová ta nová.
328             newPath = true;
329         } else {
330             newPath = false;
331         }
332     }
333 
334     public PathFollowingProperties getProperties() {
335         PathFollowingProperties properties = new PathFollowingProperties();
336         properties.setRepulsiveForce(repulsiveForce);
337         properties.setDistanceFromThePath(distanceFromThePath);
338         properties.setPath(pathFuture);
339         properties.setRegulatingForce(regulatingForce);
340         properties.setProjection(projection);
341         return properties;
342     }
343     
344 }
345         //Výpis cesty.
346         /*int i = 0;
347         for (ILocated np : path) {
348             if (SteeringManager.DEBUG) System.out.println("Cesta " + i + ": " + np.getLocation());
349             i++;
350         }*/