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
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      
25      /**
26      * Sets up the basic sim world
27      *
28      * @param x the largest x cooridnate in this world 
29      * @param y the largest y cooridnate in this world 
30      * @param z the largest z cooridnate in this world 
31      */
32      public BasicSimWorld(long x, long y, long z)
33      {   
34          // initialise world size 
35          width=x;
36          height=y;
37          length=z;
38          
39          // initialise tick counter
40          ticks=0;
41          
42          // initialise object list
43          objectList=new LinkedList();
44          
45          // set ambient light
46          ambientLight=20;
47      }
48      
49      /**
50      * Performs one update loop
51      */
52      public void tick()
53      {
54          updateObjects();
55          ticks++;
56      }
57      
58      /**
59      * Returns the number of 'ticks' since this world was started
60      *
61      * @return the number of ticks as a long
62      */
63      public long getTime()
64      {
65          return ticks;
66      }
67      
68      /**
69      * Returns the light level at the specified co-ordinate. 
70      *
71      * @return the brightness, this will always be an int between 0 and 100
72      */
73      public int getBrightness(double x, double y, double z)
74      {
75          double totalBrightness=0;
76          
77          // check through the object list for SimLights
78          for(int i=0;i<objectList.size();i++)
79          {
80              SimObject o=(SimObject)objectList.get(i);
81              
82              if(o instanceof SimLight)
83              {
84                  SimLight light=(SimLight)o;
85                  
86                  int X=Math.abs((int)(light.getXCoord()-x));
87                  int Z=Math.abs((int)(light.getZCoord()-z));
88                  
89                  // work out distance from point to light with pythagoras
90                  double distance=Math.sqrt((X*X)+(Z*Z));
91                  
92                  // work out the reduction in light coefficient
93                  double coeff=(((double)light.getBrightness()-(distance/8))/(double)light.getBrightness());
94                  
95                  // establish the brightness of this light at the point
96                  double brightness=light.getBrightness()*coeff;
97                  
98                  // add this light's brightness to the total brightness level
99                  totalBrightness+=brightness;
100             }
101         }
102         
103         // make sure light never falls below ambient level
104         if(totalBrightness < ambientLight)
105         {
106             totalBrightness=ambientLight;
107         }
108         
109         // make sure light never goes above 100
110         if(totalBrightness > 100)
111         {
112             totalBrightness=100;
113         }
114         
115         // add some  random 'noise' to the light
116         Random r=new Random(System.currentTimeMillis());
117         totalBrightness=totalBrightness+(2*r.nextFloat());
118                 
119         // return total brightness at this point as an int
120         return (int) totalBrightness;
121     }
122     
123     /**
124     * Checks whether there is an obstacle in the specified co-ordinate
125     *
126     * @param x the x coordinate
127     * @param y the y coordinate
128     * @param z the z coordinate
129     *
130     * @return true or false accordingly
131     */
132     public boolean hasObstacle(double x, double y, double z)
133     {
134         // examine each SimObject in turn
135         for (int i=0;i<objectList.size();i++)
136         {
137             SimObject o=(SimObject)objectList.get(i);
138             
139             // create a shape the same size and bearing as o
140             Shape s=createShape(o.getXCoord(),o.getZCoord(),o.getWidth(),o.getLength(),o.getActualBearingXZ());
141             
142             // if o's shape contains the coords return true (ignoring sensors)
143             if(s.contains(x,z) && !(o instanceof SimSensor))
144             {
145                 // has obstacle
146                 return true;
147             }
148         }
149         // if we've got here then there is no obstacle, so return false
150         return false;
151     }
152     
153     /**
154     * Rotates a Shape around a certain point a certain angle 
155     *
156     * @param shape the shape to be rotated
157     * @param angle the angle by which the shape is to be rotated
158     * @param X the x co-ordinate about which to rotate
159     * @param Z the z co-ordinate about which to rotate
160     *
161     * @return the rotated shape, as a new Shape
162     */
163     private Shape rotateShape(Shape shape, double angle, double X, double Z)
164     {
165         // convert the angle to radians
166         double theta=Math.toRadians(angle);
167         
168         // create a new affine transform rotator
169         AffineTransform  atx = AffineTransform.getRotateInstance(theta,X,Z); 
170         
171         // create a rotated version of the shape
172         shape = atx.createTransformedShape(shape);
173         
174         // return the shape
175         return shape;
176     }
177     
178     /**
179     * Creates a rectangle with the given properties
180     *
181     * @param x the x coordinate of the rectangle 
182     * @param z the z coordinate of the rectangle 
183     * @param width the width of the rectangle 
184     * @param width the length of the rectangle 
185     * @param angle the bearing of the rectangle 
186     *
187     * @return the rectangle
188     */
189     private Shape createShape(double x, double z, double width, double length, double angle)
190     {
191         // establish top left corner of object
192         double X=(x-(width/2));
193         double Z=(z-(length/2));
194         
195         // create shape
196         Shape s=new Rectangle2D.Double(X,Z,width,length);
197         
198         // rotate it to the correct bearing
199         s=rotateShape(s,angle,x,z);
200         
201         return s;
202     }
203     
204     /**
205     * Checks if SimObject o is colliding with SimObject p.
206     *
207     * @param o the first SimObject
208     * @param p the second SimObject
209     *
210     * @return true or false accordingly
211     */
212     public boolean colliding(SimObject o, SimObject p)
213     {
214         // create a rectangle the same size and bearing as o
215         Shape shapeO=createShape(o.getXCoord(),o.getZCoord(),o.getWidth(),o.getLength(),o.getActualBearingXZ());
216         
217         // create a rectangle the same size and bearing as p
218         Shape shapeP=createShape(p.getXCoord(),p.getZCoord(),p.getWidth(),p.getLength(),p.getActualBearingXZ());
219         
220         // 'cast' shapeP to a Rectangle2D so intersects() will work
221         Rectangle2D rectP=shapeP.getBounds2D();
222         
223         // check if o's shape intersects with p's shape
224         if (shapeO.intersects(rectP))
225         {
226             // objects are colliding
227             return true;
228         }
229         else
230         {
231             // objects aren't colliding
232             return false;
233         }
234     }
235     
236     /**
237     * Adds an object to this SimWorld
238     *
239     * @param o the SimObject to be added
240     */
241     public void addObject(SimObject s)
242     {
243         // add this object to the list
244         objectList.add(s);
245     }
246     
247     /**
248     * Returns this SimWorld's object list
249     *
250     * @return the object list as a LinkedList
251     */
252     public LinkedList getObjectList()
253     {
254         return objectList;
255     }
256     
257     /**
258     * Updates all the SimObjects in this world by one step
259     */
260     public void updateObjects()
261     {
262         // flag to determine whether any object has collided with any other
263         boolean collided;
264         
265         for (int i=0;i<objectList.size();i++)
266         {
267             // examine each object in turn
268             SimObject o=(SimObject)objectList.get(i);
269             
270             // reset collided flag
271             collided=false;
272             
273             // check for collisions:
274             
275             // ignore sensors
276             if(!(o instanceof SimSensor))
277             {
278                 // see if it is colliding with any other object in the world
279                 for (int j=0;j<objectList.size();j++)
280                 {
281                     // examine each object in turn
282                     SimObject p=(SimObject)objectList.get(j);
283                     
284                     // ignore sensors
285                     if(!(p instanceof SimSensor))
286                     {
287                         // ignore comparisons with self (no other object should ever have the same
288                         // x and z coords (if this works!)
289                         if(!(o.getXCoord()==p.getXCoord() && o.getZCoord()==p.getZCoord()))
290                         {
291                             if(colliding(o,p))
292                             {   
293                                 //System.out.println("collision: "+p.getType()+" with "+o.getType());
294                                 collided=true;
295                             }
296                         }
297                     }
298                 }
299             }
300             
301             if(!collided)
302             {
303                 // determine what the object wants to do, and do it
304                 
305                 if(o.getDesiredVelocity()>0)
306                 {
307                     moveForward(o);
308                 }
309                 else if(o.getDesiredVelocity()<0)
310                 {
311                     moveBackward(o);
312                 }
313                 
314                 if(o.getDesiredBearingVelocityXZ()>0)
315                 {
316                     moveRight(o);
317                 }
318                 else if(o.getDesiredBearingVelocityXZ()<0)
319                 {
320                     moveLeft(o);
321                 }
322             }
323             else // objects are currently collided so ...
324             {
325                 // do the opposite to what it wants to do
326                 
327                 if(o.getActualVelocity()>0 && o.getActualVelocity()!=o.getDesiredVelocity())
328                 {
329                     moveBackward(o);
330                 }
331                 else if(o.getActualVelocity()<0 && o.getActualVelocity()!=o.getDesiredVelocity())
332                 {
333                     moveForward(o);
334                 }
335                 
336                 if(o.getActualBearingVelocityXZ()>0 && o.getActualBearingVelocityXZ()!=o.getDesiredBearingVelocityXZ())
337                 {
338                     moveLeft(o);
339                 }
340                 else if(o.getActualBearingVelocityXZ()<0 && o.getActualBearingVelocityXZ()!=o.getDesiredBearingVelocityXZ())
341                 {
342                     moveRight(o);
343                 }
344             }
345         }
346     }   
347     
348     /**
349     * Moves the object forward one step according to its current bearing
350     *
351     * @param o the SimObject to be moved
352     */
353     private void moveForward(SimObject o)
354     {
355         o.setActualVelocity(1);
356         o.setXCoord(o.getXCoord() + Math.sin(Math.toRadians(o.getActualBearingXZ())));
357         o.setZCoord(o.getZCoord() - Math.cos(Math.toRadians(o.getActualBearingXZ())));
358     }
359     
360     /**
361     * Moves the object backward one step according to its current bearing
362     *
363     * @param o the SimObject to be moved
364     */
365     private void moveBackward(SimObject o)
366     {
367         o.setActualVelocity(-1);
368         o.setXCoord(o.getXCoord() - Math.sin( Math.toRadians(o.getActualBearingXZ())));
369         o.setZCoord(o.getZCoord() + Math.cos( Math.toRadians(o.getActualBearingXZ())));
370     }
371     
372     /**
373     * Turns the object one step right (clockwise)
374     *
375     * @param o the SimObject to be moved
376     */
377     private void moveRight(SimObject o)
378     {
379         o.setActualBearingVelocityXZ(1);
380         o.setActualBearingXZ((o.getActualBearingXZ()+1));
381     }
382     
383     /**
384     * Turns the object one step left (anticlockwise)
385     *
386     * @param o the SimObject to be moved
387     */
388     private void moveLeft(SimObject o)
389     {
390         o.setActualBearingVelocityXZ(-1);
391         o.setActualBearingXZ((o.getActualBearingXZ()-1));
392     }
393 }
394