View Javadoc

1   package Steerings;
2   
3   import SteeringStuff.SteeringManager;
4   import SteeringStuff.SteeringTools;
5   import SteeringStuff.RefBoolean;
6   import SteeringProperties.SteeringProperties;
7   import SteeringProperties.WallFollowingProperties;
8   import SteeringStuff.IRaysFlagChanged;
9   import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
10  import javax.vecmath.Vector3d;
11  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
12  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.AutoTraceRay;
13  import SteeringStuff.ISteering;
14  import SteeringStuff.RaycastingManager;
15  import SteeringStuff.SteeringRay;
16  import SteeringStuff.SteeringType;
17  import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
18  import java.util.HashMap;
19  import java.util.LinkedList;
20  import java.util.Random;
21  import java.util.concurrent.ExecutionException;
22  import java.util.concurrent.Future;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  import javax.vecmath.Tuple3d;
26  
27  
28  /**
29   * A class for providing wall following steering to bots via raycasting.
30   *
31   * @author Marki
32   */
33  public class WallFollowingSteer implements ISteering, IRaysFlagChanged {
34      /** This steering needs botself, raycasting manager. */
35      private UT2004Bot botself;
36      private RaycastingManager rayManager;
37  
38      private int wallForce;
39      /** Řád síly - 1-10. */
40      private int orderOfTheForce;
41      /**Steering properties: */
42      private double attractiveForceWeight;
43      /**Steering properties: */
44      private double repulsiveForceWeight;
45      /**Steering properties: */
46      private double convexEdgesForceWeight;
47      /**Steering properties: */
48      private double concaveEdgesForceWeight;
49      /**Steering properties: */
50      private boolean justMySide;
51      /**Steering properties: */
52      private boolean specialDetection;
53      /**Steering properties: */
54      private boolean frontCollisions;
55      
56  
57      /** ISteering properties:  distance from the wall - how far from the wall should the bot go.*/
58      int DISTANCE_FROM_THE_WALL = 166;
59      //How big is the value of the bot's counter when leaving a wall. (For so many ticks he is turning.)
60      private static final int DEFAULTCOUNTER = 10;
61      //Tento parametr je asi na pytel, páč to s ním stejně moc dobře nefunguje. Takže je jako konstanta a asi ho smažu.
62      private boolean goFast = false;
63  
64      //Possible states of the bot - whether he's following nothing, the left hand, or the right hand.
65      enum State { NOTHING, LEFT, RIGHT };
66      
67      
68      //The bot's state.
69      State state;
70  
71      //Counter of bot's memory, when leaving a wall (90° turns etc.).
72      int counter;
73  
74      //Help variable to remember, when we were turning beacause of the convex edge --> we will diminish the force concave edges.
75      boolean convexTurning;
76      
77      private boolean raysReady;
78      private HashMap<String, Future<AutoTraceRay>> myFutureRays;
79  
80      // Constants for rays' ids
81      protected static final String NLEFTFRONT = "wleftfront";
82      protected static final String NLEFT = "wleft";
83      protected static final String NRIGHTFRONT = "wrightfront";
84      protected static final String NRIGHT = "wright";
85      protected static final String NFRONT = "wfront";
86  
87      Random random = new Random();
88      
89      /**
90       * Bot's rays.
91       */
92      AutoTraceRay nleft, nright, nleftfront, nrightfront, nfront;
93  
94      /**
95       * Lengths of bot's rays. Short rays are supposed for side rays, long rays for nfront rays.
96       */
97      int shortSideRayLength;
98      int shortSideFrontRayLength;
99      int longSideRayLength;
100     int longSideFrontRayLength;
101     int shortFrontRayLength;
102     int longFrontRayLength;
103 
104 
105     /**
106      *
107      * @param bot Instance of the steered bot.
108      * @param rayManager Raycasting manager of the steered bot.
109      * @param newProperties Sets the steering properties.
110      */
111     public WallFollowingSteer(UT2004Bot bot, RaycastingManager rayManager, SteeringProperties newProperties) {
112         botself = bot;
113         this.rayManager = rayManager;
114         setProperties(newProperties);   //This is not necessary.
115         state = State.NOTHING;
116         counter = 0;
117         convexTurning = false;
118         prepareRays();
119     }
120 
121     /**
122      *
123      * @param bot Instance of the steered bot.
124      * @param rayManager Raycasting manager of the steered bot.
125      */
126     public WallFollowingSteer(UT2004Bot bot, RaycastingManager rayManager) {
127         botself = bot;
128         this.rayManager = rayManager;
129         setProperties(new WallFollowingProperties());   //This is not necessary.
130         state = State.NOTHING;
131         counter = 0;
132         convexTurning = false;
133         prepareRays();
134     }
135 
136     //<editor-fold defaultstate="collapsed" desc="Rays">
137 
138     //Prepares rays for the bot, removes any old rays and sets new ones.
139     private void prepareRays() {
140 
141         /*Délky postranních paprsků jsou 8 a 12. Délky šikmých se vynásobí 2 a předního se vynásobí odmocninou(3).*/
142         int shortLength = 8;
143         int longLength = 12;
144 
145         shortSideRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * shortLength * DISTANCE_FROM_THE_WALL / 166f);        //8
146         longSideRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * longLength * DISTANCE_FROM_THE_WALL / 166f);        //12
147         shortSideFrontRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * shortLength * 2 * DISTANCE_FROM_THE_WALL / 166f);  //20
148         longSideFrontRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * longLength * 2 * DISTANCE_FROM_THE_WALL / 166f);   //30
149         shortFrontRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * shortLength * Math.sqrt(3) * DISTANCE_FROM_THE_WALL / 166f);      //18
150         longFrontRayLength = (int) (UnrealUtils.CHARACTER_COLLISION_RADIUS * longLength * Math.sqrt(3) * DISTANCE_FROM_THE_WALL / 166f);       //27
151         
152         //Five rays are created.
153         LinkedList<SteeringRay> rayList = new LinkedList<SteeringRay>();
154         rayList.add(new SteeringRay(NLEFT, new Vector3d(0, -1, 0), longSideRayLength));
155         rayList.add(new SteeringRay(NLEFTFRONT, new Vector3d(Math.sqrt(3), -1, 0), longSideFrontRayLength));
156         rayList.add(new SteeringRay(NRIGHTFRONT, new Vector3d(Math.sqrt(3), 1, 0), longSideFrontRayLength));
157         rayList.add(new SteeringRay(NRIGHT, new Vector3d(0, 1, 0), longSideRayLength));
158         rayList.add(new SteeringRay(NFRONT, new Vector3d(1, 0, 0), longFrontRayLength));
159         rayManager.addRays(SteeringType.WALL_FOLLOWING, rayList, this);
160         raysReady = false;
161         //System.out.println("Rays wall preparation end.");
162     }
163 
164 
165     public void flagRaysChanged() {
166         myFutureRays = rayManager.getMyFutureRays(SteeringType.WALL_FOLLOWING);
167         raysReady = false;
168         listenToRays();
169     }
170 
171     private void listenToRays() {
172         for(String rayId : myFutureRays.keySet()) {
173             Future<AutoTraceRay> fr = myFutureRays.get(rayId);
174             if (fr.isDone()) {
175                 //System.out.println("Ray done."+rayId);
176                 try {
177                     if (rayId.equals(NLEFTFRONT)) {
178                         nleftfront = fr.get();
179                     } else if (rayId.equals(NRIGHTFRONT)) {
180                         nrightfront = fr.get();
181                     } else if (rayId.equals(NLEFT)) {
182                         nleft = fr.get();
183                     } else if (rayId.equals(NRIGHT)) {
184                         nright = fr.get();
185                     } else if (rayId.equals(NFRONT)) {
186                         nfront = fr.get();
187                     }
188                     raysReady = true;
189                 } catch (InterruptedException ex) {
190                     Logger.getLogger(ObstacleAvoidanceSteer.class.getName()).log(Level.SEVERE, null, ex);
191                 } catch (ExecutionException ex) {
192                     Logger.getLogger(ObstacleAvoidanceSteer.class.getName()).log(Level.SEVERE, null, ex);
193                 }
194             } else {
195                 //System.out.println("Ray isn't done."+rayId);
196             }
197         }
198     }
199     //</editor-fold>
200     
201     /**
202      * When called, the bot starts steering, when possible, he walks straight, he steers away from obstacles though, when necessary.
203      * If the isn't a wall, he goes straight forward.
204      */
205     public Vector3d run(Vector3d scaledActualVelocity, RefBoolean wantsToGoFaster, RefBoolean wantsToStop, Location focus) {
206 
207         //<editor-fold defaultstate="collapsed" desc="Set and init variables">
208 
209         wantsToGoFaster.setValue(false);
210         //Supposed velocity in the next tick of logic, after applying various steering forces to the bot. If no sensor is active, it will stay as the current velocity.
211         Vector3d nextVelocity = new Vector3d(0,0,0);
212 
213         if (!raysReady) {
214             listenToRays();
215             return nextVelocity;
216         }
217         
218         //A vector from the point where one of bot's rays crosses the wall to the bot.
219         Vector3d botToHitLocation;
220 
221         //Normal to the point, where bot's ray intersect with wall.
222         Vector3d normal;
223         
224         //Normal vector is multiplied by this when applied to the bot.
225         double multiplier;
226 
227         //The bot's velocity is received.
228         Vector3d actualVelocity = botself.getVelocity().getVector3d();
229 
230         //Whether any of the former short rays is triggered - i.e. whether the bot is close to any wall. If not, he may walk at normal speed.
231         boolean shortrays = false;
232 
233         //Whether sensors signalize the collision. (Computed in the run())
234         boolean sensornLeft = false;
235         boolean sensornRight = false;
236         boolean sensornLeftFront = false;
237         boolean sensornRightFront = false;
238         boolean sensornFront = false;
239         
240         //The bot checks his sensors
241         if (nleftfront != null)
242             sensornLeftFront = nleftfront.isResult();
243         if (nrightfront != null)
244             sensornRightFront = nrightfront.isResult();
245         if (nleft != null)
246             sensornLeft = nleft.isResult();
247         if (nright != null)
248             sensornRight = nright.isResult();
249         if (nfront != null)
250             sensornFront = nfront.isResult();
251 
252         //</editor-fold>
253 
254         //<editor-fold defaultstate="collapsed" desc="Set state">
255 
256         //The bot may change his state.
257         if (state == State.NOTHING) {
258             //If the bot is in NOTHING state and he touches the wall with both of his negative side rays
259             //(pleft and pleftront or pright and prightfront), he switches to the appropriate state.
260             if (sensornLeft && sensornLeftFront) {
261                 state = State.LEFT;
262                 counter = DEFAULTCOUNTER;
263             } else if (sensornRight && sensornRightFront) {
264                 state = State.RIGHT;
265                 counter = DEFAULTCOUNTER;
266             } else if (sensornFront) {
267                 if (frontCollisions) {
268                     botToHitLocation = new Vector3d(nfront.getHitLocation().x - botself.getLocation().x, nfront.getHitLocation().y - botself.getLocation().y, 0);
269                     normal = nfront.getHitNormal();
270                     if (Math.PI - normal.angle(actualVelocity) < Math.PI/5) {   //Nějak téměř kolmo na zeď.
271                         boolean turnLeft;
272                         if (normal.angle(actualVelocity) == 180) {
273                             turnLeft = random.nextBoolean();
274                             if (SteeringManager.DEBUG) {
275                                 System.out.println("Wall exactly front collision.");
276                             }
277                         } else {
278                             turnLeft = SteeringTools.pointIsLeftFromTheVector(actualVelocity, normal);
279                             if (SteeringManager.DEBUG) {
280                                 System.out.println("Wall nearly front collision.");
281                             }
282                         }
283                         if (SteeringManager.DEBUG) {
284                             System.out.println("We turn left " + turnLeft);
285                         }
286                         Vector3d turn = SteeringTools.getTurningVector(actualVelocity, turnLeft);
287                         turn.normalize();
288                         turn.scale(0.5);
289                         normal.add(turn);
290                         normal.normalize();
291                         multiplier = Math.pow(((longFrontRayLength - botToHitLocation.length()) * 2f / longFrontRayLength), orderOfTheForce);
292                         normal.scale(multiplier * wallForce);
293                         nextVelocity.add((Tuple3d) normal);
294                         return nextVelocity;
295                     }
296                 }
297             }
298         } else if (state == State.LEFT) {
299             if (sensornLeft && sensornLeftFront) {//The bot walks alongside the wall, he replenishes his counter.
300                 counter = DEFAULTCOUNTER;
301             }
302         } else if (state == State.RIGHT) {
303             if (sensornRight && sensornRightFront) {//The bot walks alongside the wall, he replenishes his counter.
304                 counter = DEFAULTCOUNTER;
305             }
306         }
307         //</editor-fold>
308 
309         //<editor-fold defaultstate="collapsed" desc="Just my side parameter">
310 
311         if (justMySide) {
312             if (state.equals(State.LEFT)) {
313                 sensornRight = false;
314                 sensornRightFront = false;
315             } else if (state.equals(State.RIGHT)) {
316                 sensornLeft = false;
317                 sensornLeftFront = false;
318             }
319         }
320         //</editor-fold>
321 
322         //<editor-fold defaultstate="collapsed" desc="Setting the wantsToGoFaster value.">
323 
324         if (goFast) {
325             if (sensornFront) { //If wall is in front of the bot, he shouldn't go faster.
326                 wantsToGoFaster.setValue(false);
327             } else {
328                 wantsToGoFaster.setValue(true);
329             }
330         } else {
331             if (!shortrays) {   //If nothing is signalling repulsively, the bot can return to its normal speed.
332                 wantsToGoFaster.setValue(true);
333             } else {
334                 wantsToGoFaster.setValue(false);
335             }
336         }
337         //</editor-fold>
338 
339         //<editor-fold defaultstate="collapsed" desc="Main ray forces of the wall">
340 
341         /*
342          *All sensors are checked respectively. When any of them signalizes a hit, a vector is added to nextvelocity.
343          *The vector added is derived from normal vector of the wall, such that when the ray is half in the wall (see multiplier),
344          *the steering power is as powerful as walking power at maximal velocity.
345          */
346         if (sensornLeft) {
347             //The force, which hauls the bot towards to the wall.
348             botToHitLocation = new Vector3d(nleft.getHitLocation().x - botself.getLocation().x, nleft.getHitLocation().y - botself.getLocation().y, 0);
349             normal = nleft.getHitNormal();
350             multiplier = Math.pow(1 - (longSideRayLength - botToHitLocation.length()) / longSideRayLength, orderOfTheForce);
351             normal.scale(attractiveForceWeight * multiplier * wallForce);
352             nextVelocity.sub((Tuple3d) normal);
353             //if (SteeringManager.DEBUG) System.out.println("left to "+normal.length());
354 
355             //When closer, the bot is pushed away from the wall by another force.
356             if (botToHitLocation.length() < shortSideRayLength) {
357                 shortrays = true;
358                 multiplier = Math.pow(((shortSideRayLength - botToHitLocation.length()) * 2f / shortSideRayLength), orderOfTheForce);
359                 normal.normalize();
360                 normal.scale(repulsiveForceWeight * multiplier * wallForce);
361                 nextVelocity.add((Tuple3d) normal);
362                 //if (SteeringManager.DEBUG) System.out.println("left from "+normal.length());
363             }
364         }
365 
366         if (sensornRight) {
367             //The force, which hauls the bot towards to the wall.
368             botToHitLocation = new Vector3d(nright.getHitLocation().x - botself.getLocation().x, nright.getHitLocation().y - botself.getLocation().y, 0);
369             normal = nright.getHitNormal();
370             multiplier = Math.pow(1 - (longSideRayLength - botToHitLocation.length()) / longSideRayLength, orderOfTheForce);
371             normal.scale(attractiveForceWeight * multiplier * wallForce);
372             nextVelocity.sub((Tuple3d) normal);
373             //if (SteeringManager.DEBUG) System.out.println("right to "+normal.length());
374 
375             //When closer, the bot is pushed away from the wall by another force.
376             if (botToHitLocation.length() < shortSideRayLength) {
377                 shortrays = true;
378                 multiplier = Math.pow(((shortSideRayLength - botToHitLocation.length()) * 2f / shortSideRayLength), orderOfTheForce);
379                 normal.normalize();
380                 normal.scale(repulsiveForceWeight * multiplier * wallForce);
381                 nextVelocity.add((Tuple3d) normal);
382                 //if (SteeringManager.DEBUG) System.out.println("right from "+normal.length());
383             }
384         }
385         if (sensornLeftFront) {
386             //The force, which hauls the bot towards to the wall.
387             botToHitLocation = new Vector3d(nleftfront.getHitLocation().x - botself.getLocation().x, nleftfront.getHitLocation().y - botself.getLocation().y, 0);
388             normal = nleftfront.getHitNormal();
389             multiplier = Math.pow(1 - (longSideFrontRayLength - botToHitLocation.length()) / longSideFrontRayLength, orderOfTheForce);
390             normal.scale(attractiveForceWeight * multiplier * wallForce);
391             nextVelocity.sub((Tuple3d) normal);
392             //if (SteeringManager.DEBUG) System.out.println("left-front to "+normal.length());
393 
394             //When closer, the bot is pushed away from the wall by another force.
395             if (botToHitLocation.length() < shortSideFrontRayLength) {
396                 shortrays = true;
397                 multiplier = Math.pow(((shortSideFrontRayLength - botToHitLocation.length()) * 2f / shortSideFrontRayLength), orderOfTheForce);
398                 normal.normalize();
399                 normal.scale(repulsiveForceWeight * multiplier * wallForce);
400                 nextVelocity.add((Tuple3d) normal);
401                 //if (SteeringManager.DEBUG) System.out.println("left-front from "+normal.length());
402             }
403 
404         }
405         if (sensornRightFront) {
406             //The force, which hauls the bot towards to the wall.
407             botToHitLocation = new Vector3d(nrightfront.getHitLocation().x - botself.getLocation().x, nrightfront.getHitLocation().y - botself.getLocation().y, 0);
408             normal = nrightfront.getHitNormal();
409             multiplier = Math.pow(1 - (longSideFrontRayLength - botToHitLocation.length()) / longSideFrontRayLength, orderOfTheForce);
410             normal.scale(attractiveForceWeight * multiplier * wallForce);
411             nextVelocity.sub((Tuple3d) normal);
412             //if (SteeringManager.DEBUG) System.out.println("right-front to "+normal.length());
413 
414             //When closer, the bot is pushed away from the wall by another force.
415             if (botToHitLocation.length() < shortSideFrontRayLength) {
416                 shortrays = true;
417                 multiplier = Math.pow(((shortSideFrontRayLength - botToHitLocation.length()) * 2f / shortSideFrontRayLength), orderOfTheForce);
418                 normal.normalize();
419                 normal.scale(repulsiveForceWeight * multiplier * wallForce);
420                 nextVelocity.add((Tuple3d) normal);
421                 //if (SteeringManager.DEBUG) System.out.println("right-front from "+normal.length());
422             }
423         }
424         if (sensornFront) {
425             //No attractive force in this case.
426             botToHitLocation = new Vector3d(nfront.getHitLocation().x - botself.getLocation().x, nfront.getHitLocation().y - botself.getLocation().y, 0);
427             normal = nfront.getHitNormal();
428             //multiplier = Math.pow(1 - (longSideFrontRayLength - botToHitLocation.length()) / longSideFrontRayLength, orderOfTheForce);
429             //normal.scale(multiplier * MAGNITUDE_OF_THE_FORCE);
430             //nextVelocity.sub((Tuple3d) normal);
431 
432             //When closer, the bot is pushed away from the wall by another force.
433             if (botToHitLocation.length() < shortFrontRayLength) {
434                 shortrays = true;
435                 multiplier = Math.pow(((shortFrontRayLength - botToHitLocation.length()) * 2f / shortFrontRayLength), orderOfTheForce);
436                 normal.normalize();
437                 normal.scale(repulsiveForceWeight * 3 * multiplier * wallForce);    //3 krát, aby to mělo větší vliv.
438                 //if (SteeringManager.DEBUG) System.out.println("Hey front "+normal.length());
439                 nextVelocity.add((Tuple3d) normal);
440             }
441         }
442         //</editor-fold>
443         
444         boolean nextTurning = false;
445         
446         //<editor-fold defaultstate="collapsed" desc="Convex edges">
447         //System.out.println("Convex turning. "+convexTurning);
448         if (specialDetection) {
449             if (state.equals(State.LEFT)) {
450                 if ( (sensornFront && (convexTurning || (Math.abs(nfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/4)) ) ||
451                    ( (sensornLeftFront && (Math.abs(nleftfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/2) && convexTurning) ||  //Pokud jsme se začali točit, tak se točíme tak dlouho, až jdeme rovnoběžně se zdí.
452                      (sensornLeftFront && (Math.abs(nleftfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/4)) || //Může se stát, že přední paprsek ten roh mine, ale přesto jdeme kolmo na zeď.
453                      (sensornLeft      && (Math.abs(nleft.getHitNormal().angle(actualVelocity)      - Math.PI) < Math.PI/2) && convexTurning) ) ) {
454                     if (SteeringManager.DEBUG) System.out.println("Left convex edge. " + SteeringTools.radiansToDegrees(nleftfront.getHitNormal().angle(actualVelocity)));
455                     if (SteeringManager.DEBUG) {
456                         if (sensornFront) System.out.println("Left convex edge front collision. " + SteeringTools.radiansToDegrees(nfront.getHitNormal().angle(actualVelocity)));
457                         else if (sensornLeftFront) System.out.println("Left convex edge side front. " + SteeringTools.radiansToDegrees(nleftfront.getHitNormal().angle(actualVelocity)));
458                         else System.out.println("Left convex edge side. " + SteeringTools.radiansToDegrees(nleft.getHitNormal().angle(actualVelocity)));
459                     }
460                     //Kdykoliv když naráží přední paprsek - či pokud jsme se začali točit a zeď levého paprsku je na nás buď kolmá, či její normála svírá s naší velocity 135° - 225°.
461                     nextTurning = true;
462                     double k = 1;
463                     if (sensornFront) {
464                         k = nfront.getHitNormal().angle(actualVelocity) / Math.PI; //Jestliže jsou vektory přímo opačné, bude úhel PI a k=1. Čím je úhel menší, tím bude k menší.
465                     }
466                     else if (sensornLeftFront) {
467                         k = 0.6*nleftfront.getHitNormal().angle(actualVelocity) / Math.PI;
468                     }
469                     else {
470                         k = 0.3*nleft.getHitNormal().angle(actualVelocity) / Math.PI;
471                     }
472                     if (SteeringManager.DEBUG) {
473                         System.out.println("Turning vector. " + k*SteeringTools.getTurningVector2(actualVelocity, false).length());
474                     }
475                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, false);
476                     turningVector.scale(k*convexEdgesForceWeight);
477                     //nextVelocity.add(turningVector);
478                     //nextVelocity = turningVector;
479                     counter = DEFAULTCOUNTER;
480                     convexTurning = nextTurning;
481                     return turningVector;
482                     //return nextVelocity;
483                 }
484             } else if (state.equals(State.RIGHT)) {
485                 if ( (sensornFront && (convexTurning || (Math.abs(nfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/4)) ) ||
486                    ( (sensornRightFront && (Math.abs(nrightfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/2) && convexTurning) ||
487                      (sensornRightFront && (Math.abs(nrightfront.getHitNormal().angle(actualVelocity) - Math.PI) < Math.PI/4)) || //Může se stát, že přední paprsek ten roh mine, ale přesto jdeme kolmo na zeď.
488                      (sensornRight      && (Math.abs(nright.getHitNormal().angle(actualVelocity)      - Math.PI) < Math.PI/2) && convexTurning) ) ) {
489                     if (SteeringManager.DEBUG) {
490                         if (sensornFront) System.out.println("Right convex edge front collision. " + SteeringTools.radiansToDegrees(nfront.getHitNormal().angle(actualVelocity)));
491                         else if (sensornRightFront) System.out.println("Right convex edge side front. " + SteeringTools.radiansToDegrees(nrightfront.getHitNormal().angle(actualVelocity)));
492                         else System.out.println("Right convex edge side. " + SteeringTools.radiansToDegrees(nright.getHitNormal().angle(actualVelocity)));
493                     }
494                     nextTurning = true;
495                     double k = 1;
496                     if (sensornFront) {
497                         k = nfront.getHitNormal().angle(actualVelocity) / Math.PI; //Jestliže jsou vektory přímo opačné, bude úhel PI a k=1. Čím je úhel menší, tím bude k menší.
498                     }
499                     else if (sensornRightFront) {
500                         k = 0.6*nrightfront.getHitNormal().angle(actualVelocity) / Math.PI;
501                     }
502                     else {
503                         k = 0.3*nright.getHitNormal().angle(actualVelocity) / Math.PI;
504                     }
505                     if (SteeringManager.DEBUG) {
506                         System.out.println("Turning vector. " + k*SteeringTools.getTurningVector2(actualVelocity, true).length());
507                     }
508                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, true);
509                     turningVector.scale(k*convexEdgesForceWeight);
510                     //nextVelocity.add(turningVector);
511                     //nextVelocity = turningVector;
512                     counter = DEFAULTCOUNTER;
513                     convexTurning = nextTurning;
514                     return turningVector;
515                     //return nextVelocity;
516                 }
517             }
518         } else {    //Basic solution of the convex edges. Convex edge <==> the front and sideFront ray hits.
519                     //If the convex edge is detected, the exactly turning vector is returned.
520             if (state == State.LEFT) {
521                 if (sensornLeftFront && sensornFront) {    //This means the concave edge. We must turn right.
522                     //if (counter > 0) {
523                     //counter--;
524                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, false);
525                     turningVector.scale(convexEdgesForceWeight*0.8);
526                     return turningVector;
527                     /*} else {
528                         state = State.NOTHING;
529                     }*/
530                 }
531             } else if (state == State.RIGHT) {
532                 if (sensornRightFront && sensornFront) {    //This means the concave edge. We must turn left.
533                     //if (counter > 0) {
534                     //counter--;
535                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, true);
536                     turningVector.scale(convexEdgesForceWeight*0.8);
537                     return turningVector;
538                     /*} else {
539                         state = State.NOTHING;
540                     }*/
541                 } 
542             }
543         }
544        
545 
546         //</editor-fold>
547 
548         //<editor-fold defaultstate="collapsed" desc="Concave edges">
549 
550         //If the concave edge is detected, the exactly turning vector is returned.
551 
552         if (state == State.LEFT) {
553             if (!sensornLeft && !sensornLeftFront) {//He left the wall completely, no sensor on the left side works. That is for turning around >=90°(& <=270° :-)) angles when he needs to "remember" that he should turn.
554                 if (SteeringManager.DEBUG) System.out.println("counter "+counter);
555                 if (counter > 0) {
556                     counter--;
557                     if (SteeringManager.DEBUG) System.out.println("Left concave edge. ");
558                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, true);
559                     if (convexTurning) turningVector.scale(0.5);
560                     turningVector.scale(concaveEdgesForceWeight*0.8);
561                     return turningVector;
562                 } else {
563                     state = State.NOTHING;
564                 }
565             }
566         } else if (state == State.RIGHT) {
567             if (!sensornRight && !sensornRightFront) {//He left the wall completely, no sensor on the left side works. That is for turning around >=90°(& <=270° :-)) angles when he needs to "remember" that he should turn.            {
568                 if (SteeringManager.DEBUG) System.out.println("counter "+counter);
569                 if (counter > 0) {
570                     counter--;
571                     if (SteeringManager.DEBUG) System.out.println("Right concave edge. ");
572                     Vector3d turningVector = SteeringTools.getTurningVector2(actualVelocity, false);
573                     if (convexTurning) turningVector.scale(0.5);
574                     turningVector.scale(concaveEdgesForceWeight*0.8);
575                     return turningVector;
576                     //nextVelocity.add(SteeringTools.getTurningVector(actualVelocity, false));
577                     //nextVelocity = SteeringTools.getTurningVector(actualVelocity, false);
578                 } else {
579                     state = State.NOTHING;
580                 }
581             }
582         }
583         //</editor-fold>
584 
585         convexTurning = nextTurning;
586         return nextVelocity;
587     }
588 
589     @Override
590     public void setProperties(SteeringProperties newProperties) {
591         this.wallForce = ((WallFollowingProperties)newProperties).getWallForce();
592         this.orderOfTheForce = ((WallFollowingProperties)newProperties).getOrderOfTheForce();
593         this.attractiveForceWeight = ((WallFollowingProperties)newProperties).getAttractiveForceWeight();
594         this.repulsiveForceWeight = ((WallFollowingProperties)newProperties).getRepulsiveForceWeight();
595         this.concaveEdgesForceWeight = ((WallFollowingProperties)newProperties).getConcaveEdgesForceWeight();
596         this.convexEdgesForceWeight = ((WallFollowingProperties)newProperties).getConvexEdgesForceWeight();
597         this.justMySide = ((WallFollowingProperties)newProperties).isJustMySide();
598         this.specialDetection = ((WallFollowingProperties)newProperties).isSpecialDetection();
599         this.frontCollisions = ((WallFollowingProperties)newProperties).isFrontCollisions();
600     }
601 
602     public WallFollowingProperties getProperties() {
603         WallFollowingProperties properties = new WallFollowingProperties();
604         properties.setWallForce(wallForce);
605         properties.setOrderOfTheForce(orderOfTheForce);
606         properties.setAttractiveForceWeight(attractiveForceWeight);
607         properties.setRepulsiveForceWeight(repulsiveForceWeight);
608         properties.setConcaveEdgesForceWeight(concaveEdgesForceWeight);
609         properties.setConvexEdgesForceWeight(convexEdgesForceWeight);
610         properties.setJustMySide(justMySide);
611         properties.setSpecialDetection(specialDetection);
612         properties.setFrontCollisions(frontCollisions);
613         return properties;
614     }
615 }