View Javadoc

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