/* * Bee foraging simulation. Copyright by Joerg Hoehne. * For suggestions or questions email me at hoehne@thinktel.de */ package foragingBee; import java.awt.Color; import java.util.Random; import javax.vecmath.Point3d; import javax.vecmath.Tuple3d; import javax.vecmath.Vector3d; import masonGlue.ForagingHoneyBeeSimulation; import sim.engine.Steppable; import sim.portrayal.SimplePortrayal2D; import utils.Filter; import utils.Geometric; /** * An abstract class for a moving object. Used by several subclasses that are * locating and moving objects in space. Every agent has an orientation (given * in degrees) and a velocity (given as a vector but heading in the same * direction as the orientation). * <p> * Copyright 2009 Joerg Hoehne * * @author hoehne (<a href="mailto:hoehne@thinktel.de">Jörg Höhne</a>) * */ public abstract class AbstractMovingAgent extends SimplePortrayal2D implements Steppable, IMovingAgent { /** * The random number generator for all agents. */ static public Random r = new Random(); /** * The simulation the agent is dealing with. */ private ForagingHoneyBeeSimulation simulation; /** * The current location of the agent. */ private Point3d location = new Point3d(); /** * The current speed of the agent. */ private Vector3d velocity = new Vector3d(); /** * The orientation of the agent. */ private double orientation = 0; /** * The color of the moving object. */ private Color color; /** * The size of the agent. */ private double size; /** * The radius of the object. */ private double radius; /** * The constructor of the agent. * * @param simulation * The simulation the agent resides in. * @param location * The current location of the agent. * @param velocity * The current velocity of the agent. * @param size * The size (diameter) of the agent. * @param color * The color of the agent. */ public AbstractMovingAgent(ForagingHoneyBeeSimulation simulation, Point3d location, Vector3d velocity, double size, Color color) { this.simulation = simulation; setLocation(location); setVelocityVector(velocity); setColor(color); setSize(size); } /** * Get the simulation this agent resides in. * * @return The reference to the current simulation. */ public final ForagingHoneyBeeSimulation getSimulation() { return simulation; } /** * Return the sphere radius the agent fills in. * * @return The radius of the sphere. */ public double getSphereRadius() { return radius; } /** * Return the size of the agent. * * @return The size (diameter) of the agent. */ public double getSize() { return this.size; } /** * Set the size of the agent. * * @param size * The size of the agent. */ public void setSize(double size) { this.size = size; this.radius = size / 2; } /** * Return the agents color. * * @return The color of the agent. */ public Color getColor() { return color; } /** * Set the color of the agent. * * @param theColor * The color of the agent. */ public void setColor(Color theColor) { color = theColor; } /** * Return the current orientation in degrees. * * @return The orientation in degrees. */ public double getOrientation() { return orientation; } /** * Set the orientation in degrees. * * @param orientation * The orientation in degrees. */ public void setOrientation(double orientation) { turnTo(orientation); } /** * Return the reference to the agent's location. * * @return The agent's location reference. */ public Point3d getLocation() { return location; } /** * Set the location of this agent. Setting the location will cause a visual * update by calling {@link #updateLocation()}. * * @param x * The x value on the x-axis. * @param y * The y value on the y-axis. * @param z * The z value on the z-axis. * */ public void setLocation(double x, double y, double z) { this.location.set(x, y, z); updateLocation(); } /** * Set the location of this agent. Setting the location will cause a visual * update by calling {@link #updateLocation()}. * * @param location * The location of this agent. */ public void setLocation(Tuple3d location) { this.location.set(location); updateLocation(); } /** * Returns the vector with the velocity. * * @return The reference to the agent's current velocity vector. */ public final Vector3d getVelocityVector() { return velocity; } /** * Set the velocity vector to a speed meaning setting the vector to a given * length. * * @param speed */ public final void setVelocity(double speed) { this.velocity.normalize(); this.velocity.scale(speed); } /** * Set the velocity vector. Setting the vector will also cause a change in * the current orientation according to the velocity vector settings. * * @param x * The velocity value for the x-axis. * @param y * The velocity value for the y-axis. * @param z * The velocity value for the z-axis. */ public final void setVelocityVector(double x, double y, double z) { velocity.set(x, y, z); orientation = Math.toDegrees(Geometric.angle(velocity)); } /** * Copies the field of the input vector in to current velocity vector. * * @param v * The new velocity vector. */ public final void setVelocityVector(Vector3d v) { setVelocityVector(v.x, v.y, v.z); } /** * Compute the orientation from agent1 to agent2 in radians. * * @param agent1 * The first agent the orientation is computed from. * @param agent2 * The second agent the orientation is computed to. * @return The orientation from agent1 to agent2 in radians. */ static final double orientation(IMovingAgent agent1, IMovingAgent agent2) { return Geometric.angle(agent1.getLocation(), agent2.getLocation()); } /** * Return the distance to an other agent. * * @param agent * The other agent * @return The distance. */ public final double distance(IMovingAgent agent) { return location.distance(agent.getLocation()); } /** * Return the squared distance to an other agent. This method saves some * execution time by not applying the square root so a squared distance is * returned. * * @param agent * @return The squared distance. */ public final double distanceSquared(IMovingAgent agent) { return location.distanceSquared(agent.getLocation()); } /** * Move the agent in the direction given by the provided vector. * * @param direction * The direction and speed as a vector. */ public final void forward(Vector3d direction) { location.add(direction); updateLocation(); } /** * Move the agent in the current direction given by {@link #velocity}. */ public final void forward() { forward(velocity); } /** * Return the objects within the distance of this object. Due to the * dimension of an object the sphere of both objects might be included in * the computation. * * @param radius * The distance we are looking for. * @param useMySphere * Use this agents sphere for reducing the distance between this * agent and others * @param useTheirSpheres * Use the others agents spheres to reduce the distance * @param includeMySelf * Shall the agent itself be included in the results? * @return The objects meeting the criteria. */ public IMovingAgent[] getObjectsWithinMyDistance(double radius, boolean useMySphere, boolean useTheirSpheres, boolean includeMySelf) { return getObjectsWithinMyDistance(radius, useMySphere, useTheirSpheres, getSimulation().getMaxSphereRadius(), includeMySelf, null); } /** * This method returns all objects that are within a radius of the this * object. The computation is done in several stages and configurations. * First the distance between objects (agents) is measured from center to * center. The center is a point so it has no dimension.<br> * The parameter radius controls the radius from the center of the current * object (agent) where the center of the other objects (agents) have to lie * within to be returned.<br> * Every agent in the simulation has a sphere given by a radius. This radius * can be used to compute if a center of an other objects lies inside the * sphere. This behaviour is configured by the parameter useMySphere. The * usage of the sphere of the other object (agent) instead of its center is * controlled by useTheirSpheres.<br> * The parameter maxSphere defines the maximum radius of all other agents * used for computing the distance in which one object (agent) have to be in * maximum to be returned.<br> * * @param radius * The radius where to look for other objects (agents). * @param useMySphere * Is the center or sphere of an other object inside by sphere. * @param useTheirSpheres * Use the sphere of the other object instead of the center * point. * @param maxSphere * The maximum sphere of all other objects (agents). * @param includeMySelf * If the returned objects shall include this object (agent). * @param type * If this type is not null only objects (agents) of the defined * type (or subclass) are returned. * @return The objects meeting the criteria. */ public IMovingAgent[] getObjectsWithinMyDistance(double radius, boolean useMySphere, boolean useTheirSpheres, double maxSphere, boolean includeMySelf, Class<?> type) { double distanceCorrection = 0; double correction = 0; if (useMySphere) { distanceCorrection += this.getSphereRadius(); correction = distanceCorrection; } if (useTheirSpheres) distanceCorrection += maxSphere; // get all objects which centers lies within a certain distance Object[] objects = simulation.getObjectsWithinDistance(this, radius + distanceCorrection); if (type != null) objects = Filter.filter(objects, type); IMovingAgent agent; int i, k; for (i = 0, k = 0; i < objects.length; i++) { agent = (IMovingAgent) objects[i]; // continue if the agent itself is included or the agent is not // myself if (includeMySelf || (agent != this)) { // get the distance from center to center double dist = this.distance(agent); // correct the distance according to the spheres dist -= correction; if (useTheirSpheres) dist -= agent.getSphereRadius(); if (dist < radius) { objects[k] = agent; k++; } } } IMovingAgent[] neighbours = new IMovingAgent[k]; System.arraycopy(objects, 0, neighbours, 0, k); return neighbours; } /** * Return the angle between this agent and the given one. This is the * absolute angle between both points represented by the agents. No * orientation is attended. * * @param agent * The other agent. * @return The angle in radians. */ public double angle(IMovingAgent agent) { return Geometric.angle(this.location, agent.getLocation()); } /** * Return the angle between this agent and the given location. This is the * absolute angle between both points represented by the agent and the * point. No orientation is attended. * * @param location * The location the angle will computed to. * @return The angle in radians. */ public double angle(Tuple3d location) { return Geometric.angle(this.location, location); } /** * Head to the direction of the given agent. * * @param agent * The agent this agent has to head to. */ public final void headTo(IMovingAgent agent) { headTo(agent.getLocation()); } /** * Head to the direction of the given point. * * @param p * The point this agent has to head to. */ public final void headTo(Tuple3d p) { double angle = Geometric.angle(this.location, p); turnTo(Math.toDegrees(angle)); } /** * Compute if the location of an agent is inside the sphere of this agent. * The outer sphere of the agent is not considered, only the center of the * agent. * * @param agent * The agent whose sphere will be tested. * @return True, if this agent is inside the other agent's sphere. */ public final boolean isInSphere(IMovingAgent agent) { return isInSphere(agent, false); } /** * Compute if the location of an agent if it is inside the sphere of this * agent. The consideration of the other agents sphere is optional. If the * sphere is used for computation false is returned if the sphere of the * agent is (partially) out of this sphere. * * @param agent * The agent whose sphere will be tested. * @param useSphere * If true the sphere of this agent will also used to decide if * this agent's sphere intersects with the other agent's sphere. * @return True, if this agent is inside the other agent's sphere. */ public final boolean isInSphere(IMovingAgent agent, boolean useSphere) { return (distanceToSphere(agent, true) <= this.getSphereRadius()); } /** * Compute the distance of the given agent to the outer sphere perimeter of * this agent. The sphere perimeter of the agent also can be used otherwise * the center. * * @param agent * The other agent. * @param useSphere * If true use the outer perimeter of the other agent. * @return The distance to the other agent's sphere. */ public final double distanceToSphere(IMovingAgent agent, boolean useSphere) { double d = distance(agent); if (useSphere) d += agent.getSphereRadius(); return d; } /** * Turn the agent by the given angle in degrees. * * @param angle The angle in degrees the agent will be turned. */ public final void turnBy(double angle) { angle = Geometric.clampAngleDegree(angle); orientation += angle; orientation = Geometric.clampAngleDegree(orientation); Geometric.rotateTo(velocity, Math.toRadians(orientation)); } /** * Turn the agent to the given angle given in degrees. * * @param angle The angle in degrees the agent will head to. */ public final void turnTo(double angle) { angle = Geometric.clampAngleDegree(angle); orientation = Geometric.clampAngleDegree(angle); Geometric.rotateTo(velocity, Math.toRadians(orientation)); } /** * Update the location of the agent using the simulation framework. */ final private void updateLocation() { simulation.updateLocation(this); } /** * Calculate the vector from this agent to the provided one. * * @param agent * The other the computed vector will head to. * @return The vector heading to the other agent. */ public final Vector3d vectorTo(IMovingAgent agent) { Vector3d v = new Vector3d(); v.sub(agent.getLocation(), location); return v; } }