/*
* File name: CarAgent.java (package eas.users.ocScenario.cars)
* Author(s): Lukas König
* Java version: 7.0
* Generation date: 09.05.2014 (14:54:18)
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.users.ocScenarios.traffic;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Vector2D;
import eas.miscellaneous.StaticMethods;
import eas.plugins.standard.liveInteraction.MethodRunnableProperties;
import eas.plugins.standard.visualization.AllroundVideoPlugin;
import eas.simulation.Wink;
import eas.simulation.agent.GenericSensor;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.startSetup.ParCollection;
import eas.users.ocScenarios.traffic.strategies.CollisionStrategy;
import eas.users.ocScenarios.traffic.strategies.DrivingStrategy;
/**
* Implementation of a car as an agent in the CarsEnvironment used in the OC
* scenario.
*
* @author Lukas König
*/
public class CarAgent extends AbstractAgent2D<TrafficEnvironment> {
/**
* All serializable classes must have this.
*/
private static final long serialVersionUID = -5355637887498848830L;
/**
* The driving strategy of this CarAgent, telling which way to turn at a
* specific time step.
*/
private DrivingStrategy drivingStrat;
/**
* The collision strategy of this CarAgent, telling at a specific time step
* if the agent can occupy fields in its neighborhood as well as what to do
* if a collision occurrs.
*/
private CollisionStrategy collStrat;
/**
* A list of trace points storing the agent's path.
*/
private LinkedList<Vector2D> trace = new LinkedList<>();
/**
* An image of the CarAgent.
*/
public static final transient BufferedImage CAR_IMG = StaticMethods.loadImage("./sharedDirectory/AgentImages/car.png");
/**
* Standard constructor.
*
* @param id An arbitrary id for the agent.
* @param env The environment of the agent.
* @param params The program parameters.
* @param drivingStrat The agent's driving strategy.
* @param collStrat The agent's collision strategy.
*/
public CarAgent(
int id,
TrafficEnvironment env,
ParCollection params,
DrivingStrategy drivingStrat,
CollisionStrategy collStrat) {
super(id, env, params);
this.drivingStrat = drivingStrat;
this.collStrat = collStrat;
/*
* The following "sensors" are visualized during the simulation to show
* some information about the street an agent is on. They are not used
* as actual sensors otherwise in this implementation.
*/
this.addSensor(new GenericSensor<Float, TrafficEnvironment, CarAgent>() {
private static final long serialVersionUID = 1554927077206871098L;
@Override
public Float sense(TrafficEnvironment env, CarAgent agent) {
return CarAgent.this.speedMax;
}
@Override
public String id() {
return "Speed";
}
});
this.addSensor(new GenericSensor<Boolean, TrafficEnvironment, CarAgent>() {
private static final long serialVersionUID = 1567658595447840894L;
@Override
public Boolean sense(TrafficEnvironment env, CarAgent agent) {
return agent.collStrat.canDriveForward(agent);
}
@Override
public String id() {
return "CanDrv";
}
});
this.addSensor(new GenericSensor<Boolean, TrafficEnvironment, CarAgent>() {
private static final long serialVersionUID = -437857495301670559L;
@Override
public Boolean sense(TrafficEnvironment env, CarAgent agent) {
return agent.isFacingInTrafficFlowDirection();
}
@Override
public String id() {
return "InFlow";
}
});
this.addSensor(new GenericSensor<String, TrafficEnvironment, CarAgent>() {
private static final long serialVersionUID = -6425480377355224417L;
@Override
public String sense(TrafficEnvironment env, CarAgent agent) {
Vector2D pos = agent.getAgentPosition();
int x = (int) pos.x;
int y = (int) pos.y;
String s = "";
if (env.isLeftLaneNtoS(x, y)) {s += "N";}
if (env.isRightLaneNtoS(x, y)) {s += "S";}
if (env.isRightLaneWtoE(x, y)) {s += "E";}
if (env.isLeftLaneWtoE(x, y)) {s += "W";}
return s;
}
@Override
public String id() {
return "Street";
}
});
this.randomizeTarget();
}
public void turnRight() {
this.setAgentAngle(this.getAgentAngle() + 90);
}
public void turnLeft() {
this.setAgentAngle(this.getAgentAngle() - 90);
}
/* **********************************************************************
* The following methods concern the visual appearance of the agent such as
* its "physical" body shape.
************************************************************************/
/**
* Generates an image of the internal state of an agent.
* The "MethodRunnableProperties(ignoreMethod=true)" annotation ensures
* that this method is not shown by the Observer/Controller plugin.
*/
@Override
@MethodRunnableProperties(ignoreMethod=true)
public BufferedImage getInsideView() {
BufferedImage img = super.getInsideView();
BufferedImage newImg = new BufferedImage(
Math.max(img.getWidth() + 100, CAR_IMG.getWidth() + 100),
img.getHeight() + CAR_IMG.getHeight() + 100, img.getType());
Graphics2D g = newImg.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, newImg.getWidth(), newImg.getHeight());
g.drawImage(img, 0, 0, null);
g.drawImage(CAR_IMG, (img.getWidth() - CAR_IMG.getWidth()) / 2, img.getHeight() + 30, null);
return newImg;
}
private Polygon2D pol;
private boolean resetRequested = true;
@Override
@MethodRunnableProperties(ignoreMethod=true)
public Color getAgentColor() {
if (this.isWaiting) {
return Color.orange;
}
return Color.blue;
}
public boolean isWaiting = false;
private void setAgentSize(double diameter) {
this.pol = new Polygon2D();
pol.add(new Vector2D(+0, +diameter - 0.2));
pol.add(new Vector2D(+diameter / 2, -diameter / 2));
pol.add(new Vector2D(-diameter / 2, -diameter / 2));
}
@MethodRunnableProperties(ignoreMethod=true)
@Override
public synchronized Polygon2D getAgentShape() {
if (pol == null) {
this.setAgentSize(0.5);
}
return pol;
}
/**
* The step method of the agent. This method is invoked any time a time
* step notification is sent by the simulation engine. The standard
* master scheduler implementation propagates every tick from
* the master scheduler to the environment step method and the agents' step
* method's. Using the "requestNotification..." methods of AbstractEnvironment
* other time steps can be requested.
*
* @param simTime The current time of the notification.
*/
@Override
public void step(Wink simTime) {
super.step(simTime);
if (this.targetLocation.distance(this.getAgentPosition()) < 2) {
this.randomizeTarget();
targetCount++;
}
if (this.resetRequested) {
this.targetCount = 0;
this.resetRequested = false;
this.drivingStrat.reset(this);
this.trace.clear();
for (int i = 0; i < this.getEnvironment().getNumOfStoredTracePoints(); i++) {
this.trace.add(new Vector2D(this.getAgentPosition()));
}
}
if (this.drivingStrat.drive(this, simTime)) {
this.isWaiting = false;
} else {
if (TrafficParameters.activateCollStrat || !this.collStrat.onCollision(this)) {
this.getEnvironment().setWaitingTime(this.getEnvironment().getNumOfWaitingCars() + 1);
this.isWaiting = true;
}
}
if (trace.size() > 0 && !trace.getLast().equals(this.getAgentPosition())) {
this.trace.removeFirst();
this.trace.add(this.getAgentPosition());
}
}
/* **********************************************************************
* The following methods provide some useful tools to inspect the agents
* state in the environment as well as to affect this state, for example,
* by randomizing the position of the target to find.
************************************************************************/
public float speedMax = 1f;
@MethodRunnableProperties(ignoreMethod=true)
public CollisionStrategy getCollStrat() {
return this.collStrat;
}
public void driveForward() {
this.setAgentPosition(new Vector2D(this.getAgentPosition()).add(this.getNormalizedLOV()));
}
public double distanceToTarget() {
return this.getAgentPosition().distance(getTargetLocation());
}
public boolean isMarked() {
AllroundVideoPlugin v = (AllroundVideoPlugin) this.getEnvironment().getSimTime().getPluginObject(new AllroundVideoPlugin().id());
return v.getMarkedAgentId() == this.id();
}
public float getSpeedD() {
if (this.isWaiting) {
return 0;
}
return this.speedMax;
}
private Vector2D targetLocation = new Vector2D(3, 4);
public void randomizeTarget() {
Vector2D v = new Vector2D(this.getEnvironment().RAND.nextInt(this.getEnvironment().getGridWidth()),
this.getEnvironment().RAND.nextInt(this.getEnvironment().getGridHeight()));
while (!this.getEnvironment().isOnStreet(v.x, v.y)) {
v = new Vector2D(this.getEnvironment().RAND.nextInt(this.getEnvironment().getGridWidth()),
this.getEnvironment().RAND.nextInt(this.getEnvironment().getGridHeight()));
}
this.targetLocation = v;
}
@MethodRunnableProperties(ignoreMethod=true)
public Vector2D getTargetLocation() {
return this.targetLocation;
}
/**
* @return Only if truly on the left side, not on the same lane!
*/
public boolean isTargetOnLeftSide() {
if (isTargetOnSameLane()) {
return false;
}
Vector2D dirToTarget = new Vector2D(this.targetLocation);
dirToTarget.sub(this.getAgentPosition());
return !this.getNormalizedLOV().drehrichtung(dirToTarget);
}
public boolean isTargetOnSameLane() {
int posX = (int) this.getAgentPosition().x;
int posY = (int) this.getAgentPosition().y;
int targetX = (int) this.targetLocation.x;
int targetY = (int) this.targetLocation.y;
if (posX == targetX || posY == targetY) {
return true;
} else {
return false;
}
}
/**
* @return Only if truly on the right side, not on the same lane!
*/
public boolean isTargetOnRightSide() {
if (isTargetOnSameLane()) {
return true;
}
Vector2D dirToTarget = new Vector2D(this.targetLocation);
dirToTarget.sub(this.getAgentPosition());
return this.getNormalizedLOV().drehrichtung(dirToTarget);
}
public boolean isTargetAhead() {
return new Vector2D(this.getAgentPosition()).add(this.getNormalizedLOV()).distance(this.targetLocation) <
this.getAgentPosition().distance(this.targetLocation);
}
public Integer isOnCrossing() {
int col = (int) this.getAgentPosition().x;
int row = (int) this.getAgentPosition().y;
Vector2D inFront = new Vector2D(col, row).add(this.getNormalizedLOV());
int inFrontCol = (int) inFront.x;
int inFrontRow = (int) inFront.y;
if (this.getEnvironment().isCrossing(inFrontCol, inFrontRow)
&& this.getEnvironment().isCrossing(col, row)) {
return 1;
}
if (!this.getEnvironment().isCrossing(inFrontCol, inFrontRow)
&& this.getEnvironment().isCrossing(col, row)) {
return 2;
}
return null;
}
public String facingInDirection() {
Vector2D vec = this.getNormalizedLOV();
if ((int) vec.x == 1) {
return "E";
}
if ((int) vec.y == 1) {
return "S";
}
if ((int) vec.x == -1) {
return "W";
}
if ((int) vec.y == -1) {
return "N";
}
return "FALSE";
}
public boolean isFacingInTrafficFlowDirection() {
String facing = this.facingInDirection();
int agentPosX = (int) this.getAgentPosition().x;
int agentPosY = (int) this.getAgentPosition().y;
return facing.equals("N") && this.getEnvironment().isLeftLaneNtoS(agentPosX, agentPosY)
|| facing.equals("E") && this.getEnvironment().isRightLaneWtoE(agentPosX, agentPosY)
|| facing.equals("S") && this.getEnvironment().isRightLaneNtoS(agentPosX, agentPosY)
|| facing.equals("W") && this.getEnvironment().isLeftLaneWtoE(agentPosX, agentPosY);
}
@MethodRunnableProperties(ignoreMethod=true)
public LinkedList<Vector2D> getTrace() {
return this.trace;
}
private int targetCount = 0;
public int getTargetCount() {
return this.targetCount;
}
public void setResetRequested() {
this.resetRequested = true;
}
}