1   package simworldobjects;
2   
3   import interfaces.*;
4   
5   import java.util.*;
6   import java.awt.*;
7   import java.awt.geom.*;
8   
9   /**
10  * Abstract class for SimWorlds, provides a base implementation of 
11  * the SimWorld interface which more complex worlds can extend. (e.g
12  * by adding lights, walls etc.)
13  *
14  * @see interfaces.SimWorld
15  *
16  * @author Graham Ritchie modified by: Simon Zienkiewicz
17  */
18  public abstract class BasicSimWorld implements SimWorld
19  {
20      private int ticks;
21      private int ambientLight;
22      private LinkedList objectList;
23      private long width, length, height;
24          private Color worldColor=Color.white;
25      
26      /**
27      * Sets up the basic sim world
28      *
29      * @param x the largest x cooridnate in this world 
30      * @param y the largest y cooridnate in this world 
31      * @param z the largest z cooridnate in this world 
32      */
33      public BasicSimWorld(long x, long y, long z)
34      {   
35          // initialise world size 
36          width=x;
37          height=y;
38          length=z;
39          
40          // initialise tick counter
41          ticks=0;
42          
43          // initialise object list
44          objectList=new LinkedList();
45          
46          // set ambient light
47          ambientLight=20;
48                  
49                  //setup walls around the world
50                  buildWallBoundaries();
51      }
52          /**
53      * Sets up the basic sim world
54          * Created by: Simon Zienkiewicz
55      *
56      * @param x the largest x cooridnate in this world 
57      * @param y the largest y cooridnate in this world 
58      * @param z the largest z cooridnate in this world 
59          * @param c the Color of the ground of the world
60      */
61      public BasicSimWorld(long x, long y, long z, Color c)
62      {   
63          // initialise world size 
64          width=x;
65          height=y;
66          length=z;
67                  
68                  //initialise world color
69                  worldColor=c;
70          
71          // initialise tick counter
72          ticks=0;
73          
74          // initialise object list
75          objectList=new LinkedList();
76          
77          // set ambient light
78          ambientLight=20;
79                  
80      }
81              
82          /**
83      * Builds walls around the desired world
84      */
85          private void buildWallBoundaries(){
86              // add a few containing walls
87              //top boundary
88              SimWall wall1=new SimWall(this.length/2,0.0,6.0,0.0,this.length-2,10.0);
89              addObject(wall1);
90  
91              //left
92              SimWall wall2=new SimWall(6.0,0.0,this.width/2,0.0,10.0,this.length-2);
93              //SimWall wall2=new SimWall(6.0,0.0,this.width/2,90.0,this.length-2,10.0);
94              addObject(wall2);
95              
96              
97              //left
98              SimWall wall3=new SimWall(this.length-6.0,0.0,this.width/2,0.0,10.0,this.length-2);
99              //SimWall wall3=new SimWall(this.length-6.0,0.0,this.width/2,90.0,this.length-2,10.0);
100             addObject(wall3);
101             
102             //left
103             SimWall wall4=new SimWall(this.length/2,0.0,this.width-6.0,0.0,this.length-2,10.0);
104             addObject(wall4);
105         
106         }
107         
108     /**
109     * Performs one update loop
110     */
111     public void tick()
112     {
113         updateObjects();
114         ticks++;
115     }
116     
117     /**
118     * Returns the number of 'ticks' since this world was started
119     *
120     * @return the number of ticks as a long
121     */
122     public long getTime()
123     {
124         return ticks;
125     }
126     
127     /**
128     * Returns the light level at the specified co-ordinate. 
129     *
130     * @return the brightness, this will always be an int between 0 and 100
131     */
132     public int getBrightness(double x, double y, double z)
133     {
134         double totalBrightness=0;
135         
136         // check through the object list for SimLights
137         for(int i=0;i<objectList.size();i++)
138         {
139             SimObject o=(SimObject)objectList.get(i);
140             
141             if(o instanceof SimLight)
142             {
143                 SimLight light=(SimLight)o;
144                 
145                 int X=Math.abs((int)(light.getXCoord()-x));
146                 int Z=Math.abs((int)(light.getZCoord()-z));
147                 
148                 // work out distance from point to light with pythagoras
149                 double distance=Math.sqrt((X*X)+(Z*Z));
150                 
151                 // work out the reduction in light coefficient
152                 double coeff=(((double)light.getBrightness()-(distance/8))/(double)light.getBrightness());
153                 
154                 // establish the brightness of this light at the point
155                 double brightness=light.getBrightness()*coeff;
156                 
157                 // add this light's brightness to the total brightness level
158                 totalBrightness+=brightness;
159             }
160         }
161         
162         // make sure light never falls below ambient level
163         if(totalBrightness < ambientLight)
164         {
165             totalBrightness=ambientLight;
166         }
167         
168         // make sure light never goes above 100
169         if(totalBrightness > 100)
170         {
171             totalBrightness=100;
172         }
173         
174         // add some  random 'noise' to the light
175         Random r=new Random(System.currentTimeMillis());
176         totalBrightness=totalBrightness+(2*r.nextFloat());
177                 
178         // return total brightness at this point as an int
179         return (int) totalBrightness;
180     }
181     
182     /**
183     * Checks whether there is an obstacle in the specified co-ordinate
184     *
185     * @param x the x coordinate
186     * @param y the y coordinate
187     * @param z the z coordinate
188     *
189     * @return true or false accordingly
190     */
191     public boolean hasObstacle(double x, double y, double z)
192     {
193         // examine each SimObject in turn
194         for (int i=0;i<objectList.size();i++)
195         {
196             SimObject o=(SimObject)objectList.get(i);
197             
198             // create a shape the same size and bearing as o
199             Shape s=createShape(o.getXCoord(),o.getZCoord(),o.getWidth(),o.getLength(),o.getActualBearingXZ());
200             
201             // if o's shape contains the coords return true (ignoring sensors)
202             if(s.contains(x,z) && !(o instanceof SimSensor) && !(o instanceof SimGround) && !(o instanceof SimRoad))
203             {
204                 // has obstacle
205                 return true;
206             }
207         }
208         // if we've got here then there is no obstacle, so return false
209         return false;
210     }
211     
212     /**
213     * Rotates a Shape around a certain point a certain angle 
214     *
215     * @param shape the shape to be rotated
216     * @param angle the angle by which the shape is to be rotated
217     * @param X the x co-ordinate about which to rotate
218     * @param Z the z co-ordinate about which to rotate
219     *
220     * @return the rotated shape, as a new Shape
221     */
222     private Shape rotateShape(Shape shape, double angle, double X, double Z)
223     {
224         // convert the angle to radians
225         double theta=Math.toRadians(angle);
226         
227         // create a new affine transform rotator
228         AffineTransform  atx = AffineTransform.getRotateInstance(theta,X,Z); 
229         
230         // create a rotated version of the shape
231         shape = atx.createTransformedShape(shape);
232         
233         // return the shape
234         return shape;
235     }
236     
237     /**
238     * Creates a rectangle with the given properties
239     *
240     * @param x the x coordinate of the rectangle 
241     * @param z the z coordinate of the rectangle 
242     * @param width the width of the rectangle 
243     * @param width the length of the rectangle 
244     * @param angle the bearing of the rectangle 
245     *
246     * @return the rectangle
247     */
248     private Shape createShape(double x, double z, double width, double length, double angle)
249     {
250         // establish top left corner of object
251         double X=(x-(width/2));
252         double Z=(z-(length/2));
253         
254         // create shape
255         Shape s=new Rectangle2D.Double(X,Z,width,length);
256         
257         // rotate it to the correct bearing
258         s=rotateShape(s,angle,x,z);
259         
260         return s;
261     }
262     
263     /**
264     * Checks if SimObject o is colliding with SimObject p.
265     *
266     * @param o the first SimObject
267     * @param p the second SimObject
268     *
269     * @return true or false accordingly
270     */
271     public boolean colliding(SimObject o, SimObject p)
272     {
273         // create a rectangle the same size and bearing as o
274         Shape shapeO=createShape(o.getXCoord(),o.getZCoord(),o.getWidth(),o.getLength(),o.getActualBearingXZ());
275         
276         // create a rectangle the same size and bearing as p
277         Shape shapeP=createShape(p.getXCoord(),p.getZCoord(),p.getWidth(),p.getLength(),p.getActualBearingXZ());
278         
279         // 'cast' shapeP to a Rectangle2D so intersects() will work
280         Rectangle2D rectP=shapeP.getBounds2D();
281         
282         // check if o's shape intersects with p's shape
283         if (shapeO.intersects(rectP))
284         {
285             // objects are colliding
286             return true;
287         }
288         else
289         {
290             // objects aren't colliding
291             return false;
292         }
293     }
294     
295     /**
296     * Adds an object to the back of this SimWorld
297     *
298     * @param o the SimObject to be added
299     */
300     public void addObject(SimObject s)
301     {
302         // add this object to the back of the list list
303         objectList.add(s);
304     }
305         
306         /**
307     * Adds an object to the front of any Robot object/part in the list
308         * Added by: Simon Zienkiewicz
309     *
310     * @param o the SimObject to be added
311     */
312     public void addObjecttoFront(SimObject s)
313     {
314         int b=this.objectList.size();
315             for(int i=0;i<this.objectList.size();i++){
316                 SimObject sim = (SimObject)objectList.get(i);
317                 if(sim instanceof SimSensor || sim instanceof SimRCX){
318                     b=i;
319                     break;
320                 }
321             }
322             this.objectList.add(b,s);
323         }
324         
325     /**
326     * Returns this SimWorld's object list
327     *
328     * @return the object list as a LinkedList
329     */
330     public LinkedList getObjectList()
331     {
332         return objectList;
333     }
334     
335     /**
336     * Updates all the SimObjects in this world by one step
337     */
338     public void updateObjects()
339     {
340             // flag to determine whether any object has collided with any other
341             boolean collided;
342 
343             for (int i=0;i<objectList.size();i++)
344             {
345                 // examine each object in turn
346                 SimObject o=(SimObject)objectList.get(i);
347 
348                 // reset collided flag
349                 collided=false;
350 
351                 // check for collisions:
352 
353                 // ignore sensors and ground/paths
354                 if(o instanceof SimRCX || o instanceof SimLightSensor)
355                 {
356                     // see if it is colliding with any other object in the world
357                     for (int j=0;j<objectList.size();j++)
358                     {
359                         // examine each object in turn
360                         SimObject p=(SimObject)objectList.get(j);
361 
362                         // ignore sensors and ground/paths
363                         if(p instanceof SimWall)
364                         {
365                             // ignore comparisons with self (no other object should ever have the same
366                             // x and z coords (if this works!)
367                             if(!(o.getXCoord()==p.getXCoord() && o.getZCoord()==p.getZCoord()))
368                             {
369                                 if(colliding(o,p))
370                                 {   
371                                         //System.out.println("collision: "+p.getType()+" with "+o.getType());
372                                         collided=true;
373                                 }
374                             }
375                         }
376                     }
377                 }
378 
379                 if(!collided)
380                 {
381                     // determine what the object wants to do, and do it
382 
383                     if(o.getDesiredVelocity()>0)
384                     {
385                         moveForward(o);
386                     }
387                     else if(o.getDesiredVelocity()<0)
388                     {
389                         moveBackward(o);
390                     }
391 
392                     if(o.getDesiredBearingVelocityXZ()>0)
393                     {
394                         moveRight(o);
395                     }
396                     else if(o.getDesiredBearingVelocityXZ()<0)
397                     {
398                         moveLeft(o);
399                     }
400                 }
401                 else // objects are currently collided so ...
402                 {
403                     if(o instanceof SimRCX){
404                         SimRCX robot = (SimRCX)o;
405                         robot.stopMoving();
406                     
407                     }
408                     else if(o instanceof SimLightSensor){
409                         SimRCX robot = (SimRCX)((SimSensor)o).getOwner();
410                         robot.stopMoving();
411                     }
412                 }
413                 //check for light or touch events:
414                 if(o instanceof SimSensor){
415                     SimSensor sensor = (SimSensor)o;
416                     int index=0;
417                     
418                     switch(sensor.getPosition()){
419                         case 'L':   index=1; break;
420                         case 'F':   index=2; break;
421                         case 'R':   index=3; break;
422                     }
423                     
424                     //check for touch events
425                     if(o instanceof SimTouchSensor){
426                         SimTouchSensor touch = (SimTouchSensor)o;
427                         if(touch.getCurrentValue() != touch.getPreviousValue()){
428                             ((SimRCX)touch.getOwner()).touchEvent(index);
429                         }
430                     }
431 
432                     //check for light events
433                     else if(o instanceof SimLightSensor){
434                         
435                         SimLightSensor light = (SimLightSensor)o;
436                         
437                         if(light.getCurrentValue() != light.getPreviousValue()){
438                             ((SimRCX)light.getOwner()).lightEvent(index);
439                         }
440                     }
441                 }
442                 
443             }
444                
445         }   
446     
447     /**
448     * Moves the object forward by the desired velocity according to its current bearing
449     * Modified by: Simon Zienkiewicz
450         *
451     * @param o the SimObject to be moved
452     */
453     private void moveForward(SimObject o)
454     {  
455             if(o.getXCoord() >= 0 && o.getZCoord() >=0 && o.getXCoord() <= this.getWorldDimensions()[0] && o.getZCoord() <= this.getWorldDimensions()[2]){ 
456                 o.setActualVelocity(o.getDesiredVelocity());
457                 o.setXCoord(o.getXCoord()+ o.getDesiredVelocity()*(Math.sin(Math.toRadians(o.getActualBearingXZ()))));
458                 o.setZCoord(o.getZCoord() - o.getDesiredVelocity()*(Math.cos(Math.toRadians(o.getActualBearingXZ()))));
459             }
460     }
461     
462     /**
463     * Moves the object backward one step according to its current bearing
464     *
465     * @param o the SimObject to be moved
466     */
467     private void moveBackward(SimObject o)
468     {   
469             if(o.getXCoord() >= 0 && o.getZCoord() >=0 && o.getXCoord() <= this.getWorldDimensions()[0] && o.getZCoord() <= this.getWorldDimensions()[2]){ 
470                 o.setActualVelocity(o.getDesiredVelocity());
471                 o.setXCoord(o.getXCoord() + o.getDesiredVelocity()*(Math.sin(Math.toRadians(o.getActualBearingXZ()))));
472                 o.setZCoord(o.getZCoord() - o.getDesiredVelocity()*(Math.cos(Math.toRadians(o.getActualBearingXZ()))));
473             }
474     }
475     
476     /**
477     * Turns the object one step right (clockwise)
478     *
479     * @param o the SimObject to be moved
480     */
481     private void moveRight(SimObject o)
482     {
483             o.setActualBearingVelocityXZ(o.getDesiredBearingVelocityXY());
484             o.setActualBearingXZ((o.getActualBearingXZ()+o.getDesiredBearingVelocityXZ()));
485             
486             if(o instanceof SimRCX){
487                 //angle is updated
488                 ((SimRCX)o).pivotRotation();
489             }
490     }
491     
492     /**
493     * Turns the object one step left (anticlockwise)
494     *
495     * @param o the SimObject to be moved
496     */
497     private void moveLeft(SimObject o)
498     {
499             if(o instanceof SimWall)
500             o.setActualBearingVelocityXZ(o.getDesiredBearingVelocityXY());
501             o.setActualBearingXZ((o.getActualBearingXZ()+o.getDesiredBearingVelocityXZ()));
502             
503             if(o instanceof SimRCX){
504                 //angle is updated
505                 ((SimRCX)o).pivotRotation();
506             }
507     }
508         
509         /**
510     * Gets the color under the position of the light sensor
511     *
512     * @param lightSensor the light sensor object
513     */
514         public Color getColorUnderLightSensor(SimLightSensor lightSensor){
515            // check through the object list for SimLights
516             
517             int b=9999;
518             for(int i=0;i<objectList.size();i++)
519             {
520                 SimObject o=(SimObject)objectList.get(i);
521 
522                 if(o instanceof SimGround)
523                 {
524                     SimGround ground=(SimGround)o;
525                     //check if the light sensor and the ground are colliding
526                     if(this.colliding(lightSensor, ground))b=i;
527                 }
528                 if(o instanceof SimRoad)
529                 {
530                     SimRoad road=(SimRoad)o;
531                     //check if the light sensor and the ground are colliding
532                     if(this.colliding(lightSensor, road))b=i;
533                 }
534             }
535             //find the most upper ground object
536             if(b!=9999) {
537                 SimObject o=(SimObject)objectList.get(b);
538                 if(o instanceof SimGround) return ((SimGround)(objectList.get(b))).getColor();
539                 else return ((SimRoad)(objectList.get(b))).getColor();              
540             }
541             else return worldColor;
542         }
543         
544         /**
545     * Gets the ground color of the world
546     * Created by: Simon Zienkiewicz
547         *
548     * @param lightSensor the light sensor object
549     */
550         public Color getWorldColor(){
551             return worldColor;
552         }
553          
554         /**
555     * Sets the ground color of the world
556     * Created by: Simon Zienkiewicz
557         *
558     * @param lightSensor the light sensor object
559     */
560         public void setWorldColor(Color color){
561             worldColor = color;
562         }
563         
564         /**
565     * Gets the dimensions of the world
566     * Created by: Simon Zienkiewicz
567         *
568     * @return the dimensions of the world
569     */
570         public int[] getWorldDimensions(){
571             int[] dimensions = {(int)this.width,(int)this.height,(int)this.length};
572             return dimensions;
573         }
574         
575         public void updateObjectList(LinkedList newList){
576             objectList=newList;
577         }
578 }